885 lines
33 KiB
C
885 lines
33 KiB
C
/*
|
|
* TCP server; communication with the ZKL3000
|
|
* Device info structures
|
|
*/
|
|
#ifndef _TCPSERVER_DEVICE_H
|
|
#define _TCPSERVER_DEVICE_H
|
|
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
|
|
#include <di-util/cp3000.h>
|
|
#include <di-util/cp3000-acl.h>
|
|
#include <di-util/list.h>
|
|
|
|
// include m.n. definitions for the version information
|
|
#include "database-if.h"
|
|
|
|
/*
|
|
** Definitions
|
|
*/
|
|
|
|
// Trivial defintions
|
|
#define CLIENTS_ALL ((client_t)-1)
|
|
#define CLIENT_SELF ((client_t)-1)
|
|
// Place holder to copy the child callbacks from the parent device
|
|
#define PARENT_CALLBACKS ((struct DEVICE_FCNTABLE*)-1)
|
|
|
|
// Flags for 'device_init()'; stored in DEVICE_INFO.flags
|
|
#define DEVICEFLG_PRIMARY 0x00000000 // placeholder really
|
|
#define DEVICEFLG_SECONDARY 0x00000001 // secondary connections will not create fifos etc.
|
|
#define DEVICEMSK_MODE 0x00000001 // (mask for the above)
|
|
#define DEVICEFLG_FRONTEND 0x00000000 // (placeholder) front-end connection to the actual device
|
|
#define DEVICEFLG_BACKEND 0x00000002 // back-end connection for MTinfo etc;
|
|
#define DEVICEFLG_SERVER 0x00000004 // server port
|
|
#define DEVICEMSK_TYPE 0x00000006 // (mask for the above)
|
|
#define DEVICEFLG_CHILD 0x00000008 // child device
|
|
#define DEVICEFLG_STATUSUPDATES 0x00000010 // back-end device wants to receive real-time status updates
|
|
#define DEVICEFLG_NO_DATABASE 0x00001000 // the device does not need a connection to the database
|
|
// Flags added to the above during initialization
|
|
#define DEVICEFLG_IS_NEW 0x00010000 // device is new and has just been added to the database
|
|
#define DEVICEFLG_SSL 0x00020000 // device uses SSL/TLS encryption
|
|
// Log file status
|
|
#define DEVICEFLG_HAS_BLACKBOX 0x00100000 // device has a physical logfile
|
|
#define DEVICEFLG_HAS_LOGFILE 0x00200000 // device has a log file (physical or emulated)
|
|
#define DEVICEFLG_LOG_CANRETRY 0x00400000 // log file can be retried (because it has a checksum)
|
|
#define DEVICEFLG_UNIFIED_LOG 0x00800000 // parent device keeps the logfile administration (set for the child device)
|
|
#define DEVICEFLG_NEW_LOGFILE 0x01000000 // device created a new logfile
|
|
#define DEVICEFLG_LOG_ABORTED 0x02000000 // sending the current logfile is aborted
|
|
// Run-time flags
|
|
#define DEVICEFLG_LOGOUT 0x10000000 // log out by terminating the main loop
|
|
#define DEVICEFLG_HAS_REPLY 0x20000000 // device has the reply it waited for; break out of the main loop
|
|
|
|
// Version information
|
|
// Note that the DEVICE_INFO struct does not contain any field for software or
|
|
// hardware versions. The structure below can be used for the device specific
|
|
// information structures.
|
|
struct DEVICE_FIRMWARE_VERSION
|
|
{
|
|
int major;
|
|
int minor;
|
|
int release;
|
|
int devtype; // device type as defined by MCUTYPE_xxx resp. (WCPUFEATURE_xxx << 16) | GSMACTIVE_xxx | WCPUTYPE_xxx
|
|
uint32_t datecode; // machine readable, i.e. 0x20090101
|
|
};
|
|
|
|
// Device identification
|
|
struct DEVICE_IDENTIFICATION
|
|
{
|
|
uint64_t present; // 'version_info' item is present (bitfield)
|
|
const char *version_info[N_VERSION_ALL];
|
|
|
|
time_t production; // production date of the device
|
|
};
|
|
|
|
// Status cache
|
|
struct DEVICE_STATUS
|
|
{
|
|
int flags; // STATUPDATE flags; -1 means that the cache is invalid
|
|
unsigned int seqnr; // incrementing sequence number
|
|
time_t t; // time when the status update was received
|
|
|
|
// cache
|
|
char str[256]; // status string, (re)built from the items below
|
|
char json[4096]; // status in JSON format
|
|
|
|
// real-time status items
|
|
// STATUPDATE_STATE
|
|
uint16_t mcu_state;
|
|
uint16_t rc_state;
|
|
uint16_t mcu_local_state;
|
|
uint32_t mcu_persistent;
|
|
// STATUPDATE_PMGT
|
|
uint16_t pm3000_state;
|
|
// STATUPDATE_SWITCH
|
|
uint16_t sw3000_state;
|
|
// STATUPDATE_WCPU
|
|
uint32_t wcpu_state;
|
|
// STATUPDATE_NATWS
|
|
unsigned int natws_counter;
|
|
signed int natws_direction;
|
|
// STATUPDATE_B_A
|
|
float b_a;
|
|
// STATUPDATE_RMS
|
|
float rms;
|
|
// STATUPDATE_AUTOCAL
|
|
float b_a_autocal;
|
|
float freq;
|
|
// STATUPDATE_BATTSEL
|
|
int batt_sel;
|
|
// STATUPDATE_BATT1LVL, STATUPDATE_BATT2LVL
|
|
float batt[N_BATTERY];
|
|
// STATUPDATE_INTTEMP
|
|
float temp_onboard;
|
|
// STATUPDATE_EXTTEMP; max. 2 sensors currently
|
|
float temp_extern[N_TEMPSENSOR];
|
|
// STATUPDATE_SW3000TEMP
|
|
float temp_sw3000;
|
|
// STATUPDATE_GSM
|
|
int gsm_rssi;
|
|
int gsm_berr;
|
|
// STATUPDATE_GPS
|
|
char gps_mode; // '-' (or 'N') invalid
|
|
// // 'U' uninitialized
|
|
// // 'I' initialized from cache
|
|
// // 'A' fix, autonomous
|
|
// // 'D' fix, diffential
|
|
// // 'E' fix, estimated
|
|
// // 'S' fix, stationary
|
|
float latitude;
|
|
float longitude;
|
|
float altitude;
|
|
float hdop;
|
|
float speed;
|
|
float heading;
|
|
// STATUPDATE_TIMESRC
|
|
char timesrc; // '-' invalid
|
|
// // 'U' uninitialized
|
|
// // 'I' initialized from cache
|
|
// // 'G' GPS
|
|
// // 'N' NITZ
|
|
// // '*' (or nothing) other source, e.g. built-in RTCC or NTP
|
|
// STATUPDATE_TIME
|
|
time_t t_gps; // GPS timestamp for the co-ordinates, or device time
|
|
// STATUPDATE_TILT
|
|
struct DEVICE_TILT { // tilt sensor
|
|
int g; // boolean: g's or degrees if zero
|
|
float x;
|
|
float y;
|
|
float z;
|
|
} tilt;
|
|
};
|
|
|
|
// Geofence cache
|
|
struct DEVICE_GEOFENCE
|
|
{
|
|
int n_inside; // number of valid ("inside") entries in 'designs'
|
|
int n_designs; // total number of designs; size of the array 'designs'
|
|
unsigned int *designs; // actual cache
|
|
};
|
|
|
|
// Device client or server communication (communication from front-end to
|
|
// back-end and v.v.)
|
|
struct DEVICE_CLIENTCOMMUNICATION;
|
|
struct DEVICE_SERVERCOMMUNICATION;
|
|
|
|
// Function table with device specific callbacks
|
|
// Parameters (in general)
|
|
// the device (cp3000_device_t object) i.e. the socket to the device
|
|
// Returns:
|
|
// CP3000 status code
|
|
struct DEVICE_FCNTABLE
|
|
{
|
|
// device specific initialization, two stages (see 'device_init()' below)
|
|
// the DEVICE_INFO structure for the device has been allocated and filled
|
|
// at this point; the device must allocate and initialize its own private
|
|
// data structures at this point.
|
|
// the data allocated here should be assigned to 'device_info' in the
|
|
// DEVICE_INFO structure
|
|
int (*init)(cp3000_device_t);
|
|
// second stage initialization for front-end and back-end connections
|
|
int (*attach)(cp3000_device_t, const void *login_tokens);
|
|
int (*connect)(cp3000_device_t, const void *login_tokens);
|
|
// clean-up and log out with the device when it is about to be destroyed;
|
|
// will be called for both the front-end and back-end, if defined
|
|
int (*detach)(cp3000_device_t);
|
|
// destroy the device's private structure
|
|
int (*destroy)(cp3000_device_t);
|
|
|
|
// get number of child devices; the identifiers for the child devices are
|
|
// collected via 'get_identification()' for each child device; return '-1'
|
|
// on error
|
|
int (*get_children)(cp3000_device_t);
|
|
// get version numbers and device identification items; 'get_identification'
|
|
// can be called with the parameter VERSIONIDX_ALL which allows the
|
|
// device to retrieve all items in one go in a pipelined fashion, as
|
|
// retrieving each individual item may take several seconds over GPRS
|
|
// if this feature is not supported by the 'get_identification' callback,
|
|
// it should return TCPERR_NOSUPP
|
|
int (*get_identification)(cp3000_device_t, int versionidx, char *buffer, size_t buffersz);
|
|
int (*set_identification)(cp3000_device_t, int versionidx, const char *data);
|
|
// get device status; must update the 'flags' to indicate which fields are
|
|
// present in the DEVICE_STATUS structure and the 't' flag to indicate the
|
|
// time of retrieval
|
|
int (*get_status)(cp3000_device_t, struct DEVICE_STATUS*);
|
|
|
|
// periodic events; the callback handles pending events and returns the
|
|
// time for the next event
|
|
// caching is desirable as this function is called after each communication
|
|
// from the device main loop, but this is the responsibility of the callback
|
|
time_t (*next_event)(cp3000_device_t, time_t now);
|
|
|
|
// send log file from the indicated position; the time base can be used to
|
|
// restore the absolute timestamps
|
|
int (*send_logfile)(cp3000_device_t, int rpgm_count, int startup_count, unsigned long pos, time_t);
|
|
|
|
// store or retrieve a key; non-CP3000 devices must translate the "key" to
|
|
// a device specific command
|
|
// mandatory values for "key" (used by MTinfo and MTinfo Secure and 'device_sync()'
|
|
// STAT - retrieve status string
|
|
// SWITCH - switch commands
|
|
// LED - dim the LEDs
|
|
// LANTERN - output port
|
|
// EXT-CONTACT - output port
|
|
// RELAY - output port
|
|
// GPS - track and trace functionality
|
|
// DBSERVER (identical to TCPSERVER), SMSSERVER, GPRS, PIN - servers, GPRS APN
|
|
// and PIN for the SIM card
|
|
// configurations settings from the database table 'zkl_config' supported by
|
|
// the device
|
|
// other, ZKL-specific keys; those keys are not requested by MTinfo (Secure)
|
|
// HEARTBEAT - enable/restart heartbeat timer (90s)
|
|
// MCU-STATUS - alter settings on the ZKL microprocessor
|
|
// RESET - device reset
|
|
// SECURE[*] - secure handshake (ZKL specific)
|
|
// TEL, TELn, TELSn - phonebook
|
|
// SYSTEM[NTC], SYSTEM[LOG], SYSTEM[T_POWERSAVE], SYSTEM[UART2], SYSTEM[STATUPDATE],
|
|
// SYSTEM[T_POWERSAVE], SYSTEM[T_WATCHDOG], SYSTEM[T_NETWORRESET], SYSTEM[CAL],
|
|
// SYSTEM[NITZ] - modify or retrieve settings
|
|
// THRESHOLD[T_ONBOARD], THRESHOLD[T_EXT1], THRESHOLD[T_EXT2], THRESHOLD[T_SW3000] -
|
|
// temperature thresholds
|
|
// ALERT[GEOFENCE], ALERT[LED] - geofence
|
|
// DEBUG - log level (0x00..0x0F)
|
|
// DEBUG[*] - debugging options (device specific)
|
|
// STAT[FLAGS] - extended status, similar to STAT's first itmes
|
|
// STAT[GSM], STAT[GSM-CCED], STAT[GPRS], STAT[TCP], STAT[GPS], STAT[GEOFENCE]
|
|
// STAT[MEASUREMENT], STAT[BATTERY], STAT[TEMP], STAT[LOG] - extended status
|
|
// IDCODE, SN - device identification
|
|
// VERSION[*] - read device information (firmware and hardware versions and more)
|
|
// GPRS-ENABLE - enable or disable GPRS i.e. the ability to access the internet
|
|
// any other device specific "key" is allowed
|
|
// store a new value for a key
|
|
int (*store)(cp3000_device_t, client_t, const char *key, const char *value);
|
|
// retrieve a key (synchronously)
|
|
int (*retrieve)(cp3000_device_t, client_t, const char *key, char *value, size_t valuesz);
|
|
// synchronize device specific items with the database, although this functionaly
|
|
// has been expanded over time to any "complex" task
|
|
// the calling functions, 'device_sync()', sychronizes the version information
|
|
// structure, servers and the configuration for the device (via 'device_store()',
|
|
// 'device_retrieve()') but additional processing is allowed; these items are
|
|
// SYNCDB_ID, SYNCDB_CONFIG and SYNCDB_SERVERS
|
|
// the 'items_to_sync' indicates which items are to be synchronized; any item that
|
|
// cannot be set at this moment, but perhaps later, must be set in 'items_to_restore'
|
|
int (*syncdb)(cp3000_device_t, client_t, uint32_t items_to_sync, uint32_t *items_to_restore);
|
|
|
|
// callbacks for the child devices (if any) or PARENT_CALLBACKS to use the same
|
|
// function table as the parent
|
|
const struct DEVICE_FCNTABLE *child_callbacks;
|
|
};
|
|
|
|
// Common device info structure
|
|
struct DEVICE_INFO
|
|
{
|
|
// device identification
|
|
zkl_t zkl; // database identifier for this device, or '-1' for undefined
|
|
struct DEVICE_IDENTIFICATION *version_info;
|
|
|
|
// database connection
|
|
db_t db;
|
|
|
|
// function table
|
|
struct DEVICE_FCNTABLE *device_callbacks;
|
|
|
|
// device operational state
|
|
volatile uint32_t flags; // role of this device object
|
|
device_t device_type; // device type identifier
|
|
int device_status; // device status (LANSSTATUS_xxx)
|
|
uint32_t capabilities; // device capabilities (combination of CAPABILITY_xxx flags)
|
|
int nr_batteries; // number of batteries
|
|
int nr_tempsensors; // number of external temperature sensors
|
|
int realtime_timeout; // time duration at which a device is considered offline (in seconds)
|
|
time_t t_connect; // time at which the device connected
|
|
// status and geofence cache; only used when 'primary' is set and supported by the device
|
|
struct DEVICE_STATUS *status_cache;
|
|
struct DEVICE_GEOFENCE *geofence_cache;
|
|
|
|
// current log file and the log file requested
|
|
struct DEVICE_LOGFILE {
|
|
int rpgm_count; // file identification
|
|
int startup_count; //
|
|
int retry; // retry sending the log file before it is marked as erroneous
|
|
} *current, *sending;
|
|
int sdcard_attic; // value for 'sdcard' in the datbase for old log files
|
|
|
|
// additional information for gateway devices
|
|
lst_t children; // child devices of a gateway
|
|
struct DEVICE_CHILDINFO { // information for a child device
|
|
int index; // index in the child devices array of the parent
|
|
cp3000_device_t parent; // the parent device
|
|
} *child;
|
|
|
|
// fifos, shared memory and clinet lists for primary devices
|
|
struct DEVICE_CLIENTCOMMUNICATION *clients;
|
|
struct DEVICE_SERVERCOMMUNICATION *server;
|
|
|
|
// statistics
|
|
struct DEVICE_STATISTICS {
|
|
unsigned long status_updates;
|
|
unsigned long log_entries;
|
|
unsigned long log_errors;
|
|
} *statistics;
|
|
|
|
void *zmq_sock;
|
|
|
|
// device specific data
|
|
void *device_info; // should be created in the device's 'init' function
|
|
};
|
|
|
|
/*
|
|
** Exported functions
|
|
*/
|
|
|
|
//
|
|
// Initialization etc.
|
|
//
|
|
|
|
// Device two-stage initialization and destruction
|
|
// The first stage creates a DEVICE_INFO structure and intializes its fields;
|
|
// the device specific 'init' function is called to allocate and initialize
|
|
// the device specific data structures
|
|
int device_init(cp3000_device_t, uint32_t flags, const struct DEVICE_FCNTABLE*);
|
|
// The first form of the second stage is intended for actual devices (including
|
|
// gateways). It will look up the device in the database and fill in the
|
|
// fields in the DEVICE_INFO structure. It creates the communication fifos,
|
|
// shared memory, etc and after this, the device specific 'attach' function is
|
|
// called.
|
|
// For gateway devices, this function will also create the DEVICE_INFO objects
|
|
// for the child devices
|
|
// The 'flags' argument for 'device_init()' must include DEVICEFLG_PRIMARY
|
|
// when this is the primary connection to the actual device in the field.
|
|
int device_attach(cp3000_device_t, const char *imei, const void *login_tokens);
|
|
// The second form is intended for the back-end for the TCP server. This function
|
|
// connects to one of the fifo created in 'device_attch'. After that it will
|
|
// call the back-end "device"s 'connect' function.
|
|
// The 'flags' for 'device_init()' should include DEVICFLG_BACKEND and, when no
|
|
// connection to the database is needed, DEVICEFLG_NO_DATABASE.
|
|
int device_connect(cp3000_device_t, zkl_t, const void *login_tokens);
|
|
// Destroy the device. The 'destroy' function in the function table is called to
|
|
// clear up the data structure(s) allocated in the 'init' function. After that,
|
|
// the connection to the database is closed and the 'cp3000_device_t' object
|
|
// itself is destroyed, effectively closing the connection to the device
|
|
int device_destroy(cp3000_device_t, int disconnect_reason);
|
|
|
|
// Attach child devices
|
|
int device_attach_children(cp3000_device_t);
|
|
// Initialize and add a new child device to its parent
|
|
int device_add_child(cp3000_device_t /* parent */, cp3000_device_t /* child */);
|
|
|
|
|
|
// Add a new device to the database
|
|
// The new device must be properly initialized (IMEI and/or serial number must
|
|
// be set and for child nodes, the parent must be set)
|
|
int device_add(cp3000_device_t);
|
|
|
|
// Retrieve database connection identifier (for debugging and logging)
|
|
unsigned int device_connection_id(cp3000_device_t device);
|
|
|
|
// Test if the device is allowed to connect to the TCP server; the parameter 'id'
|
|
// is logged to make it possible to identifier the culprit at a later moment
|
|
int device_allow(cp3000_device_t, cp3000_acl_t, const char *id);
|
|
|
|
// Set the command parser for the device (wrapper to 'cp3000_set_parser()')
|
|
int device_set_parser(cp3000_device_t, token_parser_t, void *param);
|
|
|
|
//
|
|
// Retrieve information about the device
|
|
//
|
|
|
|
// Get the device info structure for this device, IMEI, device id and database
|
|
// handle from said info
|
|
// Normally, the inlined versios are to be used
|
|
#if defined(_NO_INLINE)
|
|
|
|
struct DEVICE_INFO *device_get_info(cp3000_device_t);
|
|
int device_set_private_data(cp3000_device_t, void*);
|
|
const struct DEVICE_STATUS *device_status(cp3000_device_t);
|
|
const char *device_imei(cp3000_device_t);
|
|
zkl_t device_get_id(cp3000_device_t);
|
|
db_t device_get_db(cp3000_device_t);
|
|
uint32_t device_get_flags(cp3000_device_t);
|
|
uint32_t device_set_flags(cp3000_device_t, uint32_t set);
|
|
uint32_t device_clear_flags(cp3000_device_t, uint32_t clear);
|
|
void device_modify_flags(cp3000_device_t, uint32_t set, uint32_t clear);
|
|
int device_test_flags(cp3000_device_t device, uint32_t test);
|
|
int device_in_service(cp3000_device_t);
|
|
uint32_t device_capabilities(cp3000_device_t);
|
|
int device_nr_batteries(cp3000_device_t);
|
|
int device_nr_tempsensors(cp3000_device_t);
|
|
const char *device_get_version(cp3000_device_t, int versionidx);
|
|
int device_set_version(cp3000_device_t, int versionidx, const char*);
|
|
void device_clear_version(cp3000_device_t device, int versionidx);
|
|
int device_is_primary(cp3000_device_t);
|
|
int device_nr_children(cp3000_device_t);
|
|
cp3000_device_t device_get_parent(cp3000_device_t);
|
|
int device_get_childindex(cp3000_device_t);
|
|
cp3000_device_t device_get_child(cp3000_device_t, int index);
|
|
cp3000_device_t device_get_child_by_id(cp3000_device_t, const char *imei);
|
|
|
|
#else
|
|
|
|
// Get the device info structure for this device
|
|
static __inline__ struct DEVICE_INFO *device_get_info(cp3000_device_t device)
|
|
{
|
|
return (struct DEVICE_INFO*)cp3000_get_user_data(device);
|
|
}
|
|
|
|
// Store device specific info structure
|
|
static __inline__ int device_set_private_data(cp3000_device_t device, void *device_info)
|
|
{
|
|
((struct DEVICE_INFO*)cp3000_get_user_data(device))->device_info = device_info;
|
|
|
|
return TCPSTAT_OK;
|
|
}
|
|
|
|
// Get pointer to the status cache
|
|
static __inline__ const struct DEVICE_STATUS *device_status(cp3000_device_t device)
|
|
{
|
|
return ((struct DEVICE_INFO*)cp3000_get_user_data(device))->status_cache;
|
|
}
|
|
|
|
// Get database identifier for the device
|
|
static __inline__ zkl_t device_get_id(cp3000_device_t device)
|
|
{
|
|
return ((struct DEVICE_INFO*)cp3000_get_user_data(device))->zkl;
|
|
}
|
|
|
|
// Get database handle from device info structure
|
|
static __inline__ db_t device_get_db(cp3000_device_t device)
|
|
{
|
|
return ((struct DEVICE_INFO*)cp3000_get_user_data(device))->db;
|
|
}
|
|
|
|
// Get the device flags
|
|
static __inline__ uint32_t device_get_flags(cp3000_device_t device)
|
|
{
|
|
return ((struct DEVICE_INFO*)cp3000_get_user_data(device))->flags;
|
|
}
|
|
|
|
// Set one or more device flags
|
|
static __inline__ uint32_t device_set_flags(cp3000_device_t device, uint32_t set)
|
|
{
|
|
return ((struct DEVICE_INFO*)cp3000_get_user_data(device))->flags |= set;
|
|
}
|
|
|
|
// Clear one or more device flags
|
|
static __inline__ uint32_t device_clear_flags(cp3000_device_t device, uint32_t clear)
|
|
{
|
|
return ((struct DEVICE_INFO*)cp3000_get_user_data(device))->flags &= ~clear;
|
|
}
|
|
|
|
// Modify flags
|
|
static __inline__ void device_modify_flags(cp3000_device_t device, uint32_t set, uint32_t clear)
|
|
{
|
|
((struct DEVICE_INFO*)cp3000_get_user_data(device))->flags &= ~clear;
|
|
((struct DEVICE_INFO*)cp3000_get_user_data(device))->flags |= set;
|
|
}
|
|
|
|
// Test flags
|
|
static __inline__ int device_test_flags(cp3000_device_t device, uint32_t test)
|
|
{
|
|
return (((struct DEVICE_INFO*)cp3000_get_user_data(device))->flags & test) == test;
|
|
}
|
|
|
|
// Is the device in service? Returns boolean
|
|
static __inline__ int device_in_service(cp3000_device_t device)
|
|
{
|
|
return ((struct DEVICE_INFO*)cp3000_get_user_data(device))->device_status < LANSSTATUS_ACTIVE;
|
|
}
|
|
|
|
// Get capabilities
|
|
static __inline__ uint32_t device_capabilities(cp3000_device_t device)
|
|
{
|
|
return ((struct DEVICE_INFO*)cp3000_get_user_data(device))->capabilities;
|
|
}
|
|
|
|
// Get number of batteries
|
|
static __inline__ int device_nr_batteries(cp3000_device_t device)
|
|
{
|
|
return ((struct DEVICE_INFO*)cp3000_get_user_data(device))->nr_batteries;
|
|
}
|
|
|
|
// Get number of temperature sensors
|
|
static __inline__ int device_nr_tempsensors(cp3000_device_t device)
|
|
{
|
|
return ((struct DEVICE_INFO*)cp3000_get_user_data(device))->nr_tempsensors;
|
|
}
|
|
|
|
// Get item from the version_info array
|
|
static __inline__ const char *device_get_version(cp3000_device_t device, int versionidx)
|
|
{
|
|
struct DEVICE_INFO *p_info = device_get_info(device);
|
|
const char *ret;
|
|
if (p_info == NULL) {
|
|
ret = NULL;
|
|
} else if (p_info->version_info == NULL) {
|
|
ret = NULL;
|
|
} else if( (p_info->version_info->present & ((uint64_t)1 << versionidx)) ) {
|
|
ret = p_info->version_info->version_info[versionidx];
|
|
} else {
|
|
ret = NULL;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
// Set or replace an item in the version_info array
|
|
static __inline__ const char *device_set_version(cp3000_device_t device, int versionidx, const char *data)
|
|
{
|
|
struct DEVICE_INFO *p_info = device_get_info(device);
|
|
|
|
// delete existing data
|
|
if(
|
|
(p_info->version_info->present & ((uint64_t)1 << versionidx)) &&
|
|
p_info->version_info->version_info[versionidx]
|
|
) {
|
|
if (data && strcmp(p_info->version_info->version_info[versionidx], data) == 0) {
|
|
// not changed; keep the old data to avoid that pointers are
|
|
// corrupted
|
|
return p_info->version_info->version_info[versionidx];
|
|
} else {
|
|
free((void*)(p_info->version_info->version_info[versionidx]));
|
|
}
|
|
}
|
|
|
|
if (data)
|
|
p_info->version_info->version_info[versionidx] = (const char *)strdup(data);
|
|
else
|
|
p_info->version_info->version_info[versionidx] = NULL;
|
|
p_info->version_info->present |= ((uint64_t)1 << versionidx);
|
|
|
|
return p_info->version_info->version_info[versionidx];
|
|
}
|
|
|
|
// Clear an item in the version_info array
|
|
static __inline__ void device_clear_version(cp3000_device_t device, int versionidx)
|
|
{
|
|
struct DEVICE_INFO *p_info = device_get_info(device);
|
|
|
|
// delete existing data
|
|
if(
|
|
(p_info->version_info->present & ((uint64_t)1 << versionidx)) &&
|
|
p_info->version_info->version_info[versionidx]
|
|
) {
|
|
free((void*)(p_info->version_info->version_info[versionidx]));
|
|
p_info->version_info->version_info[versionidx] = NULL;
|
|
}
|
|
|
|
p_info->version_info->present &= ~((uint64_t)1 << versionidx);
|
|
}
|
|
|
|
// Get IMEI from device info structure
|
|
static __inline__ const char *device_imei(cp3000_device_t device)
|
|
{
|
|
struct DEVICE_INFO *p_info = device_get_info(device);
|
|
|
|
if( (p_info->version_info->present & ((uint64_t)1 << VERSIONIDX_IMEI)) ) {
|
|
return p_info->version_info->version_info[VERSIONIDX_IMEI];
|
|
}
|
|
else return NULL;
|
|
}
|
|
|
|
// Is this device the primary connection?
|
|
static __inline__ int device_is_primary(cp3000_device_t device)
|
|
{
|
|
struct DEVICE_INFO *p_info = device_get_info(device);
|
|
|
|
return (p_info->flags & DEVICEMSK_MODE) == DEVICEFLG_PRIMARY && p_info->child == NULL;
|
|
}
|
|
|
|
// Get number of children
|
|
static __inline__ int device_nr_children(cp3000_device_t device)
|
|
{
|
|
struct DEVICE_INFO *p_info = device_get_info(device);
|
|
|
|
if( p_info->children ) return lst_get_count(p_info->children);
|
|
else return 0;
|
|
}
|
|
|
|
// Get parent device
|
|
static __inline__ cp3000_device_t device_get_parent(cp3000_device_t device)
|
|
{
|
|
struct DEVICE_INFO *p_info = device_get_info(device);
|
|
|
|
if( p_info->child ) return p_info->child->parent;
|
|
else return NULL;
|
|
}
|
|
|
|
// Get child's ordinal
|
|
static __inline__ int device_get_childindex(cp3000_device_t device)
|
|
{
|
|
struct DEVICE_INFO *p_info = device_get_info(device);
|
|
|
|
if( p_info->child ) return p_info->child->index;
|
|
else return -1;
|
|
}
|
|
|
|
// Get child device
|
|
static __inline__ cp3000_device_t device_get_child(cp3000_device_t device, int index_no)
|
|
{
|
|
struct DEVICE_INFO *p_info = device_get_info(device);
|
|
|
|
if( index_no < 0 || index_no >= device_nr_children(device) )
|
|
return NULL;
|
|
else
|
|
return (cp3000_device_t)lst_get_item(p_info->children, index_no);
|
|
}
|
|
|
|
// Get child device by its identifier (IMEI or hardware address)
|
|
cp3000_device_t device_get_child_by_id(cp3000_device_t, const char *imei);
|
|
|
|
#endif
|
|
|
|
// Get a client by its unique identifier
|
|
cp3000_device_t device_get_client_by_id(cp3000_device_t, int client_id);
|
|
|
|
// Get GPS posititon from database cache
|
|
int device_get_position_from_cache(cp3000_device_t, struct DEVICE_STATUS*);
|
|
|
|
// Check if the device uses the M2M API (Vodafone's Smart Services)
|
|
// Optionally request the phone number of the SMS-MO (mobile operator, i.e. the
|
|
// phone number the SMS-ME (mobile equipment, i.e. the device) must use to send
|
|
// an SMS to)
|
|
// Returns a CP3000 status
|
|
// - TCPSTAT_OK - the device uses the M2M API
|
|
// - TCPERR_NOSUPP - the device does not use the M2M API
|
|
// - TCPERR_INTERR - database error or IMSI not set
|
|
int device_uses_m2m_api(cp3000_device_t, char *phonenr, size_t phonenr_sz);
|
|
|
|
//
|
|
// Logging
|
|
//
|
|
|
|
// Set _current_ log file; the log file identifiers 'rpgm_count' and 'startup_count'
|
|
// must be specified as '-1' when the device does not have an actual "black box"
|
|
// (i.e. a storage device on which it can store its log data) yet it supports
|
|
// logging in addition to real-time status updates (by definition this log data
|
|
// will be real-time as well)
|
|
// The filename may be NULL for nameless files. In that case, a new entry is always
|
|
// created
|
|
int device_init_logfile(cp3000_device_t, int rpgm_count, int startup_count);
|
|
int device_init_logfile_by_name(cp3000_device_t device, const char *fname);
|
|
// Test if the reprogramming and start-up counters indicate the current log file
|
|
int device_log_is_current(cp3000_device_t device, int rpgm_count, int startup_count);
|
|
|
|
#if defined(_NO_INLINE)
|
|
|
|
// Test if the reprogramming and start-up counters indicate the current log file
|
|
int device_logfile_is_current(cp3000_device_t device, int rpgm_count, int startup_count);
|
|
int device_get_current_logfile(cp3000_device_t device, int *rpgm_count, int *startup_count);
|
|
|
|
// Set or query the active log file
|
|
int device_is_logfile(cp3000_device_t, int rpgm_count, int startup_count);
|
|
int device_set_logfile(cp3000_device_t, int rpgm_count, int startup_count);
|
|
int device_clear_logfile(cp3000_device_t);
|
|
int device_get_logfile(cp3000_device_t, int *rpgm_count, int *startup_count);
|
|
|
|
#else
|
|
|
|
// Test if the reprogramming and start-up counters indicate the current log file
|
|
static __inline__ int device_logfile_is_current(cp3000_device_t device, int rpgm_count, int startup_count)
|
|
{
|
|
struct DEVICE_INFO *p_info =
|
|
device_test_flags(device, DEVICEFLG_CHILD | DEVICEFLG_UNIFIED_LOG)
|
|
? device_get_info(device_get_parent(device))
|
|
: device_get_info(device);
|
|
|
|
return
|
|
p_info->current != NULL &&
|
|
p_info->current->rpgm_count == rpgm_count &&
|
|
p_info->current->startup_count == startup_count;
|
|
}
|
|
|
|
// Get current log file i.e. the one currently being written by the device
|
|
static __inline__ int device_get_current_logfile(cp3000_device_t device, int *rpgm_count, int *startup_count)
|
|
{
|
|
struct DEVICE_INFO *p_info =
|
|
device_test_flags(device, DEVICEFLG_CHILD | DEVICEFLG_UNIFIED_LOG)
|
|
? device_get_info(device_get_parent(device))
|
|
: device_get_info(device);
|
|
|
|
if( p_info->current == NULL ) return TCPERR_NOSUPP;
|
|
|
|
if( rpgm_count ) *rpgm_count = p_info->current->rpgm_count;
|
|
if( startup_count ) *startup_count = p_info->current->startup_count;
|
|
|
|
return TCPSTAT_OK;
|
|
}
|
|
|
|
// Is it the log file we requested?
|
|
static __inline__ int device_is_logfile(cp3000_device_t device, int rpgm_count, int startup_count)
|
|
{
|
|
struct DEVICE_INFO *p_info =
|
|
device_test_flags(device, DEVICEFLG_CHILD | DEVICEFLG_UNIFIED_LOG)
|
|
? device_get_info(device_get_parent(device))
|
|
: device_get_info(device);
|
|
|
|
return
|
|
p_info->sending != NULL &&
|
|
p_info->sending->rpgm_count == rpgm_count &&
|
|
p_info->sending->startup_count == startup_count;
|
|
}
|
|
|
|
// Set the active log file
|
|
static __inline__ int device_set_logfile(cp3000_device_t device, int rpgm_count, int startup_count)
|
|
{
|
|
struct DEVICE_INFO *p_info = device_get_info(device);
|
|
|
|
if( device_test_flags(device, DEVICEFLG_CHILD | DEVICEFLG_UNIFIED_LOG) ) {
|
|
dbgprintf("[%d/%lu]: device_set_logfile(): log files are supported on the parent device only",
|
|
p_info->zkl, (unsigned long)getpid()
|
|
);
|
|
return TCPERR_INVAL;
|
|
}
|
|
else {
|
|
int retval = TCPSTAT_OK;
|
|
|
|
if( p_info->sending == NULL )
|
|
return TCPERR_NOSUPP;
|
|
|
|
// sanity check; the error is return for information only
|
|
if( p_info->sending->rpgm_count == -1 && p_info->sending->startup_count == -1 )
|
|
retval = TCPERR_ALREADY;
|
|
|
|
p_info->sending->rpgm_count = rpgm_count;
|
|
p_info->sending->startup_count = startup_count;
|
|
|
|
return retval;
|
|
}
|
|
}
|
|
|
|
// Unset the active log file
|
|
static __inline__ int device_clear_logfile(cp3000_device_t device)
|
|
{
|
|
struct DEVICE_INFO *p_info = device_get_info(device);
|
|
|
|
if( device_test_flags(device, DEVICEFLG_CHILD | DEVICEFLG_UNIFIED_LOG) ) {
|
|
dbgprintf("[%d/%lu]: device_clear_logfile(): log files are supported on the parent device only",
|
|
p_info->zkl, (unsigned long)getpid()
|
|
);
|
|
return TCPERR_INVAL;
|
|
}
|
|
else {
|
|
if( p_info->sending == NULL )
|
|
return TCPERR_NOSUPP;
|
|
|
|
p_info->sending->rpgm_count = -1;
|
|
p_info->sending->startup_count = -1;
|
|
|
|
return TCPSTAT_OK;
|
|
}
|
|
}
|
|
|
|
// Get active log file; can also be used to see if there _is_ an active log file,
|
|
// in which case this function return TCPSTAT_OK and an error otherwise
|
|
static __inline__ int device_get_logfile(cp3000_device_t device, int *rpgm_count, int *startup_count)
|
|
{
|
|
int retval = TCPSTAT_OK;
|
|
struct DEVICE_INFO *p_info =
|
|
device_test_flags(device, DEVICEFLG_CHILD | DEVICEFLG_UNIFIED_LOG)
|
|
? device_get_info(device_get_parent(device))
|
|
: device_get_info(device);
|
|
|
|
// return error when not supported for this device
|
|
if( p_info == NULL || p_info->sending == NULL ) return TCPERR_NOSUPP;
|
|
|
|
// return "error" when unset
|
|
if( p_info->sending->rpgm_count == -1 && p_info->sending->startup_count == -1 )
|
|
retval = TCPERR_NODATA;
|
|
|
|
if( rpgm_count ) *rpgm_count = p_info->sending->rpgm_count;
|
|
if( startup_count ) *startup_count = p_info->sending->startup_count;
|
|
|
|
return retval;
|
|
}
|
|
|
|
#endif
|
|
|
|
// Update status of the log file
|
|
int device_logfile_complete(cp3000_device_t, int rpgm_count, int startup_count, unsigned long filesize);
|
|
int device_logfile_set_error(cp3000_device_t, int rpgm_count, int startup_count, unsigned long filesize, int error_code);
|
|
int device_logfile_update(cp3000_device_t, int rpgm_count, int startup_count, unsigned long pos, time_t t_now, time_t t_resync);
|
|
|
|
// Create a checkpoint for the log file
|
|
int device_checkpoint_logfile(cp3000_device_t, int rpgm_count, int startup_count, unsigned long pos, time_t t);
|
|
|
|
// Record a (new) log file in the database
|
|
int device_register_logfile(cp3000_device_t, int rpgm_count, int startup_count, unsigned long filesize);
|
|
|
|
// Log file by name, for devices other than CP3000 devices
|
|
int device_register_logfile_by_name(cp3000_device_t, const char *fname, unsigned long filesize);
|
|
int device_get_logfile_by_name(cp3000_device_t, const char *fname, int *rpgm_count, int *startup_count);
|
|
|
|
// Request the device to send log data
|
|
int device_next_logfile(cp3000_device_t);
|
|
|
|
// Insert an entry into the log; all arguments must be strings
|
|
int device_log(cp3000_device_t, int rpgm_count, int startup_count, uint16_t log_id, time_t, unsigned long pos, int n_args, const char **args);
|
|
|
|
//
|
|
// Real-time status
|
|
//
|
|
|
|
// Rebuild status string, with sequence number and timestamp, for transmission to
|
|
// the back-end.
|
|
// The pointer to the status buffer may be NULL, in which case the
|
|
// buffer in the device info structure will be used (and this is usually what is
|
|
// required)
|
|
// The pointer 'buf' may be NULL, in which case the internal buffer will be updated.
|
|
int device_build_status(cp3000_device_t, const struct DEVICE_STATUS*, char *buf, size_t bufsz);
|
|
|
|
// Log, cache and forward device status (front-end to back-end)
|
|
// Parameters:
|
|
// - device
|
|
// - change flags (STATUPDATE_xxx flags)
|
|
// - device status (e.g. decomposed status string)
|
|
// Updates the following items:
|
|
// - sequence number and time
|
|
// - internal cache in 'status_cache' (individual items and status string)
|
|
// - database table 'log_realtime' (when flags != 0)
|
|
// - database table 'zkl_cache' (always)
|
|
// - shared memory
|
|
// - forwards the status to the clients connected to the status fifo
|
|
int device_log_and_forward_status(cp3000_device_t, int flags, const struct DEVICE_STATUS*);
|
|
|
|
//
|
|
// Access to the device
|
|
//
|
|
|
|
// Client communication (back-end to front-end), can also be used by the main
|
|
// device, in which case the client identifier should be '-1'
|
|
// Store a new value for a key
|
|
int device_store_value(cp3000_device_t, client_t, const char *key, const char *value);
|
|
// Retrieve a value for a key. This may be asynchronous when 'value' is NULL,
|
|
// in which case the device must forward the reply to the client asynchronously
|
|
// as well; the device must have a way to store the client identifier to make
|
|
// sure that the reply is forwarded to the correct client
|
|
int device_retrieve_value(cp3000_device_t, client_t, const char *key, char *value, size_t valuesz);
|
|
// Synchronize version information and configuration between database and device
|
|
int device_sync(cp3000_device_t, client_t, uint32_t items_to_sync);
|
|
// Forward reply from front-end to back-end
|
|
int device_forward_reply(cp3000_device_t device, client_t, int status, const char *data);
|
|
// Forward tokens from back-end to front-end
|
|
int device_forward_tokens(cp3000_device_t, token_info_t);
|
|
|
|
//
|
|
// Device main loop
|
|
//
|
|
|
|
// Device main loop
|
|
// Sorts out the time for the next periodic events, calls its handler (if defined),
|
|
// waits for data from the device and the fifos and handles this data
|
|
int device_mainloop(cp3000_device_t);
|
|
|
|
// Logout from the main loop
|
|
int device_exit_mainloop(cp3000_device_t);
|
|
|
|
// Wait for a reply
|
|
int device_wait_for_reply(cp3000_device_t);
|
|
// Device received the reply it waited for
|
|
void device_has_reply(cp3000_device_t);
|
|
|
|
// Command cannot be handled right now; put it back for later processing
|
|
int device_putback(cp3000_device_t, const void *data, size_t datasz);
|
|
|
|
#endif /* _TCPSERVER_DEVICE_H */
|