owlps/libowlps-resultreader/libowlps-resultreader.c

482 lines
13 KiB
C

/*
* This file is part of the Owl Positioning System (OwlPS).
* OwlPS is a project of the University of Franche-Comte
* (Université de Franche-Comté), France.
*
* Copyright © Université de Franche-Comté 2007-2012.
*
* Corresponding author: Matteo Cypriani <mcy@lm7.fr>
*
***********************************************************************
*
* This software is governed by the CeCILL license under French law and
* abiding by the rules of distribution of free software. You can use,
* modify and/or redistribute the software under the terms of the CeCILL
* license as circulated by CEA, CNRS and INRIA at the following URL:
* http://www.cecill.info
*
* As a counterpart to the access to the source code and rights to copy,
* modify and redistribute granted by the license, users are provided
* only with a limited warranty and the software's authors, the holder
* of the economic rights, and the successive licensors have only
* limited liability.
*
* In this respect, the user's attention is drawn to the risks
* associated with loading, using, modifying and/or developing or
* reproducing the software by the user in light of its specific status
* of free software, that may mean that it is complicated to manipulate,
* and that also therefore means that it is reserved for developers and
* experienced professionals having in-depth computer knowledge. Users
* are therefore encouraged to load and test the software's suitability
* as regards their requirements in conditions enabling the security of
* their systems and/or data to be ensured and, more generally, to use
* and operate it in the same conditions as regards security.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL license and that you accept its terms.
*
***********************************************************************
*
* This is the main source file of libowlps-resultreader.
*/
#include "owlps-resultreader.h"
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#define CSV_DELIMITER ";"
/*
* Receives a result from the socket of file descriptor 'sockfd', fills
* an owl_result structure with the received information, and returns a
* pointer to it.
*
* Note that the new owl_result is allocated with malloc() and must be
* deleted using free().
*
* In case of error, a message is printed, except if owl_run (from
* libowlps) is false, and NULL is returned.
*/
owl_result* owl_receive_position(int sockfd)
{
ssize_t nread ; // recvfrom return value
char csv[OWL_CSV_RESULT_STRLEN] ; // Read string
nread = recvfrom(sockfd, &csv, OWL_CSV_RESULT_STRLEN,
0, NULL, NULL) ;
if (nread <= 0)
{
if (owl_run)
perror("No request received from listener") ;
return NULL ;
}
return owl_fill_result(csv) ;
}
/*
* Splits the 'csv' string received from OwlPS Positioner and stores
* the fields in a new owl_result, and returns a pointer to it (or NULL
* in case of error).
*
* Note that the new owl_result is allocated with malloc() and must be
* deleted using free().
*
* Handled CSV format:
* Mobile_MAC;Request_type;Request_timestamp;Algorithm;X;Y;Z;Error;Area
* The Request_timestamp format is:
* seconds.nanoseconds
* The Area field can be empty.
*/
owl_result* owl_fill_result(char *csv)
{
char *csv_field = NULL ;
owl_result *result = NULL ;
result = malloc(sizeof(owl_result)) ;
memset(result, 0, sizeof(*result)) ;
/* Mobile MAC address */
csv_field = strsep(&csv, CSV_DELIMITER) ;
if (! csv_field)
{
fprintf(stderr,
"Error reading the mobile's MAC address from the CSV"
" string (empty string?)!\n") ;
goto error ;
}
result->mobile_mac_addr =
strndup(csv_field, OWL_ETHER_ADDR_STRLEN) ;
/* Request type */
csv_field = strsep(&csv, CSV_DELIMITER) ;
if (! csv_field)
{
fprintf(stderr,
"Error reading the request type from the CSV string!\n") ;
goto error ;
}
result->request_type = atoi(csv_field) ;
/* Timestamp */
// Seconds
csv_field = strsep(&csv, ".") ;
if (! csv_field)
{
fprintf(stderr,
"Error reading the timestamp (seconds) from the CSV"
" string!\n") ;
goto error ;
}
result->mobile_timestamp.tv_sec = atol(csv_field) ;
// Nanoseconds
csv_field = strsep(&csv, CSV_DELIMITER) ;
if (! csv_field)
{
fprintf(stderr,
"Error reading the timestamp (nanoseconds) from the CSV"
" string!\n") ;
goto error ;
}
result->mobile_timestamp.tv_nsec = atol(csv_field) ;
/* Algorithm results */
do
{
owl_algorithm_result *current_algo =
owl_fill_algorithm_result(&csv) ;
if (current_algo == NULL)
{
fprintf(stderr, "Error reading the algorithm #%d!\n",
result->nb_results + 1) ;
break ;
}
// Insert the current algorithm at the begining of the list
current_algo->next = result->results ;
result->results = current_algo ;
++result->nb_results ;
}
while (csv) ;
return result ; // Success
error:
owl_free_result(result) ;
return NULL ;
}
/*
* Splits the 'csv' string, stores the fields in a new
* owl_algorithm_result, and returns a pointer to it (or NULL
* in case of error).
*
* Note that the new owl_algorithm_result is allocated with malloc()
* and must be deleted using free().
*
* 'csv' must follow this CSV format:
* Algorithm;X;Y;Z;Error;Area
* The Area field can be empty.
*/
owl_algorithm_result* owl_fill_algorithm_result(char **csv)
{
owl_algorithm_result *algo ;
char *csv_field = NULL ;
algo = malloc(sizeof(owl_algorithm_result)) ;
memset(algo, 0, sizeof(*algo)) ;
// Algorithm name
csv_field = strsep(csv, CSV_DELIMITER) ;
if (! csv_field)
{
fprintf(stderr,
"Error reading the algorithm name from the CSV string!\n") ;
goto error ;
}
algo->algorithm =
strndup(csv_field, OWL_ALGORITHM_STRLEN) ;
// X coordinate
csv_field = strsep(csv, CSV_DELIMITER) ;
if (! csv_field)
{
fprintf(stderr,
"Error reading the X coordinate from the CSV string!\n") ;
goto error ;
}
algo->x = atof(csv_field) ;
// Y coordinate
csv_field = strsep(csv, CSV_DELIMITER) ;
if (! csv_field)
{
fprintf(stderr,
"Error reading the Y coordinate from the CSV string!\n") ;
goto error ;
}
algo->y = atof(csv_field) ;
// Z coordinate
csv_field = strsep(csv, CSV_DELIMITER) ;
if (! csv_field)
{
fprintf(stderr,
"Error reading the Z coordinate from the CSV string!\n") ;
goto error ;
}
algo->z = atof(csv_field) ;
// Distance error
csv_field = strsep(csv, CSV_DELIMITER) ;
if (! csv_field)
{
fprintf(stderr,
"Error reading the distance error from the CSV string!\n") ;
goto error ;
}
algo->error = atof(csv_field) ;
// Area name (optional)
csv_field = strsep(csv, CSV_DELIMITER) ;
if (csv_field)
algo->area = strndup(csv_field, OWL_AREA_STRLEN) ;
return algo ; // Success
error:
owl_free_algorithm_result(algo) ;
return NULL ;
}
/*
* Converts an owl_result back to a CSV string.
* 'dst' must be an allocated string of at least OWL_CSV_RESULT_STRLEN
* characters.
*
* CSV format:
* Mobile_MAC;Request_type;Request_timestamp;Nb_algo;Algo_1;…;Algo_n
* Nb_algo is the number of algorithms in the result.
* The format of Algo_i is documented in owl_algorithm_result_to_csv().
*/
void owl_result_to_csv(char dst[OWL_CSV_RESULT_STRLEN],
const owl_result *const src)
{
size_t dst_len ;
char timestamp_str[OWL_TIMESTAMP_STRLEN] ;
assert(src) ;
owl_timestamp_to_string(&src->mobile_timestamp, timestamp_str) ;
snprintf(dst, OWL_CSV_RESULT_REQUEST_STRLEN,
"%s;%"PRIu8";%s;%u",
src->mobile_mac_addr,
src->request_type,
timestamp_str,
src->nb_results) ;
dst_len = strlen(dst) ;
owl_algorithm_result *algo = src->results ;
while (algo)
{
char algo_str[OWL_CSV_ALGORITHM_RESULT_STRLEN] ;
owl_algorithm_result_to_csv(algo_str, algo) ;
dst[dst_len++] = ';' ;
strncpy(dst + dst_len, algo_str, OWL_CSV_ALGORITHM_RESULT_STRLEN) ;
dst_len += strlen(algo_str) ;
algo = algo->next ;
}
}
/*
* Converts an owl_algorithm_result back to a CSV string.
* 'dst' must be an allocated string of at least
* OWL_CSV_ALGORITHM_RESULT_STRLEN characters.
*
* CSV format:
* Algorithm_name;X;Y;Z;Error;Area_name
* Error is the distance from the true coordinates of the mobile, if
* known; if unknown, Error is set to -1.
* Area_name is the name of the area or room in which the mobile is (may
* be empty).
*/
void
owl_algorithm_result_to_csv(char dst[OWL_CSV_ALGORITHM_RESULT_STRLEN],
const owl_algorithm_result *const src)
{
assert(src) ;
snprintf(dst, OWL_CSV_ALGORITHM_RESULT_STRLEN,
"%s;%f;%f;%f;%f;%s",
src->algorithm,
src->x,
src->y,
src->z,
src->error,
src->area ? src->area : "") ;
}
/*
* Converts an owl_result back to a CSV string, in a simplified format.
* Only the *first* algorithm in the result's algorithm list will be
* included in the string.
* 'dst' must be an allocated string of at least
* OWL_CSV_RESULT_SIMPLE_STRLEN characters.
*
* CSV format:
* Mobile_MAC;First_algorithm
* First_algorithm is the first algorithm in src->results. Its format
* is documented in owl_algorithm_result_to_csv_simple().
*/
void owl_result_to_csv_simple(char dst[OWL_CSV_RESULT_SIMPLE_STRLEN],
const owl_result *const src)
{
size_t dst_len ;
char algo_str[OWL_CSV_ALGORITHM_RESULT_SIMPLE_STRLEN] ;
assert(src) ;
strncpy(dst, src->mobile_mac_addr, OWL_ETHER_ADDR_STRLEN) ;
dst[OWL_ETHER_ADDR_STRLEN - 1] = ';' ;
dst_len = OWL_ETHER_ADDR_STRLEN ;
if (! src->results)
return ;
owl_algorithm_result_to_csv_simple(algo_str, src->results) ;
strncpy(dst + dst_len, algo_str,
OWL_CSV_ALGORITHM_RESULT_SIMPLE_STRLEN) ;
}
/*
* Converts an owl_algorithm_result back to a CSV string, in a
* simplified format.
* 'dst' must be an allocated string of at least
* OWL_CSV_ALGORITHM_RESULT_SIMPLE_STRLEN characters.
*
* CSV format:
* X;Y;Z;Area_name
* Area_name is the name of the area or room in which the mobile is (may
* be empty).
*/
void owl_algorithm_result_to_csv_simple
(char dst[OWL_CSV_ALGORITHM_RESULT_SIMPLE_STRLEN],
const owl_algorithm_result *const src)
{
assert(src) ;
snprintf(dst, OWL_CSV_ALGORITHM_RESULT_SIMPLE_STRLEN,
"%f;%f;%f;%s",
src->x,
src->y,
src->z,
src->area ? src->area : "") ;
}
/*
* Prints an owl_result to the given stream.
*/
void owl_fprint_result(FILE *stream, const owl_result *const src)
{
char timestamp_str[OWL_TIMESTAMP_STRLEN] ;
assert(src) ;
owl_timestamp_to_string(&src->mobile_timestamp, timestamp_str) ;
fprintf(stream,
"Mobile MAC: %s\n"
"Request type: %"PRIu8"\n"
"Mobile timestamp: %s\n"
"%u results:\n"
,
src->mobile_mac_addr,
src->request_type,
timestamp_str,
src->nb_results) ;
owl_algorithm_result *algo = src->results ;
while (algo)
{
owl_fprint_algorithm_result(stream, algo) ;
algo = algo->next ;
}
}
/*
* Prints an owl_algorithm_result to the given stream.
*/
void owl_fprint_algorithm_result(FILE *stream,
const owl_algorithm_result *const src)
{
assert(src) ;
fprintf(stream,
"* Algorithm: %s\n"
" X: %f\n"
" Y: %f\n"
" Z: %f\n"
" Error: %f\n"
" Area: %s\n"
,
src->algorithm,
src->x,
src->y,
src->z,
src->error,
src->area ? src->area : ""
) ;
}
/*
* Frees the memory allocated by an owl_result. The 'results' and
* 'mobile_mac_addr' fields *must* be defined, either to NULL or to a
* valid memory block allocated with malloc().
* Note that the pointer will not set to NULL.
*/
void owl_free_result(owl_result *result)
{
if (! result)
return ;
while (result->results)
{
owl_algorithm_result *algo = result->results ;
result->results = algo->next ;
owl_free_algorithm_result(algo) ;
}
free(result->mobile_mac_addr) ;
free(result) ;
}
/*
* Frees the memory allocated by a single owl_algorithm_result (*not*
* recursively). The 'algorithm' and 'area' fields *must* be defined,
* either to NULL or to a valid memory block allocated with malloc().
* Note that the pointer will not set to NULL.
*/
void owl_free_algorithm_result(owl_algorithm_result *algo)
{
if (! algo)
return ;
free(algo->algorithm) ;
free(algo->area) ;
free(algo) ;
}