diff options
author | Guido Günther <agx@sigxcpu.org> | 2014-02-21 15:57:11 +0100 |
---|---|---|
committer | Guido Günther <agx@sigxcpu.org> | 2014-02-24 08:39:05 +0100 |
commit | 891b34e2ef64f354474c4c6bec8e35f905e3c1db (patch) | |
tree | 9ff4373f980caa5eab84c4f3abaff6640783d817 /src |
Initial commit
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 100 | ||||
-rw-r--r-- | src/libplanfahr.c | 29 | ||||
-rw-r--r-- | src/libplanfahr.h | 25 | ||||
-rw-r--r-- | src/libplanfahr.sym | 7 | ||||
-rw-r--r-- | src/lpf-loc.c | 196 | ||||
-rw-r--r-- | src/lpf-loc.h | 62 | ||||
-rw-r--r-- | src/lpf-manager.c | 273 | ||||
-rw-r--r-- | src/lpf-manager.h | 85 | ||||
-rw-r--r-- | src/lpf-priv.h | 40 | ||||
-rw-r--r-- | src/lpf-provider.c | 251 | ||||
-rw-r--r-- | src/lpf-provider.h | 102 | ||||
-rw-r--r-- | src/lpf-stop.c | 316 | ||||
-rw-r--r-- | src/lpf-stop.h | 59 | ||||
-rw-r--r-- | src/lpf-trip-part.c | 200 | ||||
-rw-r--r-- | src/lpf-trip-part.h | 59 | ||||
-rw-r--r-- | src/lpf-trip.c | 152 | ||||
-rw-r--r-- | src/lpf-trip.h | 59 | ||||
-rw-r--r-- | src/providers/Makefile.am | 32 | ||||
-rw-r--r-- | src/providers/de-db.c | 751 | ||||
-rw-r--r-- | src/providers/de-db.h | 50 | ||||
-rw-r--r-- | src/providers/hafas-bin6-format.h | 323 | ||||
-rw-r--r-- | src/providers/hafas-bin6.c | 119 | ||||
-rw-r--r-- | src/providers/hafas-bin6.h | 33 | ||||
-rw-r--r-- | src/providers/tests/Makefile.am | 53 | ||||
-rw-r--r-- | src/providers/tests/de-db.c | 138 | ||||
-rw-r--r-- | src/providers/tests/hafas-bin-6-station-query-1.bin | bin | 0 -> 2352 bytes | |||
-rw-r--r-- | src/providers/tests/hafas-bin6.c | 156 |
27 files changed, 3670 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..da5878c --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,100 @@ +include $(top_srcdir)/flymake.mk + +SUBDIRS = providers + +EXTRA_DIST = libplanfahr.sym + +lib_LTLIBRARIES = libplanfahr-0.0.la + +PLANFAHR_HEADER_FILES = \ + libplanfahr.h \ + lpf-manager.h \ + lpf-priv.h \ + lpf-provider.h \ + lpf-loc.h \ + lpf-stop.h \ + lpf-trip.h \ + lpf-trip-part.h \ + $(NULL) + +PLANFAHR_SOURCE_FILES = \ + libplanfahr.c \ + lpf-manager.c \ + lpf-provider.c \ + lpf-loc.c \ + lpf-stop.c \ + lpf-trip.c \ + lpf-trip-part.c \ + $(NULL) + +libplanfahr_0_0_ladir = $(includedir)/libplanfahr/ +libplanfahr_0_0_la_HEADERS = \ + $(PLANFAHR_HEADER_FILES) \ + $(NULL) + +libplanfahr_0_0_la_SOURCES = \ + $(PLANFAHR_SOURCE_FILES) \ + $(NULL) + +libplanfahr_0_0_la_CFLAGS = \ + -DG_LOG_DOMAIN="\"LibPlanFahr\"" \ + -DLPF_PROVIDERS_DIR="\"$(LPF_PROVIDERS_DIR)\"" \ + -I$(top_srcdir) \ + $(GIO2_CFLAGS) \ + $(GOBJECT2_CFLAGS) \ + $(GTHREAD2_CFLAGS) \ + $(WARN_CFLAGS) \ + $(NULL) + +libplanfahr_0_0_la_LIBADD = \ + $(GIO2_LIBS) \ + $(GOBJECT2_LIBS) \ + $(GTHREAD2_LIBS) + $(NULL) + +libplanfahr_0_0_la_DEPENDENCIES = \ + libplanfahr.sym \ + $(NULL) + +libplanfahr_0_0_la_LDFLAGS = \ + -Wl,--version-script=$(srcdir)/libplanfahr.sym \ + -version-info $(LIBPLANFAHR_VERSION_INFO) + +if WITH_GOBJECT_INTROSPECTION + +Lpf-0.0.gir: libplanfahr-0.0.la $(G_IR_SCANNER) Makefile.am + $(AM_V_GEN)$(G_IR_SCANNER) \ + --quiet \ + --warn-all \ + --namespace Lpf \ + --nsversion 0.0 \ + --include GObject-2.0 \ + --include Gio-2.0 \ + --symbol-prefix=lpf \ + --library=$(builddir)/libplanfahr-0.0.la \ + --output $@ \ + -I$(top_builddir) \ + -I$(top_srcdir) \ + --verbose \ + --c-include="libplanfahr/libplanfahr.h" \ + --pkg=gthread-2.0 \ + --pkg-export=libplanfahr-0.0 \ + $(srcdir)/libplanfahr.h \ + $(PLANFAHR_SOURCE_FILES:%=$(srcdir)/%) \ + $(PLANFAHR_HEADER_FILES:%=$(srcdir)/%) + +girdir = $(datadir)/gir-1.0 +gir_DATA = Lpf-0.0.gir + +typelibsdir = $(libdir)/girepository-1.0 +typelibs_DATA = Lpf-0.0.typelib + +%.typelib: %.gir + $(AM_V_GEN)$(G_IR_COMPILER) \ + --includedir=$(builddir) \ + --includedir=$(girdir) \ + -o $@ $< + +CLEANFILES = $(gir_DATA) $(typelibs_DATA) + +endif # WITH_GOBJECT_INTROSPECTION diff --git a/src/libplanfahr.c b/src/libplanfahr.c new file mode 100644 index 0000000..219f532 --- /dev/null +++ b/src/libplanfahr.c @@ -0,0 +1,29 @@ +/* + * libplanfahr.c: access public transport time tables + * + * Copyright (C) 2014 Guido Günther + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; 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> + */ + +#ifndef _LIBPLANFAHR +#define _LIBPLANFAHR + +#include "lpf-manager.h" +#include "lpf-provider.h" + +#endif /* _LIBPLANFAHR */ diff --git a/src/libplanfahr.h b/src/libplanfahr.h new file mode 100644 index 0000000..8837115 --- /dev/null +++ b/src/libplanfahr.h @@ -0,0 +1,25 @@ +/* + * libplanfahr.h: access public transport time tables + * + * Copyright (C) 2014 Guido Günther + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; 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 <config.h> + diff --git a/src/libplanfahr.sym b/src/libplanfahr.sym new file mode 100644 index 0000000..c9b3d47 --- /dev/null +++ b/src/libplanfahr.sym @@ -0,0 +1,7 @@ +LIBPLANFAHR_0.0.0 { + global: + lpf_*; + local: + *; +}; + diff --git a/src/lpf-loc.c b/src/lpf-loc.c new file mode 100644 index 0000000..3e2aaac --- /dev/null +++ b/src/lpf-loc.c @@ -0,0 +1,196 @@ +/* + * lpf-loc.c: a public transport location + * + * Copyright (C) 2014 Guido Günther + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; 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 <glob.h> +#include <string.h> +#include <math.h> + +#include <glib.h> +#include <glib/gprintf.h> +#include <gmodule.h> + +#include "lpf-loc.h" +#include "lpf-priv.h" + +enum { + LPF_LOC_PROP_0 = 0, + LPF_LOC_PROP_NAME, + LPF_LOC_PROP_LONG, + LPF_LOC_PROP_LAT, +}; + +/** + * SECTION:lpf-loc + * @short_description: Location + * + * A #LpfLoc represents a location (e.g. a station) + */ + +G_DEFINE_TYPE (LpfLoc, lpf_loc, G_TYPE_OBJECT) +#define GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), LPF_TYPE_LOC, LpfLocPrivate)) + +typedef struct _LpfLocPrivate LpfLocPrivate; +struct _LpfLocPrivate { + gchar *name; + gdouble long_; + gdouble lat; + gpointer opaque; +}; + +static void +lpf_loc_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + LpfLoc *self = LPF_LOC (object); + LpfLocPrivate *priv = GET_PRIVATE(self); + + switch (property_id) { + case LPF_LOC_PROP_NAME: + g_free (priv->name); + priv->name = g_value_dup_string (value); + break; + + case LPF_LOC_PROP_LONG: + priv->long_ = g_value_get_double(value); + break; + + case LPF_LOC_PROP_LAT: + priv->lat = g_value_get_double(value); + break; + + default: + /* We don't have any other property... */ + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + + +static void +lpf_loc_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + LpfLoc *self = LPF_LOC (object); + LpfLocPrivate *priv = GET_PRIVATE(self); + + switch (property_id) { + case LPF_LOC_PROP_NAME: + g_value_set_string (value, priv->name); + break; + + case LPF_LOC_PROP_LONG: + g_value_set_double (value, priv->long_); + break; + + case LPF_LOC_PROP_LAT: + g_value_set_double (value, priv->lat); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +lpf_loc_dispose(GObject *object) +{ + GObjectClass *parent_class = G_OBJECT_CLASS (lpf_loc_parent_class); + + if (parent_class->dispose != NULL) + parent_class->dispose (object); +} + + +static void +lpf_loc_finalize (GObject *object) +{ + LpfLoc *self = LPF_LOC (object); + LpfLocPrivate *priv = GET_PRIVATE (self); + GObjectClass *parent_class = G_OBJECT_CLASS (lpf_loc_parent_class); + + g_free (priv->name); + g_free (priv->opaque); + if (parent_class->finalize != NULL) + parent_class->finalize (object); +} + + +static void +lpf_loc_class_init (LpfLocClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GParamSpec *pspec; + + object_class->dispose = lpf_loc_dispose; + object_class->finalize = lpf_loc_finalize; + g_type_class_add_private (klass, sizeof (LpfLocPrivate)); + + object_class->set_property = lpf_loc_set_property; + object_class->get_property = lpf_loc_get_property; + + pspec = g_param_spec_string ("name", + "Name", + "Get Location name", + "", G_PARAM_CONSTRUCT | G_PARAM_READWRITE); + g_object_class_install_property (object_class, LPF_LOC_PROP_NAME, pspec); + + pspec = g_param_spec_double ("long", + "Longitude", + "Get Location Longitude", + -180.0, +180.0, 0, + G_PARAM_CONSTRUCT | G_PARAM_READWRITE); + g_object_class_install_property (object_class, LPF_LOC_PROP_LONG, pspec); + + pspec = g_param_spec_double ("lat", + "latitude", + "Get Location Latitude", + -90.0, +90.0, 0, + G_PARAM_CONSTRUCT | G_PARAM_READWRITE); + g_object_class_install_property (object_class, LPF_LOC_PROP_LAT, pspec); +} + +static void +lpf_loc_init (LpfLoc *self) +{ +} + +gpointer +lpf_loc_get_opaque(LpfLoc *self) +{ + LpfLocPrivate *priv = GET_PRIVATE (self); + + return priv->opaque; +} + +gpointer +lpf_loc_set_opaque(LpfLoc *self, gpointer opaque) +{ + LpfLocPrivate *priv = GET_PRIVATE (self); + + return priv->opaque = opaque; +} diff --git a/src/lpf-loc.h b/src/lpf-loc.h new file mode 100644 index 0000000..fa1b15f --- /dev/null +++ b/src/lpf-loc.h @@ -0,0 +1,62 @@ +/* + * lpf-loc.h: a public transport location + * + * Copyright (C) 2014 Guido Günther + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; 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> + */ + +#ifndef _LPF_LOC_H +#define _LPF_LOC_H + +#include <glib-object.h> + +G_BEGIN_DECLS + +#define LPF_TYPE_LOC lpf_loc_get_type() + +#define LPF_LOC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), LPF_TYPE_LOC, LpfLoc)) + +#define LPF_LOC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), LPF_TYPE_LOC, LpfLocClass)) + +#define LPF_IS_LOC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), LPF_TYPE_LOC)) + +#define LPF_IS_LOC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), LPF_TYPE_LOC)) + +#define LPF_LOC_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), LPF_TYPE_LOC, LpfLocClass)) + +typedef struct { + GObject parent; +} LpfLoc; + +typedef struct { + GObjectClass parent_class; +} LpfLocClass; + +GType lpf_loc_get_type (void); +/* FIXME: Only used by providers, don't export symbol */ +gpointer lpf_loc_get_opaque (LpfLoc *self); +gpointer lpf_loc_set_opaque (LpfLoc *self, gpointer opaque); + +G_END_DECLS + +#endif /* _LPF_LOC_H */ diff --git a/src/lpf-manager.c b/src/lpf-manager.c new file mode 100644 index 0000000..e2692d2 --- /dev/null +++ b/src/lpf-manager.c @@ -0,0 +1,273 @@ +/* + * lpf-manager.h: manage planfahr providers + * + * Copyright (C) 2014 Guido Günther + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; 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 "lpf-provider.h" +#include "lpf-manager.h" +#include "lpf-priv.h" + +#include <glob.h> +#include <string.h> + +#include <glib.h> +#include <glib/gprintf.h> +#include <gmodule.h> + +#define PROVIDER_LIBBASE "libplanfahr-provider-" + + +/** + * SECTION:lpf-manager + * @short_description: Provider Manager + * @see_also: #LpfProvider + * + * A #LpfManager handles the different public transport information providers. + */ + + +G_DEFINE_TYPE (LpfManager, lpf_manager, G_TYPE_OBJECT) +#define GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), LPF_TYPE_MANAGER, LpfManagerPrivate)) + +typedef struct _LpfManagerPrivate LpfManagerPrivate; +struct _LpfManagerPrivate { + GSList *available; /* available providers */ + GSList *active; /* active providers */ +}; + + +GQuark +lpf_manager_error_quark (void) +{ + return g_quark_from_static_string ("lpf-manager-error-quark"); +} + + +static gchar *plugin_path(const char *name) +{ + gchar *dir = NULL; + gchar *prefix = NULL; + gchar *ret; + + dir = g_strdup(g_getenv(LPF_PROVIDERS_ENV)); + if (!dir) + dir = g_strdup(LPF_PROVIDERS_DIR); + prefix = g_strjoin("/", dir, PROVIDER_LIBBASE, NULL); + ret = g_strjoin(NULL, prefix, name, ".", G_MODULE_SUFFIX, NULL); + + g_free(dir); + g_free(prefix); + return ret; +} + +/** + * lpf_manager_get_available_providers: + * Returns: (transfer full): List of available providers + */ +GStrv lpf_manager_get_available_providers(void) +{ + gint i, prefix_len; + glob_t globbuf; + gchar *pattern = NULL; + GStrv providers = NULL; + gchar **tmp; + + if (!g_module_supported ()) { + g_warning ("GModules are not supported on your platform!"); + return NULL; + } + + pattern = plugin_path("*"); + prefix_len = 1 + g_strrstr(pattern, "/") + strlen(PROVIDER_LIBBASE) - pattern ; + LPF_DEBUG("Looking for providers in %s", pattern); + if (glob(pattern, GLOB_NOSORT, NULL, &globbuf)) + goto out; + + if (!(providers = g_try_malloc (sizeof(gchar) * globbuf.gl_pathc + 1))) { + goto out; + } + + for (i = 0; i < globbuf.gl_pathc; i++) { + tmp = g_strsplit(globbuf.gl_pathv[i] + prefix_len, ".", 2); + if (!tmp) { + providers[i] = NULL; + goto out; + } + providers[i] = g_strdup(tmp[0]); + g_strfreev(tmp); + } + providers[globbuf.gl_pathc] = NULL; + + out: + globfree(&globbuf); + g_free (pattern); + return providers; +} + +static LpfProvider* +load_provider (const char *path, GError **error) +{ + LpfProvider *provider = NULL; + GModule *module; + LpfProviderCreateFunc create_func; + int *major_provider_version, *minor_provider_version; + + module = g_module_open (path, G_MODULE_BIND_LAZY); + if (!module) { + g_set_error (error, + LPF_MANAGER_ERROR, + LPF_MANAGER_ERROR_ACTIVATION_FAILED, + "Could not load provider %s: %s", path, g_module_error ()); + return NULL; + } + + if (!g_module_symbol (module, "lpf_provider_major_version",(gpointer *) &major_provider_version)) { + g_set_error (error, + LPF_MANAGER_ERROR, + LPF_MANAGER_ERROR_ACTIVATION_FAILED, + "Could not load provider %s: Missing major version info", path); + goto out; + } + + if (*major_provider_version != LPF_PROVIDER_MAJOR_VERSION) { + g_set_error (error, + LPF_MANAGER_ERROR, + LPF_MANAGER_ERROR_ACTIVATION_FAILED, + "Could not load provider %s: Provider major version %d, %d is required", + path, *major_provider_version, LPF_PROVIDER_MAJOR_VERSION); + goto out; + } + + if (!g_module_symbol (module, "lpf_provider_minor_version", (gpointer *) &minor_provider_version)) { + g_set_error (error, + LPF_MANAGER_ERROR, + LPF_MANAGER_ERROR_ACTIVATION_FAILED, + "Could not load provider %s: Missing minor version info", path); + goto out; + } + + if (*minor_provider_version != LPF_PROVIDER_MINOR_VERSION) { + g_set_error (error, + LPF_MANAGER_ERROR, + LPF_MANAGER_ERROR_ACTIVATION_FAILED, + "Could not load provider %s: Provider minor version %d, %d is required", + path, *minor_provider_version, LPF_PROVIDER_MINOR_VERSION); + goto out; + } + + if (!g_module_symbol (module, "lpf_provider_create", (gpointer *) &create_func)) { + g_set_error (error, + LPF_MANAGER_ERROR, + LPF_MANAGER_ERROR_ACTIVATION_FAILED, + "Could not load provider %s: %s", path, g_module_error ()); + goto out; + } + + provider = (*create_func) (); + if (provider) { + g_object_weak_ref (G_OBJECT (provider), (GWeakNotify) g_module_close, module); + g_message ("Loaded provider %s", lpf_provider_get_name (provider)); + } else { + g_set_error (error, + LPF_MANAGER_ERROR, + LPF_MANAGER_ERROR_ACTIVATION_FAILED, + "Could not load provider %s: initialization failed", path); + } + out: + if (!provider) + g_module_close (module); + + return provider; +} + +/** + * lpf_manager_activate_provider: + * @self: a #LpfManager + * @name: the name of the provider to activate + * @error: a #GError for errorreporting or %NULL + * + * Activate the given provider + * + * Returns: (transfer none): The activated #LpfProvider + */ +LpfProvider *lpf_manager_activate_provider(LpfManager *self, + const gchar *name, + GError **error) +{ + gchar *path; + LpfProvider *provider; + LpfManagerPrivate *priv = GET_PRIVATE (self); + + path = plugin_path(name); + provider = load_provider (path, error); + if (provider) { + lpf_provider_activate(provider, G_OBJECT(self)); + priv->active = g_slist_prepend (priv->active, provider); + } + g_free(path); + return provider; +} + +static void +deactivate_provider(gpointer provider, gpointer user_data) +{ + LpfManager *manager = LPF_MANAGER (user_data); + lpf_manager_deactivate_provider (manager, provider); +} + +void +lpf_manager_deactivate_provider(LpfManager *self, LpfProvider *provider) +{ + LPF_DEBUG ("Deactivating provider %s", lpf_provider_get_name (provider)); + lpf_provider_deactivate (provider, G_OBJECT(self)); +} + +static void +lpf_manager_dispose(GObject *object) +{ + LpfManager *self = LPF_MANAGER(object); + LpfManagerPrivate *priv = GET_PRIVATE (self); + GObjectClass *parent_class = G_OBJECT_CLASS (lpf_manager_parent_class); + + /* We need to do this before dropping the ref on applet */ + g_slist_foreach (priv->active, deactivate_provider, self); + g_slist_free (priv->active); + + if (parent_class->dispose != NULL) + parent_class->dispose (object); +} + +static void +lpf_manager_class_init (LpfManagerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = lpf_manager_dispose; + g_type_class_add_private (klass, sizeof (LpfManagerPrivate)); +} + +static void +lpf_manager_init (LpfManager *self) +{ + LpfManagerPrivate *priv = GET_PRIVATE (self); + priv->active = NULL; + priv->available = NULL; +} diff --git a/src/lpf-manager.h b/src/lpf-manager.h new file mode 100644 index 0000000..8ee1b13 --- /dev/null +++ b/src/lpf-manager.h @@ -0,0 +1,85 @@ +/* + * lpf-manager.h: manage planfahr providers + * + * Copyright (C) 2014 Guido Günther + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; 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> + */ + +#ifndef _LPF_MANAGER_H +#define _LPF_MANAGER_H + +#include <glib-object.h> +#include "lpf-provider.h" + +#define LPF_PROVIDERS_ENV "LPF_PROVIDERS" + +G_BEGIN_DECLS + +/** + * LPF_MANAGER_ERROR: + * + * Error domain for the provider manager + */ +#define LPF_MANAGER_ERROR g_spawn_error_quark () + +/** + * LpfManagerError + * @LPF_MANAGER_ERROR_ACTIVATION_FAILED: activation of the provider failed + * + * Error codes returned by manager + */ +typedef enum { + LPF_MANAGER_ERROR_ACTIVATION_FAILED, +} LpfManagerError; + +#define LPF_TYPE_MANAGER lpf_manager_get_type() + +#define LPF_MANAGER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), LPF_TYPE_MANAGER, LpfManager)) + +#define LPF_MANAGER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), LPF_TYPE_MANAGER, LpfManagerClass)) + +#define LPF_IS_MANAGER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), LPF_TYPE_MANAGER)) + +#define LPF_IS_MANAGER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), LPF_TYPE_MANAGER)) + +#define LPF_MANAGER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), LPF_TYPE_MANAGER, LpfManagerClass)) + +typedef struct { + GObject parent; +} LpfManager; + +typedef struct { + GObjectClass parent_class; +} LpfManagerClass; + +GType lpf_manager_get_type (void); +GQuark lpf_manager_error_quark (void); +GStrv lpf_manager_get_available_providers(void); +LpfProvider *lpf_manager_activate_provider(LpfManager *self, + const gchar *name, + GError **error); +void lpf_manager_deactivate_provider(LpfManager *self, LpfProvider *provider); + +G_END_DECLS + +#endif /* _LPF_MANAGER_H */ diff --git a/src/lpf-priv.h b/src/lpf-priv.h new file mode 100644 index 0000000..c84180a --- /dev/null +++ b/src/lpf-priv.h @@ -0,0 +1,40 @@ +/* + * lpf-priv.h: internal functions and macros + * + * Copyright (C) 2014 Guido Günther + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; 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> + */ + +#ifndef _LPF_PRIV_H +#define _LPF_PRIV_H + +#include <config.h> + +#ifdef ENABLE_DEBUG + +#include <glib.h> +#include <glib/gprintf.h> + +#define LPF_DEBUG(fmt,...) \ + g_printf ("DEBUG: %s: " fmt "\n", __func__, ##__VA_ARGS__) +#else +#define LPF_DEBUG(fmt,...) \ + do { } while (0) +#endif /* !ENABLE_DEBUG */ + +#endif diff --git a/src/lpf-provider.c b/src/lpf-provider.c new file mode 100644 index 0000000..871037b --- /dev/null +++ b/src/lpf-provider.c @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2014 Guido Guenther <agx@sigxcpu.org> + * + * 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. + */ + +#include "config.h" + +#include "lpf-provider.h" + +G_DEFINE_TYPE (LpfProvider, lpf_provider, G_TYPE_OBJECT) + +#define GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), LPF_TYPE_PROVIDER, LpfProviderPrivate)) + +/** + * SECTION:lpf-provider + * @short_description: Public transport information provider + * @see_also: #LpfProvider + * + * A #LpfProvider represents a provider of public transport + * information such as timetables or location information + */ +/** + * LpfProviderGotLocsNotify: + * @locs: (element-type Lpf.Loc): List of found locations + * @user_data: userdata + * @err: (transfer full): #GError + * + * Callback invoked after the locations matching the query were + * received. In case of an error @locs is #NULL. + */ +/** + * LpfProviderGotTripsNotify: + * @trips: (element-type Lpf.Loc): List of found trips + * @user_data: userdata + * @err: (transfer full): #GError + * + * Callback invoked after the trips matching the query were + * received. In case of an error @trips is #NULL. + */ + +enum { + PROP_0, + PROP_NAME, + LAST_PROP +}; + +typedef struct _LpfProviderPrivate LpfProviderPrivate; +struct _LpfProviderPrivate { + char *name; +}; + +GQuark +lpf_provider_error_quark (void) +{ + return g_quark_from_static_string ("lpf-provider-error-quark"); +} + +/** + * lpf_provider_name: + * @LpfProvider: a #LpfProvider + * + * Returns: transfer-none: the providers name + */ +const char* +lpf_provider_get_name (LpfProvider *self) +{ + g_return_val_if_fail (LPF_IS_PROVIDER (self), NULL); + LpfProviderPrivate *priv = GET_PRIVATE (self); + + return priv->name; +} + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + LpfProviderPrivate *priv = GET_PRIVATE (object); + + switch (prop_id) { + case PROP_NAME: + /* construct only */ + priv->name = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static void +get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + LpfProviderPrivate *priv = GET_PRIVATE (object); + + switch (prop_id) { + case PROP_NAME: + g_value_set_string (value, priv->name); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static void +finalize (GObject *object) +{ + LpfProviderPrivate *priv = GET_PRIVATE (object); + + g_free (priv->name); +} + +/* invoked by LprManager to activate the provider */ +void +lpf_provider_activate (LpfProvider *self, GObject *obj) +{ + LpfProviderPrivate *priv = GET_PRIVATE (self); + gchar *cache_dir = NULL; + + g_return_if_fail (LPF_IS_PROVIDER (self)); + + cache_dir = g_strdup_printf("%s/%s/%s/", g_get_user_cache_dir (), PACKAGE_NAME, priv->name); + if (cache_dir) + g_mkdir_with_parents (cache_dir, 0700); + + LPF_PROVIDER_GET_CLASS (self)->activate (self, obj); + + g_free (cache_dir); +} + +/* invoked by LpfManager to activate the provider */ +void +lpf_provider_deactivate (LpfProvider *self, GObject *obj) +{ + g_return_if_fail (LPF_IS_PROVIDER (self)); + + LPF_PROVIDER_GET_CLASS (self)->deactivate (self, obj); +} + +/** + * lpf_provider_get_locs: + * @self: a #LpfProvider + * @match: locations to match + * @callback: (scope async): #LpfProviderGotLocsNotify to invoke + * once locations are available + * @user_data: (allow-none): User data for the callback + * + * Initiate a lookup for locations that match @match. Once complete + * @callback is invoked with a #GSList of matched #LpfLocation s. The + * caller is responsible for freeing the locations list via + * #lpf_provider_free_locs. + * + * Returns: 0 on success, -1 on error + */ +gint +lpf_provider_get_locs (LpfProvider *self, const char* match, LpfProviderGotLocsNotify callback, gpointer user_data) +{ + g_return_val_if_fail (LPF_IS_PROVIDER (self), -1); + g_return_val_if_fail (match, -1); + g_return_val_if_fail (callback, -1); + + return LPF_PROVIDER_GET_CLASS (self)->get_locs (self, match, callback, user_data); +} + +/** + * lpf_provider_free_locs: + * @self: a #LpfProvider + * @locs: (element-type LpfLoc): A linked list of location + * + * Free the location list + */ +void +lpf_provider_free_locs(LpfProvider *self, GSList *locs) +{ + g_slist_free_full (locs, g_object_unref); +} + +/** + * lpf_provider_get_trips: + * @self: a #LpfProvider + * @start: start of trip location + * @end: end of trip location + * @date: Date and time the trip starts as #GDateTime + * @flags: flags + * @callback: (scope async): #LpfProviderGotTripsNotify to invoke + * once trips are available + * @user_data: (allow-none): User data for the callback + * + * Initiate a lookup for trips starting at the location @start, ending + * at @end and starting (or depending on @flags) ending at @date. + * Once completed @callback is invoked with a #GSList of matched + * trips. The caller is responsible for freeing the locations list via + * #lpf_provider_free_trips. + * + * Returns: 0 on success, -1 on error + */ + +gint +lpf_provider_get_trips (LpfProvider *self, LpfLoc *start, LpfLoc *end, GDateTime *date, guint64 flags, LpfProviderGotLocsNotify callback, gpointer user_data) +{ + g_return_val_if_fail (LPF_IS_PROVIDER (self), -1); + g_return_val_if_fail (start, -1); + g_return_val_if_fail (end, -1); + g_return_val_if_fail (date, -1); + g_return_val_if_fail (callback, -1); + + return LPF_PROVIDER_GET_CLASS (self)->get_trips (self, start, end, date, flags, callback, user_data); +} + +static void +lpf_provider_class_init (LpfProviderClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (LpfProviderPrivate)); + + object_class->get_property = get_property; + object_class->set_property = set_property; + object_class->finalize = finalize; + + g_object_class_install_property + (object_class, PROP_NAME, + g_param_spec_string (LPF_PROVIDER_PROP_NAME, + "Name", + "Provider Name", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); +} + + +static void +lpf_provider_init (LpfProvider *self G_GNUC_UNUSED) +{ +} diff --git a/src/lpf-provider.h b/src/lpf-provider.h new file mode 100644 index 0000000..1c27183 --- /dev/null +++ b/src/lpf-provider.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2014 Guido Guenther <agx@sigxcpu.org> + * + * 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. + */ + +#ifndef _LPF_PROVIDER +#define _LPF_PROVIDER + +#include <glib-object.h> +#include <lpf-loc.h> + +G_BEGIN_DECLS + +#define LPF_PROVIDER_MAJOR_VERSION 0 +#define LPF_PROVIDER_MINOR_VERSION 0 + +/** + * LPF_PROVIDER_ERROR: + * + * Error domain for providers + */ +#define LPF_PROVIDER_ERROR g_spawn_error_quark () + +/** + * LpfProviderError + * @LPF_PROVIDER_ERROR_REQUEST_FAILED: a request to fetch data from a remote failed + * @LPF_PROVIDER_ERROR_PARSE_FAILED: parsing the reply failed + * + * Error codes returned by providers + */ +typedef enum { + LPF_PROVIDER_ERROR_REQUEST_FAILED, + LPF_PROVIDER_ERROR_PARSE_FAILED, +} LpfProviderError; + +#define LPF_TYPE_PROVIDER lpf_provider_get_type() + +#define LPF_PROVIDER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), LPF_TYPE_PROVIDER, LpfProvider)) + +#define LPF_PROVIDER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), LPF_TYPE_PROVIDER, LpfProviderClass)) + +#define LPF_IS_PROVIDER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), LPF_TYPE_PROVIDER)) + +#define LPF_IS_PROVIDER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), LPF_TYPE_PROVIDER)) + +#define LPF_PROVIDER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), LPF_TYPE_PROVIDER, LpfProviderClass)) + +#define LPF_PROVIDER_PROP_NAME "name" + +typedef void (*LpfProviderGotLocsNotify) (GSList *locs, gpointer user_data, GError *err); +typedef void (*LpfProviderGotTripsNotify) (GSList *trips, gpointer user_data, GError *err); + +typedef struct { + GObject parent; +} LpfProvider; + +typedef struct { + GObjectClass parent_class; + + void (*activate) (LpfProvider *self, GObject *obj); + void (*deactivate) (LpfProvider *self, GObject *obj); + + gint (*get_locs) (LpfProvider *self, const gchar *match, LpfProviderGotLocsNotify callback, gpointer user_data); + gint (*get_trips) (LpfProvider *self, LpfLoc *start, LpfLoc *end, GDateTime *date, guint64 flags, LpfProviderGotLocsNotify callback, gpointer user_data); +} LpfProviderClass; + +GType lpf_provider_get_type (void); + +LpfProvider *lpf_provider_new (void); + +typedef LpfProvider *(*LpfProviderCreateFunc) (void); +LpfProvider *lpf_provider_create (void); + +const char* lpf_provider_get_name (LpfProvider *self); +void lpf_provider_activate (LpfProvider *self, GObject *obj); +void lpf_provider_deactivate (LpfProvider *self, GObject *obj); +GQuark lpf_provider_error_quark (void); +gint lpf_provider_get_locs (LpfProvider *self, const gchar* match, LpfProviderGotLocsNotify callback, gpointer user_data); +gint lpf_provider_get_trips (LpfProvider *self, LpfLoc *start, LpfLoc *end, GDateTime *date, guint64 flags, LpfProviderGotLocsNotify callback, gpointer user_data); +void lpf_provider_free_locs (LpfProvider *self, GSList *locs); + +G_END_DECLS + +#endif /* _LPF_PROVIDER */ diff --git a/src/lpf-stop.c b/src/lpf-stop.c new file mode 100644 index 0000000..a42806c --- /dev/null +++ b/src/lpf-stop.c @@ -0,0 +1,316 @@ +/* + * lpf-loc.c: a stop during public transport + * + * Copyright (C) 2014 Guido Günther + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; 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 <glob.h> +#include <string.h> +#include <math.h> + +#include <glib.h> +#include <glib/gprintf.h> +#include <gmodule.h> + +#include "lpf-loc.h" +#include "lpf-stop.h" +#include "lpf-priv.h" + +enum { + LPF_STOP_PROP_0 = 0, + LPF_STOP_PROP_ARRIVAL, + LPF_STOP_PROP_DEPARTURE, + LPF_STOP_PROP_ARRIVAL_PLATFORM, + LPF_STOP_PROP_DEPARTURE_PLATFORM, + LPF_STOP_PROP_RT_ARRIVAL, + LPF_STOP_PROP_RT_DEPARTURE, + LPF_STOP_PROP_ARRIVAL_DELAY, + LPF_STOP_PROP_DEPARTURE_DELAY, +}; + +/** + * SECTION:lpf-stop + * @short_description: A stop during public transport + * + * A #LpfStop adds arrival and departure times to a #LpfLoc. + */ + +G_DEFINE_TYPE (LpfStop, lpf_stop, LPF_TYPE_LOC) +#define GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), LPF_TYPE_STOP, LpfStopPrivate)) + +typedef struct _LpfStopPrivate LpfStopPrivate; +struct _LpfStopPrivate { + gchar *name; + /* Planned */ + GDateTime *arr, *dep; + gchar *arr_plat, *dep_plat; + /* Predicted */ + GDateTime *rt_arr, *rt_dep; +}; + +static void +lpf_stop_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + LpfStop *self = LPF_STOP (object); + LpfStopPrivate *priv = GET_PRIVATE(self); + + switch (property_id) { + case LPF_STOP_PROP_ARRIVAL: + if (priv->arr) + g_date_time_unref (priv->arr); + priv->arr = g_value_get_boxed (value); + break; + + case LPF_STOP_PROP_DEPARTURE: + if (priv->dep) + g_date_time_unref (priv->dep); + priv->dep = g_value_get_boxed (value); + break; + + case LPF_STOP_PROP_ARRIVAL_PLATFORM: + g_free (priv->arr_plat); + priv->arr_plat = g_value_dup_string (value); + break; + + case LPF_STOP_PROP_DEPARTURE_PLATFORM: + g_free (priv->dep_plat); + priv->dep_plat = g_value_dup_string (value); + break; + + case LPF_STOP_PROP_RT_ARRIVAL: + if (priv->rt_arr) + g_date_time_unref (priv->rt_arr); + priv->rt_arr = g_value_get_boxed (value); + break; + + case LPF_STOP_PROP_RT_DEPARTURE: + if (priv->rt_dep) + g_date_time_unref (priv->rt_dep); + priv->rt_dep = g_value_get_boxed (value); + break; + + default: + /* We don't have any other property... */ + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +/** + * calc_delay: Calculate the time difference between planned and + * real time in minutes + */ +static gint +calc_delay(GDateTime *planned, GDateTime *real) +{ + if (planned == NULL || real == NULL) + return 0; + + return g_date_time_difference (planned, real) / (60 * 1000 * 1000); +} + + +static void +lpf_stop_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + LpfStop *self = LPF_STOP (object); + LpfStopPrivate *priv = GET_PRIVATE(self); + gint delay; + + switch (property_id) { + case LPF_STOP_PROP_ARRIVAL: + g_value_set_boxed (value, priv->arr); + break; + + case LPF_STOP_PROP_DEPARTURE: + g_value_set_boxed (value, priv->dep); + break; + + case LPF_STOP_PROP_ARRIVAL_PLATFORM: + g_value_set_string (value, priv->arr_plat); + break; + + case LPF_STOP_PROP_DEPARTURE_PLATFORM: + g_value_set_string (value, priv->dep_plat); + break; + + case LPF_STOP_PROP_RT_ARRIVAL: + g_value_set_boxed (value, priv->rt_arr); + break; + + case LPF_STOP_PROP_RT_DEPARTURE: + g_value_set_boxed (value, priv->rt_dep); + break; + + case LPF_STOP_PROP_ARRIVAL_DELAY: + delay = calc_delay (priv->arr, priv->rt_arr); + g_value_set_int (value, delay); + break; + + case LPF_STOP_PROP_DEPARTURE_DELAY: + delay = calc_delay (priv->dep, priv->rt_dep); + g_value_set_int (value, delay); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +lpf_stop_dispose(GObject *object) +{ + GObjectClass *parent_class = G_OBJECT_CLASS (lpf_stop_parent_class); + + if (parent_class->dispose != NULL) + parent_class->dispose (object); +} + + +static void +lpf_stop_finalize (GObject *object) +{ + LpfStop *self = LPF_STOP (object); + LpfStopPrivate *priv = GET_PRIVATE (self); + GObjectClass *parent_class = G_OBJECT_CLASS (lpf_stop_parent_class); + + g_date_time_unref (priv->dep); + g_date_time_unref (priv->arr); + g_free (priv->name); + g_free (priv->arr_plat); + g_free (priv->dep_plat); + if (priv->rt_dep) + g_date_time_unref (priv->rt_dep); + if (priv->rt_arr) + g_date_time_unref (priv->rt_arr); + if (parent_class->finalize != NULL) + parent_class->finalize (object); +} + + +static void +lpf_stop_class_init (LpfStopClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = lpf_stop_dispose; + object_class->finalize = lpf_stop_finalize; + g_type_class_add_private (klass, sizeof (LpfStopPrivate)); + + object_class->set_property = lpf_stop_set_property; + object_class->get_property = lpf_stop_get_property; + +/** + * LpfStop:departure: + * + * Train departs at this stop at this date and time + */ + g_object_class_install_property (object_class, + LPF_STOP_PROP_DEPARTURE, + g_param_spec_boxed ("departure", + "Departure", + "Departure date and time", + G_TYPE_DATE_TIME, + G_PARAM_READWRITE)); +/** + * LpfStop:arrival: + * + * Train arrives at this stop at this date and time + */ + g_object_class_install_property (object_class, + LPF_STOP_PROP_ARRIVAL, + g_param_spec_boxed ("arrival", + "Arrival", + "Arrival date and time", + G_TYPE_DATE_TIME, + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + LPF_STOP_PROP_ARRIVAL_PLATFORM, + g_param_spec_string ("arr_plat", + "Arrival Platform", + "Arrival platform", + "", + G_PARAM_READWRITE)); + g_object_class_install_property (object_class, + LPF_STOP_PROP_DEPARTURE_PLATFORM, + g_param_spec_string ("dep_plat", + "Departure platform", + "Departure platform", + "", + G_PARAM_READWRITE)); + +/** + * LpfStop:rt_departure: + * + * Real time departure time + */ + g_object_class_install_property (object_class, + LPF_STOP_PROP_RT_DEPARTURE, + g_param_spec_boxed ("rt_departure", + "Real Time Departure", + "Real Time Departure date and time", + G_TYPE_DATE_TIME, + G_PARAM_READWRITE)); +/** + * LpfStop:rt_arrival: + * + * Real time arrival time + */ + g_object_class_install_property (object_class, + LPF_STOP_PROP_RT_ARRIVAL, + g_param_spec_boxed ("rt_arrival", + "Real Time Arrival", + "Real time arrival date and time", + G_TYPE_DATE_TIME, + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + LPF_STOP_PROP_ARRIVAL_DELAY, + g_param_spec_int ("arrival_delay", + "Arrival Delay", + "Arrival delay", + INT_MIN, + INT_MAX, + 0, + G_PARAM_READABLE)); + + g_object_class_install_property (object_class, + LPF_STOP_PROP_DEPARTURE_DELAY, + g_param_spec_int ("departure_delay", + "Departure Delay", + "Departure delay", + INT_MIN, + INT_MAX, + 0, + G_PARAM_READABLE)); +} + +static void +lpf_stop_init (LpfStop *self) +{ +} diff --git a/src/lpf-stop.h b/src/lpf-stop.h new file mode 100644 index 0000000..c7a3bbf --- /dev/null +++ b/src/lpf-stop.h @@ -0,0 +1,59 @@ +/* + * lpf-stop.h: a public transport stop + * + * Copyright (C) 2014 Guido Günther + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; 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> + */ + +#ifndef _LPF_STOP_H +#define _LPF_STOP_H + +#include <glib-object.h> + +G_BEGIN_DECLS + +#define LPF_TYPE_STOP lpf_stop_get_type() + +#define LPF_STOP(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), LPF_TYPE_STOP, LpfStop)) + +#define LPF_STOP_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), LPF_TYPE_STOP, LpfStopClass)) + +#define LPF_IS_STOP(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), LPF_TYPE_STOP)) + +#define LPF_IS_STOP_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), LPF_TYPE_STOP)) + +#define LPF_STOP_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), LPF_TYPE_STOP, LpfStopClass)) + +typedef struct { + GObject parent; +} LpfStop; + +typedef struct { + GObjectClass parent_class; +} LpfStopClass; + +GType lpf_stop_get_type (void); + +G_END_DECLS + +#endif /* _LPF_STOP_H */ diff --git a/src/lpf-trip-part.c b/src/lpf-trip-part.c new file mode 100644 index 0000000..15ef7f5 --- /dev/null +++ b/src/lpf-trip-part.c @@ -0,0 +1,200 @@ +/* + * lpf-loc.c: a part of a trip via public transport + * + * Copyright (C) 2014 Guido Günther + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; 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 <glob.h> +#include <string.h> +#include <math.h> + +#include <glib.h> +#include <glib/gprintf.h> +#include <gmodule.h> + +#include "lpf-trip-part.h" +#include "lpf-stop.h" +#include "lpf-priv.h" + +enum { + LPF_TRIP_PART_PROP_0 = 0, + LPF_TRIP_PART_PROP_START, + LPF_TRIP_PART_PROP_END, + LPF_TRIP_PART_PROP_LINE, + LPF_TRIP_PART_PROP_STOPS, +}; + +/** + * SECTION:lpf-trip-part + * @short_description: A part of a trip + * + * A #LpfTripPart represents a part of a trip. It has a start and end + * #LpfStop. + */ + +G_DEFINE_TYPE (LpfTripPart, lpf_trip_part, G_TYPE_OBJECT) +#define GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), LPF_TYPE_TRIP_PART, LpfTripPartPrivate)) + +typedef struct _LpfTripPartPrivate LpfTripPartPrivate; +struct _LpfTripPartPrivate { + LpfStop *start; + LpfStop *end; + gchar *line; + GSList *stops; +}; + +static void +lpf_trip_part_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + LpfTripPart *self = LPF_TRIP_PART (object); + LpfTripPartPrivate *priv = GET_PRIVATE(self); + + switch (property_id) { + case LPF_TRIP_PART_PROP_START: + if (priv->start) + g_object_unref (priv->start); + priv->start = g_value_get_object(value); + break; + + case LPF_TRIP_PART_PROP_END: + if (priv->end) + g_object_unref (priv->end); + priv->end = g_value_get_object(value); + break; + + case LPF_TRIP_PART_PROP_LINE: + g_free (priv->line); + priv->line = g_value_dup_string(value); + break; + + case LPF_TRIP_PART_PROP_STOPS: + priv->stops = g_value_get_pointer(value); + break; + + default: + /* We don't have any other property... */ + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +lpf_trip_part_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + LpfTripPart *self = LPF_TRIP_PART (object); + LpfTripPartPrivate *priv = GET_PRIVATE(self); + + switch (property_id) { + case LPF_TRIP_PART_PROP_START: + g_value_set_object (value, priv->start); + break; + + case LPF_TRIP_PART_PROP_END: + g_value_set_object (value, priv->end); + break; + + case LPF_TRIP_PART_PROP_LINE: + g_value_set_string (value, priv->line); + break; + + case LPF_TRIP_PART_PROP_STOPS: + g_value_set_pointer (value, priv->stops); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + + +static void +lpf_trip_part_finalize (GObject *object) +{ + LpfTripPart *self = LPF_TRIP_PART (object); + LpfTripPartPrivate *priv = GET_PRIVATE (self); + GObjectClass *parent_class = G_OBJECT_CLASS (lpf_trip_part_parent_class); + + g_object_unref (priv->start); + g_object_unref (priv->end); + g_slist_free_full (priv->stops, g_object_unref); + g_free (priv->line); + g_slist_free_full (priv->stops, g_object_unref); + + if (parent_class->finalize != NULL) + parent_class->finalize (object); +} + + +static void +lpf_trip_part_class_init (LpfTripPartClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = lpf_trip_part_finalize; + g_type_class_add_private (klass, sizeof (LpfTripPartPrivate)); + + object_class->set_property = lpf_trip_part_set_property; + object_class->get_property = lpf_trip_part_get_property; + + g_object_class_install_property (object_class, + LPF_TRIP_PART_PROP_START, + g_param_spec_object ("start", + "Start", + "The start location and date/time", + LPF_TYPE_STOP, + G_PARAM_CONSTRUCT | G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + LPF_TRIP_PART_PROP_END, + g_param_spec_object ("end", + "End", + "The end location and date/time", + LPF_TYPE_STOP, + G_PARAM_CONSTRUCT | G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + LPF_TRIP_PART_PROP_LINE, + g_param_spec_string ("line", + "Line", + "The Line that operates between start and end", + "", + G_PARAM_CONSTRUCT | G_PARAM_READWRITE)); +/** + * LpfTripPart:stops: (type GSList(LpfStop)) + */ + g_object_class_install_property (object_class, + LPF_TRIP_PART_PROP_STOPS, + g_param_spec_pointer ("stops", + "Stops", + "Stops between start and end", + G_PARAM_CONSTRUCT | G_PARAM_READWRITE)); +} + +static void +lpf_trip_part_init (LpfTripPart *self) +{ +} diff --git a/src/lpf-trip-part.h b/src/lpf-trip-part.h new file mode 100644 index 0000000..f514bf6 --- /dev/null +++ b/src/lpf-trip-part.h @@ -0,0 +1,59 @@ +/* + * lpf-trip.h: a part of a trip via public transport + * + * Copyright (C) 2014 Guido Günther + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; 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> + */ + +#ifndef _LPF_TRIP_PART_H +#define _LPF_TRIP_PART_H + +#include <glib-object.h> + +G_BEGIN_DECLS + +#define LPF_TYPE_TRIP_PART lpf_trip_part_get_type() + +#define LPF_TRIP_PART(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), LPF_TYPE_TRIP_PART, LpfTripPart)) + +#define LPF_TRIP_PART_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), LPF_TYPE_TRIP_PART, LpfTripPartClass)) + +#define LPF_IS_TRIP_PART(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), LPF_TYPE_TRIP_PART)) + +#define LPF_IS_TRIP_PART_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), LPF_TYPE_TRIP_PART)) + +#define LPF_TRIP_PART_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), LPF_TYPE_TRIP_PART, LpfTripPartClass)) + +typedef struct { + GObject parent; +} LpfTripPart; + +typedef struct { + GObjectClass parent_class; +} LpfTripPartClass; + +GType lpf_trip_part_get_type (void); + +G_END_DECLS + +#endif /* _LPF_TRIP_PART_H */ diff --git a/src/lpf-trip.c b/src/lpf-trip.c new file mode 100644 index 0000000..aa874db --- /dev/null +++ b/src/lpf-trip.c @@ -0,0 +1,152 @@ +/* + * lpf-loc.c: a trip via public transport + * + * Copyright (C) 2014 Guido Günther + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; 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 <glob.h> +#include <string.h> +#include <math.h> + +#include <glib.h> +#include <glib/gprintf.h> +#include <gmodule.h> + +#include "lpf-trip.h" +#include "lpf-loc.h" +#include "lpf-priv.h" + +enum { + LPF_TRIP_PROP_0 = 0, + LPF_TRIP_PROP_PARTS, +}; + +/** + * SECTION:lpf-trip + * @short_description: A trip via public transport + * + * A #LpfTrip represents a trip. It consists of several #LpfTripParts each with a start + * and end location. + */ + +G_DEFINE_TYPE (LpfTrip, lpf_trip, G_TYPE_OBJECT) +#define GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), LPF_TYPE_TRIP, LpfTripPrivate)) + +typedef struct _LpfTripPrivate LpfTripPrivate; +struct _LpfTripPrivate { + GSList *parts; +}; + +static void +lpf_trip_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + LpfTrip *self = LPF_TRIP (object); + LpfTripPrivate *priv = GET_PRIVATE(self); + + switch (property_id) { + case LPF_TRIP_PROP_PARTS: + priv->parts = g_value_get_pointer(value); + break; + + default: + /* We don't have any other property... */ + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +lpf_trip_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + LpfTrip *self = LPF_TRIP (object); + LpfTripPrivate *priv = GET_PRIVATE(self); + + switch (property_id) { + case LPF_TRIP_PROP_PARTS: + g_value_set_pointer (value, priv->parts); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + + +static void +lpf_trip_dispose(GObject *object) +{ + GObjectClass *parent_class = G_OBJECT_CLASS (lpf_trip_parent_class); + + if (parent_class->dispose != NULL) + parent_class->dispose (object); +} + + +static void +lpf_trip_finalize (GObject *object) +{ + LpfTrip *self = LPF_TRIP (object); + LpfTripPrivate *priv = GET_PRIVATE (self); + GObjectClass *parent_class = G_OBJECT_CLASS (lpf_trip_parent_class); + + g_slist_free_full (priv->parts, g_object_unref); + + if (parent_class->finalize != NULL) + parent_class->finalize (object); +} + + +static void +lpf_trip_class_init (LpfTripClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = lpf_trip_dispose; + object_class->finalize = lpf_trip_finalize; + g_type_class_add_private (klass, sizeof (LpfTripPrivate)); + + object_class->set_property = lpf_trip_set_property; + object_class->get_property = lpf_trip_get_property; + + +/** + * LpfTrip:parts: (type GSList(LpfTripPart)) + * + * The parts of a trip as #LpfTripPart. + */ + g_object_class_install_property (object_class, + LPF_TRIP_PROP_PARTS, + g_param_spec_pointer ("parts", + "trip parts", + "The parts of the trip", + G_PARAM_CONSTRUCT | G_PARAM_READWRITE)); +} + +static void +lpf_trip_init (LpfTrip *self) +{ +} diff --git a/src/lpf-trip.h b/src/lpf-trip.h new file mode 100644 index 0000000..d0f1542 --- /dev/null +++ b/src/lpf-trip.h @@ -0,0 +1,59 @@ +/* + * lpf-trip.h: a trip via public transport + * + * Copyright (C) 2014 Guido Günther + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; 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> + */ + +#ifndef _LPF_TRIP_H +#define _LPF_TRIP_H + +#include <glib-object.h> + +G_BEGIN_DECLS + +#define LPF_TYPE_TRIP lpf_trip_get_type() + +#define LPF_TRIP(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), LPF_TYPE_TRIP, LpfTrip)) + +#define LPF_TRIP_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), LPF_TYPE_TRIP, LpfTripClass)) + +#define LPF_IS_TRIP(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), LPF_TYPE_TRIP)) + +#define LPF_IS_TRIP_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), LPF_TYPE_TRIP)) + +#define LPF_TRIP_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), LPF_TYPE_TRIP, LpfTripClass)) + +typedef struct { + GObject parent; +} LpfTrip; + +typedef struct { + GObjectClass parent_class; +} LpfTripClass; + +GType lpf_trip_get_type (void); + +G_END_DECLS + +#endif /* _LPF_TRIP_H */ diff --git a/src/providers/Makefile.am b/src/providers/Makefile.am new file mode 100644 index 0000000..5bc3a84 --- /dev/null +++ b/src/providers/Makefile.am @@ -0,0 +1,32 @@ +include $(top_srcdir)/flymake.mk + +SUBDIRS = tests + +pkglibdir = $(LPF_PROVIDERS_DIR) + +pkglib_LTLIBRARIES = \ + libplanfahr-provider-de-db.la \ + $(NULL) + +libplanfahr_provider_de_db_la_SOURCES = \ + de-db.h \ + de-db.c \ + hafas-bin6.h \ + hafas-bin6.c \ + $(NULL) + +libplanfahr_provider_de_db_la_CFLAGS = \ + $(GIO2_CFLAGS) \ + $(GOBJECT2_CFLAGS) \ + $(GTHREAD2_CFLAGS) \ + $(LIBXML2_CFLAGS) \ + $(LIBSOUP_CFLAGS) \ + $(WARN_CFLAGS) \ + -I$(top_srcdir)/src \ + $(NULL) + +libplanfahr_provider_de_db_la_LDFLAGS = \ + -module \ + -avoid-version \ + $(LIBSOUP_LIBS) \ + $(NULL) diff --git a/src/providers/de-db.c b/src/providers/de-db.c new file mode 100644 index 0000000..d58aba2 --- /dev/null +++ b/src/providers/de-db.c @@ -0,0 +1,751 @@ +/* + * 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> +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-trip.h" +#include "lpf-trip-part.h" +#include "lpf-stop.h" +#include "de-db.h" +#include "hafas-bin6.h" + +#define LOC_URL "http://mobile.bahn.de/bin/mobil/query.exe/en" +#define TRIPS_URL "http://reiseauskunft.bahn.de/bin/query.exe/eox" + +#define PROVIDER_NAME "de_db" + +/* transfers data between invocation and passed in callback */ +typedef struct _LpfProviderGotItUserData { + LpfProvider *self; + gpointer callback; + gpointer user_data; +} LpfProviderGotItUserData; + +G_DEFINE_TYPE (LpfProviderDeDb, lpf_provider_de_db, LPF_TYPE_PROVIDER) +#define GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), LPF_TYPE_PROVIDER_DE_DB, LpfProviderDeDbPrivate)) + +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 { + SoupSession *session; + char *logdir; + gboolean debug; +}; + +static void +lpf_provider_de_db_finalize (GObject *object) +{ + G_OBJECT_CLASS (lpf_provider_de_db_parent_class)->finalize (object); +} + +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 = 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 (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 (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 = 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 = 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 = hafas_bin6_date_time (base, day_off, h, m); + g_object_set (start, "rt_departure", dt, NULL); + } + + if (pd->dep_pred != HAFAS_BIN6_NO_REALTIME) { + h = pd->dep_pred / 100; + m = pd->dep_pred % 100; + dt = hafas_bin6_date_time (base, day_off, h, m); + g_object_set (end, "rt_arrival", 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", hafas_bin6_date_time (base, + day_off, + stop->arr / 100, + stop->arr % 100), + "departure", hafas_bin6_date_time (base, + day_off, + stop->dep / 100, + stop->dep % 100), + NULL + ); + + if (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 = inlen * 2; + 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); + while (TRUE) { + g_free (outbuf); + outbuf = g_try_malloc (outbuflen); + if (outbuf == NULL) + break; + conv = g_converter_convert (decomp, + in, inlen, + outbuf, outbuflen, + G_CONVERTER_INPUT_AT_END, + &read, &written, + err); + if (conv == G_CONVERTER_ERROR) { + if ((*err)->code == G_IO_ERROR_NO_SPACE) { + outbuflen *= 2; + g_clear_error (err); + continue; + } else { + break; + } + } else if (conv == G_CONVERTER_FINISHED) { + if (read != inlen) { + g_warning ("Expected %" G_GSIZE_FORMAT + ", got %" G_GSIZE_FORMAT, inlen, read); + break; + } + ret = 0; + break; + } else { + g_warning ("Unhandled condition %d", conv); + ret = -1; + break; + } + } + + if (ret == 0) { + *out = outbuf; + *outlen = written; + } 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); + LpfProviderClass *plugin_class = LPF_PROVIDER_CLASS (klass); + + g_type_class_add_private (klass, sizeof (LpfProviderDeDbPrivate)); + + plugin_class->activate = lpf_provider_de_db_activate; + plugin_class->deactivate = lpf_provider_de_db_deactivate; + plugin_class->get_locs = lpf_provider_de_db_get_locs; + plugin_class->get_trips = lpf_provider_de_db_get_trips; + object_class->finalize = lpf_provider_de_db_finalize; +} + +static void +lpf_provider_de_db_init (LpfProviderDeDb *self) +{ +} + +LpfProviderDeDb * +lpf_provider_de_db_new (void) +{ + return g_object_new (LPF_TYPE_PROVIDER_DE_DB, LPF_PROVIDER_PROP_NAME, + PROVIDER_NAME, NULL); +} diff --git a/src/providers/de-db.h b/src/providers/de-db.h new file mode 100644 index 0000000..a0278c9 --- /dev/null +++ b/src/providers/de-db.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2014 Guido Guenther <agx@sigxcpu.org> + * + * 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. + */ + +#ifndef _LPF_PROVIDER_DE_DB_H +#define _LPF_PROVIDER_DE_DB_H + +#include "lpf-provider.h" + +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)) +#define LPF_PROVIDER_DE_DB_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), LPF_TYPE_PROVIDER_DE_DB, LpfProviderDe_DbClass)) +#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)) + +typedef struct { + LpfProvider parent; +} LpfProviderDeDb; + +typedef struct { + LpfProviderClass parent_class; +} LpfProviderDeDbClass; + +GType lpf_provider_de_db_get_type (void); + +LpfProviderDeDb *lpf_provider_de_db_new (void); + +G_END_DECLS +#endif /* _LPF_PROVIDER_DB */ diff --git a/src/providers/hafas-bin6-format.h b/src/providers/hafas-bin6-format.h new file mode 100644 index 0000000..e2878b0 --- /dev/null +++ b/src/providers/hafas-bin6-format.h @@ -0,0 +1,323 @@ +/* + * hafas-bin6-format.h Hafas Binary 6 format + * + * 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> + */ + +#ifndef _HAFAS_BIN6_FORMAT_H +#define _HAFAS_BIN6_FORMAT_H + +/** + * Hafas Binary format version 6 + * + * The binary format uses several headers that mark contain the start + * different tables (*_tbl variables). An byte offset into such a table + * is marked by a variable ending in _off. If the elements in a table + * are fixed size (array) indexes are used. These end in _idx. + * + * Based on AbstractHafasProvider in public-transport-enabler and + * + */ + +/** + * HafasBin6Header: + * hafas binary format header version 6 + * +*/ +typedef struct _HafasBin6Header { + guint16 version; /* 0x00 */ + gchar start[14]; /* HafasBin6Loc */ /* 0x02 */ + gchar end[14]; /* HafasBin6Loc */ /* 0x10 */ + guint16 num_trips; /* # of trips in this resp. */ /* 0x1e */ + guint32 service_tbl; /* Start of service table */ /* 0x20 */ + guint32 strings_tbl; /* start of strings table */ /* 0x24 */ + gint16 days; /* date base in days since 1980 */ /* 0x28 */ + gchar unknown0[12]; /* 0x2a */ + guint32 stations_tbl; /* start of stations table */ /* 0x36 */ + guint32 comments_tbl; /* start of comments table */ /* 0x3a */ + gchar unknown1[8]; /* 0x3e */ + guint32 ext; /* Start of extenson header */ /* 0x46 */ +} __attribute__ ((packed)) HafasBin6Header; + + +typedef enum _HafasBin6LocTypes { + HAFAS_BIN6_LOC_TYPE_STATION = 1, + HAFAS_BIN6_LOC_TYPE_ADDRESS = 2, + HAFAS_BIN6_LOC_TYPE_POI = 3, +} HafasBin6LocTypes; + +/** + * HafasBin6Loc: + * + * Start and end location from #HafasBin6Header + */ +typedef struct _HafasBin6Loc { + guint16 name_off; /* offset into string table */ /* 0x00 */ + guint16 unknown0; /* 0x02 */ + guint16 type; /* as in HafasBinLocTypes */ /* 0x04 */ + gint32 lon; /* longitude * 10^6 */ /* 0x06 */ + gint32 lat; /* latitude * 10^6 */ /* 0x0a */ +} __attribute__ ((packed)) HafasBin6Loc; + +/** + * HafasBin6Trip: + * + * One entry per trip, follows directly after #HafasBin6Header + */ +typedef struct _HafasBin6Trip { + guint16 service_off; /* offset into service days table */ /* 0x00 */ + guint32 parts_off; /* offset after header */ /* 0x02 */ + guint16 part_cnt; /* number of parts in this trip */ /* 0x06 */ + guint16 changes; /* number of train changes in this trip */ /* 0x08 */ + guint16 unknown0; /* 0x0a */ +} __attribute__ ((packed)) HafasBin6Trip; + +/** + * HafasBin6Station: + * + * A station in the stations table as pointed to by the #HafasBin6Header + */ +typedef struct _HafasBin6Station { + guint16 name_off; /* offset in string table */ /* 0x00 */ + guint32 id; /* id of this station */ /* 0x02 */ + gint32 lon; /* longitute * 10^6 */ /* 0x06 */ + gint32 lat; /* latitude * 10^6 */ /* 0x0a */ +} __attribute__ ((packed)) HafasBin6Station; + +/** + * HafasBin6Errors: + * + * Errors in err field of #HafasBin6ExtHeader + */ +typedef enum _HafasBin6Errors { + HAFAS_BIN6_ERROR_NONE = 0, + HAFAS_BIN6_ERROR_SESSION_EXPIRED = 1, + /* TBD */ +} HafasBin6Errors; + +/** + * HafasBin6ExtHeader: + * + * Hafas Binary format version 6 extension header. If attrs_index0 != + * 0 and length >= 0x30 then attribute indexes for all trips follow + * after this header. Location of this header is determined by ext in + * the #HafasBin6Header. + */ +typedef struct _HafasBin6ExtHeader { + guint32 length; /* 0x00 */ + guint32 unknown0; /* 0x04 */ + guint16 seq; /* 0x08 */ + guint16 req_id_off; /* request id offset into string table */ /* 0x0a */ + guint32 details_tbl; /* offset to trip details header */ /* 0x0c */ + guint16 err; /* 0x10 */ + gchar unknown1[14]; /* 0x12 */ + guint16 enc_off; /* encoding offset into string table */ /* 0x20 */ + guint16 ld_off; /* ld offset into string table */ /* 0x22 */ + guint16 attrs_off; /* attributes offset */ /* 0x24 */ + gchar pad[6]; /* 0x26 */ + guint32 attrs_index0; /* attribute indexes start here */ /* 0x2c */ +} HafasBin6ExtHeader; + +/** + * HafasBin6TripDetailsHeader: + * + * Header for all trip details following this header. The details_index_off + * is relative to this header and stores just another offset to the final + * location of the #HafasBin6TripDetail. + */ +typedef struct _HafasBin6TripDetailsHeader { + guint16 version; /* 0x00 */ + guint16 unknown0; /* 0x02 */ + guint16 details_index_off; /* 0x04 */ + guint16 part_details_off; /* size of part details struct */ /* 0x06 */ + guint16 part_detail_size; /* 0x08 */ + guint16 stop_size; /* size of stop struct */ /* 0x0a */ + guint16 stops_off; /* 0x0c */ +} HafasBin6TripDetailsHeader; + +/** + * HafasBin6ServiceDay: + * + * The service day table is used to calculate day offsets on top of + * #HafasBin6Header days. See #hafas_binary_parse_service_day for details. + */ +typedef struct _HafasBin6ServiceDay { + guint16 days_off; /* offset into string table */ /* 0x00 */ + guint16 byte_base; /* days * 8 */ /* 0x02 */ + guint16 byte_len; /* # of bytes to follow */ /* 0x04 */ + gchar byte0; /* first day offset byte */ /* 0x06 */ +} __attribute__ ((packed)) HafasBin6ServiceDay; + +typedef enum _HafasBin6TripDetailRTStatus { + HAFAS_BIN6_RTSTATUS_CANCELLED = 0x0002, + HAFAS_BIN6_RTSTATUS_NORMAL = 0xFFFF, +} HafasBin6TripDetailRTStatus; + +/** + * HafasBin6TripDetail: + * + * Status of this trip + */ +typedef struct _HafasBin6TripDetail { + guint16 rt_status; /* real time status */ /* 0x00 */ + guint16 delay; /* 0x02 */ +} HafasBin6TripDetail; + +typedef enum _HafasBin6TripPartType { + footway = 1, + train = 2, +} HafasBin6TripPartType; + +/** + * HafasBin6TripPartType: + * + * A part of the trip using one "vehicle". + */ +typedef struct _HafasBin6TripPart { + guint16 dep; /* planned departure time at start */ /* 0x00 */ + guint16 dep_off; /* start, offset into stations table */ /* 0x02 */ + guint16 arr; /* planned arrival time at end */ /* 0x04 */ + guint16 arr_off; /* end, offset into stations table */ /* 0x06 */ + guint16 type; /* 0x08 */ + guint16 line_off; /* offset into string table */ /* 0x0a */ + guint16 dep_pos_off; /* offset into string table */ /* 0x0c */ + guint16 arr_pos_off; /* offset into string table */ /* 0x0e */ + guint16 attr_index; /* 0x10 */ + guint16 comments_off; /* offset into comments table */ /* 0x12 */ +} HafasBin6TripPart; + +/** + * HAFAS_BIN6_NO_PLATFORM: no platform for this stop + */ +#define HAFAS_BIN6_NO_PLATFORM "---" +/** + * HAFAS_BIN6_NO_REALTIME: no realtime information for this stop + */ +#define HAFAS_BIN6_NO_REALTIME 0xFFFF + +/** + * HafasBin6TripPartDetail: + * + * More details of one part of the trip. In contrast to + * #HafasBin6TripPart times and platforms are predicted instead of + * planned values. + */ +typedef struct _HafasBin6TripPartDetail { + guint16 dep_pred; /* predicted departure time */ /* 0x00 */ + guint16 arr_pred; /* predicted arrival time */ /* 0x02 */ + guint16 dep_pos_pred_off; /* offset into string table */ /* 0x04 */ + guint16 arr_pos_pred_off; /* offset into string table */ /* 0x06 */ + guint32 unknown0; /* 0x08 */ + guint16 stop_index; /* index of first stop */ /* 0x0c */ + guint16 stops_cnt; /* number of stops */ /* 0x0e */ +} HafasBin6TripPartDetail; + +/** + * HafasBin6Stop: + * + * Platforms and times (planned and predicted) for stops on the trip + */ +typedef struct _HafasBin6TripStop { + guint16 dep; /* planned departure time at this stop */ /* 0x00 */ + guint16 arr; /* planned arrival time at this stop */ /* 0x02 */ + guint16 dep_pos_off; /* platform, offset into string table */ /* 0x04 */ + guint16 arr_pos_off; /* platfrom, offset into string table */ /* 0x06 */ + guint32 unknown0; /* 0x08 */ + guint16 dep_pred; /* predicted departure time at this stop */ /* 0x0c */ + guint16 arr_pred; /* predicted arrival time at this stop */ /* 0x0e */ + guint16 dep_pos_pred_off; /* offset into string table */ /* 0x10 */ + guint16 arr_pos_pred_off; /* offset into string table */ /* 0x12 */ + guint32 unknown1; /* 0x14 */ + guint16 stop_idx; /* index into stations table */ /* 0x18 */ + +} __attribute__ ((packed)) HafasBin6TripStop; + +typedef struct _HafasBin6Attr { + guint16 key_off; /* offset into string table */ /* 0x00 */ + guint16 val_off; /* offset into string table */ /* 0x02 */ +} HafasBin6Attr; + +/* + * Access to headers and tables + */ +/* the main header */ +#define HAFAS_BIN6_HEADER(data) ((HafasBin6Header*)(data)) +/* extension header */ +#define HAFAS_BIN6_EXT_HEADER(data) ((HafasBin6ExtHeader*)((data) + (((HafasBin6Header*)(data))->ext))) +/* trip details header */ +#define HAFAS_BIN6_TRIP_DETAILS_HEADER(data) ((HafasBin6TripDetailsHeader*)((data) + HAFAS_BIN6_EXT_HEADER(data)->details_tbl)) +/* trip details index table */ +#define _HAFAS_BIN6_TRIP_DETAILS_INDEX(data) ((gchar*)(((gchar*)HAFAS_BIN6_TRIP_DETAILS_HEADER(data)) + \ + (HAFAS_BIN6_TRIP_DETAILS_HEADER(data)->details_index_off))) +/* trip part details index table */ +#define _HAFAS_BIN6_TRIP_PART_DETAILS_INDEX(data) ((gchar*)(((gchar*)HAFAS_BIN6_TRIP_DETAILS_HEADER(data)) + \ + (HAFAS_BIN6_TRIP_DETAILS_HEADER(data)->part_details_off))) +/* stops index table */ +#define _HAFAS_BIN6_STOPS_INDEX(data) ((gchar*)(((gchar*)HAFAS_BIN6_TRIP_DETAILS_HEADER(data)) + \ + (HAFAS_BIN6_TRIP_DETAILS_HEADER(data)->stops_off))) +/* trips table */ +#define _HAFAS_BIN6_TRIPS_TABLE(data) ((gchar*)((data) + sizeof(HafasBin6Header))) + +/* + * Access to the data structures making up the trips + */ +/* Start and end of trip */ +#define HAFAS_BIN6_START(data) ((HafasBin6Loc*)(((HafasBin6Header*)data)->start)) +#define HAFAS_BIN6_END(data) ((HafasBin6Loc*)(((HafasBin6Header*)data)->end)) +/* trip details and trip part details use an indirection via a common index table */ +#define _HAFAS_BIN6_TRIP_INDEX(data, idx) \ + (*((guint16*)(_HAFAS_BIN6_TRIP_DETAILS_INDEX(data) + 2 * (idx)))) +/* Get a string at offset off */ +#define HAFAS_BIN6_STR(data, off) \ + ((const gchar*)((gchar*)((data) + (((HafasBin6Header*)(data))->strings_tbl) + (off)))) +/* Get the n-th station from the staitons table */ +#define HAFAS_BIN6_STATION(data, idx) \ + ((HafasBin6Station*)((data) + (((HafasBin6Header*)(data))->stations_tbl) + ((idx) * sizeof(HafasBin6Station)))) +/* Get the idx-th trip */ +#define HAFAS_BIN6_TRIP(data, idx) \ + ((HafasBin6Trip*)((data) + sizeof(HafasBin6Header) + ((idx) * sizeof(HafasBin6Trip)))) +/* Get the service table entry for the idx-th trip */ +#define HAFAS_BIN6_SERVICE_DAY(data, idx) \ + ((HafasBin6ServiceDay*)((data) + (((HafasBin6Header*)(data))->service_tbl) + \ + (HAFAS_BIN6_TRIP(data, idx)->service_off))) +/* Get the idy-th part of the idx-th trip */ +#define HAFAS_BIN6_TRIP_PART(data, idx, idy) \ + ((HafasBin6TripPart*)(_HAFAS_BIN6_TRIPS_TABLE(data) + \ + (HAFAS_BIN6_TRIP(data, idx)->parts_off) + \ + idy * sizeof(HafasBin6TripPart))) +/* Get the trip details for the idx-th trip */ +#define HAFAS_BIN6_TRIP_DETAIL(data, idx) \ + ((HafasBin6TripDetail*)(_HAFAS_BIN6_TRIP_DETAILS_INDEX(data) + \ + _HAFAS_BIN6_TRIP_INDEX(data, idx))) +/* Get the trip part details for the idx-th trip and the idy-th part */ +#define HAFAS_BIN6_TRIP_PART_DETAIL(data, idx, idy) \ + ((HafasBin6TripPartDetail*)(_HAFAS_BIN6_TRIP_PART_DETAILS_INDEX(data) + \ + _HAFAS_BIN6_TRIP_INDEX(data, idx) + \ + ((idy) * sizeof(HafasBin6TripPartDetail)))) +/* Get the idz-th stop of the idy-th part in the idx-th trip */ +#define HAFAS_BIN6_STOP(data, idx, idy, idz) \ + ((HafasBin6TripStop*)(_HAFAS_BIN6_STOPS_INDEX(data) + \ + (HAFAS_BIN6_TRIP_PART_DETAIL(data, idx, idy)->stop_index) * sizeof(HafasBin6TripStop) + \ + idz * sizeof(HafasBin6TripStop))) + + +/* Convert hafas longitude/latitude information to floating point */ +#define HAFAS_BIN6_LL_DOUBlE(l) ((gdouble) (l) / 1000000.0) + +#endif /* _HAFAS_BINARY_H */ diff --git a/src/providers/hafas-bin6.c b/src/providers/hafas-bin6.c new file mode 100644 index 0000000..e860284 --- /dev/null +++ b/src/providers/hafas-bin6.c @@ -0,0 +1,119 @@ +/* + * hafas-bin6.c: HAFAS Binary Format Version 6 provider + * + * 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 <config.h> +#include <libsoup/soup.h> + + +#include "hafas-bin6.h" +#include "lpf-loc.h" +#include "lpf-priv.h" + +gint +hafas_bin6_parse_station(const gchar *data, guint16 off, LpfLoc *loc, const char *enc) +{ + gchar *name; + HafasBin6Station *station; + gdouble lon, lat; + gint ret = -1; + + station = HAFAS_BIN6_STATION(data, off); + + name = g_convert (HAFAS_BIN6_STR(data, station->name_off), + -1, + "utf-8", + enc, + NULL, NULL, NULL); + if (name == NULL) { + g_warning ("Failed to convert station name at %d", off); + goto err; + } + lon = HAFAS_BIN6_LL_DOUBlE (station->lon); + lat = HAFAS_BIN6_LL_DOUBlE (station->lat); + + LPF_DEBUG("name: %s", name); + g_object_set (loc, "name", name, "long", lon, "lat", lat, + NULL); + ret = 0; +err: + return ret; +} + + +/** + * hafas_bin6_parse_service_day: + * + * Parse a servide day entry and return the offset from the base date in days. + */ +guint +hafas_bin6_parse_service_day (const char *data, int idx) +{ + gint i; + gchar bits; + guint off; + const HafasBin6ServiceDay *s; + + s = HAFAS_BIN6_SERVICE_DAY(data, idx); + off = s->byte_base * 8; + + /* look at all service bytes */ + for (i = 0; i < s->byte_len; i++) { + bits = (&(s->byte0))[i]; + if (bits == 0) { /* zero means +8 days */ + off += 8; + continue; + } + + /* count leading zeros meaning +1 day */ + while ((bits & 0x80) == 0) { + bits <<= 1; + off++; + } + break; + } + return off; +} + +/** + * hafas_bin6_date_time: + * @base_days: day off set from 1980-01-01 + * @off_days: day offset from base_days + * @hours: hour trip starts/ends + * @minutes: minute the trip starts/ends + * + * Calculate date and time from hafas bin 6 input + * + * Returns: the travel date and time as #GDateTime + */ +GDateTime* +hafas_bin6_date_time(guint base_days, guint off_days, guint hours, guint min) +{ + /* FIXME: should we always use Europe/Berlin as TZ? */ + GDateTime *dt, *base = g_date_time_new_local (1979, 12, 31, 0, 0, 0); + + dt = g_date_time_add (base, + (base_days + off_days) * G_TIME_SPAN_DAY + + hours * G_TIME_SPAN_HOUR + + min * G_TIME_SPAN_MINUTE); + g_date_time_unref (base); + return dt; +} diff --git a/src/providers/hafas-bin6.h b/src/providers/hafas-bin6.h new file mode 100644 index 0000000..a402da4 --- /dev/null +++ b/src/providers/hafas-bin6.h @@ -0,0 +1,33 @@ +/* + * hafas-bin6.h: hafas binary format version 6 + * + * 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> + */ + +#ifndef _HAFAS_BIN6_H +#define _HAFAS_BIN6_H + +#include "hafas-bin6-format.h" +#include "lpf-loc.h" + +gint hafas_bin6_parse_station(const gchar *data, guint16 off, LpfLoc *station, const char *enc); +guint hafas_bin6_parse_service_day (const char *data, int idx); +GDateTime* hafas_bin6_date_time(guint base_days, guint off_days, guint hours, guint min); + +#endif /* _HAFAS_BIN6_H */ diff --git a/src/providers/tests/Makefile.am b/src/providers/tests/Makefile.am new file mode 100644 index 0000000..721885d --- /dev/null +++ b/src/providers/tests/Makefile.am @@ -0,0 +1,53 @@ +include $(top_srcdir)/flymake.mk + +check_PROGRAMS = de-db hafas-bin6 + +AM_CFLAGS = \ + $(GLIB2_CFLAGS) \ + $(GIO2_CFLAGS) \ + $(GOBJECT2_CFLAGS) \ + $(GTHREAD2_CFLAGS) \ + $(LIBXML2_CFLAGS) \ + $(WARN_CFLAGS) \ + -I$(top_srcdir)/src \ + -DLPF_TEST_SRCDIR=\""$(abs_srcdir)"\" \ + $(NULL) + +LDADD = \ + $(top_builddir)/src/libplanfahr-0.0.la \ + $(GLIB2_LIBS) \ + $(GIO2_LIBS) \ + $(GOBJECT2_LIBS) \ + $(GTHREAD2_LIBS) \ + $(NULL) + +de_db_SOURCES = de-db.c +de_db_CFLAGS = \ + $(AM_CFLAGS) \ + $(LIBSOUP_CFLAGS) \ + $(LIBXML2_CFLAGS) \ + $(NULL) +de_db_LDADD = \ + $(LDADD) \ + $(LIBSOUP_LIBS) \ + $(LIBXML2_LIBS) \ + $(NULL) + +hafas_bin6_SOURCES = \ + hafas-bin6.c \ + ../hafas-bin6-format.h \ + $(NULL) +hafas_bin6_CFLAGS = \ + $(AM_CFLAGS) \ + $(NULL) +hafas_bin6_LDADD = \ + $(LDADD) \ + $(NULL) + + +uninstalled_testdir = tests/ +dist_uninstalled_test_DATA = \ + hafas-bin-6-station-query-1.bin \ + $(NULL) + +TESTS = $(check_PROGRAMS) diff --git a/src/providers/tests/de-db.c b/src/providers/tests/de-db.c new file mode 100644 index 0000000..db34274 --- /dev/null +++ b/src/providers/tests/de-db.c @@ -0,0 +1,138 @@ +/* + * 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" +#include "../hafas-bin6.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-bin-6-station-query-1.bin b/src/providers/tests/hafas-bin-6-station-query-1.bin Binary files differnew file mode 100644 index 0000000..87afa7f --- /dev/null +++ b/src/providers/tests/hafas-bin-6-station-query-1.bin diff --git a/src/providers/tests/hafas-bin6.c b/src/providers/tests/hafas-bin6.c new file mode 100644 index 0000000..ea5e277 --- /dev/null +++ b/src/providers/tests/hafas-bin6.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; +} |