/* * TCP server; communication with the ZKL3000 * Device info structures */ #ifndef _TCPSERVER_DEVICE_H #define _TCPSERVER_DEVICE_H #include #include #include #include #include #include // 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 */