Implement HTTP server for requests

feature/trainlines-wip
Markus Koch 2020-05-03 19:59:05 +02:00
parent fe1547b37d
commit 6e5b86c076
7 changed files with 275 additions and 45 deletions

View File

@ -157,13 +157,18 @@ struct dijkstra_path *dijkstra_path_dup_shallow(struct dijkstra_path *path)
return dup;
}
float dijkstra_position_get_distance(struct position *position_a,
struct position *position_b) {
return sqrt(pow(position_a->x - position_b->x, 2) +
pow(position_a->y - position_b->y, 2));
}
dijkstra_cost dijkstra_get_weight_from_distance(struct dijkstra_node *node_a,
struct dijkstra_node *node_b)
{
float dist;
dist = sqrt(pow(node_a->position.x - node_b->position.x, 2) +
pow(node_a->position.y - node_b->position.y, 2));
dist = dijkstra_position_get_distance(&node_a->position, &node_b->position);
if (dist < DIJKSTRA_COST_MIN)
dist = DIJKSTRA_COST_MIN;
@ -408,3 +413,23 @@ inline struct dijkstra_node *dijkstra_node_get_connection(struct dijkstra_node *
}
return (path->destination == node ? path->source : path->destination);
}
struct dijkstra_node *dijkstra_node_find_closest_node(struct dijkstra_solver *solver,
struct position *position) {
GList *l = NULL;
struct dijkstra_node *node = NULL;
struct dijkstra_node *closest_node = NULL;
float closest_distance;
float distance;
for (l = solver->nodes; l != NULL; l = l->next) {
node = (struct dijkstra_node*) l->data;
distance = dijkstra_position_get_distance(position, &node->position);
if (!closest_node || distance < closest_distance) {
closest_node = node;
closest_distance = distance;
}
}
return closest_node;
}

View File

