SVN checkout 11/12/2010
[monav:monav.git] / routingdaemon / routingdaemon.h
1 /*
2 Copyright 2010  Christian Vetter veaac.fdirct@gmail.com
3
4 This file is part of MoNav.
5
6 MoNav is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 MoNav is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with MoNav.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #ifndef ROUTINDDAEMON_H
21 #define ROUTINDDAEMON_H
22
23 #include <QtCore>
24 #include <QSettings>
25 #include <QFile>
26 #include <QtDebug>
27 #include <QLocalServer>
28 #include <QLocalSocket>
29
30 #include "qtservice.h"
31
32 #include "interfaces/irouter.h"
33 #include "interfaces/igpslookup.h"
34 #include "signals.h"
35
36 class RoutingDaemon : public QObject, public QtService< QCoreApplication > {
37         Q_OBJECT
38 public:
39         RoutingDaemon( int argc, char** argv ) : QtService< QCoreApplication >( argc, argv, "MoNav Routing Daemon" )
40         {
41                  setServiceDescription( "The MoNav Routing Daemon" );
42                  m_loaded = false;
43                  m_gpsLookup = NULL,
44                  m_router = NULL;
45                  m_server = new QLocalServer( this );
46                  connect( m_server, SIGNAL(newConnection()), this, SLOT(newConnection()) );
47         }
48
49         virtual ~RoutingDaemon()
50         {
51                 unloadPlugins();
52         }
53
54 public slots:
55
56         void newConnection()
57         {
58                 QLocalSocket* connection = m_server->nextPendingConnection();
59                 connect( connection, SIGNAL(disconnected()), connection, SLOT(deleteLater()) );
60                 RoutingDaemonCommand command;
61                 RoutingDaemonResult result;
62
63                 if ( !command.read( connection ) )
64                         return;
65
66                 result.type = RoutingDaemonResult::Success;
67
68                 if ( !m_loaded || command.dataDirectory != m_dataDirectory ) {
69                         unloadPlugins();
70                         m_loaded = loadPlugins( command.dataDirectory );
71                         m_dataDirectory = command.dataDirectory;
72                 }
73
74                 if ( m_loaded ) {
75                         QVector< IRouter::Node > pathNodes;
76                         QVector< IRouter::Edge > pathEdges;
77                         double distance = 0;
78                         bool success = true;
79                         for ( int i = 1; i < command.waypoints.size(); i++ ) {
80                                 if ( i != 1 )
81                                         result.pathNodes.pop_back();
82                                 double segmentDistance;
83                                 GPSCoordinate source( command.waypoints[i - 1].latitude, command.waypoints[i - 1].longitude );
84                                 GPSCoordinate target( command.waypoints[i].latitude, command.waypoints[i].longitude );
85                                 pathNodes.clear();
86                                 pathEdges.clear();
87                                 if ( !computeRoute( &segmentDistance, &pathNodes, &pathEdges, source, target, command.lookupRadius ) ) {
88                                         success = false;
89                                         break;
90                                 }
91                                 distance += segmentDistance;
92
93                                 for ( int j = 0; j < pathNodes.size(); j++ ) {
94                                         RoutingDaemonNode node;
95                                         GPSCoordinate gps = pathNodes[j].coordinate.ToGPSCoordinate();
96                                         node.latitude = gps.latitude;
97                                         node.longitude = gps.longitude;
98                                         result.pathNodes.push_back( node );
99                                 }
100
101                                 for ( int j = 0; j < pathEdges.size(); j++ ) {
102                                         RoutingDaemonEdge edge;
103                                         edge.length = pathEdges[j].length;
104                                         edge.name = pathEdges[j].name;
105                                         edge.type = pathEdges[j].type;
106                                         edge.seconds = pathEdges[j].seconds;
107                                         edge.branchingPossible = pathEdges[j].branchingPossible;
108                                         result.pathEdges.push_back( edge );
109                                 }
110                         }
111                         result.seconds = distance;
112
113                         if ( success ) {
114                                 if ( command.lookupStrings ) {
115                                         unsigned lastNameID = std::numeric_limits< unsigned >::max();
116                                         QString lastName;
117                                         unsigned lastTypeID = std::numeric_limits< unsigned >::max();
118                                         QString lastType;
119                                         for ( int j = 0; j < result.pathEdges.size(); j++ ) {
120                                                 if ( lastNameID != result.pathEdges[j].name ) {
121                                                         lastNameID = result.pathEdges[j].name;
122                                                         if ( !m_router->GetName( &lastName, lastNameID ) )
123                                                                 result.type = RoutingDaemonResult::NameLookupFailed;
124                                                         result.nameStrings.push_back( lastName );
125                                                 }
126
127                                                 if ( lastTypeID != result.pathEdges[j].type ) {
128                                                         lastTypeID = result.pathEdges[j].type;
129                                                         if ( !m_router->GetType( &lastType, lastTypeID ) )
130                                                                 result.type = RoutingDaemonResult::TypeLookupFailed;
131                                                         result.typeStrings.push_back( lastType );
132                                                 }
133
134                                                 result.pathEdges[j].name = result.nameStrings.size() - 1;
135                                                 result.pathEdges[j].type = result.typeStrings.size() - 1;
136                                         }
137                                 }
138                         } else {
139                                 result.type = RoutingDaemonResult::RouteFailed;
140                         }
141                 } else {
142                         result.type = RoutingDaemonResult::LoadFailed;
143                 }
144
145                 if ( connection->state() != QLocalSocket::ConnectedState )
146                         return;
147
148                 result.post( connection );
149                 connection->flush();
150                 connection->disconnectFromServer();
151         }
152
153 protected:
154
155         virtual void start()
156         {
157                 if ( !m_server->listen( "MoNavD" ) ) {
158                         // try to clean up after possible crash
159                         m_server->removeServer( "MoNavD" );
160                         if ( !m_server->listen( "MoNavD" ) ) {
161                                 qCritical() << "unable to start server";
162                                 exit( -1 );
163                         }
164                 }
165         }
166
167         virtual void stop()
168         {
169                 m_server->close();
170         }
171
172         bool computeRoute( double* resultDistance, QVector< IRouter::Node >* resultNodes, QVector< IRouter::Edge >* resultEdge, GPSCoordinate source, GPSCoordinate target, double lookupRadius )
173         {
174                 if ( m_gpsLookup == NULL || m_router == NULL ) {
175                         qCritical() << "tried to query route before setting valid data directory";
176                         return false;
177                 }
178                 UnsignedCoordinate sourceCoordinate( source );
179                 UnsignedCoordinate targetCoordinate( target );
180                 IGPSLookup::Result sourcePosition;
181                 QTime time;
182                 time.start();
183                 bool found = m_gpsLookup->GetNearestEdge( &sourcePosition, sourceCoordinate, lookupRadius );
184                 qDebug() << "GPS Lookup:" << time.restart() << "ms";
185                 if ( !found ) {
186                         qDebug() << "no edge near source found";
187                         return false;
188                 }
189                 IGPSLookup::Result targetPosition;
190                 found = m_gpsLookup->GetNearestEdge( &targetPosition, targetCoordinate, lookupRadius );
191                 qDebug() << "GPS Lookup:" << time.restart() << "ms";
192                 if ( !found ) {
193                         qDebug() << "no edge near target found";
194                         return false;
195                 }
196                 found = m_router->GetRoute( resultDistance, resultNodes, resultEdge, sourcePosition, targetPosition );
197                 qDebug() << "Routing:" << time.restart() << "ms";
198                 return found;
199         }
200
201         bool loadPlugins( QString dataDirectory )
202         {
203                 QDir dir( dataDirectory );
204                 QString configFilename = dir.filePath( "MoNav.ini" );
205                 if ( !QFile::exists( configFilename ) ) {
206                         qCritical() << "Not a valid data directory: Missing MoNav.ini";
207                         return false;
208                 }
209                 QSettings pluginSettings( configFilename, QSettings::IniFormat );
210                 int iniVersion = pluginSettings.value( "configVersion" ).toInt();
211                 if ( iniVersion != 1 ) {
212                         qCritical() << "Config File not compatible";
213                         return false;
214                 }
215                 QString routerName = pluginSettings.value( "router" ).toString();
216                 QString gpsLookupName = pluginSettings.value( "gpsLookup" ).toString();
217
218                 foreach ( QObject *plugin, QPluginLoader::staticInstances() )
219                         testPlugin( plugin, routerName, gpsLookupName );
220
221                 try
222                 {
223                         if ( m_gpsLookup == NULL ) {
224                                 qCritical() << "GPSLookup plugin not found:" << gpsLookupName;
225                                 return false;
226                         }
227                         int gpsLookupFileFormat = pluginSettings.value( "gpsLookupFileFormatVersion" ).toInt();
228                         if ( !m_gpsLookup->IsCompatible( gpsLookupFileFormat ) ) {
229                                 qCritical() << "GPS Lookup file format not compatible";
230                                 return false;
231                         }
232                         m_gpsLookup->SetInputDirectory( dataDirectory );
233                         if ( !m_gpsLookup->LoadData() ) {
234                                 qCritical() << "could not load GPSLookup data";
235                                 return false;
236                         }
237
238                         if ( m_router == NULL ) {
239                                 qCritical() << "router plugin not found:" << routerName;
240                                 return false;
241                         }
242                         int routerFileFormat = pluginSettings.value( "routerFileFormatVersion" ).toInt();
243                         if ( !m_gpsLookup->IsCompatible( routerFileFormat ) ) {
244                                 qCritical() << "GPS Lookup file format not compatible";
245                                 return false;
246                         }
247                         m_router->SetInputDirectory( dataDirectory );
248                         if ( !m_router->LoadData() ) {
249                                 qCritical() << "could not load router data";
250                                 return false;
251                         }
252                 }
253                 catch ( ... )
254                 {
255                         qCritical() << "caught exception while loading plugins";
256                         return false;
257                 }
258
259                 qDebug() << "loaded:" << pluginSettings.value( "name" ).toString() << pluginSettings.value( "description" ).toString();
260
261                 return true;
262         }
263
264         void testPlugin( QObject* plugin, QString routerName, QString gpsLookupName )
265         {
266                 if ( IGPSLookup *interface = qobject_cast< IGPSLookup* >( plugin ) ) {
267                         qDebug() << "found plugin:" << interface->GetName();
268                         if ( interface->GetName() == gpsLookupName )
269                                 m_gpsLookup = interface;
270                 }
271                 if ( IRouter *interface = qobject_cast< IRouter* >( plugin ) ) {
272                         qDebug() << "found plugin:" << interface->GetName();
273                         if ( interface->GetName() == routerName )
274                                 m_router = interface;
275                 }
276         }
277
278         void unloadPlugins()
279         {
280                 m_router = NULL;
281                 m_gpsLookup = NULL;
282         }
283
284         bool m_loaded;
285         QString m_dataDirectory;
286         IGPSLookup* m_gpsLookup;
287         IRouter* m_router;
288         QLocalServer* m_server;
289
290 };
291
292 #endif // ROUTINDDAEMON_H