src.dualinventive.com/mtinfo/tcpserver/include/device.h

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 */