@ -49,6 +49,8 @@ void dijkstra_solver_free(struct dijkstra_solver *solver);
/* Library functions */
struct dijkstra_node *dijkstra_node_get_connection(struct dijkstra_node *node,
struct dijkstra_path *path);
struct dijkstra_node *dijkstra_node_find_closest_node(struct dijkstra_solver *solver,
struct position *position);
/* For debugging only */
struct dijkstra_node *dijkstra_node_new_to_list(GList **list,

View File

@ -203,8 +203,27 @@ GList *dijkstra_search_get_route(struct dijkstra_search *search,
return l;
}
void dijkstra_search_route_to_geojson(struct dijkstra_search *search,
GList *route)
int printb(char **buf, int *offset, const char *__restrict __format, ...)
{
va_list vargs;
int len;
va_start(vargs, __format);
len = vsnprintf(NULL, 0, __format, vargs);
va_end(vargs);
*buf = realloc(*buf, *offset + len + 1);
va_start(vargs, __format);
len = vsprintf(*buf + *offset, __format, vargs);
va_end(vargs);
*offset += len;
return len;
}
// TODO: Rewrite to use glib-json... On the other hand, this is probably faster.
int dijkstra_search_route_to_geojson(struct dijkstra_search *search,
GList *route,
char **buf)
{
GList *l = NULL;
struct dijkstra_node *node = NULL;
@ -213,8 +232,11 @@ void dijkstra_search_route_to_geojson(struct dijkstra_search *search,
float last_heading = 0;
char *relative_direction_str = NULL;
char *format_str = NULL;
int offset = 0;
printf("[");
*buf = NULL;
printb(buf, &offset, "[");
for (l = route; l != NULL; l = l->next) {
node = (struct dijkstra_node*) l->data;
@ -236,31 +258,33 @@ void dijkstra_search_route_to_geojson(struct dijkstra_search *search,
format_str = "Go %s onto %s.";
if (node_last != search->start)
printf(",");
printf("\n");
printf("{\"type\": \"Feature\",\n"
printb(buf, &offset, ",");
printb(buf, &offset, "\n");
printb(buf, &offset, "{\"type\": \"Feature\",\n"
" \"geometry\": {\n"
" \"type\": \"LineString\", \"coordinates\": [[%f,%f],[%f,%f]]\n"
" },\n",
node_last->position.x, node_last->position.y,
node->position.x, node->position.y);
printf(" \"properties\": {\n"
printb(buf, &offset, " \"properties\": {\n"
" \"heading\": \"%f\",\n"
" \"through\": \"%s\",\n",
heading, search->states[node->uid]->cheapest_path->name);
printf(" \"description\": \"");
printf(format_str, relative_direction_str, search->states[node->uid]->cheapest_path->name);
printf("\"\n");
printb(buf, &offset, " \"description\": \"");
printb(buf, &offset, format_str, relative_direction_str, search->states[node->uid]->cheapest_path->name);
printb(buf, &offset, "\"\n");
printf(" }\n");
printf("}");
printb(buf, &offset, " }\n");
printb(buf, &offset, "}");
last_heading = heading;
}
node_last = node;
}
printf("]\n");
printb(buf, &offset, "\n]\n");
return offset;
}
void dijkstra_print_path(struct dijkstra_search *search,

View File

@ -32,6 +32,7 @@ GList *dijkstra_search_get_route(struct dijkstra_search *search,
void dijkstra_print_path(struct dijkstra_search *search,
struct dijkstra_node *destination);
void dijkstra_print_nodes(struct dijkstra_search *search);
void dijkstra_search_route_to_geojson(struct dijkstra_search *search,
GList *route);
int dijkstra_search_route_to_geojson(struct dijkstra_search *search,
GList *route,
char **buf);
#endif // DIJKSTRASEARCH_H

172
src/dijkstraserv.c 100644
View File

@ -0,0 +1,172 @@
#include "dijkstraserv.h"
static int dijkstra_serv_perform_search(struct dijkstra_serv *serv,
struct position *pos_start,
struct position *pos_destination,
char **result) {
struct dijkstra_node *start = NULL;
struct dijkstra_node *destination = NULL;
struct dijkstra_search *search;
int iterations;
GList *route;
report(LL_INFO, "Requested route from %f,%f to %f,%f.",
pos_start->x, pos_start->y,
pos_destination->x, pos_destination->y);
/* Find closest node */ // TODO: This should be closest point on path, but for now this'll do.
start = dijkstra_node_find_closest_node(serv->solver, pos_start);
if (!start)
return 0;
destination = dijkstra_node_find_closest_node(serv->solver, pos_destination);
if (!destination)
return 0;
report(LL_DEBUG, "Searching route from %f,%f to %f,%f.",
start->position.x, start->position.y,
destination->position.x, destination->position.y);
search = dijkstra_search_new(serv->solver);
if (!search) {
report(LL_CRITICAL, "Error allocating dijkstra_search");
return 0;
}
dijkstra_search_set_start(search, start);
if ((iterations = dijkstra_search_find_path(search, destination)) > 0) {
report(LL_INFO, " Found path from %d to %d after %d iterations.", start->uid, destination->uid, iterations);
route = dijkstra_search_get_route(search, destination);
return dijkstra_search_route_to_geojson(search, route, result);
} else {
report(LL_INFO, " Couldn't find path.");
}
return 0;
}
static void dijkstra_serv_soup_callback (SoupServer *server,
SoupMessage *msg,
const char *path,
GHashTable *query,
SoupClientContext *client,
gpointer user_data)
{
struct dijkstra_serv *serv = (struct dijkstra_serv *) user_data;
GError *err = NULL;
GMatchInfo *matchInfo;
GRegex *regex;
char *body;
int body_len;
int i = 0;
struct position pos_start, pos_destination;
report(LL_INFO, "New request: %s", path);
if (msg->method != SOUP_METHOD_GET) {
soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
return;
}
if (strlen(path) > 20) {
soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST);
return;
}
regex = g_regex_new("([\\-0-9]+)", 0, 0, &err);
g_regex_match (regex, path, 0, &matchInfo);
while (g_match_info_matches(matchInfo)) {
float result_num;
gchar *result;
result = g_match_info_fetch(matchInfo, 0);
if (!result)
break;
result_num = atof(result);
switch (i) {
case 0:
pos_start.x = result_num;
break;
case 1:
pos_start.y = result_num;
break;
case 2:
pos_destination.x = result_num;
break;
case 3:
pos_destination.y = result_num;
break;
}
g_match_info_next(matchInfo, &err);
g_free(result);
i++;
if (i >= 4)
break;
}
g_regex_unref(regex);
if (i == 4) {
body_len = dijkstra_serv_perform_search(serv, &pos_start, &pos_destination, &body);
if (!body_len) {
body = strdup("Not found.");
body_len = 9;
soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND);
} else {
soup_message_set_status (msg, SOUP_STATUS_OK);
}
soup_message_set_response (msg, "application/json", SOUP_MEMORY_COPY,
body, body_len);
free(body);
} else {
report(LL_WARNING, " Invalid request.");
soup_message_set_status (msg, SOUP_STATUS_MALFORMED);
soup_message_set_response (msg, "text/plain", SOUP_MEMORY_COPY,
"Invalid request", 15);
}
}
struct dijkstra_serv *dijkstra_serv_new(
struct dijkstra_solver *solver,
int port)
{
struct dijkstra_serv *serv;
serv = (struct dijkstra_serv*) malloc(sizeof(*serv));
if (serv) {
serv->solver = solver;
serv->port = port;
serv->soupserver = soup_server_new(NULL, NULL);
if (!serv->soupserver) {
free(serv);
return NULL;
}
soup_server_add_handler (serv->soupserver,
"/", dijkstra_serv_soup_callback,
serv, NULL); // TODO: The last arg here should probably not be NULL...
}
return serv;
}
int dijkstra_serv_run(struct dijkstra_serv *serv)
{
GError *err = NULL;
if (!soup_server_listen_all(serv->soupserver, serv->port, 0, &err)) {
report(LL_CRITICAL, "Couldn't start server: %s", err->message);
g_error_free(err);
return -1;
}
report(LL_INFO, "Listening on port %d.", serv->port);
GMainLoop *loop;
loop = g_main_loop_new(NULL, TRUE);
g_main_loop_run(loop); // TODO: Add way to gracefully exit loop
return 0;
}

22
src/dijkstraserv.h 100644
View File

@ -0,0 +1,22 @@
#ifndef DIJKSTRASERV_H
#define DIJKSTRASERV_H
#include "dijkstragraph.h"
#include "dijkstrasearch.h"
#include "logging.h"
#include <libsoup/soup.h>
struct dijkstra_serv {
struct dijkstra_solver *solver;
int port;
SoupServer *soupserver;
};
struct dijkstra_serv *dijkstra_serv_new(
struct dijkstra_solver *solver,
int port);
int dijkstra_serv_run(struct dijkstra_serv *serv);
#endif // DIJKSTRASERV_H

View File

@ -5,6 +5,7 @@
#include "dijkstragraph.h"
#include "dijkstrasearch.h"
#include "dijkstraserv.h"
#include "geojson.h"
#include "logging.h"
@ -48,16 +49,14 @@ int main(int argc, char *argv[])
struct dijkstra_node *node;
struct dijkstra_path *path;
struct dijkstra_search *search;
int iterations;
GList *route = NULL;
int port = 6802;
struct dijkstra_serv *serv;
/* Init stuff */
set_log_level(LL_DEBUG);
/* Process arguments */
while((opt = getopt(argc, argv, "vs")) != -1) {
while((opt = getopt(argc, argv, "vsp")) != -1) {
switch(opt)
{
case 'v':
@ -66,6 +65,9 @@ int main(int argc, char *argv[])
case 's':
set_log_level(0);
break;
case 'p':
port = atoi(argv[i]);
break;
case ':':
case '?':
help(argv[0]);
@ -92,13 +94,6 @@ int main(int argc, char *argv[])
add_geojson_to_dijkstra(solver, argv[i]);
}
// add_test_map(solver);
// TODO: This start / end coordinates are only for testing
start = dijkstra_node_new_to_list(&solver->nodes, -856, -393);
dest = dijkstra_node_new_to_list(&solver->nodes, 388, 99);
//start = dijkstra_node_new_to_list(&solver->nodes, -957, -467);
//dest = dijkstra_node_new_to_list(&solver->nodes, -712, -467);
if (should_report(LL_NOISY)) {
for (l = solver->nodes; l != NULL; l = l->next) {
node = (struct dijkstra_node*) l->data;
@ -121,25 +116,14 @@ int main(int argc, char *argv[])
g_list_length(solver->nodes),
g_list_length(solver->paths));
/* Perform search */
search = dijkstra_search_new(solver);
if (!search) {
report(LL_ERROR, "Error allocating dijkstra_search");
/* Start server */
serv = dijkstra_serv_new(solver, port);
if (!serv) {
dijkstra_solver_free(solver);
return -3;
}
dijkstra_search_set_start(search, start);
if ((iterations = dijkstra_search_find_path(search, dest)) > 0) {
report(LL_INFO, "Found path from %d to %d after %d iterations.", start->uid, dest->uid, iterations);
route = dijkstra_search_get_route(search, dest);
dijkstra_search_route_to_geojson(search, route);
// dijkstra_print_path(search, dest);
// dijkstra_print_nodes(search);
} else {
report(LL_WARNING, "Couldn't find path.");
return -4;
}
dijkstra_serv_run(serv);
dijkstra_search_free(search);
dijkstra_solver_free(solver);
return 0;