diff options
author | Guido Günther <agx@sigxcpu.org> | 2014-03-10 09:57:47 +0100 |
---|---|---|
committer | Guido Günther <agx@sigxcpu.org> | 2014-03-10 10:29:01 +0100 |
commit | 1b38625628edf159668f30b95a1badb248baa4e6 (patch) | |
tree | 0dcbfd27cb1f3de233dba2f415463d0e34edb235 /libplanfahr/providers | |
parent | 68dcd1e4fe6520d757bd4c2bc0d13dc0271fb77f (diff) |
Make the code match the docs
We should be able to include libplanfahr via
#incluce <libplanfahr/libplanfahr.h>
only
Diffstat (limited to 'libplanfahr/providers')
-rw-r--r-- | libplanfahr/providers/Makefile.am | 89 | ||||
-rw-r--r-- | libplanfahr/providers/ch-sbb.c | 146 | ||||
-rw-r--r-- | libplanfahr/providers/ch-sbb.h | 50 | ||||
-rw-r--r-- | libplanfahr/providers/de-bvg.c | 146 | ||||
-rw-r--r-- | libplanfahr/providers/de-bvg.h | 50 | ||||
-rw-r--r-- | libplanfahr/providers/de-db.c | 143 | ||||
-rw-r--r-- | libplanfahr/providers/de-db.h | 50 | ||||
-rw-r--r-- | libplanfahr/providers/hafas-bin6-format.h | 323 | ||||
-rw-r--r-- | libplanfahr/providers/hafas-bin6.c | 908 | ||||
-rw-r--r-- | libplanfahr/providers/hafas-bin6.h | 64 | ||||
-rw-r--r-- | libplanfahr/providers/tests/Makefile.am | 58 | ||||
-rw-r--r-- | libplanfahr/providers/tests/hafas-bin-6-station-query-1.bin | bin | 0 -> 2352 bytes | |||
-rw-r--r-- | libplanfahr/providers/tests/hafas-bin6-format.c | 156 | ||||
-rw-r--r-- | libplanfahr/providers/tests/hafas-bin6.c | 137 |
14 files changed, 2320 insertions, 0 deletions
diff --git a/libplanfahr/providers/Makefile.am b/libplanfahr/providers/Makefile.am new file mode 100644 index 0000000..3516708 --- /dev/null +++ b/libplanfahr/providers/Makefile.am @@ -0,0 +1,89 @@ +include $(top_srcdir)/flymake.mk + +SUBDIRS = tests + +pkglibdir = $(LPF_PROVIDERS_DIR) + +AM_CPPFLAGS = \ + -DG_LOG_DOMAIN="\"LibPlanFahr\"" \ + -DLIBPLANFAHR_COMPILATION \ + -DLPF_PROVIDERS_DIR="\"$(LPF_PROVIDERS_DIR)\"" \ + $(NULL) + +pkglib_LTLIBRARIES = \ + libplanfahr-provider-ch-sbb.la \ + libplanfahr-provider-de-db.la \ + libplanfahr-provider-de-bvg.la \ + $(NULL) + +libplanfahr_provider_ch_sbb_la_SOURCES = \ + ch-sbb.h \ + ch-sbb.c \ + hafas-bin6.h \ + hafas-bin6.c \ + $(NULL) + +libplanfahr_provider_ch_sbb_la_CFLAGS = \ + $(AM_CPPFLAGS) \ + $(GIO2_CFLAGS) \ + $(GOBJECT2_CFLAGS) \ + $(GTHREAD2_CFLAGS) \ + $(LIBXML2_CFLAGS) \ + $(LIBSOUP_CFLAGS) \ + $(WARN_CFLAGS) \ + -I$(top_srcdir)/libplanfahr \ + $(NULL) + +libplanfahr_provider_ch_sbb_la_LDFLAGS = \ + -module \ + -avoid-version \ + $(LIBSOUP_LIBS) \ + $(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 = \ + $(AM_CPPFLAGS) \ + $(GIO2_CFLAGS) \ + $(GOBJECT2_CFLAGS) \ + $(GTHREAD2_CFLAGS) \ + $(LIBXML2_CFLAGS) \ + $(LIBSOUP_CFLAGS) \ + $(WARN_CFLAGS) \ + -I$(top_srcdir)/libplanfahr \ + $(NULL) + +libplanfahr_provider_de_db_la_LDFLAGS = \ + -module \ + -avoid-version \ + $(LIBSOUP_LIBS) \ + $(NULL) + +libplanfahr_provider_de_bvg_la_SOURCES = \ + de-bvg.h \ + de-bvg.c \ + hafas-bin6.h \ + hafas-bin6.c \ + $(NULL) + +libplanfahr_provider_de_bvg_la_CFLAGS = \ + $(AM_CPPFLAGS) \ + $(GIO2_CFLAGS) \ + $(GOBJECT2_CFLAGS) \ + $(GTHREAD2_CFLAGS) \ + $(LIBXML2_CFLAGS) \ + $(LIBSOUP_CFLAGS) \ + $(WARN_CFLAGS) \ + -I$(top_srcdir)/libplanfahr \ + $(NULL) + +libplanfahr_provider_de_bvg_la_LDFLAGS = \ + -module \ + -avoid-version \ + $(LIBSOUP_LIBS) \ + $(NULL) diff --git a/libplanfahr/providers/ch-sbb.c b/libplanfahr/providers/ch-sbb.c new file mode 100644 index 0000000..e29405d --- /dev/null +++ b/libplanfahr/providers/ch-sbb.c @@ -0,0 +1,146 @@ +/* + * ch-sbb.c: SBB 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 <stdlib.h> +#include <string.h> + +#include <gmodule.h> + +#include "ch-sbb.h" +#include "hafas-bin6.h" +#include "lpf-provider.h" + +#define LOC_URL "http://fahrplan.sbb.ch/bin/query.exe/dn" +#define TRIPS_URL "http://fahrplan.sbb.ch/bin/query.exe/dn" + +#define PROVIDER_NAME "ch_sbb" + +enum { + PROP_0, + PROP_NAME, + LAST_PROP +}; + +static void lpf_provider_ch_sbb_interface_init (LpfProviderInterface *iface); + +G_DEFINE_TYPE_WITH_CODE (LpfProviderChSbb, lpf_provider_ch_sbb, LPF_TYPE_PROVIDER_HAFAS_BIN6, + G_IMPLEMENT_INTERFACE (LPF_TYPE_PROVIDER, lpf_provider_ch_sbb_interface_init)); + +int lpf_provider_major_version = LPF_PROVIDER_MAJOR_VERSION; +int lpf_provider_minor_version = LPF_PROVIDER_MINOR_VERSION; + + +static const char* +lpf_provider_ch_sbb_get_name (LpfProvider *self) +{ + return PROVIDER_NAME; +} + + +static const char* +lpf_provider_ch_sbb_locs_url(LpfProviderHafasBin6 *self) +{ + return LOC_URL; +} + + +static const char* +lpf_provider_ch_sbb_trips_url(LpfProviderHafasBin6 *self) +{ + return TRIPS_URL; +} + + +G_MODULE_EXPORT LpfProvider * +lpf_provider_create (void) +{ + return LPF_PROVIDER (lpf_provider_ch_sbb_new ()); +} + + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + switch (prop_id) { + case PROP_NAME: + g_warn_if_reached (); + 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) +{ + switch (prop_id) { + case PROP_NAME: + g_value_set_string (value, PROVIDER_NAME); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static void +lpf_provider_ch_sbb_class_init (LpfProviderChSbbClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + LpfProviderHafasBin6Class *hafas_class = LPF_PROVIDER_HAFAS_BIN6_CLASS (klass); + + object_class->get_property = get_property; + object_class->set_property = set_property; + + /* LpfProvider */ + g_object_class_override_property (object_class, + PROP_NAME, + "name"); + + /* LpfProviderHafasBin6 */ + hafas_class->locs_url = lpf_provider_ch_sbb_locs_url; + hafas_class->trips_url = lpf_provider_ch_sbb_trips_url; +} + +static void +lpf_provider_ch_sbb_interface_init (LpfProviderInterface *iface) +{ + iface->get_name = lpf_provider_ch_sbb_get_name; +} + +static void +lpf_provider_ch_sbb_init (LpfProviderChSbb *self) +{ +} + +LpfProviderChSbb * +lpf_provider_ch_sbb_new (void) +{ + return g_object_new (LPF_TYPE_PROVIDER_CH_SBB, NULL); +} diff --git a/libplanfahr/providers/ch-sbb.h b/libplanfahr/providers/ch-sbb.h new file mode 100644 index 0000000..482d1e7 --- /dev/null +++ b/libplanfahr/providers/ch-sbb.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_CH_SBB_H +#define _LPF_PROVIDER_CH_SBB_H + +#include "hafas-bin6.h" + +G_BEGIN_DECLS +#define LPF_TYPE_PROVIDER_CH_SBB lpf_provider_ch_sbb_get_type() +#define LPF_PROVIDER_CH_SBB(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), LPF_TYPE_PROVIDER_CH_SBB, LpfProviderChSbb)) +#define LPF_PROVIDER_CH_SBB_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), LPF_TYPE_PROVIDER_CH_SBB, LpfProviderChSbbClass)) +#define LPF_IS_PROVIDER_CH_SBB(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), LPF_TYPE_PROVIDER_CH_SBB)) +#define LPF_IS_PROVIDER_CH_SBB_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), LPF_TYPE_PROVIDER_CH_SBB)) +#define LPF_PROVIDER_CH_SBB_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), LPF_TYPE_PROVIDER_CH_SBB, LpfProviderChSbbClass)) + +typedef struct { + LpfProviderHafasBin6 parent; +} LpfProviderChSbb; + +typedef struct { + LpfProviderHafasBin6Class parent_class; +} LpfProviderChSbbClass; + +GType lpf_provider_ch_sbb_get_type (void); + +LpfProviderChSbb *lpf_provider_ch_sbb_new (void); + +G_END_DECLS +#endif /* _LPF_PROVIDER_CH_SBB */ diff --git a/libplanfahr/providers/de-bvg.c b/libplanfahr/providers/de-bvg.c new file mode 100644 index 0000000..0b33834 --- /dev/null +++ b/libplanfahr/providers/de-bvg.c @@ -0,0 +1,146 @@ +/* + * de-bvg.c: BVG 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 <stdlib.h> +#include <string.h> + +#include <gmodule.h> + +#include "de-bvg.h" +#include "hafas-bin6.h" +#include "lpf-provider.h" + +#define LOC_URL "http://www.fahrinfo-berlin.de/Fahrinfo/bin/query.bin/d" +#define TRIPS_URL "http://www.fahrinfo-berlin.de/Fahrinfo/bin/query.bin/d" + +#define PROVIDER_NAME "de_bvg" + +enum { + PROP_0, + PROP_NAME, + LAST_PROP +}; + +static void lpf_provider_de_bvg_interface_init (LpfProviderInterface *iface); + +G_DEFINE_TYPE_WITH_CODE (LpfProviderDeBvg, lpf_provider_de_bvg, LPF_TYPE_PROVIDER_HAFAS_BIN6, + G_IMPLEMENT_INTERFACE (LPF_TYPE_PROVIDER, lpf_provider_de_bvg_interface_init)); + +int lpf_provider_major_version = LPF_PROVIDER_MAJOR_VERSION; +int lpf_provider_minor_version = LPF_PROVIDER_MINOR_VERSION; + + +static const char* +lpf_provider_de_bvg_get_name (LpfProvider *self) +{ + return PROVIDER_NAME; +} + + +static const char* +lpf_provider_de_bvg_locs_url(LpfProviderHafasBin6 *self) +{ + return LOC_URL; +} + + +static const char* +lpf_provider_de_bvg_trips_url(LpfProviderHafasBin6 *self) +{ + return TRIPS_URL; +} + + +G_MODULE_EXPORT LpfProvider * +lpf_provider_create (void) +{ + return LPF_PROVIDER (lpf_provider_de_bvg_new ()); +} + + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + switch (prop_id) { + case PROP_NAME: + g_warn_if_reached (); + 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) +{ + switch (prop_id) { + case PROP_NAME: + g_value_set_string (value, PROVIDER_NAME); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static void +lpf_provider_de_bvg_class_init (LpfProviderDeBvgClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + LpfProviderHafasBin6Class *hafas_class = LPF_PROVIDER_HAFAS_BIN6_CLASS (klass); + + object_class->get_property = get_property; + object_class->set_property = set_property; + + /* LpfProvider */ + g_object_class_override_property (object_class, + PROP_NAME, + "name"); + + /* LpfProviderHafasBin6 */ + hafas_class->locs_url = lpf_provider_de_bvg_locs_url; + hafas_class->trips_url = lpf_provider_de_bvg_trips_url; +} + +static void +lpf_provider_de_bvg_interface_init (LpfProviderInterface *iface) +{ + iface->get_name = lpf_provider_de_bvg_get_name; +} + +static void +lpf_provider_de_bvg_init (LpfProviderDeBvg *self) +{ +} + +LpfProviderDeBvg * +lpf_provider_de_bvg_new (void) +{ + return g_object_new (LPF_TYPE_PROVIDER_DE_BVG, NULL); +} diff --git a/libplanfahr/providers/de-bvg.h b/libplanfahr/providers/de-bvg.h new file mode 100644 index 0000000..9ef055c --- /dev/null +++ b/libplanfahr/providers/de-bvg.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_BVG_H +#define _LPF_PROVIDER_DE_BVG_H + +#include "hafas-bin6.h" + +G_BEGIN_DECLS +#define LPF_TYPE_PROVIDER_DE_BVG lpf_provider_de_bvg_get_type() +#define LPF_PROVIDER_DE_BVG(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), LPF_TYPE_PROVIDER_DE_BVG, LpfProviderDeBvg)) +#define LPF_PROVIDER_DE_BVG_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), LPF_TYPE_PROVIDER_DE_BVG, LpfProviderDeBvgClass)) +#define LPF_IS_PROVIDER_DE_BVG(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), LPF_TYPE_PROVIDER_DE_BVG)) +#define LPF_IS_PROVIDER_DE_BVG_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), LPF_TYPE_PROVIDER_DE_BVG)) +#define LPF_PROVIDER_DE_BVG_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), LPF_TYPE_PROVIDER_DE_BVG, LpfProviderDeBvgClass)) + +typedef struct { + LpfProviderHafasBin6 parent; +} LpfProviderDeBvg; + +typedef struct { + LpfProviderHafasBin6Class parent_class; +} LpfProviderDeBvgClass; + +GType lpf_provider_de_bvg_get_type (void); + +LpfProviderDeBvg *lpf_provider_de_bvg_new (void); + +G_END_DECLS +#endif /* _LPF_PROVIDER_DB */ diff --git a/libplanfahr/providers/de-db.c b/libplanfahr/providers/de-db.c new file mode 100644 index 0000000..f9051fd --- /dev/null +++ b/libplanfahr/providers/de-db.c @@ -0,0 +1,143 @@ +/* + * 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 "de-db.h" +#include "hafas-bin6.h" +#include "lpf-provider.h" + +#define LOC_URL "http://mobile.bahn.de/bin/mobil/query.exe/en" +#define TRIPS_URL "http://reiseauskunft.bahn.de/bin/query.exe/eox" + +#define PROVIDER_NAME "de_db" + +enum { + PROP_0, + PROP_NAME, + LAST_PROP +}; + +static void lpf_provider_de_db_interface_init (LpfProviderInterface *iface); + +G_DEFINE_TYPE_WITH_CODE (LpfProviderDeDb, lpf_provider_de_db, LPF_TYPE_PROVIDER_HAFAS_BIN6, + G_IMPLEMENT_INTERFACE (LPF_TYPE_PROVIDER, lpf_provider_de_db_interface_init)); + +int lpf_provider_major_version = LPF_PROVIDER_MAJOR_VERSION; +int lpf_provider_minor_version = LPF_PROVIDER_MINOR_VERSION; + + +static const char* +lpf_provider_de_db_get_name (LpfProvider *self) +{ + return PROVIDER_NAME; +} + + +static const char* +lpf_provider_de_db_locs_url(LpfProviderHafasBin6 *self) +{ + return LOC_URL; +} + + +static const char* +lpf_provider_de_db_trips_url(LpfProviderHafasBin6 *self) +{ + return TRIPS_URL; +} + + +G_MODULE_EXPORT LpfProvider * +lpf_provider_create (void) +{ + return LPF_PROVIDER (lpf_provider_de_db_new ()); +} + + +static void +set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + switch (prop_id) { + case PROP_NAME: + g_warn_if_reached (); + 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) +{ + switch (prop_id) { + case PROP_NAME: + g_value_set_string (value, PROVIDER_NAME); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static void +lpf_provider_de_db_class_init (LpfProviderDeDbClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + LpfProviderHafasBin6Class *hafas_class = LPF_PROVIDER_HAFAS_BIN6_CLASS (klass); + + object_class->get_property = get_property; + object_class->set_property = set_property; + + /* LpfProvider */ + g_object_class_override_property (object_class, + PROP_NAME, + "name"); + + /* LpfProviderHafasBin6 */ + hafas_class->locs_url = lpf_provider_de_db_locs_url; + hafas_class->trips_url = lpf_provider_de_db_trips_url; +} + +static void +lpf_provider_de_db_interface_init (LpfProviderInterface *iface) +{ + iface->get_name = lpf_provider_de_db_get_name; +} + +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, NULL); +} diff --git a/libplanfahr/providers/de-db.h b/libplanfahr/providers/de-db.h new file mode 100644 index 0000000..a011d7f --- /dev/null +++ b/libplanfahr/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 "hafas-bin6.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, LpfProviderDeDb)) +#define LPF_PROVIDER_DE_DB_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), LPF_TYPE_PROVIDER_DE_DB, LpfProviderDeDbClass)) +#define LPF_IS_PROVIDER_DE_DB(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), LPF_TYPE_PROVIDER_DE_DB)) +#define LPF_IS_PROVIDER_DE_DB_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), LPF_TYPE_PROVIDER_DE_DB)) +#define LPF_PROVIDER_DE_DB_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), LPF_TYPE_PROVIDER_DE_DB, LpfProviderDeDbClass)) + +typedef struct { + LpfProviderHafasBin6 parent; +} LpfProviderDeDb; + +typedef struct { + LpfProviderHafasBin6Class 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/libplanfahr/providers/hafas-bin6-format.h b/libplanfahr/providers/hafas-bin6-format.h new file mode 100644 index 0000000..e2878b0 --- /dev/null +++ b/libplanfahr/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/libplanfahr/providers/hafas-bin6.c b/libplanfahr/providers/hafas-bin6.c new file mode 100644 index 0000000..33edc4e --- /dev/null +++ b/libplanfahr/providers/hafas-bin6.c @@ -0,0 +1,908 @@ +/* + * lpf-provider-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 <string.h> +#include <libxml/parser.h> +#include <libxml/xpath.h> + +#include <libsoup/soup.h> + +#include "hafas-bin6.h" +#include "lpf-loc.h" +#include "lpf-priv.h" +#include "lpf-provider.h" +#include "lpf-trip.h" +#include "lpf-trip-part.h" +#include "lpf-stop.h" + +static void lpf_provider_hafas_bin6_interface_init (LpfProviderInterface *iface); + +G_DEFINE_TYPE_WITH_CODE (LpfProviderHafasBin6, lpf_provider_hafas_bin6, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (LPF_TYPE_PROVIDER, lpf_provider_hafas_bin6_interface_init)); + +#define PROVIDER_NAME "hafas_bin6" + +enum { + PROP_0, + PROP_NAME, + LAST_PROP +}; + +/* transfers data between invocation and the passed in callback */ +typedef struct _LpfProviderGotItUserData { + LpfProvider *self; + gpointer callback; + gpointer user_data; +} LpfProviderGotItUserData; + +#define GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), LPF_TYPE_PROVIDER_HAFAS_BIN6, LpfProviderHafasBin6Private)) + + +typedef struct _LpfProviderHafasBin6Private LpfProviderHafasBin6Private; + +struct _LpfProviderHafasBin6Private { + SoupSession *session; + char *logdir; + gboolean debug; +}; + + +const gchar* +lpf_provider_hafas_bin6_locs_url(LpfProviderHafasBin6 *self) +{ + g_return_val_if_fail (LPF_IS_PROVIDER_HAFAS_BIN6 (self), NULL); + + if (LPF_PROVIDER_HAFAS_BIN6_GET_CLASS (self)->locs_url != NULL) + return LPF_PROVIDER_HAFAS_BIN6_GET_CLASS (self)->locs_url(self); + else + g_warning ("Class '%s' does not override the mandatory " + ".locs_url() virtual function.", + G_OBJECT_TYPE_NAME (self)); + return NULL; +} + + +const gchar* +lpf_provider_hafas_bin6_trips_url(LpfProviderHafasBin6 *self) +{ + g_return_val_if_fail (LPF_IS_PROVIDER_HAFAS_BIN6 (self), NULL); + + if (LPF_PROVIDER_HAFAS_BIN6_GET_CLASS (self)->trips_url != NULL) + return LPF_PROVIDER_HAFAS_BIN6_GET_CLASS (self)->trips_url(self); + else + g_warning ("Class '%s' does not override the mandatory " + ".trips_url() virtual function.", + G_OBJECT_TYPE_NAME (self)); + return NULL; +} + + +static GSList* +parse_locs_xml (const char *xml) +{ + gint i = 0; + xmlDocPtr doc = NULL; + xmlChar *xpath = (xmlChar*) "//MLc[@t=\"ST\"]"; + xmlXPathContextPtr context = NULL; + xmlXPathObjectPtr result = NULL; + xmlNodeSetPtr nodeset = NULL; + gchar *name, *x, *y; + gdouble lo, la; + LpfLoc *loc; + char *id; + GSList *locs = NULL; + + g_return_val_if_fail (xml, NULL); + + LPF_DEBUG("%s", xml); + doc = xmlParseMemory(xml, strlen(xml)); + if (doc == NULL ) { + g_warning("Document not parsed successfully"); + return NULL; + } + + context = xmlXPathNewContext(doc); + if (context == NULL) { + g_warning("Error in xmlXPathNewContext\n"); + goto out; + } + + result = xmlXPathEvalExpression(xpath, context); + if (result == NULL) { + g_warning("Error in xmlXPathEvalExpression\n"); + goto out; + } + + if(xmlXPathNodeSetIsEmpty(result->nodesetval)){ + g_warning("No result\n"); + goto out; + } + + nodeset = result->nodesetval; + for (i=0; i < nodeset->nodeNr; i++) { + name = (gchar*) xmlGetProp(nodeset->nodeTab[i], BAD_CAST "n"); + x = (gchar*) xmlGetProp(nodeset->nodeTab[i], BAD_CAST "x"); + y = (gchar*) xmlGetProp(nodeset->nodeTab[i], BAD_CAST "y"); + id = (gchar*) xmlGetProp(nodeset->nodeTab[i], BAD_CAST "i"); + + lo = (gdouble) g_ascii_strtod(x, NULL) / 1000000.0; + la = (gdouble) g_ascii_strtod(y, NULL) / 1000000.0; + + LPF_DEBUG ("%s (%lf, %lf) - %s", name, lo, la, id); + + loc = (LpfLoc*) g_object_new (LPF_TYPE_LOC, "name", name, "long", lo, "lat", la, NULL); + lpf_loc_set_opaque (loc, id); + locs = g_slist_append(locs, loc); + g_free(x); + g_free(y); + } + out: + xmlXPathFreeContext(context); + xmlXPathFreeObject(result); + xmlFreeDoc(doc); + return locs; +} + + +static void +log_response_body(LpfProviderHafasBin6 *self, SoupMessage *msg, const char* type) +{ + LpfProviderHafasBin6Private *priv = GET_PRIVATE(self); + gchar *querylog = NULL; + gchar *filename = NULL; + gchar *time = NULL; + GDateTime *now; + + if (G_LIKELY(!priv->debug)) + return; + + now = g_date_time_new_now_local(); + + time = g_date_time_format (now, "%F_%T"); + filename = g_strdup_printf ("%s-%s.body", type, time); + + querylog = g_build_path (G_DIR_SEPARATOR_S, + priv->logdir, + filename, + NULL); + + if (!g_file_set_contents (querylog, msg->response_body->data, msg->response_body->length, NULL)) + g_warning ("Failed tpo write out query contents to %s", querylog); + + g_free (querylog); + g_free (filename); + g_free (time); + g_date_time_unref(now); +} + + +static void +got_locs (SoupSession *session, SoupMessage *msg, gpointer user_data) +{ + GSList *locs = NULL; + LpfProviderGotItUserData *locs_data = (LpfProviderGotItUserData*)user_data; + LpfProviderGotLocsNotify callback; + LpfProviderHafasBin6 *self; + gpointer data; + GError *err = NULL; + + g_return_if_fail(session); + g_return_if_fail(msg); + g_return_if_fail(user_data); + + callback = locs_data->callback; + data = locs_data->user_data; + self = LPF_PROVIDER_HAFAS_BIN6(locs_data->self); + g_free (locs_data); + + LPF_DEBUG("Status: %d", msg->status_code); + if (!SOUP_STATUS_IS_SUCCESSFUL(msg->status_code)) { + LPF_DEBUG("HTTP request failed"); + g_set_error (&err, + LPF_PROVIDER_ERROR, + LPF_PROVIDER_ERROR_REQUEST_FAILED, + "HTTP request failed: %d", msg->status_code); + goto out; + } + + log_response_body (self, msg, "station"); + + if ((locs = parse_locs_xml(msg->response_body->data)) == NULL) { + g_set_error (&err, + LPF_PROVIDER_ERROR, + LPF_PROVIDER_ERROR_PARSE_FAILED, + "Failed to parse locations"); + goto out; + } + +out: + (*callback)(locs, data, err); +} + + +static gint +lpf_provider_hafas_bin6_get_locs (LpfProvider *self, const char* match, LpfProviderGotLocsNotify callback, gpointer user_data) +{ + LpfProviderHafasBin6Private *priv = GET_PRIVATE(self); + SoupMessage *msg; + char *xml; + LpfProviderGotItUserData *locs_data = NULL; + gint ret = -1; + + g_return_val_if_fail (priv->session, -1); + + locs_data = g_malloc(sizeof(LpfProviderHafasBin6Private)); + if (!locs_data) + goto out; + xml = g_strdup_printf ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "<ReqC ver=\"1.1\" prod=\"String\" lang=\"EN\">" + "<MLcReq><MLc n=\"%s\" t=\"ST\"/>" + "</MLcReq></ReqC>", match); + + msg = soup_message_new ("POST", lpf_provider_hafas_bin6_locs_url(LPF_PROVIDER_HAFAS_BIN6(self))); + if (!msg) + goto out; + soup_message_set_request (msg, "text/xml", SOUP_MEMORY_TAKE, xml, strlen (xml)); + + locs_data->user_data = user_data; + locs_data->callback = callback; + locs_data->self = self; + + soup_session_queue_message (priv->session, msg, got_locs, locs_data); + ret = 0; + out: + if (ret < 0) + g_free (locs_data); + return ret; +} + + +static GSList* +hafas_bin6_parse_each_trip (const gchar *data, gsize num, guint base, const char *enc) +{ + gint i, j, k; + const HafasBin6Trip *t; + const HafasBin6TripPartDetail *pd; + const HafasBin6TripPart *p; + const HafasBin6TripStop *stop; + guint16 day_off; + +#ifdef ENABLE_DEBUG + const HafasBin6TripDetail *d; +#endif + guint h, m; + LpfTrip *trip = NULL; + LpfTripPart *part = NULL; + LpfStop *start = NULL, *end = NULL, *astop = NULL; + GDateTime *dt; + GSList *trips = NULL, *parts = NULL, *stops = NULL; + const char *line; + + for (i = 0; i < num; i++) { + /* The trips itself */ + t = HAFAS_BIN6_TRIP(data, i); + day_off = lpf_provider_hafas_bin6_parse_service_day(data, i); + + LPF_DEBUG("Trip #%d, Changes: %4d", i, t->changes); +#ifdef ENABLE_DEBUG + /* trip details */ + d = HAFAS_BIN6_TRIP_DETAIL(data, i); + LPF_DEBUG("Trip #%d, Status: %4d", i, d->rt_status); + LPF_DEBUG("Trip #%d, Delay: %4d", i, d->delay); +#endif + /* trip parts */ + for (j = 0; j < t->part_cnt; j++) { + p = HAFAS_BIN6_TRIP_PART(data, i, j); + start = g_object_new(LPF_TYPE_STOP, NULL); + if (lpf_provider_hafas_bin6_parse_station(data, p->dep_off, LPF_LOC(start), enc) < 0) { + g_warning("Failed to parse start station %d/%d", i, j); + goto error; + } + end = g_object_new(LPF_TYPE_STOP, NULL); + if (lpf_provider_hafas_bin6_parse_station(data, p->arr_off, LPF_LOC(end), enc) < 0) { + g_warning("Failed to parse end station %d/%d", i, j); + goto error; + } + line = HAFAS_BIN6_STR(data, p->line_off); + + h = p->dep / 100; + m = p->dep % 100; + dt = lpf_provider_hafas_bin6_date_time (base, day_off, h, m); + g_object_set (start, "departure", dt, NULL); + + if (g_strcmp0 (HAFAS_BIN6_NO_PLATFORM, HAFAS_BIN6_STR(data, p->dep_pos_off))) { + g_object_set (start, + "dep_plat", HAFAS_BIN6_STR(data, p->dep_pos_off), + NULL); + } + + h = p->arr / 100; + m = p->arr % 100; + dt = lpf_provider_hafas_bin6_date_time (base, day_off, h, m); + g_object_set (end, "arrival", dt, NULL); + + if (g_strcmp0 (HAFAS_BIN6_NO_PLATFORM, HAFAS_BIN6_STR(data, p->arr_pos_off))) { + g_object_set (end, + "arr_plat", HAFAS_BIN6_STR(data, p->arr_pos_off), + NULL); + } + + /* trip part details */ + pd = HAFAS_BIN6_TRIP_PART_DETAIL(data, i, j); + + if (pd->arr_pred != HAFAS_BIN6_NO_REALTIME) { + h = pd->arr_pred / 100; + m = pd->arr_pred % 100; + dt = lpf_provider_hafas_bin6_date_time (base, day_off, h, m); + g_object_set (start, "rt_arrival", dt, NULL); + } + + if (pd->dep_pred != HAFAS_BIN6_NO_REALTIME) { + h = pd->dep_pred / 100; + m = pd->dep_pred % 100; + dt = lpf_provider_hafas_bin6_date_time (base, day_off, h, m); + g_object_set (end, "rt_departure", dt, NULL); + } + + LPF_DEBUG("Trip #%d, part #%d, Pred. Dep Plat: %s, Pred. Arr Plat: %s", i, j, + HAFAS_BIN6_STR(data, pd->dep_pos_pred_off), + HAFAS_BIN6_STR(data, pd->arr_pos_pred_off)); + + for (k = 0; k < pd->stops_cnt; k++) { + stop = HAFAS_BIN6_STOP(data, i, j, k); + + astop = g_object_new (LPF_TYPE_STOP, + "arrival", lpf_provider_hafas_bin6_date_time (base, + day_off, + stop->arr / 100, + stop->arr % 100), + "departure", lpf_provider_hafas_bin6_date_time (base, + day_off, + stop->dep / 100, + stop->dep % 100), + NULL + ); + + if (lpf_provider_hafas_bin6_parse_station(data, stop->stop_idx, LPF_LOC(astop), enc) < 0) { + g_warning("Failed to parse stop %d/%d", i, j); + goto error; + } + +#ifdef ENABLE_DEBUG + /* FIXME: add these and predicted dep/arr/plat to LpfStop too */ + LPF_DEBUG("Trip #%d, part #%d," + "Dep: %.6s Arr: %.6s", i, j, + HAFAS_BIN6_STR(data, stop->dep_pos_off), + HAFAS_BIN6_STR(data, stop->arr_pos_off)); +#endif + stops = g_slist_append (stops, astop); + astop = NULL; + } + part = g_object_new (LPF_TYPE_TRIP_PART, + "start", start, + "end", end, + "line", line, + "stops", stops, + NULL); + start = end = NULL; + line = NULL; + stops = NULL; + + parts = g_slist_append (parts, part); + part = NULL; + } + trip = g_object_new (LPF_TYPE_TRIP, + "parts", parts, + NULL); + parts = NULL; + trips = g_slist_append (trips, trip); + trip = NULL; + } + + return trips; + +error: + if (trips) + g_slist_free_full (trips, g_object_unref); + if (parts) + g_slist_free_full (parts, g_object_unref); + if (stops) + g_slist_free_full (stops, g_object_unref); + if (start) + g_object_unref (start); + if (trip) + g_object_unref (trip); + if (part) + g_object_unref (part); + if (astop) + g_object_unref (astop); + + return NULL; +} + + +static GSList* +hafas_binary_parse_trips (const char *data, gsize length) +{ + HafasBin6Header *header; +#ifdef ENABLE_DEBUG + HafasBin6Loc *start, *end; +#endif + HafasBin6ExtHeader *ext; + HafasBin6TripDetailsHeader *details; + GSList *trips = NULL; + guint16 version; + const gchar *encoding; + + g_return_val_if_fail (data, NULL); + g_return_val_if_fail (length, NULL); + + version = *(guint16*)data; + g_return_val_if_fail(version == 6, NULL); + + g_return_val_if_fail(sizeof (HafasBin6Header) < length, NULL); + header = (HafasBin6Header*) data; +#ifdef ENABLE_DEBUG + start = (HafasBin6Loc*)&header->start; + end = (HafasBin6Loc*)&header->end; +#endif + LPF_DEBUG("%d Trips from '%s' to '%s'", + header->num_trips, + HAFAS_BIN6_STR(data, start->name_off), + HAFAS_BIN6_STR(data, end->name_off)); + + g_return_val_if_fail ((sizeof (HafasBin6Header) + + sizeof(HafasBin6Header) * header->num_trips) < length, + NULL); + + ext = HAFAS_BIN6_EXT_HEADER(data); + g_return_val_if_fail (header->ext + + sizeof(HafasBin6ExtHeader) < length, NULL); + + /* We might not have attrs_index0 */ + g_return_val_if_fail(sizeof (HafasBin6ExtHeader) - 1 < ext->length, NULL); + + LPF_DEBUG("Ext length: 0x%.4x", ext->length); + LPF_DEBUG("Errors: 0x%.4x", ext->err); + LPF_DEBUG("Sequence: 0x%.4x", ext->seq); + LPF_DEBUG("Detail table: 0x%.4x", ext->details_tbl); + encoding = HAFAS_BIN6_STR(data, ext->enc_off); + LPF_DEBUG("Encoding: %s", encoding); + LPF_DEBUG("Request Id: %s", HAFAS_BIN6_STR(data, ext->req_id_off)); + + if (ext->err) { + g_warning ("Error %d", ext->err); + goto out; + } + + if (ext->seq <= 0) { + g_warning("Illegal sequence number %d", ext->seq); + goto out; + } + + g_return_val_if_fail (ext->details_tbl + + sizeof (HafasBin6TripDetailsHeader) < length, NULL); + details = HAFAS_BIN6_TRIP_DETAILS_HEADER(data); + LPF_DEBUG("Trip detail version: %d", details->version); + g_return_val_if_fail (details->version == 1, NULL); + g_return_val_if_fail (details->stop_size == sizeof(HafasBin6TripStop), NULL); + g_return_val_if_fail (details->part_detail_size == sizeof(HafasBin6TripPartDetail), NULL); + + trips = hafas_bin6_parse_each_trip (data, header->num_trips, header->days, encoding); + g_return_val_if_fail (trips, NULL); + out: + return trips; +} + +static gint +decompress (const gchar *in, gsize inlen, + gchar **out, gsize *outlen, + GError **err) { + gint ret = -1; + gsize read, written; + GConverter *decomp = NULL; + GConverterResult conv; + gsize outbuflen, buflen, outpos = 0, inpos = 0; + gchar *outbuf = NULL; + + g_return_val_if_fail (inlen > 0, ret); + g_return_val_if_fail (in, ret); + g_return_val_if_fail (err, ret); + + decomp = (GConverter *)g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP); + + outbuflen = buflen = inlen * 2; + outbuf = g_try_malloc (outbuflen); + if (outbuf == NULL) + goto out; + + do { + conv = g_converter_convert (decomp, + (in + inpos), inlen, + (outbuf + outpos), buflen, + G_CONVERTER_INPUT_AT_END, + &read, &written, + err); + + switch (conv) { + case G_CONVERTER_ERROR: + goto out; + case G_CONVERTER_FINISHED: + outpos += written; + ret = 0; + goto out; + case G_CONVERTER_CONVERTED: + outpos += written; + inpos += read; + inlen -= read; + if (outbuflen - written < buflen) { + outbuflen += buflen; + outbuf = g_try_realloc (outbuf, outbuflen); + if (outbuf == NULL) + goto out; + } + break; + case G_CONVERTER_FLUSHED: + default: + g_warning ("Unhandled condition %d", conv); + goto out; + } + } while (TRUE); + +out: + if (ret == 0) { + *out = outbuf; + *outlen = outpos; + } else + g_free (outbuf); + + g_object_unref (decomp); + return ret; +} + +static void +got_trips (SoupSession *session, SoupMessage *msg, gpointer user_data) +{ + GSList *trips = NULL; + LpfProviderGotItUserData *trips_data = (LpfProviderGotItUserData*)user_data; + LpfProviderGotTripsNotify callback; + LpfProviderHafasBin6 *self; + gpointer data; + gchar *decomp = NULL; + gsize len; + GError *err = NULL; + + g_return_if_fail(session); + g_return_if_fail(msg); + g_return_if_fail(user_data); + + g_object_ref (msg); + + callback = trips_data->callback; + data = trips_data->user_data; + self = (LpfProviderHafasBin6*)trips_data->self; + g_free (trips_data); + + LPF_DEBUG("Status: %d", msg->status_code); + if (!SOUP_STATUS_IS_SUCCESSFUL(msg->status_code)) { + g_set_error (&err, + LPF_PROVIDER_ERROR, + LPF_PROVIDER_ERROR_REQUEST_FAILED, + "HTTP request failed: %d", msg->status_code); + goto out; + } + + log_response_body (self, msg, "trip"); + + if (decompress(msg->response_body->data, msg->response_body->length, &decomp, &len, &err) < 0) { + goto out; + } + + LPF_DEBUG("Decompressed to %" G_GSIZE_FORMAT " bytes", len); + if ((trips = hafas_binary_parse_trips(decomp, len)) == NULL) { + g_set_error (&err, + LPF_PROVIDER_ERROR, + LPF_PROVIDER_ERROR_PARSE_FAILED, + "Failed to parse trips"); + goto out; + } + +out: + g_object_unref (msg); + g_free (decomp); + + (*callback)(trips, data, err); +} + + +static gint +lpf_provider_hafas_bin6_get_trips (LpfProvider *self, + LpfLoc *start, + LpfLoc *end, + GDateTime *date, + guint64 flags, + LpfProviderGotTripsNotify callback, + gpointer user_data) +{ + LpfProviderHafasBin6Private *priv = GET_PRIVATE(self); + SoupMessage *msg; + SoupURI *uri = NULL; + LpfProviderGotItUserData *trips_data = NULL; + gint ret = -1; + gchar *datestr = NULL, *timestr = NULL; + /* allowed vehicle types */ + const gchar *train_restriction = "11111111111111"; + /* whether time is arrival or departure time */ + const gchar *by_departure = "1"; + char *start_id = NULL, *end_id = NULL; + + g_return_val_if_fail (start, -1); + g_return_val_if_fail (end, -1); + g_return_val_if_fail (callback, -1); + g_return_val_if_fail (priv->session, -1); + g_return_val_if_fail (date, -1); + + g_object_ref (start); + g_object_ref (end); + + trips_data = g_try_malloc(sizeof(LpfProviderHafasBin6Private)); + if (!trips_data) + goto out; + + datestr = g_date_time_format (date, "%d.%m.%y"); + timestr = g_date_time_format (date, "%H:%M"); + + start_id = lpf_loc_get_opaque(start); + end_id = lpf_loc_get_opaque(end); + if (start_id == NULL || end_id == NULL) { + g_warning ("Details missing."); + goto out; + } + + uri = soup_uri_new (lpf_provider_hafas_bin6_trips_url(LPF_PROVIDER_HAFAS_BIN6(self))); + + soup_uri_set_query_from_fields (uri, + "start", "Suchen", + "REQ0JourneyStopsS0ID", start_id, + "REQ0JourneyStopsZ0ID", end_id, + "REQ0JourneyDate", datestr, + "REQ0JourneyTime", timestr, + "REQ0HafasSearchForw", by_departure, + "REQ0JourneyProduct_prod_list_1", train_restriction, + "h2g-direct", "11", NULL); + + LPF_DEBUG ("URI: %s", soup_uri_to_string (uri, FALSE)); + + msg = soup_message_new_from_uri ("GET", uri); + if (!msg) + goto out; + + trips_data->user_data = user_data; + trips_data->callback = callback; + trips_data->self = self; + + soup_session_queue_message (priv->session, msg, got_trips, trips_data); + ret = 0; + out: + g_free (datestr); + g_free (timestr); + g_object_unref (start); + g_object_unref (end); + if (ret < 0) + g_free (trips_data); + return ret; +} + + +static void +lpf_provider_hafas_bin6_set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + switch (prop_id) { + case PROP_NAME: + g_warn_if_reached (); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static void +lpf_provider_hafas_bin6_get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + switch (prop_id) { + case PROP_NAME: + g_value_set_string (value, PROVIDER_NAME); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +gint +lpf_provider_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; +} + + +/** + * lpf_provicer_hafas_bin6_parse_service_day: + * + * Parse a servide day entry and return the offset from the base date in days. + */ +guint +lpf_provider_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; +} + +/** + * lpf_provider_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* +lpf_provider_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; +} + +static void +lpf_provider_hafas_bin6_activate (LpfProvider *self, GObject *obj) +{ + LpfProviderHafasBin6Private *priv = GET_PRIVATE(self); + GFile *dir; + gchar *debugstr; + + priv->session = soup_session_new(); + priv->logdir = g_build_path(G_DIR_SEPARATOR_S, + g_get_user_cache_dir(), + PACKAGE, + lpf_provider_get_name (self), + NULL); + + debugstr = getenv ("LPF_DEBUG"); + if (debugstr && strstr (debugstr, "provider")) + priv->debug = TRUE; + + dir = g_file_new_for_path (priv->logdir); + g_file_make_directory_with_parents (dir, NULL, NULL); + g_object_unref (dir); + +} + + +static void +lpf_provider_hafas_bin6_deactivate (LpfProvider *self, GObject *obj) +{ + LpfProviderHafasBin6Private *priv = GET_PRIVATE(self); + + if (priv->session) + g_object_unref (priv->session); + + g_free (priv->logdir); +} + + +static void +lpf_provider_hafas_bin6_class_init (LpfProviderHafasBin6Class *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (LpfProviderHafasBin6Private)); + + object_class->get_property = lpf_provider_hafas_bin6_get_property; + object_class->set_property = lpf_provider_hafas_bin6_set_property; + + klass->locs_url = lpf_provider_hafas_bin6_locs_url; + klass->trips_url = lpf_provider_hafas_bin6_trips_url; + + g_object_class_override_property (object_class, + PROP_NAME, + "name"); +} + +static void +lpf_provider_hafas_bin6_interface_init (LpfProviderInterface *iface) +{ + /* abstract base class */ + iface->activate = lpf_provider_hafas_bin6_activate; + iface->deactivate = lpf_provider_hafas_bin6_deactivate; + + /* To be implemented by each provider */ + iface->get_locs = lpf_provider_hafas_bin6_get_locs; + iface->get_trips = lpf_provider_hafas_bin6_get_trips; +} + +static void +lpf_provider_hafas_bin6_init (LpfProviderHafasBin6 *self) +{ +} diff --git a/libplanfahr/providers/hafas-bin6.h b/libplanfahr/providers/hafas-bin6.h new file mode 100644 index 0000000..1177c45 --- /dev/null +++ b/libplanfahr/providers/hafas-bin6.h @@ -0,0 +1,64 @@ +/* + * 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" + +G_BEGIN_DECLS +#define LPF_TYPE_PROVIDER_HAFAS_BIN6 lpf_provider_hafas_bin6_get_type() +#define LPF_PROVIDER_HAFAS_BIN6(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), LPF_TYPE_PROVIDER_HAFAS_BIN6, LpfProviderHafasBin6)) +#define LPF_PROVIDER_HAFAS_BIN6_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), LPF_TYPE_PROVIDER_HAFAS_BIN6, LpfProviderHafasBin6Class)) +#define LPF_IS_PROVIDER_HAFAS_BIN6(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), LPF_TYPE_PROVIDER_HAFAS_BIN6)) +#define LPF_IS_PROVIDER_HAFAS_BIN6_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), LPF_TYPE_PROVIDER_HAFAS_BIN6)) +#define LPF_PROVIDER_HAFAS_BIN6_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), LPF_TYPE_PROVIDER_HAFAS_BIN6, LpfProviderHafasBin6Class)) + +typedef struct { + GObject parent; +} LpfProviderHafasBin6; + +typedef struct { + GObjectClass parent_class; + + const char* (*locs_url)(LpfProviderHafasBin6 *self); + const char* (*trips_url)(LpfProviderHafasBin6 *self); +} LpfProviderHafasBin6Class; + +GType lpf_provider_hafas_bin6_get_type (void); + +gint lpf_provider_hafas_bin6_parse_station(const gchar *data, guint16 off, LpfLoc *station, const char *enc); +guint lpf_provider_hafas_bin6_parse_service_day (const char *data, int idx); +GDateTime* lpf_provider_hafas_bin6_date_time(guint base_days, guint off_days, guint hours, guint min); + +/* Pure virtual methods */ +const gchar* lpf_provider_hafas_bin6_locs_url(LpfProviderHafasBin6 *self); +const gchar* lpf_provider_hafas_bin6_trips_url(LpfProviderHafasBin6 *self); + +G_END_DECLS +#endif /* _HAFAS_BIN6_H */ diff --git a/libplanfahr/providers/tests/Makefile.am b/libplanfahr/providers/tests/Makefile.am new file mode 100644 index 0000000..d3ba939 --- /dev/null +++ b/libplanfahr/providers/tests/Makefile.am @@ -0,0 +1,58 @@ +include $(top_srcdir)/flymake.mk + +check_PROGRAMS = hafas-bin6 hafas-bin6-format + +AM_CPPFLAGS = \ + -DLIBPLANFAHR_COMPILATION \ + -DLPF_TEST_SRCDIR=\""$(abs_srcdir)"\" \ + $(GLIB2_CFLAGS) \ + $(GIO2_CFLAGS) \ + $(GOBJECT2_CFLAGS) \ + $(GTHREAD2_CFLAGS) \ + $(LIBXML2_CFLAGS) \ + $(WARN_CFLAGS) \ + -I$(top_srcdir)/libplanfahr \ + $(NULL) + +LDADD = \ + $(top_builddir)/libplanfahr/libplanfahr-0.0.la \ + $(GLIB2_LIBS) \ + $(GIO2_LIBS) \ + $(GOBJECT2_LIBS) \ + $(GTHREAD2_LIBS) \ + $(NULL) + +hafas_bin6_SOURCES = \ + hafas-bin6.c \ + $(NULL) + +hafas_bin6_CFLAGS = \ + $(AM_CPPFLAGS) \ + $(LIBSOUP_CFLAGS) \ + $(LIBXML2_CFLAGS) \ + $(NULL) +hafas_bin6_LDADD = \ + ../libplanfahr-provider-de-db.la \ + $(LDADD) \ + $(LIBSOUP_LIBS) \ + $(LIBXML2_LIBS) \ + $(NULL) + +hafas_bin6_format_SOURCES = \ + hafas-bin6-format.c \ + ../hafas-bin6-format.h \ + $(NULL) +hafas_bin6_format_CFLAGS = \ + $(AM_CPPFLAGS) \ + $(NULL) +hafas_bin6_format_LDADD = \ + $(LDADD) \ + $(NULL) + + +uninstalled_testdir = tests/ +dist_uninstalled_test_DATA = \ + hafas-bin-6-station-query-1.bin \ + $(NULL) + +TESTS = $(check_PROGRAMS) diff --git a/libplanfahr/providers/tests/hafas-bin-6-station-query-1.bin b/libplanfahr/providers/tests/hafas-bin-6-station-query-1.bin Binary files differnew file mode 100644 index 0000000..87afa7f --- /dev/null +++ b/libplanfahr/providers/tests/hafas-bin-6-station-query-1.bin diff --git a/libplanfahr/providers/tests/hafas-bin6-format.c b/libplanfahr/providers/tests/hafas-bin6-format.c new file mode 100644 index 0000000..ea5e277 --- /dev/null +++ b/libplanfahr/providers/tests/hafas-bin6-format.c @@ -0,0 +1,156 @@ +/* + * hafas-bin6.c: test hafas binary v6 parser + * + * Copyright (C) 2014 Guido Günther + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Author: Guido Günther <agx@sigxcpu.org> + */ + +#include <glib.h> + +#include "../hafas-bin6.h" + + +/* sanity check our parser's structures */ +static void +test_sizes (void) +{ + g_assert_cmpint(sizeof(HafasBin6Header), ==, 0x4a); + g_assert_cmpint(sizeof(HafasBin6Trip), ==, 0x0c); + g_assert_cmpint(sizeof(HafasBin6Loc), ==, 0x0e); + g_assert_cmpint(sizeof(HafasBin6ExtHeader), ==, 0x30); + g_assert_cmpint(sizeof(HafasBin6TripDetailsHeader), ==, 0x0e); + g_assert_cmpint(sizeof(HafasBin6Station), ==, 0x0e); + g_assert_cmpint(sizeof(HafasBin6ServiceDay), ==, 0x07); + g_assert_cmpint(sizeof(HafasBin6TripDetail), ==, 0x04); + g_assert_cmpint(sizeof(HafasBin6TripPart), ==, 0x14); + g_assert_cmpint(sizeof(HafasBin6TripPartDetail), ==, 0x10); + g_assert_cmpint(sizeof(HafasBin6TripStop), ==, 0x1a); +} + +/* + * Make sure we can parse the binary data trip information This is + * just to test the raw parser. The wrapping into Lpf objects and + * conversions of e.g. time and date are tested separately. + */ +static void +test_parse_erpel_unkel(void) +{ + gchar *bin; + gsize length; + HafasBin6Loc *start, *end; + HafasBin6ExtHeader *ext; + HafasBin6TripDetailsHeader *detail_header; + HafasBin6TripPartDetail *part_detail; + HafasBin6TripDetail *detail; + HafasBin6Trip *trip; + HafasBin6TripPart *part; + HafasBin6ServiceDay *day; + HafasBin6TripStop *stop; + HafasBin6Station *station; + gint i, j, k; + guint expected_changes[3] = { 0, 0, 0 }; + guint expected_parts[3] = { 1, 2, 1 }; + const gchar *expected_part_line[3][2] = { {"RB 12560", "doesnot" }, + {"Fussweg", "Bus 565" }, + {"RB 12562", "matter" }}; + const gint expected_part_dep[3][2] = { { 956, 0}, + { 958, 1003 }, + {1056, 0 }}; + + const gint expected_part_arr[3][2] = { { 959, 2}, + {1003, 1014}, + {1059, 0 }}; + guint expected_stops[3][2] = { {0, 0}, {0, 8}, {0, 0} }; + + g_assert_true(g_file_get_contents(LPF_TEST_SRCDIR "/hafas-bin-6-station-query-1.bin", &bin, &length, NULL)); + + g_assert_cmpint(HAFAS_BIN6_HEADER(bin)->num_trips, ==, 3); + + /* header information */ + start = HAFAS_BIN6_START(bin); + end = HAFAS_BIN6_END(bin); + g_assert_cmpint (start->lon, ==, 7241593); + g_assert_cmpint (start->lat, ==, 50582067); + g_assert_cmpint (start->type, ==, HAFAS_BIN6_LOC_TYPE_STATION); + g_assert_cmpint (end->lon, ==, 7219803); + g_assert_cmpint (end->lat, ==, 50602742); + g_assert_cmpint (end->type, ==, HAFAS_BIN6_LOC_TYPE_STATION); + + /* ext header information */ + ext = HAFAS_BIN6_EXT_HEADER(bin); + g_assert_cmpint (ext->err, ==, HAFAS_BIN6_ERROR_NONE); + g_assert_cmpstr (HAFAS_BIN6_STR(bin, ext->enc_off), ==, "iso-8859-1"); + g_assert_cmpstr (HAFAS_BIN6_STR(bin, ext->req_id_off), ==, "50.02519042.1387923122"); + g_assert_cmpint (ext->seq, ==, 1); + + /* detail header information */ + detail_header = HAFAS_BIN6_TRIP_DETAILS_HEADER(bin); + g_assert_cmpint (detail_header->version, ==, 1); + g_assert_cmpint (detail_header->stop_size, ==, sizeof(HafasBin6TripStop)); + g_assert_cmpint (detail_header->part_detail_size, == ,sizeof(HafasBin6TripPartDetail)); + + for (i = 0; i < HAFAS_BIN6_HEADER(bin)->num_trips; i++) { + trip = HAFAS_BIN6_TRIP(bin, i); + g_assert_cmpint (trip->changes, ==, expected_changes[i]); + g_assert_cmpint (trip->part_cnt, ==, expected_parts[i]); + + day = HAFAS_BIN6_SERVICE_DAY(bin, i); + g_assert_cmpint (day->byte_base, ==, 0); + g_assert_cmpint (day->byte_len, ==, 2); + g_assert_cmpint (day->byte0, ==, (gchar)(0x80)); + + detail = HAFAS_BIN6_TRIP_DETAIL(bin, i); + g_assert_cmpint (detail->rt_status, ==, HAFAS_BIN6_RTSTATUS_NORMAL); + g_assert_cmpint (detail->delay, ==, 0); + + for (j = 0; j < trip->part_cnt; j++) { + /* planned departures and arrivals */ + part = HAFAS_BIN6_TRIP_PART(bin, i, j); + g_assert_cmpstr (HAFAS_BIN6_STR(bin, part->line_off), ==, expected_part_line[i][j]); + g_assert_cmpint (part->arr, ==, expected_part_arr[i][j]); + g_assert_cmpint (part->dep, ==, expected_part_dep[i][j]); + /* predicted departures and arrivals */ + part_detail = HAFAS_BIN6_TRIP_PART_DETAIL(bin, i, j); + g_assert_cmpint (part_detail->arr_pred, ==, 65535); + g_assert_cmpint (part_detail->dep_pred, ==, 65535); + /* stops */ + g_assert_cmpint (part_detail->stops_cnt, ==, expected_stops[i][j]); + for (k = 0; k < part_detail->stops_cnt; k++) { + stop = HAFAS_BIN6_STOP(bin, i, j, k); + g_assert_cmpstr (HAFAS_BIN6_STR(bin, stop->dep_pos_pred_off), ==, "---"); + } + /* check a single station more thoroughly */ + stop = HAFAS_BIN6_STOP(bin, 1, 1, 2); + station = HAFAS_BIN6_STATION(bin, stop->stop_idx); + g_assert_cmpstr (HAFAS_BIN6_STR(bin, station->name_off), ==, "Heister Kapelle, Unkel"); + } + } +} + + +int main(int argc, char **argv) +{ + gboolean ret; + g_test_init (&argc, &argv, NULL); + + g_test_add_func ("/providers/hafas-bin6/sizes", test_sizes); + g_test_add_func ("/providers/hafas_bin6/parse_erpel_unkel", test_parse_erpel_unkel); + + ret = g_test_run (); + return ret; +} diff --git a/libplanfahr/providers/tests/hafas-bin6.c b/libplanfahr/providers/tests/hafas-bin6.c new file mode 100644 index 0000000..8c256e0 --- /dev/null +++ b/libplanfahr/providers/tests/hafas-bin6.c @@ -0,0 +1,137 @@ +/* + * 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 "../hafas-bin6.c" + +/* Make sure we can parse the station xml list as returned by the current Deutsche Bahn Hafas */ +static void +test_parse_locs (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_locs_xml(xml); + g_assert_nonnull (locs); + g_assert_cmpint (g_slist_length (locs), ==, 7); + + loc = g_slist_nth (locs, 4)->data; + g_object_get (loc, "name", &name, "long", &lon, "lat", &lat, NULL); + + g_assert_cmpstr (name, ==, "Orsberg Ort, Erpel"); + g_assert_cmpint (lon, ==, 7); + g_assert_cmpint (lat, ==, 50); + + g_slist_free_full (locs, g_object_unref); + g_free (xml); + g_free (name); +} + +/* Make sure we can parse the binary data trip information */ +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_locs); + g_test_add_func ("/providers/de-db/parse_trips", test_parse_trips); + + ret = g_test_run (); + return ret; +} |