2017-11-28 19:24:53 +00:00
// SPDX-License-Identifier: GPL-2.0
# ifdef __clang__
// Clang has a bug on zero-initialization of C structs.
# pragma clang diagnostic ignored "-Wmissing-field-initializers"
# endif
2018-05-22 07:07:42 +00:00
# include "ssrf.h"
2017-11-28 19:24:53 +00:00
# include "dive.h"
# include "parse.h"
2020-10-25 12:28:55 +00:00
# include "sample.h"
# include "subsurface-string.h"
2017-11-28 19:24:53 +00:00
# include "divelist.h"
core: introduce divelog structure
The parser API was very annoying, as a number of tables
to-be-filled were passed in as pointers. The goal of this
commit is to collect all these tables in a single struct.
This should make it (more or less) clear what is actually
written into the divelog files.
Moreover, it should now be rather easy to search for
instances, where the global logfile is accessed (and it
turns out that there are many!).
The divelog struct does not contain the tables as substructs,
but only collects pointers. The idea is that the "divelog.h"
file can be included without all the other files describing
the numerous tables.
To make it easier to use from C++ parts of the code, the
struct implements a constructor and a destructor. Sadly,
we can't use smart pointers, since the pointers are accessed
from C code. Therfore the constructor and destructor are
quite complex.
The whole commit is large, but was mostly an automatic
conversion.
One oddity of note: the divelog structure also contains
the "autogroup" flag, since that is saved in the divelog.
This actually fixes a bug: Before, when importing dives
from a different log, the autogroup flag was overwritten.
This was probably not intended and does not happen anymore.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2022-11-08 20:31:08 +00:00
# include "divelog.h"
2017-11-28 19:24:53 +00:00
# include "device.h"
# include "membuffer.h"
# include "gettext.h"
2019-05-30 16:29:36 +00:00
# include "tag.h"
2017-11-28 19:24:53 +00:00
2020-10-25 13:10:52 +00:00
# include <stdlib.h>
2018-10-17 16:45:22 +00:00
static int dm4_events ( void * param , int columns , char * * data , char * * column )
2017-11-28 19:24:53 +00:00
{
2018-05-22 07:07:42 +00:00
UNUSED ( columns ) ;
UNUSED ( column ) ;
2018-10-17 16:45:22 +00:00
struct parser_state * state = ( struct parser_state * ) param ;
2017-11-28 19:24:53 +00:00
2018-10-17 16:45:22 +00:00
event_start ( state ) ;
2017-11-28 19:24:53 +00:00
if ( data [ 1 ] )
2018-10-17 16:45:22 +00:00
state - > cur_event . time . seconds = atoi ( data [ 1 ] ) ;
2017-11-28 19:24:53 +00:00
if ( data [ 2 ] ) {
switch ( atoi ( data [ 2 ] ) ) {
case 1 :
/* 1 Mandatory Safety Stop */
2018-10-17 16:45:22 +00:00
strcpy ( state - > cur_event . name , " safety stop (mandatory) " ) ;
2017-11-28 19:24:53 +00:00
break ;
case 3 :
/* 3 Deco */
/* What is Subsurface's term for going to
* deco ? */
2018-10-17 16:45:22 +00:00
strcpy ( state - > cur_event . name , " deco " ) ;
2017-11-28 19:24:53 +00:00
break ;
case 4 :
/* 4 Ascent warning */
2018-10-17 16:45:22 +00:00
strcpy ( state - > cur_event . name , " ascent " ) ;
2017-11-28 19:24:53 +00:00
break ;
case 5 :
/* 5 Ceiling broken */
2018-10-17 16:45:22 +00:00
strcpy ( state - > cur_event . name , " violation " ) ;
2017-11-28 19:24:53 +00:00
break ;
case 6 :
/* 6 Mandatory safety stop ceiling error */
2018-10-17 16:45:22 +00:00
strcpy ( state - > cur_event . name , " violation " ) ;
2017-11-28 19:24:53 +00:00
break ;
case 7 :
/* 7 Below deco floor */
2018-10-17 16:45:22 +00:00
strcpy ( state - > cur_event . name , " below floor " ) ;
2017-11-28 19:24:53 +00:00
break ;
case 8 :
/* 8 Dive time alarm */
2018-10-17 16:45:22 +00:00
strcpy ( state - > cur_event . name , " divetime " ) ;
2017-11-28 19:24:53 +00:00
break ;
case 9 :
/* 9 Depth alarm */
2018-10-17 16:45:22 +00:00
strcpy ( state - > cur_event . name , " maxdepth " ) ;
2017-11-28 19:24:53 +00:00
break ;
case 10 :
/* 10 OLF 80% */
case 11 :
/* 11 OLF 100% */
2018-10-17 16:45:22 +00:00
strcpy ( state - > cur_event . name , " OLF " ) ;
2017-11-28 19:24:53 +00:00
break ;
case 12 :
/* 12 High pO₂ */
2018-10-17 16:45:22 +00:00
strcpy ( state - > cur_event . name , " PO2 " ) ;
2017-11-28 19:24:53 +00:00
break ;
case 13 :
/* 13 Air time */
2018-10-17 16:45:22 +00:00
strcpy ( state - > cur_event . name , " airtime " ) ;
2017-11-28 19:24:53 +00:00
break ;
case 17 :
/* 17 Ascent warning */
2018-10-17 16:45:22 +00:00
strcpy ( state - > cur_event . name , " ascent " ) ;
2017-11-28 19:24:53 +00:00
break ;
case 18 :
/* 18 Ceiling error */
2018-10-17 16:45:22 +00:00
strcpy ( state - > cur_event . name , " ceiling " ) ;
2017-11-28 19:24:53 +00:00
break ;
case 19 :
/* 19 Surfaced */
2018-10-17 16:45:22 +00:00
strcpy ( state - > cur_event . name , " surface " ) ;
2017-11-28 19:24:53 +00:00
break ;
case 20 :
/* 20 Deco */
2018-10-17 16:45:22 +00:00
strcpy ( state - > cur_event . name , " deco " ) ;
2017-11-28 19:24:53 +00:00
break ;
case 22 :
case 32 :
/* 22 Mandatory safety stop violation */
/* 32 Deep stop violation */
2018-10-17 16:45:22 +00:00
strcpy ( state - > cur_event . name , " violation " ) ;
2017-11-28 19:24:53 +00:00
break ;
case 30 :
/* Tissue level warning */
2018-10-17 16:45:22 +00:00
strcpy ( state - > cur_event . name , " tissue warning " ) ;
2017-11-28 19:24:53 +00:00
break ;
case 37 :
/* Tank pressure alarm */
2018-10-17 16:45:22 +00:00
strcpy ( state - > cur_event . name , " tank pressure " ) ;
2017-11-28 19:24:53 +00:00
break ;
case 257 :
/* 257 Dive active */
/* This seems to be given after surface when
* descending again . */
2018-10-17 16:45:22 +00:00
strcpy ( state - > cur_event . name , " surface " ) ;
2017-11-28 19:24:53 +00:00
break ;
case 258 :
/* 258 Bookmark */
if ( data [ 3 ] ) {
2018-10-17 16:45:22 +00:00
strcpy ( state - > cur_event . name , " heading " ) ;
state - > cur_event . value = atoi ( data [ 3 ] ) ;
2017-11-28 19:24:53 +00:00
} else {
2018-10-17 16:45:22 +00:00
strcpy ( state - > cur_event . name , " bookmark " ) ;
2017-11-28 19:24:53 +00:00
}
break ;
case 259 :
/* Deep stop */
2018-10-17 16:45:22 +00:00
strcpy ( state - > cur_event . name , " Deep stop " ) ;
2017-11-28 19:24:53 +00:00
break ;
case 260 :
/* Deep stop */
2018-10-17 16:45:22 +00:00
strcpy ( state - > cur_event . name , " Deep stop cleared " ) ;
2017-11-28 19:24:53 +00:00
break ;
case 266 :
/* Mandatory safety stop activated */
2018-10-17 16:45:22 +00:00
strcpy ( state - > cur_event . name , " safety stop (mandatory) " ) ;
2017-11-28 19:24:53 +00:00
break ;
case 267 :
/* Mandatory safety stop deactivated */
/* DM5 shows this only on event list, not on the
* profile so skipping as well for now */
break ;
default :
2018-10-17 16:45:22 +00:00
strcpy ( state - > cur_event . name , " unknown " ) ;
state - > cur_event . value = atoi ( data [ 2 ] ) ;
2017-11-28 19:24:53 +00:00
break ;
}
}
2018-10-17 16:45:22 +00:00
event_end ( state ) ;
2017-11-28 19:24:53 +00:00
return 0 ;
}
2018-10-17 16:45:22 +00:00
static int dm4_tags ( void * param , int columns , char * * data , char * * column )
2017-11-28 19:24:53 +00:00
{
2018-05-22 07:07:42 +00:00
UNUSED ( columns ) ;
UNUSED ( column ) ;
2018-10-17 16:45:22 +00:00
struct parser_state * state = ( struct parser_state * ) param ;
2017-11-28 19:24:53 +00:00
if ( data [ 0 ] )
2018-10-17 16:45:22 +00:00
taglist_add_tag ( & state - > cur_dive - > tag_list , data [ 0 ] ) ;
2017-11-28 19:24:53 +00:00
return 0 ;
}
2018-10-17 16:45:22 +00:00
static int dm4_dive ( void * param , int columns , char * * data , char * * column )
2017-11-28 19:24:53 +00:00
{
2018-05-22 07:07:42 +00:00
UNUSED ( columns ) ;
UNUSED ( column ) ;
2017-12-17 17:12:08 +00:00
int i ;
2017-11-28 19:24:53 +00:00
int interval , retval = 0 ;
2018-10-17 16:45:22 +00:00
struct parser_state * state = ( struct parser_state * ) param ;
sqlite3 * handle = state - > sql_handle ;
2017-11-28 19:24:53 +00:00
float * profileBlob ;
unsigned char * tempBlob ;
int * pressureBlob ;
char get_events_template [ ] = " select * from Mark where DiveId = %d " ;
char get_tags_template [ ] = " select Text from DiveTag where DiveId = %d " ;
char get_events [ 64 ] ;
2019-08-04 16:44:57 +00:00
cylinder_t * cyl ;
2017-11-28 19:24:53 +00:00
2018-10-17 16:45:22 +00:00
dive_start ( state ) ;
state - > cur_dive - > number = atoi ( data [ 0 ] ) ;
2017-11-28 19:24:53 +00:00
2018-10-17 16:45:22 +00:00
state - > cur_dive - > when = ( time_t ) ( atol ( data [ 1 ] ) ) ;
2017-11-28 19:24:53 +00:00
if ( data [ 2 ] )
2018-10-17 16:45:22 +00:00
utf8_string ( data [ 2 ] , & state - > cur_dive - > notes ) ;
2017-11-28 19:24:53 +00:00
/*
* DM4 stores Duration and DiveTime . It looks like DiveTime is
* 10 to 60 seconds shorter than Duration . However , I have no
* idea what is the difference and which one should be used .
* Duration = data [ 3 ]
* DiveTime = data [ 15 ]
*/
if ( data [ 3 ] )
2018-10-17 16:45:22 +00:00
state - > cur_dive - > duration . seconds = atoi ( data [ 3 ] ) ;
2017-11-28 19:24:53 +00:00
if ( data [ 15 ] )
2018-10-17 16:45:22 +00:00
state - > cur_dive - > dc . duration . seconds = atoi ( data [ 15 ] ) ;
2017-11-28 19:24:53 +00:00
/*
* TODO : the deviceid hash should be calculated here .
*/
2018-10-17 16:45:22 +00:00
settings_start ( state ) ;
dc_settings_start ( state ) ;
2017-11-28 19:24:53 +00:00
if ( data [ 4 ] )
2018-10-17 16:45:22 +00:00
utf8_string ( data [ 4 ] , & state - > cur_settings . dc . serial_nr ) ;
2017-11-28 19:24:53 +00:00
if ( data [ 5 ] )
2018-10-17 16:45:22 +00:00
utf8_string ( data [ 5 ] , & state - > cur_settings . dc . model ) ;
2017-11-28 19:24:53 +00:00
2018-10-17 16:45:22 +00:00
state - > cur_settings . dc . deviceid = 0xffffffff ;
dc_settings_end ( state ) ;
settings_end ( state ) ;
2017-11-28 19:24:53 +00:00
if ( data [ 6 ] )
2018-10-17 16:45:22 +00:00
state - > cur_dive - > dc . maxdepth . mm = lrint ( strtod_flags ( data [ 6 ] , NULL , 0 ) * 1000 ) ;
2017-11-28 19:24:53 +00:00
if ( data [ 8 ] )
2018-10-17 16:45:22 +00:00
state - > cur_dive - > dc . airtemp . mkelvin = C_to_mkelvin ( atoi ( data [ 8 ] ) ) ;
2017-11-28 19:24:53 +00:00
if ( data [ 9 ] )
2018-10-17 16:45:22 +00:00
state - > cur_dive - > dc . watertemp . mkelvin = C_to_mkelvin ( atoi ( data [ 9 ] ) ) ;
2017-11-28 19:24:53 +00:00
/*
* TODO : handle multiple cylinders
*/
2019-08-04 16:59:14 +00:00
cyl = cylinder_start ( state ) ;
2017-11-28 19:24:53 +00:00
if ( data [ 22 ] & & atoi ( data [ 22 ] ) > 0 )
2019-08-04 16:44:57 +00:00
cyl - > start . mbar = atoi ( data [ 22 ] ) ;
2017-11-28 19:24:53 +00:00
else if ( data [ 10 ] & & atoi ( data [ 10 ] ) > 0 )
2019-08-04 16:44:57 +00:00
cyl - > start . mbar = atoi ( data [ 10 ] ) ;
2017-11-28 19:24:53 +00:00
if ( data [ 23 ] & & atoi ( data [ 23 ] ) > 0 )
2019-08-04 16:44:57 +00:00
cyl - > end . mbar = ( atoi ( data [ 23 ] ) ) ;
2017-11-28 19:24:53 +00:00
if ( data [ 11 ] & & atoi ( data [ 11 ] ) > 0 )
2019-08-04 16:44:57 +00:00
cyl - > end . mbar = ( atoi ( data [ 11 ] ) ) ;
2017-11-28 19:24:53 +00:00
if ( data [ 12 ] )
2019-08-04 16:44:57 +00:00
cyl - > type . size . mliter = lrint ( ( strtod_flags ( data [ 12 ] , NULL , 0 ) ) * 1000 ) ;
2017-11-28 19:24:53 +00:00
if ( data [ 13 ] )
2019-08-04 16:44:57 +00:00
cyl - > type . workingpressure . mbar = ( atoi ( data [ 13 ] ) ) ;
2017-11-28 19:24:53 +00:00
if ( data [ 20 ] )
2019-08-04 16:44:57 +00:00
cyl - > gasmix . o2 . permille = atoi ( data [ 20 ] ) * 10 ;
2017-11-28 19:24:53 +00:00
if ( data [ 21 ] )
2019-08-04 16:44:57 +00:00
cyl - > gasmix . he . permille = atoi ( data [ 21 ] ) * 10 ;
2018-10-17 16:45:22 +00:00
cylinder_end ( state ) ;
2017-11-28 19:24:53 +00:00
if ( data [ 14 ] )
2018-10-17 16:45:22 +00:00
state - > cur_dive - > dc . surface_pressure . mbar = ( atoi ( data [ 14 ] ) * 1000 ) ;
2017-11-28 19:24:53 +00:00
interval = data [ 16 ] ? atoi ( data [ 16 ] ) : 0 ;
profileBlob = ( float * ) data [ 17 ] ;
tempBlob = ( unsigned char * ) data [ 18 ] ;
pressureBlob = ( int * ) data [ 19 ] ;
2018-10-17 16:45:22 +00:00
for ( i = 0 ; interval & & i * interval < state - > cur_dive - > duration . seconds ; i + + ) {
sample_start ( state ) ;
state - > cur_sample - > time . seconds = i * interval ;
2017-11-28 19:24:53 +00:00
if ( profileBlob )
2018-10-17 16:45:22 +00:00
state - > cur_sample - > depth . mm = lrintf ( profileBlob [ i ] * 1000.0f ) ;
2017-11-28 19:24:53 +00:00
else
2018-10-17 16:45:22 +00:00
state - > cur_sample - > depth . mm = state - > cur_dive - > dc . maxdepth . mm ;
2017-11-28 19:24:53 +00:00
if ( data [ 18 ] & & data [ 18 ] [ 0 ] )
2018-10-17 16:45:22 +00:00
state - > cur_sample - > temperature . mkelvin = C_to_mkelvin ( tempBlob [ i ] ) ;
2017-11-28 19:24:53 +00:00
if ( data [ 19 ] & & data [ 19 ] [ 0 ] )
2018-10-17 16:45:22 +00:00
state - > cur_sample - > pressure [ 0 ] . mbar = pressureBlob [ i ] ;
sample_end ( state ) ;
2017-11-28 19:24:53 +00:00
}
2018-10-17 16:45:22 +00:00
snprintf ( get_events , sizeof ( get_events ) - 1 , get_events_template , state - > cur_dive - > number ) ;
2019-10-26 21:11:44 +00:00
retval = sqlite3_exec ( handle , get_events , & dm4_events , state , NULL ) ;
2017-11-28 19:24:53 +00:00
if ( retval ! = SQLITE_OK ) {
fprintf ( stderr , " %s " , " Database query dm4_events failed. \n " ) ;
return 1 ;
}
2018-10-17 16:45:22 +00:00
snprintf ( get_events , sizeof ( get_events ) - 1 , get_tags_template , state - > cur_dive - > number ) ;
2019-10-26 21:11:44 +00:00
retval = sqlite3_exec ( handle , get_events , & dm4_tags , state , NULL ) ;
2017-11-28 19:24:53 +00:00
if ( retval ! = SQLITE_OK ) {
fprintf ( stderr , " %s " , " Database query dm4_tags failed. \n " ) ;
return 1 ;
}
2018-10-17 16:45:22 +00:00
dive_end ( state ) ;
2017-11-28 19:24:53 +00:00
/*
for ( i = 0 ; i < columns ; + + i ) {
fprintf ( stderr , " %s \t " , column [ i ] ) ;
}
fprintf ( stderr , " \n " ) ;
for ( i = 0 ; i < columns ; + + i ) {
fprintf ( stderr , " %s \t " , data [ i ] ) ;
}
fprintf ( stderr , " \n " ) ;
//exit(0);
*/
return SQLITE_OK ;
}
core: introduce divelog structure
The parser API was very annoying, as a number of tables
to-be-filled were passed in as pointers. The goal of this
commit is to collect all these tables in a single struct.
This should make it (more or less) clear what is actually
written into the divelog files.
Moreover, it should now be rather easy to search for
instances, where the global logfile is accessed (and it
turns out that there are many!).
The divelog struct does not contain the tables as substructs,
but only collects pointers. The idea is that the "divelog.h"
file can be included without all the other files describing
the numerous tables.
To make it easier to use from C++ parts of the code, the
struct implements a constructor and a destructor. Sadly,
we can't use smart pointers, since the pointers are accessed
from C code. Therfore the constructor and destructor are
quite complex.
The whole commit is large, but was mostly an automatic
conversion.
One oddity of note: the divelog structure also contains
the "autogroup" flag, since that is saved in the divelog.
This actually fixes a bug: Before, when importing dives
from a different log, the autogroup flag was overwritten.
This was probably not intended and does not happen anymore.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2022-11-08 20:31:08 +00:00
int parse_dm4_buffer ( sqlite3 * handle , const char * url , const char * buffer , int size , struct divelog * log )
2017-11-28 19:24:53 +00:00
{
2018-05-22 07:07:42 +00:00
UNUSED ( buffer ) ;
UNUSED ( size ) ;
2017-11-28 19:24:53 +00:00
int retval ;
char * err = NULL ;
2018-10-17 16:45:22 +00:00
struct parser_state state ;
init_parser_state ( & state ) ;
2022-11-12 07:57:56 +00:00
state . log = log ;
2018-10-17 16:45:22 +00:00
state . sql_handle = handle ;
2017-11-28 19:24:53 +00:00
/* StartTime is converted from Suunto's nano seconds to standard
* time . We also need epoch , not seconds since year 1. */
char get_dives [ ] = " select D.DiveId,StartTime/10000000-62135596800,Note,Duration,SourceSerialNumber,Source,MaxDepth,SampleInterval,StartTemperature,BottomTemperature,D.StartPressure,D.EndPressure,Size,CylinderWorkPressure,SurfacePressure,DiveTime,SampleInterval,ProfileBlob,TemperatureBlob,PressureBlob,Oxygen,Helium,MIX.StartPressure,MIX.EndPressure FROM Dive AS D JOIN DiveMixture AS MIX ON D.DiveId=MIX.DiveId " ;
2018-10-17 16:45:22 +00:00
retval = sqlite3_exec ( handle , get_dives , & dm4_dive , & state , & err ) ;
free_parser_state ( & state ) ;
2017-11-28 19:24:53 +00:00
if ( retval ! = SQLITE_OK ) {
fprintf ( stderr , " Database query failed '%s'. \n " , url ) ;
return 1 ;
}
return 0 ;
}
2018-10-17 16:45:22 +00:00
static int dm5_cylinders ( void * param , int columns , char * * data , char * * column )
2017-11-28 19:24:53 +00:00
{
2018-05-22 07:07:42 +00:00
UNUSED ( columns ) ;
UNUSED ( column ) ;
2018-10-17 16:45:22 +00:00
struct parser_state * state = ( struct parser_state * ) param ;
2019-08-04 16:44:57 +00:00
cylinder_t * cyl ;
2017-11-28 19:24:53 +00:00
2019-08-04 16:59:14 +00:00
cyl = cylinder_start ( state ) ;
2017-11-28 19:24:53 +00:00
if ( data [ 7 ] & & atoi ( data [ 7 ] ) > 0 & & atoi ( data [ 7 ] ) < 350000 )
2019-08-04 16:44:57 +00:00
cyl - > start . mbar = atoi ( data [ 7 ] ) ;
2017-11-28 19:24:53 +00:00
if ( data [ 8 ] & & atoi ( data [ 8 ] ) > 0 & & atoi ( data [ 8 ] ) < 350000 )
2019-08-04 16:44:57 +00:00
cyl - > end . mbar = ( atoi ( data [ 8 ] ) ) ;
2017-11-28 19:24:53 +00:00
if ( data [ 6 ] ) {
/* DM5 shows tank size of 12 liters when the actual
* value is 0 ( and using metric units ) . So we just use
* the same 12 liters when size is not available */
2019-08-04 16:44:57 +00:00
if ( strtod_flags ( data [ 6 ] , NULL , 0 ) = = 0.0 & & cyl - > start . mbar )
cyl - > type . size . mliter = 12000 ;
2017-11-28 19:24:53 +00:00
else
2019-08-04 16:44:57 +00:00
cyl - > type . size . mliter = lrint ( ( strtod_flags ( data [ 6 ] , NULL , 0 ) ) * 1000 ) ;
2017-11-28 19:24:53 +00:00
}
if ( data [ 2 ] )
2019-08-04 16:44:57 +00:00
cyl - > gasmix . o2 . permille = atoi ( data [ 2 ] ) * 10 ;
2017-11-28 19:24:53 +00:00
if ( data [ 3 ] )
2019-08-04 16:44:57 +00:00
cyl - > gasmix . he . permille = atoi ( data [ 3 ] ) * 10 ;
2018-10-17 16:45:22 +00:00
cylinder_end ( state ) ;
2017-11-28 19:24:53 +00:00
return 0 ;
}
2018-10-17 16:45:22 +00:00
static int dm5_gaschange ( void * param , int columns , char * * data , char * * column )
2017-11-28 19:24:53 +00:00
{
2018-05-22 07:07:42 +00:00
UNUSED ( columns ) ;
UNUSED ( column ) ;
2018-10-17 16:45:22 +00:00
struct parser_state * state = ( struct parser_state * ) param ;
2017-11-28 19:24:53 +00:00
2018-10-17 16:45:22 +00:00
event_start ( state ) ;
2017-11-28 19:24:53 +00:00
if ( data [ 0 ] )
2018-10-17 16:45:22 +00:00
state - > cur_event . time . seconds = atoi ( data [ 0 ] ) ;
2017-11-28 19:24:53 +00:00
if ( data [ 1 ] ) {
2018-10-17 16:45:22 +00:00
strcpy ( state - > cur_event . name , " gaschange " ) ;
state - > cur_event . value = lrint ( strtod_flags ( data [ 1 ] , NULL , 0 ) ) ;
2017-11-28 19:24:53 +00:00
}
/* He part of the mix */
if ( data [ 2 ] )
2018-10-17 16:45:22 +00:00
state - > cur_event . value + = lrint ( strtod_flags ( data [ 2 ] , NULL , 0 ) ) < < 16 ;
event_end ( state ) ;
2017-11-28 19:24:53 +00:00
return 0 ;
}
2018-10-17 16:45:22 +00:00
static int dm5_dive ( void * param , int columns , char * * data , char * * column )
2017-11-28 19:24:53 +00:00
{
2018-05-22 07:07:42 +00:00
UNUSED ( columns ) ;
UNUSED ( column ) ;
2017-12-17 17:12:08 +00:00
int i ;
2018-04-07 04:46:37 +00:00
int tempformat = 0 ;
2017-11-28 19:24:53 +00:00
int interval , retval = 0 , block_size ;
2018-10-17 16:45:22 +00:00
struct parser_state * state = ( struct parser_state * ) param ;
sqlite3 * handle = state - > sql_handle ;
2017-11-28 19:24:53 +00:00
unsigned const char * sampleBlob ;
char get_events_template [ ] = " select * from Mark where DiveId = %d " ;
char get_tags_template [ ] = " select Text from DiveTag where DiveId = %d " ;
char get_cylinders_template [ ] = " select * from DiveMixture where DiveId = %d " ;
char get_gaschange_template [ ] = " select GasChangeTime,Oxygen,Helium from DiveGasChange join DiveMixture on DiveGasChange.DiveMixtureId=DiveMixture.DiveMixtureId where DiveId = %d " ;
char get_events [ 512 ] ;
2018-10-17 16:45:22 +00:00
dive_start ( state ) ;
state - > cur_dive - > number = atoi ( data [ 0 ] ) ;
2017-11-28 19:24:53 +00:00
2018-10-17 16:45:22 +00:00
state - > cur_dive - > when = ( time_t ) ( atol ( data [ 1 ] ) ) ;
2017-11-28 19:24:53 +00:00
if ( data [ 2 ] )
2018-10-17 16:45:22 +00:00
utf8_string ( data [ 2 ] , & state - > cur_dive - > notes ) ;
2017-11-28 19:24:53 +00:00
if ( data [ 3 ] )
2018-10-17 16:45:22 +00:00
state - > cur_dive - > duration . seconds = atoi ( data [ 3 ] ) ;
2017-11-28 19:24:53 +00:00
if ( data [ 15 ] )
2018-10-17 16:45:22 +00:00
state - > cur_dive - > dc . duration . seconds = atoi ( data [ 15 ] ) ;
2017-11-28 19:24:53 +00:00
/*
* TODO : the deviceid hash should be calculated here .
*/
2018-10-17 16:45:22 +00:00
settings_start ( state ) ;
dc_settings_start ( state ) ;
2017-11-28 19:24:53 +00:00
if ( data [ 4 ] ) {
2018-10-17 16:45:22 +00:00
utf8_string ( data [ 4 ] , & state - > cur_settings . dc . serial_nr ) ;
state - > cur_settings . dc . deviceid = atoi ( data [ 4 ] ) ;
2017-11-28 19:24:53 +00:00
}
if ( data [ 5 ] )
2018-10-17 16:45:22 +00:00
utf8_string ( data [ 5 ] , & state - > cur_settings . dc . model ) ;
2017-11-28 19:24:53 +00:00
2018-10-17 16:45:22 +00:00
dc_settings_end ( state ) ;
settings_end ( state ) ;
2017-11-28 19:24:53 +00:00
if ( data [ 6 ] )
2018-10-17 16:45:22 +00:00
state - > cur_dive - > dc . maxdepth . mm = lrint ( strtod_flags ( data [ 6 ] , NULL , 0 ) * 1000 ) ;
2017-11-28 19:24:53 +00:00
if ( data [ 8 ] )
2018-10-17 16:45:22 +00:00
state - > cur_dive - > dc . airtemp . mkelvin = C_to_mkelvin ( atoi ( data [ 8 ] ) ) ;
2017-11-28 19:24:53 +00:00
if ( data [ 9 ] )
2018-10-17 16:45:22 +00:00
state - > cur_dive - > dc . watertemp . mkelvin = C_to_mkelvin ( atoi ( data [ 9 ] ) ) ;
2017-11-28 19:24:53 +00:00
if ( data [ 4 ] ) {
2018-10-17 16:45:22 +00:00
state - > cur_dive - > dc . deviceid = atoi ( data [ 4 ] ) ;
2017-11-28 19:24:53 +00:00
}
if ( data [ 5 ] )
2018-10-17 16:45:22 +00:00
utf8_string ( data [ 5 ] , & state - > cur_dive - > dc . model ) ;
2017-11-28 19:24:53 +00:00
2022-01-29 08:03:15 +00:00
if ( data [ 25 ] ) {
//enum divemode_t {OC, CCR, PSCR, FREEDIVE, NUM_DIVEMODE, UNDEF_COMP_TYPE}; // Flags (Open-circuit and Closed-circuit-rebreather) for setting dive computer type
switch ( atoi ( data [ 25 ] ) ) {
case 1 :
state - > cur_dive - > dc . divemode = 0 ;
break ;
case 5 :
state - > cur_dive - > dc . divemode = 1 ;
break ;
default :
state - > cur_dive - > dc . divemode = 0 ;
break ;
}
}
2018-10-17 16:45:22 +00:00
snprintf ( get_events , sizeof ( get_events ) - 1 , get_cylinders_template , state - > cur_dive - > number ) ;
2019-10-26 21:11:44 +00:00
retval = sqlite3_exec ( handle , get_events , & dm5_cylinders , state , NULL ) ;
2017-11-28 19:24:53 +00:00
if ( retval ! = SQLITE_OK ) {
fprintf ( stderr , " %s " , " Database query dm5_cylinders failed. \n " ) ;
return 1 ;
}
if ( data [ 14 ] )
2018-10-17 16:45:22 +00:00
state - > cur_dive - > dc . surface_pressure . mbar = ( atoi ( data [ 14 ] ) / 100 ) ;
2017-11-28 19:24:53 +00:00
interval = data [ 16 ] ? atoi ( data [ 16 ] ) : 0 ;
2018-04-07 04:41:04 +00:00
/*
* sampleBlob [ 0 ] version number , indicates the size of one sample
*
* Following ones describe single sample , bugs in interpretation of the binary blob are likely :
*
* sampleBlob [ 3 ] depth
* sampleBlob [ 7 - 9 ] pressure
* sampleBlob [ 11 ] temperature - either full Celsius or float , might be different field for some version of DM
*/
2017-11-28 19:24:53 +00:00
sampleBlob = ( unsigned const char * ) data [ 24 ] ;
if ( sampleBlob ) {
switch ( sampleBlob [ 0 ] ) {
2018-04-07 04:45:30 +00:00
case 1 :
// Log is converted from DM4 to DM5
block_size = 16 ;
break ;
2017-11-28 19:24:53 +00:00
case 2 :
block_size = 19 ;
break ;
case 3 :
block_size = 23 ;
break ;
case 4 :
2018-04-07 04:46:37 +00:00
// Temperature is stored in float
tempformat = 1 ;
2017-11-28 19:24:53 +00:00
block_size = 26 ;
break ;
2018-06-16 00:03:16 +00:00
case 5 :
// Temperature is stored in float
tempformat = 1 ;
block_size = 30 ;
break ;
2017-11-28 19:24:53 +00:00
default :
block_size = 16 ;
break ;
}
}
2018-10-17 16:45:22 +00:00
for ( i = 0 ; interval & & sampleBlob & & i * interval < state - > cur_dive - > duration . seconds ; i + + ) {
2017-11-28 19:24:53 +00:00
float * depth = ( float * ) & sampleBlob [ i * block_size + 3 ] ;
int32_t pressure = ( sampleBlob [ i * block_size + 9 ] < < 16 ) + ( sampleBlob [ i * block_size + 8 ] < < 8 ) + sampleBlob [ i * block_size + 7 ] ;
2018-10-17 16:45:22 +00:00
sample_start ( state ) ;
state - > cur_sample - > time . seconds = i * interval ;
state - > cur_sample - > depth . mm = lrintf ( depth [ 0 ] * 1000.0f ) ;
2018-04-07 04:46:37 +00:00
if ( tempformat = = 1 ) {
float * temp = ( float * ) & ( sampleBlob [ i * block_size + 11 ] ) ;
2018-10-17 16:45:22 +00:00
state - > cur_sample - > temperature . mkelvin = C_to_mkelvin ( * temp ) ;
2018-04-07 04:46:37 +00:00
} else {
2018-04-07 05:15:15 +00:00
if ( ( sampleBlob [ i * block_size + 11 ] ) ! = 0x7F ) {
2018-10-17 16:45:22 +00:00
state - > cur_sample - > temperature . mkelvin = C_to_mkelvin ( sampleBlob [ i * block_size + 11 ] ) ;
2018-04-07 05:15:15 +00:00
}
2018-04-07 04:46:37 +00:00
}
2017-11-28 19:24:53 +00:00
/*
2018-04-07 04:46:37 +00:00
* Limit cylinder pressures to somewhat sensible values
2017-11-28 19:24:53 +00:00
*/
if ( pressure > = 0 & & pressure < 350000 )
2018-10-17 16:45:22 +00:00
state - > cur_sample - > pressure [ 0 ] . mbar = pressure ;
sample_end ( state ) ;
2017-11-28 19:24:53 +00:00
}
/*
* Log was converted from DM4 , thus we need to parse the profile
* from DM4 format
*/
if ( i = = 0 ) {
float * profileBlob ;
unsigned char * tempBlob ;
int * pressureBlob ;
profileBlob = ( float * ) data [ 17 ] ;
tempBlob = ( unsigned char * ) data [ 18 ] ;
pressureBlob = ( int * ) data [ 19 ] ;
2018-10-17 16:45:22 +00:00
for ( i = 0 ; interval & & i * interval < state - > cur_dive - > duration . seconds ; i + + ) {
sample_start ( state ) ;
state - > cur_sample - > time . seconds = i * interval ;
2017-11-28 19:24:53 +00:00
if ( profileBlob )
2018-10-17 16:45:22 +00:00
state - > cur_sample - > depth . mm = lrintf ( profileBlob [ i ] * 1000.0f ) ;
2017-11-28 19:24:53 +00:00
else
2018-10-17 16:45:22 +00:00
state - > cur_sample - > depth . mm = state - > cur_dive - > dc . maxdepth . mm ;
2017-11-28 19:24:53 +00:00
if ( data [ 18 ] & & data [ 18 ] [ 0 ] )
2018-10-17 16:45:22 +00:00
state - > cur_sample - > temperature . mkelvin = C_to_mkelvin ( tempBlob [ i ] ) ;
2017-11-28 19:24:53 +00:00
if ( data [ 19 ] & & data [ 19 ] [ 0 ] )
2018-10-17 16:45:22 +00:00
state - > cur_sample - > pressure [ 0 ] . mbar = pressureBlob [ i ] ;
sample_end ( state ) ;
2017-11-28 19:24:53 +00:00
}
}
2018-10-17 16:45:22 +00:00
snprintf ( get_events , sizeof ( get_events ) - 1 , get_gaschange_template , state - > cur_dive - > number ) ;
2019-10-26 21:11:44 +00:00
retval = sqlite3_exec ( handle , get_events , & dm5_gaschange , state , NULL ) ;
2017-11-28 19:24:53 +00:00
if ( retval ! = SQLITE_OK ) {
fprintf ( stderr , " %s " , " Database query dm5_gaschange failed. \n " ) ;
return 1 ;
}
2018-10-17 16:45:22 +00:00
snprintf ( get_events , sizeof ( get_events ) - 1 , get_events_template , state - > cur_dive - > number ) ;
2019-10-26 21:11:44 +00:00
retval = sqlite3_exec ( handle , get_events , & dm4_events , state , NULL ) ;
2017-11-28 19:24:53 +00:00
if ( retval ! = SQLITE_OK ) {
fprintf ( stderr , " %s " , " Database query dm4_events failed. \n " ) ;
return 1 ;
}
2018-10-17 16:45:22 +00:00
snprintf ( get_events , sizeof ( get_events ) - 1 , get_tags_template , state - > cur_dive - > number ) ;
2019-10-26 21:11:44 +00:00
retval = sqlite3_exec ( handle , get_events , & dm4_tags , state , NULL ) ;
2017-11-28 19:24:53 +00:00
if ( retval ! = SQLITE_OK ) {
fprintf ( stderr , " %s " , " Database query dm4_tags failed. \n " ) ;
return 1 ;
}
2018-10-17 16:45:22 +00:00
dive_end ( state ) ;
2017-11-28 19:24:53 +00:00
return SQLITE_OK ;
}
core: introduce divelog structure
The parser API was very annoying, as a number of tables
to-be-filled were passed in as pointers. The goal of this
commit is to collect all these tables in a single struct.
This should make it (more or less) clear what is actually
written into the divelog files.
Moreover, it should now be rather easy to search for
instances, where the global logfile is accessed (and it
turns out that there are many!).
The divelog struct does not contain the tables as substructs,
but only collects pointers. The idea is that the "divelog.h"
file can be included without all the other files describing
the numerous tables.
To make it easier to use from C++ parts of the code, the
struct implements a constructor and a destructor. Sadly,
we can't use smart pointers, since the pointers are accessed
from C code. Therfore the constructor and destructor are
quite complex.
The whole commit is large, but was mostly an automatic
conversion.
One oddity of note: the divelog structure also contains
the "autogroup" flag, since that is saved in the divelog.
This actually fixes a bug: Before, when importing dives
from a different log, the autogroup flag was overwritten.
This was probably not intended and does not happen anymore.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
2022-11-08 20:31:08 +00:00
int parse_dm5_buffer ( sqlite3 * handle , const char * url , const char * buffer , int size , struct divelog * log )
2017-11-28 19:24:53 +00:00
{
2018-05-22 07:07:42 +00:00
UNUSED ( buffer ) ;
UNUSED ( size ) ;
2017-11-28 19:24:53 +00:00
int retval ;
char * err = NULL ;
2018-10-17 16:45:22 +00:00
struct parser_state state ;
init_parser_state ( & state ) ;
2022-11-12 07:57:56 +00:00
state . log = log ;
2018-10-17 16:45:22 +00:00
state . sql_handle = handle ;
2017-11-28 19:24:53 +00:00
/* StartTime is converted from Suunto's nano seconds to standard
* time . We also need epoch , not seconds since year 1. */
2022-01-29 08:03:15 +00:00
char get_dives [ ] = " select DiveId,StartTime/10000000-62135596800,Note,Duration,coalesce(SourceSerialNumber,SerialNumber),Source,MaxDepth,SampleInterval,StartTemperature,BottomTemperature,StartPressure,EndPressure,'','',SurfacePressure,DiveTime,SampleInterval,ProfileBlob,TemperatureBlob,PressureBlob,'','','','',SampleBlob,Mode FROM Dive where Deleted is null " ;
2017-11-28 19:24:53 +00:00
2018-10-17 16:45:22 +00:00
retval = sqlite3_exec ( handle , get_dives , & dm5_dive , & state , & err ) ;
free_parser_state ( & state ) ;
2017-11-28 19:24:53 +00:00
if ( retval ! = SQLITE_OK ) {
fprintf ( stderr , " Database query failed '%s'. \n " , url ) ;
return 1 ;
}
return 0 ;
}