diff options
author | Guido Günther <agx@sigxcpu.org> | 2014-03-04 07:08:37 +0100 |
---|---|---|
committer | Guido Günther <agx@sigxcpu.org> | 2014-03-05 16:38:53 +0100 |
commit | 3219400a832d82dcf826c92d22a3eb42d4d91098 (patch) | |
tree | 7437b92c2f2373e25a0c1c20bd907fa30c52ec59 /src | |
parent | 77c5360d6de8e48b7b64bcbd74e64c02c9b75366 (diff) |
Move hafas code from LpfDeDbProvider to LpfHafasBin6Provider
Diffstat (limited to 'src')
-rw-r--r-- | src/providers/de-db.c | 712 | ||||
-rw-r--r-- | src/providers/de-db.h | 6 | ||||
-rw-r--r-- | src/providers/hafas-bin6.c | 708 | ||||
-rw-r--r-- | src/providers/hafas-bin6.h | 7 | ||||
-rw-r--r-- | src/providers/tests/Makefile.am | 18 | ||||
-rw-r--r-- | src/providers/tests/de-db.c | 137 | ||||
-rw-r--r-- | src/providers/tests/hafas-bin6-format.c | 156 | ||||
-rw-r--r-- | src/providers/tests/hafas-bin6.c | 209 |
8 files changed, 1000 insertions, 953 deletions
diff --git a/src/providers/de-db.c b/src/providers/de-db.c index 207a8c3..76ceadd 100644 --- a/src/providers/de-db.c +++ b/src/providers/de-db.c @@ -23,20 +23,10 @@ n */ #include <config.h> #include <gmodule.h> -#include <libsoup/soup.h> -#include <string.h> -#include <libxml/parser.h> -#include <libxml/xpath.h> - -#include "lpf-priv.h" -#include "lpf-loc.h" -#include "lpf-provider.h" -#include "lpf-trip.h" -#include "lpf-trip-part.h" -#include "lpf-stop.h" #include "de-db.h" #include "hafas-bin6.h" +#include "lpf-provider.h" #define LOC_URL "http://mobile.bahn.de/bin/mobil/query.exe/en" #define TRIPS_URL "http://reiseauskunft.bahn.de/bin/query.exe/eox" @@ -49,13 +39,6 @@ enum { LAST_PROP }; -/* transfers data between invocation and the passed in callback */ -typedef struct _LpfProviderGotItUserData { - LpfProvider *self; - gpointer callback; - gpointer user_data; -} LpfProviderGotItUserData; - static void lpf_provider_de_db_interface_init (LpfProviderInterface *iface); G_DEFINE_TYPE_WITH_CODE (LpfProviderDeDb, lpf_provider_de_db, LPF_TYPE_PROVIDER_HAFAS_BIN6, @@ -67,19 +50,9 @@ G_DEFINE_TYPE_WITH_CODE (LpfProviderDeDb, lpf_provider_de_db, LPF_TYPE_PROVIDER_ int lpf_provider_major_version = LPF_PROVIDER_MAJOR_VERSION; int lpf_provider_minor_version = LPF_PROVIDER_MINOR_VERSION; -G_MODULE_EXPORT LpfProvider * -lpf_provider_create (void) -{ - return LPF_PROVIDER (lpf_provider_de_db_new ()); -} - typedef struct _LpfProviderDeDbPrivate LpfProviderDeDbPrivate; - struct _LpfProviderDeDbPrivate { gchar *name; - SoupSession *session; - char *logdir; - gboolean debug; }; @@ -92,6 +65,27 @@ lpf_provider_de_db_get_name (LpfProvider *self) } +static const char* +lpf_provider_de_db_locs_url(LpfProviderHafasBin6 *self) +{ + return LOC_URL; +} + + +static const char* +lpf_provider_de_db_trips_url(LpfProviderHafasBin6 *self) +{ + return TRIPS_URL; +} + + +G_MODULE_EXPORT LpfProvider * +lpf_provider_create (void) +{ + return LPF_PROVIDER (lpf_provider_de_db_new ()); +} + + static void set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) @@ -136,663 +130,12 @@ lpf_provider_de_db_finalize (GObject *self) G_OBJECT_CLASS (lpf_provider_de_db_parent_class)->finalize (self); } -static void -lpf_provider_de_db_activate (LpfProvider *self, GObject *obj) -{ - LpfProviderDeDbPrivate *priv = GET_PRIVATE(self); - GFile *dir; - gchar *debugstr; - - priv->session = soup_session_new(); - priv->logdir = g_build_path(G_DIR_SEPARATOR_S, - g_get_user_cache_dir(), - PACKAGE, - PROVIDER_NAME, - NULL); - - debugstr = getenv ("LPF_DEBUG"); - if (debugstr && strstr (debugstr, "provider")) - priv->debug = TRUE; - - dir = g_file_new_for_path (priv->logdir); - g_file_make_directory_with_parents (dir, NULL, NULL); - g_object_unref (dir); -} - -static void -lpf_provider_de_db_deactivate (LpfProvider *self, GObject *obj) -{ - LpfProviderDeDbPrivate *priv = GET_PRIVATE(self); - - if (priv->session) - g_object_unref (priv->session); - - g_free (priv->logdir); - - xmlCleanupParser(); -} - - -static GSList* -parse_stations_xml (const char *xml) -{ - gint i = 0; - xmlDocPtr doc = NULL; - xmlChar *xpath = (xmlChar*) "//MLc[@t=\"ST\"]"; - xmlXPathContextPtr context = NULL; - xmlXPathObjectPtr result = NULL; - xmlNodeSetPtr nodeset = NULL; - gchar *name, *x, *y; - gdouble lo, la; - LpfLoc *loc; - char *id; - GSList *locs = NULL; - - g_return_val_if_fail (xml, NULL); - - LPF_DEBUG("%s", xml); - doc = xmlParseMemory(xml, strlen(xml)); - if (doc == NULL ) { - g_warning("Document not parsed successfully"); - return NULL; - } - - context = xmlXPathNewContext(doc); - if (context == NULL) { - g_warning("Error in xmlXPathNewContext\n"); - goto out; - } - - result = xmlXPathEvalExpression(xpath, context); - if (result == NULL) { - g_warning("Error in xmlXPathEvalExpression\n"); - goto out; - } - - if(xmlXPathNodeSetIsEmpty(result->nodesetval)){ - g_warning("No result\n"); - goto out; - } - - nodeset = result->nodesetval; - for (i=0; i < nodeset->nodeNr; i++) { - name = (gchar*) xmlGetProp(nodeset->nodeTab[i], BAD_CAST "n"); - x = (gchar*) xmlGetProp(nodeset->nodeTab[i], BAD_CAST "x"); - y = (gchar*) xmlGetProp(nodeset->nodeTab[i], BAD_CAST "y"); - id = (gchar*) xmlGetProp(nodeset->nodeTab[i], BAD_CAST "i"); - - lo = (gdouble) g_ascii_strtod(x, NULL) / 1000000.0; - la = (gdouble) g_ascii_strtod(y, NULL) / 1000000.0; - - LPF_DEBUG ("%s (%lf, %lf) - %s", name, lo, la, id); - - loc = (LpfLoc*) g_object_new (LPF_TYPE_LOC, "name", name, "long", lo, "lat", la, NULL); - lpf_loc_set_opaque (loc, id); - locs = g_slist_append(locs, loc); - g_free(x); - g_free(y); - } - out: - xmlXPathFreeContext(context); - xmlXPathFreeObject(result); - xmlFreeDoc(doc); - return locs; -} - - -static void -log_response_body(LpfProviderDeDb *self, SoupMessage *msg, const char* type) -{ - LpfProviderDeDbPrivate *priv = GET_PRIVATE(self); - gchar *querylog = NULL; - gchar *filename = NULL; - gchar *time = NULL; - GDateTime *now; - - if (G_LIKELY(!priv->debug)) - return; - - now = g_date_time_new_now_local(); - - time = g_date_time_format (now, "%F_%T"); - filename = g_strdup_printf ("%s-%s.body", type, time); - - querylog = g_build_path (G_DIR_SEPARATOR_S, - priv->logdir, - filename, - NULL); - - if (!g_file_set_contents (querylog, msg->response_body->data, msg->response_body->length, NULL)) - g_warning ("Failed tpo write out query contents to %s", querylog); - - g_free (querylog); - g_free (filename); - g_free (time); - g_date_time_unref(now); -} - - -static void -got_stations (SoupSession *session, SoupMessage *msg, gpointer user_data) -{ - GSList *locs = NULL; - LpfProviderGotItUserData *locs_data = (LpfProviderGotItUserData*)user_data; - LpfProviderGotLocsNotify callback; - LpfProviderDeDb *self; - gpointer data; - GError *err = NULL; - - g_return_if_fail(session); - g_return_if_fail(msg); - g_return_if_fail(user_data); - - callback = locs_data->callback; - data = locs_data->user_data; - self = (LpfProviderDeDb*)locs_data->self; - g_free (locs_data); - - LPF_DEBUG("Status: %d", msg->status_code); - if (!SOUP_STATUS_IS_SUCCESSFUL(msg->status_code)) { - LPF_DEBUG("HTTP request failed"); - g_set_error (&err, - LPF_PROVIDER_ERROR, - LPF_PROVIDER_ERROR_REQUEST_FAILED, - "HTTP request failed: %d", msg->status_code); - goto out; - } - - log_response_body (self, msg, "station"); - - if ((locs = parse_stations_xml(msg->response_body->data)) == NULL) { - g_set_error (&err, - LPF_PROVIDER_ERROR, - LPF_PROVIDER_ERROR_PARSE_FAILED, - "Failed to parse locations"); - goto out; - } - -out: - (*callback)(locs, data, err); -} - - -static gint -lpf_provider_de_db_get_locs (LpfProvider *self, const char* match, LpfProviderGotLocsNotify callback, gpointer user_data) -{ - LpfProviderDeDbPrivate *priv = GET_PRIVATE(self); - SoupMessage *msg; - char *xml; - LpfProviderGotItUserData *locs_data = NULL; - gint ret = -1; - - g_return_val_if_fail (priv->session, -1); - - locs_data = g_malloc(sizeof(LpfProviderDeDbPrivate)); - if (!locs_data) - goto out; - xml = g_strdup_printf ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" - "<ReqC ver=\"1.1\" prod=\"String\" lang=\"EN\">" - "<MLcReq><MLc n=\"%s\" t=\"ST\"/>" - "</MLcReq></ReqC>", match); - - msg = soup_message_new ("POST", LOC_URL); - if (!msg) - goto out; - soup_message_set_request (msg, "text/xml", SOUP_MEMORY_TAKE, xml, strlen (xml)); - - locs_data->user_data = user_data; - locs_data->callback = callback; - locs_data->self = self; - - soup_session_queue_message (priv->session, msg, got_stations, locs_data); - ret = 0; - out: - if (ret < 0) - g_free (locs_data); - return ret; -} - - -static GSList* -hafas_binary_parse_each_trip (const gchar *data, gsize num, guint base, const char *enc) -{ - gint i, j, k; - const HafasBin6Trip *t; - const HafasBin6TripPartDetail *pd; - const HafasBin6TripPart *p; - const HafasBin6TripStop *stop; - guint16 day_off; - -#ifdef ENABLE_DEBUG - const HafasBin6TripDetail *d; -#endif - guint h, m; - LpfTrip *trip = NULL; - LpfTripPart *part = NULL; - LpfStop *start = NULL, *end = NULL, *astop = NULL; - GDateTime *dt; - GSList *trips = NULL, *parts = NULL, *stops = NULL; - const char *line; - - for (i = 0; i < num; i++) { - /* The trips itself */ - t = HAFAS_BIN6_TRIP(data, i); - day_off = lpf_provider_hafas_bin6_parse_service_day(data, i); - - LPF_DEBUG("Trip #%d, Changes: %4d", i, t->changes); -#ifdef ENABLE_DEBUG - /* trip details */ - d = HAFAS_BIN6_TRIP_DETAIL(data, i); - LPF_DEBUG("Trip #%d, Status: %4d", i, d->rt_status); - LPF_DEBUG("Trip #%d, Delay: %4d", i, d->delay); -#endif - /* trip parts */ - for (j = 0; j < t->part_cnt; j++) { - p = HAFAS_BIN6_TRIP_PART(data, i, j); - start = g_object_new(LPF_TYPE_STOP, NULL); - if (lpf_provider_hafas_bin6_parse_station(data, p->dep_off, LPF_LOC(start), enc) < 0) { - g_warning("Failed to parse start station %d/%d", i, j); - goto error; - } - end = g_object_new(LPF_TYPE_STOP, NULL); - if (lpf_provider_hafas_bin6_parse_station(data, p->arr_off, LPF_LOC(end), enc) < 0) { - g_warning("Failed to parse end station %d/%d", i, j); - goto error; - } - line = HAFAS_BIN6_STR(data, p->line_off); - - h = p->dep / 100; - m = p->dep % 100; - dt = lpf_provider_hafas_bin6_date_time (base, day_off, h, m); - g_object_set (start, "departure", dt, NULL); - - if (g_strcmp0 (HAFAS_BIN6_NO_PLATFORM, HAFAS_BIN6_STR(data, p->dep_pos_off))) { - g_object_set (start, - "dep_plat", HAFAS_BIN6_STR(data, p->dep_pos_off), - NULL); - } - - h = p->arr / 100; - m = p->arr % 100; - dt = lpf_provider_hafas_bin6_date_time (base, day_off, h, m); - g_object_set (end, "arrival", dt, NULL); - - if (g_strcmp0 (HAFAS_BIN6_NO_PLATFORM, HAFAS_BIN6_STR(data, p->arr_pos_off))) { - g_object_set (end, - "arr_plat", HAFAS_BIN6_STR(data, p->arr_pos_off), - NULL); - } - - /* trip part details */ - pd = HAFAS_BIN6_TRIP_PART_DETAIL(data, i, j); - - if (pd->arr_pred != HAFAS_BIN6_NO_REALTIME) { - h = pd->arr_pred / 100; - m = pd->arr_pred % 100; - dt = lpf_provider_hafas_bin6_date_time (base, day_off, h, m); - g_object_set (start, "rt_arrival", dt, NULL); - } - - if (pd->dep_pred != HAFAS_BIN6_NO_REALTIME) { - h = pd->dep_pred / 100; - m = pd->dep_pred % 100; - dt = lpf_provider_hafas_bin6_date_time (base, day_off, h, m); - g_object_set (end, "rt_departure", dt, NULL); - } - - LPF_DEBUG("Trip #%d, part #%d, Pred. Dep Plat: %s, Pred. Arr Plat: %s", i, j, - HAFAS_BIN6_STR(data, pd->dep_pos_pred_off), - HAFAS_BIN6_STR(data, pd->arr_pos_pred_off)); - - for (k = 0; k < pd->stops_cnt; k++) { - stop = HAFAS_BIN6_STOP(data, i, j, k); - - astop = g_object_new (LPF_TYPE_STOP, - "arrival", lpf_provider_hafas_bin6_date_time (base, - day_off, - stop->arr / 100, - stop->arr % 100), - "departure", lpf_provider_hafas_bin6_date_time (base, - day_off, - stop->dep / 100, - stop->dep % 100), - NULL - ); - - if (lpf_provider_hafas_bin6_parse_station(data, stop->stop_idx, LPF_LOC(astop), enc) < 0) { - g_warning("Failed to parse stop %d/%d", i, j); - goto error; - } - -#ifdef ENABLE_DEBUG - /* FIXME: add these and predicted dep/arr/plat to LpfStop too */ - LPF_DEBUG("Trip #%d, part #%d," - "Dep: %.6s Arr: %.6s", i, j, - HAFAS_BIN6_STR(data, stop->dep_pos_off), - HAFAS_BIN6_STR(data, stop->arr_pos_off)); -#endif - stops = g_slist_append (stops, astop); - astop = NULL; - } - part = g_object_new (LPF_TYPE_TRIP_PART, - "start", start, - "end", end, - "line", line, - "stops", stops, - NULL); - start = end = NULL; - line = NULL; - stops = NULL; - - parts = g_slist_append (parts, part); - part = NULL; - } - trip = g_object_new (LPF_TYPE_TRIP, - "parts", parts, - NULL); - parts = NULL; - trips = g_slist_append (trips, trip); - trip = NULL; - } - - return trips; - -error: - if (trips) - g_slist_free_full (trips, g_object_unref); - if (parts) - g_slist_free_full (parts, g_object_unref); - if (stops) - g_slist_free_full (stops, g_object_unref); - if (start) - g_object_unref (start); - if (trip) - g_object_unref (trip); - if (part) - g_object_unref (part); - if (astop) - g_object_unref (astop); - - return NULL; -} - - -static GSList* -hafas_binary_parse_trips (const char *data, gsize length) -{ - HafasBin6Header *header; -#ifdef ENABLE_DEBUG - HafasBin6Loc *start, *end; -#endif - HafasBin6ExtHeader *ext; - HafasBin6TripDetailsHeader *details; - GSList *trips = NULL; - guint16 version; - const gchar *encoding; - - g_return_val_if_fail (data, NULL); - g_return_val_if_fail (length, NULL); - - version = *(guint16*)data; - g_return_val_if_fail(version == 6, NULL); - - g_return_val_if_fail(sizeof (HafasBin6Header) < length, NULL); - header = (HafasBin6Header*) data; -#ifdef ENABLE_DEBUG - start = (HafasBin6Loc*)&header->start; - end = (HafasBin6Loc*)&header->end; -#endif - LPF_DEBUG("%d Trips from '%s' to '%s'", - header->num_trips, - HAFAS_BIN6_STR(data, start->name_off), - HAFAS_BIN6_STR(data, end->name_off)); - - g_return_val_if_fail ((sizeof (HafasBin6Header) + - sizeof(HafasBin6Header) * header->num_trips) < length, - NULL); - - ext = HAFAS_BIN6_EXT_HEADER(data); - g_return_val_if_fail (header->ext + - sizeof(HafasBin6ExtHeader) < length, NULL); - - /* We might not have attrs_index0 */ - g_return_val_if_fail(sizeof (HafasBin6ExtHeader) - 1 < ext->length, NULL); - - LPF_DEBUG("Ext length: 0x%.4x", ext->length); - LPF_DEBUG("Errors: 0x%.4x", ext->err); - LPF_DEBUG("Sequence: 0x%.4x", ext->seq); - LPF_DEBUG("Detail table: 0x%.4x", ext->details_tbl); - encoding = HAFAS_BIN6_STR(data, ext->enc_off); - LPF_DEBUG("Encoding: %s", encoding); - LPF_DEBUG("Request Id: %s", HAFAS_BIN6_STR(data, ext->req_id_off)); - - if (ext->err) { - g_warning ("Error %d", ext->err); - goto out; - } - - if (ext->seq <= 0) { - g_warning("Illegal sequence number %d", ext->seq); - goto out; - } - - g_return_val_if_fail (ext->details_tbl + - sizeof (HafasBin6TripDetailsHeader) < length, NULL); - details = HAFAS_BIN6_TRIP_DETAILS_HEADER(data); - LPF_DEBUG("Trip detail version: %d", details->version); - g_return_val_if_fail (details->version == 1, NULL); - g_return_val_if_fail (details->stop_size == sizeof(HafasBin6TripStop), NULL); - g_return_val_if_fail (details->part_detail_size == sizeof(HafasBin6TripPartDetail), NULL); - - trips = hafas_binary_parse_each_trip (data, header->num_trips, header->days, encoding); - g_return_val_if_fail (trips, NULL); - out: - return trips; -} - -static gint -decompress (const gchar *in, gsize inlen, - gchar **out, gsize *outlen, - GError **err) { - gint ret = -1; - gsize read, written; - GConverter *decomp = NULL; - GConverterResult conv; - gsize outbuflen, buflen, outpos = 0, inpos = 0; - gchar *outbuf = NULL; - - g_return_val_if_fail (inlen > 0, ret); - g_return_val_if_fail (in, ret); - g_return_val_if_fail (err, ret); - - decomp = (GConverter *)g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP); - - outbuflen = buflen = inlen * 2; - outbuf = g_try_malloc (outbuflen); - if (outbuf == NULL) - goto out; - - do { - conv = g_converter_convert (decomp, - (in + inpos), inlen, - (outbuf + outpos), buflen, - G_CONVERTER_INPUT_AT_END, - &read, &written, - err); - - switch (conv) { - case G_CONVERTER_ERROR: - goto out; - case G_CONVERTER_FINISHED: - outpos += written; - ret = 0; - goto out; - case G_CONVERTER_CONVERTED: - outpos += written; - inpos += read; - inlen -= read; - if (outbuflen - written < buflen) { - outbuflen += buflen; - outbuf = g_try_realloc (outbuf, outbuflen); - if (outbuf == NULL) - goto out; - } - break; - case G_CONVERTER_FLUSHED: - default: - g_warning ("Unhandled condition %d", conv); - goto out; - } - } while (TRUE); - -out: - if (ret == 0) { - *out = outbuf; - *outlen = outpos; - } else - g_free (outbuf); - - g_object_unref (decomp); - return ret; -} - -static void -got_trips (SoupSession *session, SoupMessage *msg, gpointer user_data) -{ - GSList *trips = NULL; - LpfProviderGotItUserData *trips_data = (LpfProviderGotItUserData*)user_data; - LpfProviderGotTripsNotify callback; - LpfProviderDeDb *self; - gpointer data; - gchar *decomp = NULL; - gsize len; - GError *err = NULL; - - g_return_if_fail(session); - g_return_if_fail(msg); - g_return_if_fail(user_data); - - g_object_ref (msg); - - callback = trips_data->callback; - data = trips_data->user_data; - self = (LpfProviderDeDb*)trips_data->self; - g_free (trips_data); - - LPF_DEBUG("Status: %d", msg->status_code); - if (!SOUP_STATUS_IS_SUCCESSFUL(msg->status_code)) { - g_set_error (&err, - LPF_PROVIDER_ERROR, - LPF_PROVIDER_ERROR_REQUEST_FAILED, - "HTTP request failed: %d", msg->status_code); - goto out; - } - - log_response_body (self, msg, "trip"); - - if (decompress(msg->response_body->data, msg->response_body->length, &decomp, &len, &err) < 0) { - goto out; - } - - LPF_DEBUG("Decompressed to %" G_GSIZE_FORMAT " bytes", len); - if ((trips = hafas_binary_parse_trips(decomp, len)) == NULL) { - g_set_error (&err, - LPF_PROVIDER_ERROR, - LPF_PROVIDER_ERROR_PARSE_FAILED, - "Failed to parse trips"); - goto out; - } - -out: - g_object_unref (msg); - g_free (decomp); - - (*callback)(trips, data, err); -} - - -static gint -lpf_provider_de_db_get_trips (LpfProvider *self, - LpfLoc *start, - LpfLoc *end, - GDateTime *date, - guint64 flags, - LpfProviderGotTripsNotify callback, - gpointer user_data) -{ - LpfProviderDeDbPrivate *priv = GET_PRIVATE(self); - SoupMessage *msg; - SoupURI *uri = NULL; - LpfProviderGotItUserData *trips_data = NULL; - gint ret = -1; - gchar *datestr = NULL, *timestr = NULL; - /* allowed vehicle types */ - const gchar *train_restriction = "11111111111111"; - /* whether time is arrival or departure time */ - const gchar *by_departure = "1"; - char *start_id = NULL, *end_id = NULL; - - g_return_val_if_fail (start, -1); - g_return_val_if_fail (end, -1); - g_return_val_if_fail (callback, -1); - g_return_val_if_fail (priv->session, -1); - g_return_val_if_fail (date, -1); - - g_object_ref (start); - g_object_ref (end); - - trips_data = g_try_malloc(sizeof(LpfProviderDeDbPrivate)); - if (!trips_data) - goto out; - - datestr = g_date_time_format (date, "%d.%m.%y"); - timestr = g_date_time_format (date, "%H:%M"); - - start_id = lpf_loc_get_opaque(start); - end_id = lpf_loc_get_opaque(end); - if (start_id == NULL || end_id == NULL) { - g_warning ("Details missing."); - goto out; - } - - uri = soup_uri_new (TRIPS_URL); - soup_uri_set_query_from_fields (uri, - "start", "Suchen", - "REQ0JourneyStopsS0ID", start_id, - "REQ0JourneyStopsZ0ID", end_id, - "REQ0JourneyDate", datestr, - "REQ0JourneyTime", timestr, - "REQ0HafasSearchForw", by_departure, - "REQ0JourneyProduct_prod_list_1", train_restriction, - "h2g-direct", "11", NULL); - - LPF_DEBUG ("URI: %s", soup_uri_to_string (uri, FALSE)); - - msg = soup_message_new_from_uri ("GET", uri); - if (!msg) - goto out; - - trips_data->user_data = user_data; - trips_data->callback = callback; - trips_data->self = self; - - soup_session_queue_message (priv->session, msg, got_trips, trips_data); - ret = 0; - out: - g_free (datestr); - g_free (timestr); - g_object_unref (start); - g_object_unref (end); - if (ret < 0) - g_free (trips_data); - return ret; -} - static void lpf_provider_de_db_class_init (LpfProviderDeDbClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); + LpfProviderHafasBin6Class *hafas_class = LPF_PROVIDER_HAFAS_BIN6_CLASS (klass); g_type_class_add_private (klass, sizeof (LpfProviderDeDbPrivate)); @@ -800,19 +143,20 @@ lpf_provider_de_db_class_init (LpfProviderDeDbClass *klass) object_class->set_property = set_property; object_class->finalize = lpf_provider_de_db_finalize; + /* LpfProvider */ g_object_class_override_property (object_class, PROP_NAME, "name"); + + /* LpfProviderHafasBin6 */ + hafas_class->locs_url = lpf_provider_de_db_locs_url; + hafas_class->trips_url = lpf_provider_de_db_trips_url; } static void lpf_provider_de_db_interface_init (LpfProviderInterface *iface) { iface->get_name = lpf_provider_de_db_get_name; - iface->activate = lpf_provider_de_db_activate; - iface->deactivate = lpf_provider_de_db_deactivate; - iface->get_locs = lpf_provider_de_db_get_locs; - iface->get_trips = lpf_provider_de_db_get_trips; } static void diff --git a/src/providers/de-db.h b/src/providers/de-db.h index e9cc4c9..a011d7f 100644 --- a/src/providers/de-db.h +++ b/src/providers/de-db.h @@ -24,15 +24,15 @@ G_BEGIN_DECLS #define LPF_TYPE_PROVIDER_DE_DB lpf_provider_de_db_get_type() #define LPF_PROVIDER_DE_DB(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST ((obj), LPF_TYPE_PROVIDER_DE_DB, LpfProviderDe_Db)) + (G_TYPE_CHECK_INSTANCE_CAST ((obj), LPF_TYPE_PROVIDER_DE_DB, LpfProviderDeDb)) #define LPF_PROVIDER_DE_DB_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST ((klass), LPF_TYPE_PROVIDER_DE_DB, LpfProviderDe_DbClass)) + (G_TYPE_CHECK_CLASS_CAST ((klass), LPF_TYPE_PROVIDER_DE_DB, LpfProviderDeDbClass)) #define LPF_IS_PROVIDER_DE_DB(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), LPF_TYPE_PROVIDER_DE_DB)) #define LPF_IS_PROVIDER_DE_DB_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), LPF_TYPE_PROVIDER_DE_DB)) #define LPF_PROVIDER_DE_DB_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), LPF_TYPE_PROVIDER_DE_DB, LpfProviderDe_DbClass)) + (G_TYPE_INSTANCE_GET_CLASS ((obj), LPF_TYPE_PROVIDER_DE_DB, LpfProviderDeDbClass)) typedef struct { LpfProviderHafasBin6 parent; diff --git a/src/providers/hafas-bin6.c b/src/providers/hafas-bin6.c index a6d8655..c0be135 100644 --- a/src/providers/hafas-bin6.c +++ b/src/providers/hafas-bin6.c @@ -21,13 +21,20 @@ */ #include <config.h> -#include <libsoup/soup.h> +#include <string.h> +#include <libxml/parser.h> +#include <libxml/xpath.h> + +#include <libsoup/soup.h> #include "hafas-bin6.h" #include "lpf-loc.h" #include "lpf-priv.h" #include "lpf-provider.h" +#include "lpf-trip.h" +#include "lpf-trip-part.h" +#include "lpf-stop.h" static void lpf_provider_hafas_bin6_interface_init (LpfProviderInterface *iface); @@ -42,6 +49,13 @@ enum { LAST_PROP }; +/* transfers data between invocation and the passed in callback */ +typedef struct _LpfProviderGotItUserData { + LpfProvider *self; + gpointer callback; + gpointer user_data; +} LpfProviderGotItUserData; + #define GET_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), LPF_TYPE_PROVIDER_HAFAS_BIN6, LpfProviderHafasBin6Private)) @@ -50,8 +64,659 @@ typedef struct _LpfProviderHafasBin6Private LpfProviderHafasBin6Private; struct _LpfProviderHafasBin6Private { gchar *name; + SoupSession *session; + char *logdir; + gboolean debug; }; + +const gchar* +lpf_provider_hafas_bin6_locs_url(LpfProviderHafasBin6 *self) +{ + g_return_val_if_fail (LPF_IS_PROVIDER_HAFAS_BIN6 (self), NULL); + + if (LPF_PROVIDER_HAFAS_BIN6_GET_CLASS (self)->locs_url != NULL) + return LPF_PROVIDER_HAFAS_BIN6_GET_CLASS (self)->locs_url(self); + else + g_warning ("Class '%s' does not override the mandatory " + ".locs_url() virtual function.", + G_OBJECT_TYPE_NAME (self)); + return NULL; +} + + +const gchar* +lpf_provider_hafas_bin6_trips_url(LpfProviderHafasBin6 *self) +{ + g_return_val_if_fail (LPF_IS_PROVIDER_HAFAS_BIN6 (self), NULL); + + if (LPF_PROVIDER_HAFAS_BIN6_GET_CLASS (self)->trips_url != NULL) + return LPF_PROVIDER_HAFAS_BIN6_GET_CLASS (self)->trips_url(self); + else + g_warning ("Class '%s' does not override the mandatory " + ".trips_url() virtual function.", + G_OBJECT_TYPE_NAME (self)); + return NULL; +} + + +static GSList* +parse_locs_xml (const char *xml) +{ + gint i = 0; + xmlDocPtr doc = NULL; + xmlChar *xpath = (xmlChar*) "//MLc[@t=\"ST\"]"; + xmlXPathContextPtr context = NULL; + xmlXPathObjectPtr result = NULL; + xmlNodeSetPtr nodeset = NULL; + gchar *name, *x, *y; + gdouble lo, la; + LpfLoc *loc; + char *id; + GSList *locs = NULL; + + g_return_val_if_fail (xml, NULL); + + LPF_DEBUG("%s", xml); + doc = xmlParseMemory(xml, strlen(xml)); + if (doc == NULL ) { + g_warning("Document not parsed successfully"); + return NULL; + } + + context = xmlXPathNewContext(doc); + if (context == NULL) { + g_warning("Error in xmlXPathNewContext\n"); + goto out; + } + + result = xmlXPathEvalExpression(xpath, context); + if (result == NULL) { + g_warning("Error in xmlXPathEvalExpression\n"); + goto out; + } + + if(xmlXPathNodeSetIsEmpty(result->nodesetval)){ + g_warning("No result\n"); + goto out; + } + + nodeset = result->nodesetval; + for (i=0; i < nodeset->nodeNr; i++) { + name = (gchar*) xmlGetProp(nodeset->nodeTab[i], BAD_CAST "n"); + x = (gchar*) xmlGetProp(nodeset->nodeTab[i], BAD_CAST "x"); + y = (gchar*) xmlGetProp(nodeset->nodeTab[i], BAD_CAST "y"); + id = (gchar*) xmlGetProp(nodeset->nodeTab[i], BAD_CAST "i"); + + lo = (gdouble) g_ascii_strtod(x, NULL) / 1000000.0; + la = (gdouble) g_ascii_strtod(y, NULL) / 1000000.0; + + LPF_DEBUG ("%s (%lf, %lf) - %s", name, lo, la, id); + + loc = (LpfLoc*) g_object_new (LPF_TYPE_LOC, "name", name, "long", lo, "lat", la, NULL); + lpf_loc_set_opaque (loc, id); + locs = g_slist_append(locs, loc); + g_free(x); + g_free(y); + } + out: + xmlXPathFreeContext(context); + xmlXPathFreeObject(result); + xmlFreeDoc(doc); + return locs; +} + + +static void +log_response_body(LpfProviderHafasBin6 *self, SoupMessage *msg, const char* type) +{ + LpfProviderHafasBin6Private *priv = GET_PRIVATE(self); + gchar *querylog = NULL; + gchar *filename = NULL; + gchar *time = NULL; + GDateTime *now; + + if (G_LIKELY(!priv->debug)) + return; + + now = g_date_time_new_now_local(); + + time = g_date_time_format (now, "%F_%T"); + filename = g_strdup_printf ("%s-%s.body", type, time); + + querylog = g_build_path (G_DIR_SEPARATOR_S, + priv->logdir, + filename, + NULL); + + if (!g_file_set_contents (querylog, msg->response_body->data, msg->response_body->length, NULL)) + g_warning ("Failed tpo write out query contents to %s", querylog); + + g_free (querylog); + g_free (filename); + g_free (time); + g_date_time_unref(now); +} + + +static void +got_locs (SoupSession *session, SoupMessage *msg, gpointer user_data) +{ + GSList *locs = NULL; + LpfProviderGotItUserData *locs_data = (LpfProviderGotItUserData*)user_data; + LpfProviderGotLocsNotify callback; + LpfProviderHafasBin6 *self; + gpointer data; + GError *err = NULL; + + g_return_if_fail(session); + g_return_if_fail(msg); + g_return_if_fail(user_data); + + callback = locs_data->callback; + data = locs_data->user_data; + self = LPF_PROVIDER_HAFAS_BIN6(locs_data->self); + g_free (locs_data); + + LPF_DEBUG("Status: %d", msg->status_code); + if (!SOUP_STATUS_IS_SUCCESSFUL(msg->status_code)) { + LPF_DEBUG("HTTP request failed"); + g_set_error (&err, + LPF_PROVIDER_ERROR, + LPF_PROVIDER_ERROR_REQUEST_FAILED, + "HTTP request failed: %d", msg->status_code); + goto out; + } + + log_response_body (self, msg, "station"); + + if ((locs = parse_locs_xml(msg->response_body->data)) == NULL) { + g_set_error (&err, + LPF_PROVIDER_ERROR, + LPF_PROVIDER_ERROR_PARSE_FAILED, + "Failed to parse locations"); + goto out; + } + +out: + (*callback)(locs, data, err); +} + + +static gint +lpf_provider_hafas_bin6_get_locs (LpfProvider *self, const char* match, LpfProviderGotLocsNotify callback, gpointer user_data) +{ + LpfProviderHafasBin6Private *priv = GET_PRIVATE(self); + SoupMessage *msg; + char *xml; + LpfProviderGotItUserData *locs_data = NULL; + gint ret = -1; + + g_return_val_if_fail (priv->session, -1); + + locs_data = g_malloc(sizeof(LpfProviderHafasBin6Private)); + if (!locs_data) + goto out; + xml = g_strdup_printf ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "<ReqC ver=\"1.1\" prod=\"String\" lang=\"EN\">" + "<MLcReq><MLc n=\"%s\" t=\"ST\"/>" + "</MLcReq></ReqC>", match); + + msg = soup_message_new ("POST", lpf_provider_hafas_bin6_locs_url(LPF_PROVIDER_HAFAS_BIN6(self))); + if (!msg) + goto out; + soup_message_set_request (msg, "text/xml", SOUP_MEMORY_TAKE, xml, strlen (xml)); + + locs_data->user_data = user_data; + locs_data->callback = callback; + locs_data->self = self; + + soup_session_queue_message (priv->session, msg, got_locs, locs_data); + ret = 0; + out: + if (ret < 0) + g_free (locs_data); + return ret; +} + + +static GSList* +hafas_bin6_parse_each_trip (const gchar *data, gsize num, guint base, const char *enc) +{ + gint i, j, k; + const HafasBin6Trip *t; + const HafasBin6TripPartDetail *pd; + const HafasBin6TripPart *p; + const HafasBin6TripStop *stop; + guint16 day_off; + +#ifdef ENABLE_DEBUG + const HafasBin6TripDetail *d; +#endif + guint h, m; + LpfTrip *trip = NULL; + LpfTripPart *part = NULL; + LpfStop *start = NULL, *end = NULL, *astop = NULL; + GDateTime *dt; + GSList *trips = NULL, *parts = NULL, *stops = NULL; + const char *line; + + for (i = 0; i < num; i++) { + /* The trips itself */ + t = HAFAS_BIN6_TRIP(data, i); + day_off = lpf_provider_hafas_bin6_parse_service_day(data, i); + + LPF_DEBUG("Trip #%d, Changes: %4d", i, t->changes); +#ifdef ENABLE_DEBUG + /* trip details */ + d = HAFAS_BIN6_TRIP_DETAIL(data, i); + LPF_DEBUG("Trip #%d, Status: %4d", i, d->rt_status); + LPF_DEBUG("Trip #%d, Delay: %4d", i, d->delay); +#endif + /* trip parts */ + for (j = 0; j < t->part_cnt; j++) { + p = HAFAS_BIN6_TRIP_PART(data, i, j); + start = g_object_new(LPF_TYPE_STOP, NULL); + if (lpf_provider_hafas_bin6_parse_station(data, p->dep_off, LPF_LOC(start), enc) < 0) { + g_warning("Failed to parse start station %d/%d", i, j); + goto error; + } + end = g_object_new(LPF_TYPE_STOP, NULL); + if (lpf_provider_hafas_bin6_parse_station(data, p->arr_off, LPF_LOC(end), enc) < 0) { + g_warning("Failed to parse end station %d/%d", i, j); + goto error; + } + line = HAFAS_BIN6_STR(data, p->line_off); + + h = p->dep / 100; + m = p->dep % 100; + dt = lpf_provider_hafas_bin6_date_time (base, day_off, h, m); + g_object_set (start, "departure", dt, NULL); + + if (g_strcmp0 (HAFAS_BIN6_NO_PLATFORM, HAFAS_BIN6_STR(data, p->dep_pos_off))) { + g_object_set (start, + "dep_plat", HAFAS_BIN6_STR(data, p->dep_pos_off), + NULL); + } + + h = p->arr / 100; + m = p->arr % 100; + dt = lpf_provider_hafas_bin6_date_time (base, day_off, h, m); + g_object_set (end, "arrival", dt, NULL); + + if (g_strcmp0 (HAFAS_BIN6_NO_PLATFORM, HAFAS_BIN6_STR(data, p->arr_pos_off))) { + g_object_set (end, + "arr_plat", HAFAS_BIN6_STR(data, p->arr_pos_off), + NULL); + } + + /* trip part details */ + pd = HAFAS_BIN6_TRIP_PART_DETAIL(data, i, j); + + if (pd->arr_pred != HAFAS_BIN6_NO_REALTIME) { + h = pd->arr_pred / 100; + m = pd->arr_pred % 100; + dt = lpf_provider_hafas_bin6_date_time (base, day_off, h, m); + g_object_set (start, "rt_arrival", dt, NULL); + } + + if (pd->dep_pred != HAFAS_BIN6_NO_REALTIME) { + h = pd->dep_pred / 100; + m = pd->dep_pred % 100; + dt = lpf_provider_hafas_bin6_date_time (base, day_off, h, m); + g_object_set (end, "rt_departure", dt, NULL); + } + + LPF_DEBUG("Trip #%d, part #%d, Pred. Dep Plat: %s, Pred. Arr Plat: %s", i, j, + HAFAS_BIN6_STR(data, pd->dep_pos_pred_off), + HAFAS_BIN6_STR(data, pd->arr_pos_pred_off)); + + for (k = 0; k < pd->stops_cnt; k++) { + stop = HAFAS_BIN6_STOP(data, i, j, k); + + astop = g_object_new (LPF_TYPE_STOP, + "arrival", lpf_provider_hafas_bin6_date_time (base, + day_off, + stop->arr / 100, + stop->arr % 100), + "departure", lpf_provider_hafas_bin6_date_time (base, + day_off, + stop->dep / 100, + stop->dep % 100), + NULL + ); + + if (lpf_provider_hafas_bin6_parse_station(data, stop->stop_idx, LPF_LOC(astop), enc) < 0) { + g_warning("Failed to parse stop %d/%d", i, j); + goto error; + } + +#ifdef ENABLE_DEBUG + /* FIXME: add these and predicted dep/arr/plat to LpfStop too */ + LPF_DEBUG("Trip #%d, part #%d," + "Dep: %.6s Arr: %.6s", i, j, + HAFAS_BIN6_STR(data, stop->dep_pos_off), + HAFAS_BIN6_STR(data, stop->arr_pos_off)); +#endif + stops = g_slist_append (stops, astop); + astop = NULL; + } + part = g_object_new (LPF_TYPE_TRIP_PART, + "start", start, + "end", end, + "line", line, + "stops", stops, + NULL); + start = end = NULL; + line = NULL; + stops = NULL; + + parts = g_slist_append (parts, part); + part = NULL; + } + trip = g_object_new (LPF_TYPE_TRIP, + "parts", parts, + NULL); + parts = NULL; + trips = g_slist_append (trips, trip); + trip = NULL; + } + + return trips; + +error: + if (trips) + g_slist_free_full (trips, g_object_unref); + if (parts) + g_slist_free_full (parts, g_object_unref); + if (stops) + g_slist_free_full (stops, g_object_unref); + if (start) + g_object_unref (start); + if (trip) + g_object_unref (trip); + if (part) + g_object_unref (part); + if (astop) + g_object_unref (astop); + + return NULL; +} + + +static GSList* +hafas_binary_parse_trips (const char *data, gsize length) +{ + HafasBin6Header *header; +#ifdef ENABLE_DEBUG + HafasBin6Loc *start, *end; +#endif + HafasBin6ExtHeader *ext; + HafasBin6TripDetailsHeader *details; + GSList *trips = NULL; + guint16 version; + const gchar *encoding; + + g_return_val_if_fail (data, NULL); + g_return_val_if_fail (length, NULL); + + version = *(guint16*)data; + g_return_val_if_fail(version == 6, NULL); + + g_return_val_if_fail(sizeof (HafasBin6Header) < length, NULL); + header = (HafasBin6Header*) data; +#ifdef ENABLE_DEBUG + start = (HafasBin6Loc*)&header->start; + end = (HafasBin6Loc*)&header->end; +#endif + LPF_DEBUG("%d Trips from '%s' to '%s'", + header->num_trips, + HAFAS_BIN6_STR(data, start->name_off), + HAFAS_BIN6_STR(data, end->name_off)); + + g_return_val_if_fail ((sizeof (HafasBin6Header) + + sizeof(HafasBin6Header) * header->num_trips) < length, + NULL); + + ext = HAFAS_BIN6_EXT_HEADER(data); + g_return_val_if_fail (header->ext + + sizeof(HafasBin6ExtHeader) < length, NULL); + + /* We might not have attrs_index0 */ + g_return_val_if_fail(sizeof (HafasBin6ExtHeader) - 1 < ext->length, NULL); + + LPF_DEBUG("Ext length: 0x%.4x", ext->length); + LPF_DEBUG("Errors: 0x%.4x", ext->err); + LPF_DEBUG("Sequence: 0x%.4x", ext->seq); + LPF_DEBUG("Detail table: 0x%.4x", ext->details_tbl); + encoding = HAFAS_BIN6_STR(data, ext->enc_off); + LPF_DEBUG("Encoding: %s", encoding); + LPF_DEBUG("Request Id: %s", HAFAS_BIN6_STR(data, ext->req_id_off)); + + if (ext->err) { + g_warning ("Error %d", ext->err); + goto out; + } + + if (ext->seq <= 0) { + g_warning("Illegal sequence number %d", ext->seq); + goto out; + } + + g_return_val_if_fail (ext->details_tbl + + sizeof (HafasBin6TripDetailsHeader) < length, NULL); + details = HAFAS_BIN6_TRIP_DETAILS_HEADER(data); + LPF_DEBUG("Trip detail version: %d", details->version); + g_return_val_if_fail (details->version == 1, NULL); + g_return_val_if_fail (details->stop_size == sizeof(HafasBin6TripStop), NULL); + g_return_val_if_fail (details->part_detail_size == sizeof(HafasBin6TripPartDetail), NULL); + + trips = hafas_bin6_parse_each_trip (data, header->num_trips, header->days, encoding); + g_return_val_if_fail (trips, NULL); + out: + return trips; +} + +static gint +decompress (const gchar *in, gsize inlen, + gchar **out, gsize *outlen, + GError **err) { + gint ret = -1; + gsize read, written; + GConverter *decomp = NULL; + GConverterResult conv; + gsize outbuflen, buflen, outpos = 0, inpos = 0; + gchar *outbuf = NULL; + + g_return_val_if_fail (inlen > 0, ret); + g_return_val_if_fail (in, ret); + g_return_val_if_fail (err, ret); + + decomp = (GConverter *)g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP); + + outbuflen = buflen = inlen * 2; + outbuf = g_try_malloc (outbuflen); + if (outbuf == NULL) + goto out; + + do { + conv = g_converter_convert (decomp, + (in + inpos), inlen, + (outbuf + outpos), buflen, + G_CONVERTER_INPUT_AT_END, + &read, &written, + err); + + switch (conv) { + case G_CONVERTER_ERROR: + goto out; + case G_CONVERTER_FINISHED: + outpos += written; + ret = 0; + goto out; + case G_CONVERTER_CONVERTED: + outpos += written; + inpos += read; + inlen -= read; + if (outbuflen - written < buflen) { + outbuflen += buflen; + outbuf = g_try_realloc (outbuf, outbuflen); + if (outbuf == NULL) + goto out; + } + break; + case G_CONVERTER_FLUSHED: + default: + g_warning ("Unhandled condition %d", conv); + goto out; + } + } while (TRUE); + +out: + if (ret == 0) { + *out = outbuf; + *outlen = outpos; + } else + g_free (outbuf); + + g_object_unref (decomp); + return ret; +} + +static void +got_trips (SoupSession *session, SoupMessage *msg, gpointer user_data) +{ + GSList *trips = NULL; + LpfProviderGotItUserData *trips_data = (LpfProviderGotItUserData*)user_data; + LpfProviderGotTripsNotify callback; + LpfProviderHafasBin6 *self; + gpointer data; + gchar *decomp = NULL; + gsize len; + GError *err = NULL; + + g_return_if_fail(session); + g_return_if_fail(msg); + g_return_if_fail(user_data); + + g_object_ref (msg); + + callback = trips_data->callback; + data = trips_data->user_data; + self = (LpfProviderHafasBin6*)trips_data->self; + g_free (trips_data); + + LPF_DEBUG("Status: %d", msg->status_code); + if (!SOUP_STATUS_IS_SUCCESSFUL(msg->status_code)) { + g_set_error (&err, + LPF_PROVIDER_ERROR, + LPF_PROVIDER_ERROR_REQUEST_FAILED, + "HTTP request failed: %d", msg->status_code); + goto out; + } + + log_response_body (self, msg, "trip"); + + if (decompress(msg->response_body->data, msg->response_body->length, &decomp, &len, &err) < 0) { + goto out; + } + + LPF_DEBUG("Decompressed to %" G_GSIZE_FORMAT " bytes", len); + if ((trips = hafas_binary_parse_trips(decomp, len)) == NULL) { + g_set_error (&err, + LPF_PROVIDER_ERROR, + LPF_PROVIDER_ERROR_PARSE_FAILED, + "Failed to parse trips"); + goto out; + } + +out: + g_object_unref (msg); + g_free (decomp); + + (*callback)(trips, data, err); +} + + +static gint +lpf_provider_hafas_bin6_get_trips (LpfProvider *self, + LpfLoc *start, + LpfLoc *end, + GDateTime *date, + guint64 flags, + LpfProviderGotTripsNotify callback, + gpointer user_data) +{ + LpfProviderHafasBin6Private *priv = GET_PRIVATE(self); + SoupMessage *msg; + SoupURI *uri = NULL; + LpfProviderGotItUserData *trips_data = NULL; + gint ret = -1; + gchar *datestr = NULL, *timestr = NULL; + /* allowed vehicle types */ + const gchar *train_restriction = "11111111111111"; + /* whether time is arrival or departure time */ + const gchar *by_departure = "1"; + char *start_id = NULL, *end_id = NULL; + + g_return_val_if_fail (start, -1); + g_return_val_if_fail (end, -1); + g_return_val_if_fail (callback, -1); + g_return_val_if_fail (priv->session, -1); + g_return_val_if_fail (date, -1); + + g_object_ref (start); + g_object_ref (end); + + trips_data = g_try_malloc(sizeof(LpfProviderHafasBin6Private)); + if (!trips_data) + goto out; + + datestr = g_date_time_format (date, "%d.%m.%y"); + timestr = g_date_time_format (date, "%H:%M"); + + start_id = lpf_loc_get_opaque(start); + end_id = lpf_loc_get_opaque(end); + if (start_id == NULL || end_id == NULL) { + g_warning ("Details missing."); + goto out; + } + + uri = soup_uri_new (lpf_provider_hafas_bin6_trips_url(LPF_PROVIDER_HAFAS_BIN6(self))); + + soup_uri_set_query_from_fields (uri, + "start", "Suchen", + "REQ0JourneyStopsS0ID", start_id, + "REQ0JourneyStopsZ0ID", end_id, + "REQ0JourneyDate", datestr, + "REQ0JourneyTime", timestr, + "REQ0HafasSearchForw", by_departure, + "REQ0JourneyProduct_prod_list_1", train_restriction, + "h2g-direct", "11", NULL); + + LPF_DEBUG ("URI: %s", soup_uri_to_string (uri, FALSE)); + + msg = soup_message_new_from_uri ("GET", uri); + if (!msg) + goto out; + + trips_data->user_data = user_data; + trips_data->callback = callback; + trips_data->self = self; + + soup_session_queue_message (priv->session, msg, got_trips, trips_data); + ret = 0; + out: + g_free (datestr); + g_free (timestr); + g_object_unref (start); + g_object_unref (end); + if (ret < 0) + g_free (trips_data); + return ret; +} + + static void lpf_provider_hafas_bin6_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) @@ -179,22 +844,50 @@ lpf_provider_hafas_bin6_date_time(guint base_days, guint off_days, guint hours, static void lpf_provider_hafas_bin6_activate (LpfProvider *self, GObject *obj) { - g_warn_if_reached (); + LpfProviderHafasBin6Private *priv = GET_PRIVATE(self); + GFile *dir; + gchar *debugstr; + + priv->session = soup_session_new(); + priv->logdir = g_build_path(G_DIR_SEPARATOR_S, + g_get_user_cache_dir(), + PACKAGE, + lpf_provider_get_name (self), + NULL); + + debugstr = getenv ("LPF_DEBUG"); + if (debugstr && strstr (debugstr, "provider")) + priv->debug = TRUE; + + dir = g_file_new_for_path (priv->logdir); + g_file_make_directory_with_parents (dir, NULL, NULL); + g_object_unref (dir); + } + static void lpf_provider_hafas_bin6_deactivate (LpfProvider *self, GObject *obj) { - g_warn_if_reached (); + LpfProviderHafasBin6Private *priv = GET_PRIVATE(self); + + if (priv->session) + g_object_unref (priv->session); + + g_free (priv->logdir); } static void lpf_provider_hafas_bin6_finalize (GObject *self) { + LpfProviderHafasBin6Private *priv = GET_PRIVATE(self); + g_free (priv->name); + G_OBJECT_CLASS (lpf_provider_hafas_bin6_parent_class)->finalize (self); } + static void lpf_provider_hafas_bin6_class_init (LpfProviderHafasBin6Class *klass) { @@ -206,6 +899,9 @@ lpf_provider_hafas_bin6_class_init (LpfProviderHafasBin6Class *klass) object_class->set_property = lpf_provider_hafas_bin6_set_property; object_class->finalize = lpf_provider_hafas_bin6_finalize; + klass->locs_url = lpf_provider_hafas_bin6_locs_url; + klass->trips_url = lpf_provider_hafas_bin6_trips_url; + g_object_class_override_property (object_class, PROP_NAME, "name"); @@ -218,9 +914,9 @@ lpf_provider_hafas_bin6_interface_init (LpfProviderInterface *iface) iface->activate = lpf_provider_hafas_bin6_activate; iface->deactivate = lpf_provider_hafas_bin6_deactivate; - /* To be implemented */ - iface->get_locs = NULL; /* lpf_provider_hafas_bin6_get_locs; */ - iface->get_trips = NULL; /* lpf_provider_hafas_bin6_get_trips; */ + /* To be implemented by each provider */ + iface->get_locs = lpf_provider_hafas_bin6_get_locs; + iface->get_trips = lpf_provider_hafas_bin6_get_trips; } static void diff --git a/src/providers/hafas-bin6.h b/src/providers/hafas-bin6.h index cc6a3a1..1177c45 100644 --- a/src/providers/hafas-bin6.h +++ b/src/providers/hafas-bin6.h @@ -45,6 +45,9 @@ typedef struct { typedef struct { GObjectClass parent_class; + + const char* (*locs_url)(LpfProviderHafasBin6 *self); + const char* (*trips_url)(LpfProviderHafasBin6 *self); } LpfProviderHafasBin6Class; GType lpf_provider_hafas_bin6_get_type (void); @@ -53,5 +56,9 @@ gint lpf_provider_hafas_bin6_parse_station(const gchar *data, guint16 off, LpfLo guint lpf_provider_hafas_bin6_parse_service_day (const char *data, int idx); GDateTime* lpf_provider_hafas_bin6_date_time(guint base_days, guint off_days, guint hours, guint min); +/* Pure virtual methods */ +const gchar* lpf_provider_hafas_bin6_locs_url(LpfProviderHafasBin6 *self); +const gchar* lpf_provider_hafas_bin6_trips_url(LpfProviderHafasBin6 *self); + G_END_DECLS #endif /* _HAFAS_BIN6_H */ diff --git a/src/providers/tests/Makefile.am b/src/providers/tests/Makefile.am index 6fb8de4..764c17a 100644 --- a/src/providers/tests/Makefile.am +++ b/src/providers/tests/Makefile.am @@ -1,6 +1,6 @@ include $(top_srcdir)/flymake.mk -check_PROGRAMS = de-db hafas-bin6 +check_PROGRAMS = hafas-bin6 hafas-bin6-format AM_CFLAGS = \ $(GLIB2_CFLAGS) \ @@ -21,30 +21,30 @@ LDADD = \ $(GTHREAD2_LIBS) \ $(NULL) -de_db_SOURCES = \ - de-db.c \ +hafas_bin6_SOURCES = \ + hafas-bin6.c \ $(NULL) -de_db_CFLAGS = \ +hafas_bin6_CFLAGS = \ $(AM_CFLAGS) \ $(LIBSOUP_CFLAGS) \ $(LIBXML2_CFLAGS) \ $(NULL) -de_db_LDADD = \ +hafas_bin6_LDADD = \ ../libplanfahr-provider-de-db.la \ $(LDADD) \ $(LIBSOUP_LIBS) \ $(LIBXML2_LIBS) \ $(NULL) -hafas_bin6_SOURCES = \ - hafas-bin6.c \ +hafas_bin6_format_SOURCES = \ + hafas-bin6-format.c \ ../hafas-bin6-format.h \ $(NULL) -hafas_bin6_CFLAGS = \ +hafas_bin6_format_CFLAGS = \ $(AM_CFLAGS) \ $(NULL) -hafas_bin6_LDADD = \ +hafas_bin6_format_LDADD = \ $(LDADD) \ $(NULL) diff --git a/src/providers/tests/de-db.c b/src/providers/tests/de-db.c deleted file mode 100644 index b542d54..0000000 --- a/src/providers/tests/de-db.c +++ /dev/null @@ -1,137 +0,0 @@ -/* - * db.c: Deutsche Bahn provider for libplanfahr - * - * Copyright (C) 2014 Guido Günther - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * Author: Guido Günther <agx@sigxcpu.org> - */ - -#include "../de-db.c" - -/* Make sure we can parse the station xml list as returned by the current Deutsche Bahn Hafas */ -static void -test_parse_stations (void) -{ - GSList *locs; - LpfLoc *loc; - char *name; - double lon, lat; - - char *xml = g_strjoin(NULL, -"<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>", -"<ResC xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"No path to XML scheme defined\" ver=\"1.1\" prod=\"String\" lang=\"EN\">", -" <MLcRes flag=\"FINAL\">", -" <MLc t=\"ST\" n=\"Erpel(Rhein)\" i=\"A=1@O=Erpel(Rhein)@X=7241593@Y=50582067@U=80@L=008001858@B=1@p=1386184594@\" x=\"7241593\" y=\"50582067\" />", -" <MLc t=\"ST\" n=\"Erpel B42\" i=\"A=1@O=Erpel B42@X=7231174@Y=50583649@U=81@L=000441204@B=1@p=1387219918@\" x=\"7231174\" y=\"50583649\" />", -" <MLc t=\"ST\" n=\"Bahnhofstr., Erpel\" i=\"A=1@O=Bahnhofstr., Erpel@X=7243175@Y=50580943@U=81@L=000448602@B=1@p=1387219918@\" x=\"7243175\" y=\"50580943\" />", -" <MLc t=\"ST\" n=\"Rheinfähre, Erpel\" i=\"A=1@O=Rheinfähre, Erpel@X=7238401@Y=50581663@U=81@L=000441205@B=1@p=1387219918@\" x=\"7238401\" y=\"50581663\" />", -" <MLc t=\"ST\" n=\"Orsberg Ort, Erpel\" i=\"A=1@O=Orsberg Ort, Erpel@X=7243399@Y=50593061@U=81@L=000454657@B=1@p=1387219918@\" x=\"7243399\" y=\"50593061\" />", -" <MLc t=\"ST\" n=\"Neutor, Erpel\" i=\"A=1@O=Neutor, Erpel@X=7235282@Y=50584108@U=81@L=000454652@B=1@p=1387219918@\" x=\"7235282\" y=\"50584108\" />", -" <MLc t=\"ST\" n=\"Erpeldange Am Schlass, Luxemburg\" i=\"A=1@O=Erpeldange Am Schlass, Luxemburg@X=6114741@Y=49852458@U=81@L=000864180@B=1@p=1387219918@\" x=\"6114741\" y=\"49852458\" />", -" </MLcRes>", -"</ResC>", NULL); - - locs = parse_stations_xml(xml); - g_assert_nonnull (locs); - g_assert_cmpint (g_slist_length (locs), ==, 7); - - loc = g_slist_nth (locs, 4)->data; - g_object_get (loc, "name", &name, "long", &lon, "lat", &lat, NULL); - - g_assert_cmpstr (name, ==, "Orsberg Ort, Erpel"); - g_assert_cmpint (lon, ==, 7); - g_assert_cmpint (lat, ==, 50); - - g_slist_free_full (locs, g_object_unref); - g_free (xml); - g_free (name); -} - -/* Make sure we can parse the binary data trip information */ -static void -test_parse_trips (void) -{ - GSList *trips; - gchar *binary; - gsize length; - int i; - GSList *parts; - LpfTrip *trip; - LpfTripPart *part; - LpfLoc *stop; - gchar *name; - GDateTime *dep, *arr; - - g_assert_true(g_file_get_contents(LPF_TEST_SRCDIR "/hafas-bin-6-station-query-1.bin", &binary, &length, NULL)); - - trips = hafas_binary_parse_trips (binary, length); - - g_assert (g_slist_length (trips) == 3); - - for (i = 0; i < g_slist_length (trips); i++) { - trip = LPF_TRIP(g_slist_nth_data (trips, i)); - g_object_get (G_OBJECT(trip), "parts", &parts, NULL); - - part = LPF_TRIP_PART(g_slist_nth_data (parts, 0)); - g_object_get (G_OBJECT(part), "start", &stop, NULL); - g_object_get (G_OBJECT(stop), - "name", &name, - "arrival", &arr, - "departure", &dep, - NULL); - /* All trips start in Erpel */ - g_assert (!g_strcmp0 (name, "Erpel(Rhein)")); - g_free (name); - - g_assert (dep != NULL); - g_assert (arr == NULL); - g_date_time_unref (dep); - - /* All trips end in Unkel */ - part = LPF_TRIP_PART(g_slist_nth_data (parts, - g_slist_length(parts)-1)); - g_object_get (G_OBJECT(part), "end", &stop, NULL); - g_object_get (G_OBJECT(stop), - "name", &name, - "arrival", &arr, - "departure", &dep, - NULL); - - g_assert (dep == NULL); - g_assert (arr != NULL); - g_date_time_unref (arr); - - g_assert (g_strrstr (name, "Unkel") != NULL); - g_free (name); - } - - g_slist_free (trips); - -} - - -int main(int argc, char **argv) -{ - gboolean ret; - g_test_init (&argc, &argv, NULL); - - g_test_add_func ("/providers/de-db/parse_stations", test_parse_stations); - g_test_add_func ("/providers/de-db/parse_trips", test_parse_trips); - - ret = g_test_run (); - return ret; -} diff --git a/src/providers/tests/hafas-bin6-format.c b/src/providers/tests/hafas-bin6-format.c new file mode 100644 index 0000000..ea5e277 --- /dev/null +++ b/src/providers/tests/hafas-bin6-format.c @@ -0,0 +1,156 @@ +/* + * hafas-bin6.c: test hafas binary v6 parser + * + * Copyright (C) 2014 Guido Günther + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Author: Guido Günther <agx@sigxcpu.org> + */ + +#include <glib.h> + +#include "../hafas-bin6.h" + + +/* sanity check our parser's structures */ +static void +test_sizes (void) +{ + g_assert_cmpint(sizeof(HafasBin6Header), ==, 0x4a); + g_assert_cmpint(sizeof(HafasBin6Trip), ==, 0x0c); + g_assert_cmpint(sizeof(HafasBin6Loc), ==, 0x0e); + g_assert_cmpint(sizeof(HafasBin6ExtHeader), ==, 0x30); + g_assert_cmpint(sizeof(HafasBin6TripDetailsHeader), ==, 0x0e); + g_assert_cmpint(sizeof(HafasBin6Station), ==, 0x0e); + g_assert_cmpint(sizeof(HafasBin6ServiceDay), ==, 0x07); + g_assert_cmpint(sizeof(HafasBin6TripDetail), ==, 0x04); + g_assert_cmpint(sizeof(HafasBin6TripPart), ==, 0x14); + g_assert_cmpint(sizeof(HafasBin6TripPartDetail), ==, 0x10); + g_assert_cmpint(sizeof(HafasBin6TripStop), ==, 0x1a); +} + +/* + * Make sure we can parse the binary data trip information This is + * just to test the raw parser. The wrapping into Lpf objects and + * conversions of e.g. time and date are tested separately. + */ +static void +test_parse_erpel_unkel(void) +{ + gchar *bin; + gsize length; + HafasBin6Loc *start, *end; + HafasBin6ExtHeader *ext; + HafasBin6TripDetailsHeader *detail_header; + HafasBin6TripPartDetail *part_detail; + HafasBin6TripDetail *detail; + HafasBin6Trip *trip; + HafasBin6TripPart *part; + HafasBin6ServiceDay *day; + HafasBin6TripStop *stop; + HafasBin6Station *station; + gint i, j, k; + guint expected_changes[3] = { 0, 0, 0 }; + guint expected_parts[3] = { 1, 2, 1 }; + const gchar *expected_part_line[3][2] = { {"RB 12560", "doesnot" }, + {"Fussweg", "Bus 565" }, + {"RB 12562", "matter" }}; + const gint expected_part_dep[3][2] = { { 956, 0}, + { 958, 1003 }, + {1056, 0 }}; + + const gint expected_part_arr[3][2] = { { 959, 2}, + {1003, 1014}, + {1059, 0 }}; + guint expected_stops[3][2] = { {0, 0}, {0, 8}, {0, 0} }; + + g_assert_true(g_file_get_contents(LPF_TEST_SRCDIR "/hafas-bin-6-station-query-1.bin", &bin, &length, NULL)); + + g_assert_cmpint(HAFAS_BIN6_HEADER(bin)->num_trips, ==, 3); + + /* header information */ + start = HAFAS_BIN6_START(bin); + end = HAFAS_BIN6_END(bin); + g_assert_cmpint (start->lon, ==, 7241593); + g_assert_cmpint (start->lat, ==, 50582067); + g_assert_cmpint (start->type, ==, HAFAS_BIN6_LOC_TYPE_STATION); + g_assert_cmpint (end->lon, ==, 7219803); + g_assert_cmpint (end->lat, ==, 50602742); + g_assert_cmpint (end->type, ==, HAFAS_BIN6_LOC_TYPE_STATION); + + /* ext header information */ + ext = HAFAS_BIN6_EXT_HEADER(bin); + g_assert_cmpint (ext->err, ==, HAFAS_BIN6_ERROR_NONE); + g_assert_cmpstr (HAFAS_BIN6_STR(bin, ext->enc_off), ==, "iso-8859-1"); + g_assert_cmpstr (HAFAS_BIN6_STR(bin, ext->req_id_off), ==, "50.02519042.1387923122"); + g_assert_cmpint (ext->seq, ==, 1); + + /* detail header information */ + detail_header = HAFAS_BIN6_TRIP_DETAILS_HEADER(bin); + g_assert_cmpint (detail_header->version, ==, 1); + g_assert_cmpint (detail_header->stop_size, ==, sizeof(HafasBin6TripStop)); + g_assert_cmpint (detail_header->part_detail_size, == ,sizeof(HafasBin6TripPartDetail)); + + for (i = 0; i < HAFAS_BIN6_HEADER(bin)->num_trips; i++) { + trip = HAFAS_BIN6_TRIP(bin, i); + g_assert_cmpint (trip->changes, ==, expected_changes[i]); + g_assert_cmpint (trip->part_cnt, ==, expected_parts[i]); + + day = HAFAS_BIN6_SERVICE_DAY(bin, i); + g_assert_cmpint (day->byte_base, ==, 0); + g_assert_cmpint (day->byte_len, ==, 2); + g_assert_cmpint (day->byte0, ==, (gchar)(0x80)); + + detail = HAFAS_BIN6_TRIP_DETAIL(bin, i); + g_assert_cmpint (detail->rt_status, ==, HAFAS_BIN6_RTSTATUS_NORMAL); + g_assert_cmpint (detail->delay, ==, 0); + + for (j = 0; j < trip->part_cnt; j++) { + /* planned departures and arrivals */ + part = HAFAS_BIN6_TRIP_PART(bin, i, j); + g_assert_cmpstr (HAFAS_BIN6_STR(bin, part->line_off), ==, expected_part_line[i][j]); + g_assert_cmpint (part->arr, ==, expected_part_arr[i][j]); + g_assert_cmpint (part->dep, ==, expected_part_dep[i][j]); + /* predicted departures and arrivals */ + part_detail = HAFAS_BIN6_TRIP_PART_DETAIL(bin, i, j); + g_assert_cmpint (part_detail->arr_pred, ==, 65535); + g_assert_cmpint (part_detail->dep_pred, ==, 65535); + /* stops */ + g_assert_cmpint (part_detail->stops_cnt, ==, expected_stops[i][j]); + for (k = 0; k < part_detail->stops_cnt; k++) { + stop = HAFAS_BIN6_STOP(bin, i, j, k); + g_assert_cmpstr (HAFAS_BIN6_STR(bin, stop->dep_pos_pred_off), ==, "---"); + } + /* check a single station more thoroughly */ + stop = HAFAS_BIN6_STOP(bin, 1, 1, 2); + station = HAFAS_BIN6_STATION(bin, stop->stop_idx); + g_assert_cmpstr (HAFAS_BIN6_STR(bin, station->name_off), ==, "Heister Kapelle, Unkel"); + } + } +} + + +int main(int argc, char **argv) +{ + gboolean ret; + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/providers/hafas-bin6/sizes", test_sizes); + g_test_add_func ("/providers/hafas_bin6/parse_erpel_unkel", test_parse_erpel_unkel); + + ret = g_test_run (); + return ret; +} diff --git a/src/providers/tests/hafas-bin6.c b/src/providers/tests/hafas-bin6.c index ea5e277..8c256e0 100644 --- a/src/providers/tests/hafas-bin6.c +++ b/src/providers/tests/hafas-bin6.c @@ -1,5 +1,5 @@ /* - * hafas-bin6.c: test hafas binary v6 parser + * db.c: Deutsche Bahn provider for libplanfahr * * Copyright (C) 2014 Guido Günther * @@ -20,126 +20,107 @@ * Author: Guido Günther <agx@sigxcpu.org> */ -#include <glib.h> +#include "../hafas-bin6.c" -#include "../hafas-bin6.h" - - -/* sanity check our parser's structures */ +/* Make sure we can parse the station xml list as returned by the current Deutsche Bahn Hafas */ static void -test_sizes (void) +test_parse_locs (void) { - g_assert_cmpint(sizeof(HafasBin6Header), ==, 0x4a); - g_assert_cmpint(sizeof(HafasBin6Trip), ==, 0x0c); - g_assert_cmpint(sizeof(HafasBin6Loc), ==, 0x0e); - g_assert_cmpint(sizeof(HafasBin6ExtHeader), ==, 0x30); - g_assert_cmpint(sizeof(HafasBin6TripDetailsHeader), ==, 0x0e); - g_assert_cmpint(sizeof(HafasBin6Station), ==, 0x0e); - g_assert_cmpint(sizeof(HafasBin6ServiceDay), ==, 0x07); - g_assert_cmpint(sizeof(HafasBin6TripDetail), ==, 0x04); - g_assert_cmpint(sizeof(HafasBin6TripPart), ==, 0x14); - g_assert_cmpint(sizeof(HafasBin6TripPartDetail), ==, 0x10); - g_assert_cmpint(sizeof(HafasBin6TripStop), ==, 0x1a); + GSList *locs; + LpfLoc *loc; + char *name; + double lon, lat; + + char *xml = g_strjoin(NULL, +"<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>", +"<ResC xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"No path to XML scheme defined\" ver=\"1.1\" prod=\"String\" lang=\"EN\">", +" <MLcRes flag=\"FINAL\">", +" <MLc t=\"ST\" n=\"Erpel(Rhein)\" i=\"A=1@O=Erpel(Rhein)@X=7241593@Y=50582067@U=80@L=008001858@B=1@p=1386184594@\" x=\"7241593\" y=\"50582067\" />", +" <MLc t=\"ST\" n=\"Erpel B42\" i=\"A=1@O=Erpel B42@X=7231174@Y=50583649@U=81@L=000441204@B=1@p=1387219918@\" x=\"7231174\" y=\"50583649\" />", +" <MLc t=\"ST\" n=\"Bahnhofstr., Erpel\" i=\"A=1@O=Bahnhofstr., Erpel@X=7243175@Y=50580943@U=81@L=000448602@B=1@p=1387219918@\" x=\"7243175\" y=\"50580943\" />", +" <MLc t=\"ST\" n=\"Rheinfähre, Erpel\" i=\"A=1@O=Rheinfähre, Erpel@X=7238401@Y=50581663@U=81@L=000441205@B=1@p=1387219918@\" x=\"7238401\" y=\"50581663\" />", +" <MLc t=\"ST\" n=\"Orsberg Ort, Erpel\" i=\"A=1@O=Orsberg Ort, Erpel@X=7243399@Y=50593061@U=81@L=000454657@B=1@p=1387219918@\" x=\"7243399\" y=\"50593061\" />", +" <MLc t=\"ST\" n=\"Neutor, Erpel\" i=\"A=1@O=Neutor, Erpel@X=7235282@Y=50584108@U=81@L=000454652@B=1@p=1387219918@\" x=\"7235282\" y=\"50584108\" />", +" <MLc t=\"ST\" n=\"Erpeldange Am Schlass, Luxemburg\" i=\"A=1@O=Erpeldange Am Schlass, Luxemburg@X=6114741@Y=49852458@U=81@L=000864180@B=1@p=1387219918@\" x=\"6114741\" y=\"49852458\" />", +" </MLcRes>", +"</ResC>", NULL); + + locs = parse_locs_xml(xml); + g_assert_nonnull (locs); + g_assert_cmpint (g_slist_length (locs), ==, 7); + + loc = g_slist_nth (locs, 4)->data; + g_object_get (loc, "name", &name, "long", &lon, "lat", &lat, NULL); + + g_assert_cmpstr (name, ==, "Orsberg Ort, Erpel"); + g_assert_cmpint (lon, ==, 7); + g_assert_cmpint (lat, ==, 50); + + g_slist_free_full (locs, g_object_unref); + g_free (xml); + g_free (name); } -/* - * Make sure we can parse the binary data trip information This is - * just to test the raw parser. The wrapping into Lpf objects and - * conversions of e.g. time and date are tested separately. - */ +/* Make sure we can parse the binary data trip information */ static void -test_parse_erpel_unkel(void) +test_parse_trips (void) { - gchar *bin; + GSList *trips; + gchar *binary; gsize length; - HafasBin6Loc *start, *end; - HafasBin6ExtHeader *ext; - HafasBin6TripDetailsHeader *detail_header; - HafasBin6TripPartDetail *part_detail; - HafasBin6TripDetail *detail; - HafasBin6Trip *trip; - HafasBin6TripPart *part; - HafasBin6ServiceDay *day; - HafasBin6TripStop *stop; - HafasBin6Station *station; - gint i, j, k; - guint expected_changes[3] = { 0, 0, 0 }; - guint expected_parts[3] = { 1, 2, 1 }; - const gchar *expected_part_line[3][2] = { {"RB 12560", "doesnot" }, - {"Fussweg", "Bus 565" }, - {"RB 12562", "matter" }}; - const gint expected_part_dep[3][2] = { { 956, 0}, - { 958, 1003 }, - {1056, 0 }}; - - const gint expected_part_arr[3][2] = { { 959, 2}, - {1003, 1014}, - {1059, 0 }}; - guint expected_stops[3][2] = { {0, 0}, {0, 8}, {0, 0} }; - - g_assert_true(g_file_get_contents(LPF_TEST_SRCDIR "/hafas-bin-6-station-query-1.bin", &bin, &length, NULL)); - - g_assert_cmpint(HAFAS_BIN6_HEADER(bin)->num_trips, ==, 3); - - /* header information */ - start = HAFAS_BIN6_START(bin); - end = HAFAS_BIN6_END(bin); - g_assert_cmpint (start->lon, ==, 7241593); - g_assert_cmpint (start->lat, ==, 50582067); - g_assert_cmpint (start->type, ==, HAFAS_BIN6_LOC_TYPE_STATION); - g_assert_cmpint (end->lon, ==, 7219803); - g_assert_cmpint (end->lat, ==, 50602742); - g_assert_cmpint (end->type, ==, HAFAS_BIN6_LOC_TYPE_STATION); - - /* ext header information */ - ext = HAFAS_BIN6_EXT_HEADER(bin); - g_assert_cmpint (ext->err, ==, HAFAS_BIN6_ERROR_NONE); - g_assert_cmpstr (HAFAS_BIN6_STR(bin, ext->enc_off), ==, "iso-8859-1"); - g_assert_cmpstr (HAFAS_BIN6_STR(bin, ext->req_id_off), ==, "50.02519042.1387923122"); - g_assert_cmpint (ext->seq, ==, 1); - - /* detail header information */ - detail_header = HAFAS_BIN6_TRIP_DETAILS_HEADER(bin); - g_assert_cmpint (detail_header->version, ==, 1); - g_assert_cmpint (detail_header->stop_size, ==, sizeof(HafasBin6TripStop)); - g_assert_cmpint (detail_header->part_detail_size, == ,sizeof(HafasBin6TripPartDetail)); - - for (i = 0; i < HAFAS_BIN6_HEADER(bin)->num_trips; i++) { - trip = HAFAS_BIN6_TRIP(bin, i); - g_assert_cmpint (trip->changes, ==, expected_changes[i]); - g_assert_cmpint (trip->part_cnt, ==, expected_parts[i]); - - day = HAFAS_BIN6_SERVICE_DAY(bin, i); - g_assert_cmpint (day->byte_base, ==, 0); - g_assert_cmpint (day->byte_len, ==, 2); - g_assert_cmpint (day->byte0, ==, (gchar)(0x80)); - - detail = HAFAS_BIN6_TRIP_DETAIL(bin, i); - g_assert_cmpint (detail->rt_status, ==, HAFAS_BIN6_RTSTATUS_NORMAL); - g_assert_cmpint (detail->delay, ==, 0); - - for (j = 0; j < trip->part_cnt; j++) { - /* planned departures and arrivals */ - part = HAFAS_BIN6_TRIP_PART(bin, i, j); - g_assert_cmpstr (HAFAS_BIN6_STR(bin, part->line_off), ==, expected_part_line[i][j]); - g_assert_cmpint (part->arr, ==, expected_part_arr[i][j]); - g_assert_cmpint (part->dep, ==, expected_part_dep[i][j]); - /* predicted departures and arrivals */ - part_detail = HAFAS_BIN6_TRIP_PART_DETAIL(bin, i, j); - g_assert_cmpint (part_detail->arr_pred, ==, 65535); - g_assert_cmpint (part_detail->dep_pred, ==, 65535); - /* stops */ - g_assert_cmpint (part_detail->stops_cnt, ==, expected_stops[i][j]); - for (k = 0; k < part_detail->stops_cnt; k++) { - stop = HAFAS_BIN6_STOP(bin, i, j, k); - g_assert_cmpstr (HAFAS_BIN6_STR(bin, stop->dep_pos_pred_off), ==, "---"); - } - /* check a single station more thoroughly */ - stop = HAFAS_BIN6_STOP(bin, 1, 1, 2); - station = HAFAS_BIN6_STATION(bin, stop->stop_idx); - g_assert_cmpstr (HAFAS_BIN6_STR(bin, station->name_off), ==, "Heister Kapelle, Unkel"); - } + int i; + GSList *parts; + LpfTrip *trip; + LpfTripPart *part; + LpfLoc *stop; + gchar *name; + GDateTime *dep, *arr; + + g_assert_true(g_file_get_contents(LPF_TEST_SRCDIR "/hafas-bin-6-station-query-1.bin", &binary, &length, NULL)); + + trips = hafas_binary_parse_trips (binary, length); + + g_assert (g_slist_length (trips) == 3); + + for (i = 0; i < g_slist_length (trips); i++) { + trip = LPF_TRIP(g_slist_nth_data (trips, i)); + g_object_get (G_OBJECT(trip), "parts", &parts, NULL); + + part = LPF_TRIP_PART(g_slist_nth_data (parts, 0)); + g_object_get (G_OBJECT(part), "start", &stop, NULL); + g_object_get (G_OBJECT(stop), + "name", &name, + "arrival", &arr, + "departure", &dep, + NULL); + /* All trips start in Erpel */ + g_assert (!g_strcmp0 (name, "Erpel(Rhein)")); + g_free (name); + + g_assert (dep != NULL); + g_assert (arr == NULL); + g_date_time_unref (dep); + + /* All trips end in Unkel */ + part = LPF_TRIP_PART(g_slist_nth_data (parts, + g_slist_length(parts)-1)); + g_object_get (G_OBJECT(part), "end", &stop, NULL); + g_object_get (G_OBJECT(stop), + "name", &name, + "arrival", &arr, + "departure", &dep, + NULL); + + g_assert (dep == NULL); + g_assert (arr != NULL); + g_date_time_unref (arr); + + g_assert (g_strrstr (name, "Unkel") != NULL); + g_free (name); } + + g_slist_free (trips); + } @@ -148,8 +129,8 @@ int main(int argc, char **argv) gboolean ret; g_test_init (&argc, &argv, NULL); - g_test_add_func ("/providers/hafas-bin6/sizes", test_sizes); - g_test_add_func ("/providers/hafas_bin6/parse_erpel_unkel", test_parse_erpel_unkel); + g_test_add_func ("/providers/de-db/parse_stations", test_parse_locs); + g_test_add_func ("/providers/de-db/parse_trips", test_parse_trips); ret = g_test_run (); return ret; |