src.dualinventive.com/mtinfo/dist/webroot/rc-4.05/include/cp3000-tcpclient.php

1192 lines
42 KiB
PHP
Raw Permalink Blame History

<?php
/*
************************************************************************
**
** Copyright (c) 2009, 2010 by
** Core|Vision B.V.
** Cereslaan 10b
** 5384 VT Heesch
** The Netherlands
**
** All Rights Reserved
**
************************************************************************
*/
/*
************************************************************************
**
** Project name: Dual Inventive: MTinfo and web support scripts
** Filename: cp3000-tcpclient.php (formerly zkl-tcpclient.php)
** Author: Jack Weeland
** Date: January 28, 2009
** File version: 1.00 of January 28, 2009
** 1.10 of February 23, 2009
** - A socket open with 'fsockopen()' is a file
** resource and _not_ a socket :-S
** In casu, it cannot be used for 'socket_select()'.
** - 'socket_read()' reads only _one_ of \n and \r.
** 1.20 of November 30, 2009
** - Suport for Switch3000 and PM3000.
** 2.00 of September 20, 2010
** - Uses PHP extension "cp3000"
**
************************************************************************
*/
/*
************************************************************************
**
** CP3000 client
**
************************************************************************
*/
/*
** Constants
*/
//
// How to connect to a device?
//
// for the parameter $alarmmsg to 'zkl_tcplogin()'
define("ZKL_NORMAL", 0); // TCP-server
define("ZKL_STATUS", 1); // TCP-server, but status updates are passed through
define("ZKL_SHMEM", 2); // shared memory (cached status only)
//
// Return and status codes
//
// okay
define("TCPSTAT_OK", 0x00);
define("TCPSTAT_WARNING", 0x02); // okay, but only partially
define("TCPSTAT_BADSTATE", 0x03); // state is not as expected, but still okay(ish)
define("TCPSTAT_SKIPPED", 0x04); // Skipped
// end of file (on the communication socket)
define("TCPERR_EOF", 0x01);
// protocol errors
define("TCPERR_INVAL", 0x10); // invalid command or parameter
define("TCPERR_PARAM", 0x11); // parameter count wrong
define("TCPERR_TOOLONG", 0x12); // parameter too long
define("TCPERR_INTERR", 0x13); // internal error
define("TCPERR_CHKSUM", 0x14); // checksum error
define("TCPERR_STALE", 0x15); // data has expired
define("TCPERR_FILEERR", 0x16); // file error (see 'errno' for details)
// transient errors, can be retried
define("TCPERR_BUSY", 0x40); // too many connections from database servers (try again)
define("TCPSTAT_BUSY", (TCPERR_BUSY));
define("TCPERR_ABORTED", 0x41); // server terminated; try to connect again
define("TCPERR_TOOMANY", 0x42); // too many connections (peer can try again)
// server errors, cannot be retried1
define("TCPERR_ACCESS", 0x80); // access denied
define("TCPERR_NOFIFO", 0x81); // local communication fifo does not exist
define("TCPERR_DISC", 0x82); // zkl closed its connection
define("TCPERR_UNKNOWN", 0x83); // zkl not in the database
define("TCPERR_CONNECT", 0x84); // connection refused (server not running?)
define("TCPERR_SOCKET", 0x85); // socket error on read or write
define("TCPERR_TIMEOUT", 0x86); // time-out
define("TCPERR_NOSUPP", 0x87); // operation not supported
define("TCPERR_NOMEM", 0x88); // out of memory
define("TCPERR_BUFSZ", 0x89); // buffer too small
define("TCPERR_NODATA", 0x8A); // data source exhausted
define("TCPERR_DATACHG", 0x8B); // data changed while this is not allowed
define("TCPERR_LOCKED", 0x8C); // access denied because a user or some functionality is locked
define("TCPERR_BADSTATE", 0x8D); // the current state is not expected or faulty
define("TCPERR_DEVICE", 0x8E); // generic device error
define("TCPERR_GEOFENCE", 0x8F); // geo-fencing error
// intermediate result - real result code will follow
define("TCPSTAT_INTERMEDIATE", 0xFF);
//
// Fields changed for the real-time status update
//
define("STATUPDATE_STATE", 0x0001); // global mcu state flags
define("STATUPDATE_WCPU_STATE", 0x0002); // global wcpu state flags
define("STATUPDATE_RC", 0x0004); // remote control state flags (incl. pm3000)
define("STATUPDATE_SWITCH", 0x0008); // switch3000 state flags
define("STATUPDATE_RMS", 0x0010); // detection: RMS value of the measurement (V_ref)
define("STATUPDATE_B_A", 0x0020); // b/a of the measurement
define("STATUPDATE_AUTOCAL", 0x0080); // calibratie b/a and PWM frequency
define("STATUPDATE_BATTSEL", 0x0800); // battery selection changed
define("STATUPDATE_BATT1LVL", 0x0100); // battery voltage update
define("STATUPDATE_BATT2LVL", 0x0200); // battery voltage update
define("STATUPDATE_INTTEMP", 0x1000); // on-board temperature update
define("STATUPDATE_EXTTEMP", 0x2000); // ntc temperature or switch3000 update
define("STATUPDATE_GPS", 0x4000); // update for gps co-ordinates, speed and heading (and hdop and time)
define("STATUPDATE_GSM", 0x8000); // gsm signal quality and connectivity update
define("STATUPDATE_NEW", 0); // fresh status update (all fields contain new values)
define("STATUPDATE_ANY", 0xFFFF); // fresh status update (all fields contain new values)
//
// Fields for the status array ['mcu']['state']
//
define("MCUSTAT_MEASUREMENT", 0x0001);
define("MCUSTAT_DETECTION_OK", 0x0002);
define("MCUSTAT_MEASUREMENT_FAIL", 0x0004);
define("MCUSTAT_RELAY_OPEN", 0x0008);
// system initialization errors and state
define("MCUERR_NONE", 0x0000);
define("MCUERR_I2C", 0x0010); // I2C failure (MCU->WCPU path)
define("MCUERR_LOG", 0x0020); // <20>ALFAT or SD-card failure
define("MCUERR_GSM", 0x0040); // WCPU unreachable
define("MCUERR_SMS", 0x0080); // not used
define("MCUERR_ANY", 0x00F0);
// battery status
define("MCUSTAT_BATTERY_OK", 0);
define("MCUSTAT_BATTERY_LOW", 1);
define("MCUSTAT_BATTERY_CRITICAL", 2);
define("MCUSTAT_BATTERY_ABSENT", 3);
define("MCUSTAT_BATTERY1_LOW", (1 << (MCUSTAT_BATTERY_LOW + 7)));
define("MCUSTAT_BATTERY1_CRITICAL", (1 << (MCUSTAT_BATTERY_CRITICAL + 7)));
define("MCUSTAT_BATTERY1_ABSENT", (1 << (MCUSTAT_BATTERY_ABSENT + 7)));
define("MCUSTAT_BATTERY2_LOW", (1 << (MCUSTAT_BATTERY_LOW + 11)));
define("MCUSTAT_BATTERY2_CRITICAL", (1 << (MCUSTAT_BATTERY_CRITICAL + 11)));
define("MCUSTAT_BATTERY2_ABSENT", (1 << (MCUSTAT_BATTERY_ABSENT + 11)));
define("MCUSTAT_BATTERY", 0xFF00); // combination of all battery status flags
define("MCUSTAT_BATTERY1", 0x0F00);
define("MCUSTAT_BATTERY2", 0xF000);
//
// Fields for the status array ['mcu']['local state']
//
define("MCULOC_IN_SWEEP", 0x0003); // sweep busy
define("MCULOC_IN_FREQ_SWEEP", 0x0001); // frequency sweep
define("MCULOC_IN_AMPL_SWEEP", 0x0002); // amplitude sweep
define("MCULOC_GPSPOWER", 0x0008); // GPS power enabled (for trace & trace)
define("MCULOC_GPS_RECEIVED", 0x0010); // GPS co-ordinates received
define("MCULOC_RTC_RECEIVED", 0x0020); // clock data received
define("MCULOC_LED", 0x0040); // LED intensity is "high" / deprecated 20091111
define("MCULOC_LANTERN", 0x0080); // lantern is "on" / deprecated 20091111
define("MCULOC_AUTOCAL", 0x0100); // auto-calibration enabled
define("MCULOC_AUTOCAL_FAIL", 0x0200); // auto-calibration failed (internal state)
define("MCULOC_EXT_CONTACT", 0x0400); // external contact enabled
define("MCULOC_RELAY_DRIVER", 0x0800); // relay driver enabled
define("MCULOC_GSM_ENABLED", 0x2000); // GSM is ready
define("MCULOC_TCP_CONNECTED", 0x4000); // logged in with TCP server
define("MCULOC_INIT_DONE", 0x8000); // MCU is initialized
//
// Fields for the status array ['mcu']['persistent']
//
define("MCUFLG_AUTOCAL_DONE", 0x0001); // device is calibrated
define("MCUFLG_SW3000_AVAILABLE", 0x0004); // device is equiped with a Switch 3000 module
define("MCUFLG_PM3000_AVAILABLE", 0x0008); // device is equiped with a Power Management 3000 module
define("MCUFLG_MEASUREMENT", 0x0010); // measurement busy
define("MCUFLG_MEASUREMENT_RC", 0x0020); // measurements turned on remotely
define("MCUFLG_MEASUREMENT_KEY", 0x0040); // measurements turned on with the key
define("MCUFLG_SW3000_ENABLED", 0x0080); // Switch 3000 is enabled
define("MCUFLG_BATTERY_MASK", 0xFF00);
// Battery type (t) and voltage (v) for battery #1 (b=0) or #2 (b=1)
// MCUFLG_BATTERY_TYPE(b,t,v) ((((t) << 2) | (v)) << (8 + (4*b)))
define("MCUFLG_BATT_NiMH", 0); // default: NiMH battery
define("MCUFLG_BATT_NiCd", 1); // option: NiCd battery
define("MCUFLG_BATT_LiIon", 2); // option: LiIon or LiPolymer battery
define("MCUFLG_BATT_Alkaline", 3); // option: alkaline battery (1.5V per cell instead of 1.2V)
// (when MCUFLG_BATT_Li is _not_ set)
define("MCUFLG_BATT_12V", 0); // default: 12V battery
define("MCUFLG_BATT_6V", 1); // option: 6V battery
define("MCUFLG_BATT_24V", 3); // option: 24V battery
// (when MCUFLG_BATT_Li is set - currently only MCUTYPE_ZKL3000_RC)
define("MCUFLG_BATT_7_2V", 0); // default: 7.2V battery
define("MCUFLG_BATT_10_8V", 1); // option: 10.8V
define("MCUFLG_BATT_14_4V", 2); // option: 14.4V
define("MCUFLG_BATT_3_6V", 3); // (not for a ZKL, but other devices with a 3.6/3.7 V LiIon battery)
//
// Fields for the status array ['mcu']['remote control status']
//
define("MCURC_MEASUREMENT_ON", 0x0001); // transient state: measurements are turned on from MTinfo
define("MCURC_DETECTION_OK", 0x0002); // detection status (short circuit between the rails detected)
// when the Switch3000 was turned on or off
define("MCURC_KEY_SWITCH_ON", 0x0004); // key for Switch3000 is "on"
define("MCURC_KEY_SWITCH_OFF", 0x0008); // key for Switch3000 is "off"
define("MCURCERR_SWI2C", 0x0010); // software I2C error
define("MCURCERR_SW1W", 0x0020); // software 1-Wire error
define("MCURCERR_SW3000", 0x0020); // (alias)
define("MCURCERR_PM3000", 0x0040); // PM3000 failure
define("MCURCERR_ANY", 0x0070); //
define("MCURC_PM_SW3000_ON", 0x0080); // Switch 3000 power always on
define("MCURC_PM_SWITCH1", 0x0100); // power management: switch 1 is "on" (Switch 3000)
define("MCURC_PM_SWITCH2", 0x0200); // power management: switch 2 is "on" (undefined)
define("MCURC_PM_CHARGE", 0x0400); // power management: charging LiIon battery
define("MCURC_PM_BOOST", 0x0800); // transient state: 9V boost for the analogue circuit enabled
define("MCURC_GPS_POWER_ON", 0x1000); // transient state: GPS (5V circuit) powered up
define("MCURC_PM_CHARGE_MANUAL", 0x2000); // battery charger controlled manually
define("MCURC_SW3000_ONESHOT", 0x4000); // Switch 3000: one shot measurement
define("MCURC_STATUS_VALID", 0x8000); // transient state: status is retrieved for Switch 3000
// and it ready to be read (the flag will disappear
// immediately after that)
//
// Fields for the status array ['mcu']['switch3000 status']
//
define("MCUSW_SEC1_ON", 0x0001); // section #1 is enabled
define("MCUSW_SEC2_ON", 0x0002); // section #2 is enabled
define("MCUSW_SEC3_ON", 0x0004); // section #3 is enabled
define("MCUSW_SEC4_ON", 0x0008); // section #4 is enabled
define("MCUSW_SEC_NONE", 0x0000); // (placeholder)
define("MCUSW_SEC_ALL", 0x000F); // (all sections)
define("MCUSWERR_TIMEOUT", 0x0010); // time-out error
define("MCUSWERR_ERROR", 0x0020); // generic error, most likely in the 1-Wire signaling
define("MCUSW_SEC1_BATT_OK", 0x0100); // battery for section #1 is okay
define("MCUSW_SEC2_BATT_OK", 0x0200); // battery for section #2 is okay
define("MCUSW_SEC3_BATT_OK", 0x0400); // battery for section #3 is okay
define("MCUSW_SEC4_BATT_OK", 0x0800); // battery for section #4 is okay
define("MCUSW_SECE_BATT_OK", 0x8000); // battery for emergency section is okay
define("MCUSW_SEC_ALL_BATT", 0x8F00); // (all batteries)
//
// Wireless CPU status (used internally in the WCPU)
//
define("WCPUERR_NONE", 0x00000000);
define("WCPUERR_INTERNAL", 0x00000001); // WCPU internal datastructure failed to initialize
define("WCPUERR_GPRS", 0x00000002); // GPRS or TCP failure
define("WCPUERR_SMS", 0x00000004); // SMS service couldn't be started
define("WCPUERR_SIM", 0x00000008); // SIM or SIM service failed
define("WCPUERR_UART1", 0x00000010); // UART1 failure (MCU->WCPU path)
define("WCPUERR_UART2", 0x00000020); // UART2 failure (WCPU<->GPS path)
define("WCPUERR_I2C", 0x00000040); // I2C error (WCPU->MCU path)
define("WCPUERR_MCU", 0x00000080); // MCU does not respond on I2C bus
define("WPCUERR_ANY", 0x000000FF);
// Actived modules
define("GSMACTIVE_GSM", 0x00000100);
define("GSMACTIVE_SIM", 0x00000200);
define("GSMACTIVE_GPRS", 0x00000400);
define("GSMACTIVE_TCP", 0x00000800);
define("GSMACTIVE_GPS", 0x00001000);
define("GSMACTIVE_SMS", 0x00002000);
define("GSMACTIVE_SSL", 0x00004000);
define("GSMACTIVE_RTOS", 0x00008000); // _never_ in wcpu_flags (feature only)
// Others
define("WCPUERR_TEMP_ONBOARD", 0x00100000); // on-board temperature over threshold
define("WCPUERR_TEMP_EXT1", 0x00200000); // NTC temperature over threshold
define("WCPUERR_TEMP_EXT2", 0x00400000); // (reserved)
define("WCPUERR_TEMP_SW3000", 0x00800000); // Switch 3000 temperature over threshold
define("WCPUSTAT_GPS_FIX", 0x00008000); // GPS fix quality indicator set
define("WCPUSTAT_GEOFENCE_INSIDE", 0x00080000); // inside a geofence
// Log replay state (for debugging purposes)
define("WPCULOG_ENABLED", 0x00010000); // connected to the server for streaming data
define("WCPULOG_STALLED", 0x00020000); // stalled because data cannot be sent immediately
// WCPULOG_REQ(what) ((u32)(what) << (16 + 8))
define("WCPULOG_REQ_NEW", 0x01); // pending request for new file, no reply
define("WCPULOG_REQ_CONT", 0x02); // pending request, same file, not yet replied
define("WCPULOG_REQ_ABORT", 0x03); // log reply is aborted
define("WCPULOG_REQ_ERROR", 0x08); // error detected
define("WCPULOG_REQ_ACK", 0x10); // request was acknowledge, orred with REQ_NEW or REQ_CONT
define("WCPULOG_REQ_READING", 0x20); // reading data from MCU, orred with REQ_NEW or REQ_CONT
define("WCPULOG_REQ_PROCESSING", 0x30); // processing log data
define("WCPULOG_REQ_NEXT", 0x40); // requesting next file from TCP-server
define("WCPULOG_REQ_LISTING", 0x80); // sending directory listing to TCP-server
define("WCPULOG_REQ_DONE", 0xF0); // current log file completed
define("WCPULOG_REQ_IDLE", 0x00); // log replay not yet started
define("WCPULOG_REQ_MASK", 0xF0); // mask request state (inverted) and overall state
/*
** Globals
*/
// status of the last transfer or command
global $zkl_status, $zkl_error;
$zkl_status = 0;
$zkl_error = "";
/*
** Private functions
*/
//
// Calculate the checksum of a message
//
function calc_checksum($buffer)
{
$checksum = 0;
$ptr = 0;
// skip initial $ or !
if( $buffer[0] == '$' || $buffer[0] == '!' )
$buffer = substr($buffer, 1);
while( strlen($buffer) != 0 && $buffer[0] != "*" )
{
// read a quoted string up to the end quote
if( $buffer[0] == '"' ) {
do {
$checksum ^= ord($buffer);
if( $buffer[0] == '\\' ) {
// escaped character
$buffer = substr($buffer, 1);
$checksum ^= ord($buffer);
$buffer = substr($buffer, 1);
}
else {
$buffer = substr($buffer, 1);
}
// return immediately when there is no ending quote
if( strlen($buffer) == 0 ) return -1;
} while( $buffer[0] != '"' );
}
$checksum ^= ord($buffer);
$buffer = substr($buffer, 1);
}
return $checksum & 255;
}
// append the second array to the first - PHP doesn't have this function...
function array_append(&$array1, $array2)
{
foreach( $array2 as $item ) $array1[] = $item;
}
// wait for one or more sockets, with a timeout
// $sockets, an array with sockets to wait for, will be modified
function wait_for_sockets(&$sockets, &$timeout)
{
return cp3000_wait_data($sockets, $timeout);
}
// wait for a single socket, with a timeout
function wait_for_socket($socket, &$timeout)
{
return cp3000_wait_data($socket, $timeout);
}
//
// Send a command, contained in '$fmt', over the socket. The command '$fmt'
// can contains format specifiers for 'sprintf()', the arguments for which
// are provided in the array '$args'.
// The checksum is calculated for resulting command, after expansion of
// '$args' and this is sent to the TCP server.
//
function zkl_send_command($socket, $fmt, $args = NULL)
{
global $zkl_status, $zkl_error;
if( !$fmt ) {
// library crashes on an empty string ($ forgotten?)
$zkl_status = TCPERR_SOCKET;
$zkl_error = "cp3000_send_data(): invalid NULL command";
return FALSE;
}
// send command, with checksum
$cmd = cp3000_tokens2cmd(vsprintf($fmt, $args));
if( @cp3000_send_data($socket, $cmd) === FALSE ) {
$zkl_status = TCPERR_SOCKET;
$zkl_error = "cp3000_send_data(): " . cp3000_error_string($socket, $zkl_status);
return FALSE;
}
// DEBUGGING: else echo "--[" . microtime() . "]-> " . $cmd . "\n";
return TRUE;
}
//
// Queue retrieval of the value of a key.
// The value must read using 'zkl_read_result()'
//
function zkl_queue_retrieval($socket, $key)
{
// send the command, its arguments and the checksum
if( zkl_send_command($socket, "\$RETR,\"%s\"", $key) === FALSE )
return FALSE;
}
//
// Read the result of the previous command from the socket and tokenize it.
// Also used for asynchronous replies.
//
function zkl_read_result($socket, &$result, $timeout = NULL)
{
global $zkl_status, $zkl_error;
// read result
$result = "";
do {
$reply = @cp3000_read_data($socket, $timeout);
if( $reply === FALSE ) {
if( $timeout == 0.0 )
$zkl_status = TCPERR_TIMEOUT;
else
$zkl_status = TCPERR_SOCKET;
$zkl_error = "cp3000_read_data(): " . cp3000_error_string($socket, $zkl_status);
return $zkl_status;
}
// DEBUGGING: else echo "<-[" . microtime() . "]-- " . strlen($reply) . ": " . $reply . "\n";
if( $reply[0] == '!' ) {
$tokens = cp3000_cmd2tokens($reply);
$zkl_status = $tokens['status'];
// save error in '$zkl_error'
if( $zkl_status != TCPSTAT_OK && $zkl_status != TCPSTAT_INTERMEDIATE && $zkl_status != TCPSTAT_WARNING && $zkl_status != TCPSTAT_BADSTATE ) {
$zkl_error = $tokens['args'][0];
}
// save result and/or error string
if( is_array($tokens['args']) ) {
if( $result ) $result .= ",";
$result .= implode(",", $tokens['args']);
}
}
} while( $zkl_status == TCPSTAT_INTERMEDIATE || $reply[0] == '$' );
return $zkl_status;
}
/*
** Low level functions
*/
//
// Send a command to the TCP-server and wait for its reply
// When a time-out is not needed, pass NULL as the second argument to this function
//
function zkl_command($socket, $timeout, $fmt)
{
// send the command, its arguments and the checksum
$args = func_get_args();
$args = array_slice($args, 3);
if( zkl_send_command($socket, $fmt, $args) === FALSE )
return FALSE;
if( zkl_read_result($socket, $result, $timeout) != TCPSTAT_OK )
return FALSE;
else return $result;
}
/*
** Global functions
*/
//
// Security (SSL): set private key
//
// Inputs:
// $key - private key (name of a PEM encoded file)
// $passphrase - passphrase for the key, a filename of a raw (!) file
// containing the actual passphrase; this file must only
// the passphrase, i.e. it must not have line endings
// or comments or whatever (or rather: the _entire_ file
// contents is used as the passphrase)
//
function zkl_set_private_key($key, $passphrase)
{
global $_SECURITY;
if( !isset($_SECURITY) ) $_SECURITY = array();
$_SECURITY['private']['key'] = $key;
$_SECURITY['private']['passphrase'] = $passphrase;
}
//
// Security (SSL): set public key
//
// Inputs:
// $cert - "my" certificate (name of a PEM encoded file)
// $signer - certificate of the CA that signed "my" certificate
// (name of a PEM encoded file)
//
function zkl_set_public_key($cert, $signer)
{
global $_SECURITY;
if( !isset($_SECURITY) ) $_SECURITY = array();
$_SECURITY['public']['cert'] = $cert;
$_SECURITY['public']['trust'] = $signer;
}
//
// Login with the TCP-server
//
// Inputs:
// $zkl - database ID of the ZKL
// $server - hostname:port with which to connect
// $priv_key - NOT USED, use "zkl_set_private_key()" instead
// $pub_key - NOI USED, use "zkl_set_public_key()" instead
// $alarmmsg - this channel is for alarm messages ($alarmmsg = TRUE)
// or retrieve or storing keys on the ZKL (the default,
// $alarmmsg = FALSE)
// $timeout - Max timeout of the $DB command
// $shmem_fallback - When shmem failed, login?
//
// Returns:
// CP3000 channel descriptor for reading and writing (historically
// called a socket)
//
function zkl_tcplogin($zkl, $server, $priv_key = NULL, $pub_key = NULL, $alarmmsg = ZKL_NORMAL, $timeout = 3, $shmem_fallback = TRUE)
{
global $zkl_status, $zkl_error;
global $_SECURITY, $_HOSTNAME;
// Setup connection to shared memory object or TCP-server
// Isolate the host name to test if it is an alias for the local host
$host = $server;
$host_pos = strpos($host, ":");
if( $host_pos !== FALSE ) $host = substr($host, 0, $host_pos);
// try shared memory object and semaphore - fallback to TCP if the shared memory object
// or the semaphore can't be opened
// NB: string parameter to 'cp3000_sysv_key()' _must_ be correctly
// formatted, i.e. don't change this
if(
$alarmmsg == ZKL_SHMEM &&
isset($_HOSTNAME) &&
(is_array($_HOSTNAME) ? in_array($host, $_HOSTNAME) : $host == $_HOSTNAME) &&
(
$shm_id = @shmop_open(
cp3000_sysv_key($server . "-" . sprintf("%u", $zkl) . "-shm"),
"a",
0,
0
)
) !== FALSE &&
(
$semaphore = @sem_get(
cp3000_sysv_key($server . "-" . sprintf("%u", $zkl) . "-sem")
)
) !== FALSE
) {
// shared memory object and semaphore are available
return array('shm' => $shm_id, 'sem' => $semaphore);
}
else {
if (($alarmmsg != ZKL_SHMEM) || ($shmem_fallback)) {
// TCP-server or TCP-server Secure
if( isset($_SECURITY) ) {
$socket = @cp3000_connect(
$server,
$_SECURITY['public']['cert'],
$_SECURITY['public']['trust'],
$_SECURITY['private']['key'],
$_SECURITY['private']['passphrase']
);
}
else $socket = @cp3000_connect($server);
// connected?
if( !$socket ) {
return FALSE;
}
// login
if( zkl_command($socket, $timeout, "\$DB,%u,%d", $zkl, $alarmmsg ? 1 : 0) === FALSE ) {
@cp3000_close($socket);
return FALSE;
}
else return $socket;
}
else {
return FALSE;
}
}
}
//
// Set up connection with server
//
// Inputs:
// $server - hostname:port with which to connect
//
function zkl_connect($server)
{
return @cp3000_connect($server);
}
//
// Disconnect connection
//
function zkl_disconnect($socket)
{
@cp3000_close($socket);
return TRUE;
}
//
// Logout from TCP-server (trivial...)
//
function zkl_tcplogout($socket)
{
if( is_cp3000($socket) ) {
zkl_disconnect($socket);
}
else {
@shmop_close($socket['shm']);
// nothing special needed for the semaphore
}
return TRUE;
}
//
// Is the connection a TCP connection?
//
function zkl_via_tcp($socket)
{
return is_cp3000($socket);
}
//
// Get peer name
//
// Returns:
// - SSL common name of the peer, as determined by its certificate;
// - hostname (reverse DNS look-up, or IP addres if not found) for
// unsecure connections
//
function zkl_get_peername($socket)
{
if( is_cp3000($socket) ) return @cp3000_get_peername($socket);
else return "<shmem>";
}
//
// Retrieve the value of a key
//
function zkl_retrieve($socket, $key, $timeout = NULL)
{
return zkl_command($socket, $timeout, "\$RETR,\"%s\"", $key);
}
//
// Store the value of a key
//
function zkl_store($socket, $key, $value)
{
return zkl_send_command($socket, "\$STOR,\"%s\",\"%s\"", array($key, $value)) !== FALSE;
}
/*
** High level functions
*/
//
// Get real-time status
// Return an associative array
//
function zkl_get_status($socket, $timeout = NULL)
{
if( !is_cp3000($socket) ) return FALSE;
return zkl_interpret_status(zkl_retrieve($socket, "STAT", $timeout));
}
function zkl_get_cached_status($socket, $timeout = NULL)
{
if( is_cp3000($socket) ) {
$status = zkl_command($socket, $timeout, "\$STAT");
}
else {
// 256 bytes _should_ be long enough to contain the status string
// (at the moment, 20101027, it is about 140 bytes or less)
@sem_acquire($socket['sem']);
$status = @shmop_read($socket['shm'], 0, 256);
@sem_release($socket['sem']);
// trim the string - PHP strings are a bit awkward as it is a strange
// mix between C type strings and byte arrays with a length field;
// 'strrpos()' in one of those functions that treats it as a byte
// array so we trim the data read from shared memory to the length
// of the C string it contains
$pos_eos = strpos($status, "\0");
if( $pos_eos !== FALSE ) $status = substr($status, 0, $pos_eos);
}
return zkl_interpret_status($status);
}
function zkl_interpret_status($statbuf)
{
if( !$statbuf ) return FALSE;
$seqnr_pos = strrpos($statbuf, "@");
if( $seqnr_pos !== FALSE ) {
$seqnr_time = substr($statbuf, $seqnr_pos + 1);
$statbuf = substr($statbuf, 0, $seqnr_pos);
$time_pos = strpos($seqnr_time, ".");
if( $time_pos) {
$status['time'] = intval(substr($seqnr_time, $time_pos + 1));
$seqnr_time = substr($seqnr_time, 0, $time_pos);
}
else $status['time'] = 0;
$status['seqnr'] = intval($seqnr_time);
}
else $status['seqnr'] = -1;
$statbuf = explode(",", $statbuf);
// gather status bitfields; will be expanded
$mcu_state = array();
$has_switch_status = preg_match("|([0-9A-F]{4})/([0-9A-F]{4})/([0-9A-F]{4})|", $statbuf[0], $mcu_state) > 0;
if( $has_switch_status ) {
$statbuf[0] = $mcu_state[1];
}
$status['mcu']['status'] = intval($statbuf[0], 16);
$status['mcu']['local state'] = intval($statbuf[1], 16);
$status['mcu']['persistent'] = intval($statbuf[2], 16);
$wcpu_status = trim($statbuf[3]);
if( strlen($wcpu_status) == 8 && intval("80000000", 16) != 0x80000000 ) {
// argh! the conversion on 32-bit, which the most significant bit set, goes gaga
// the log status is stripped because it corrupts the conversion on 32-bit system
// (and it is not interesting anyway)
$status['wcpu']['status'] =
(intval(substr($wcpu_status, 2, 2), 16) << 16) +
(intval(substr($wcpu_status, 4, 4), 16) << 0);
}
else {
// on 64-bit systems...
$status['wcpu']['status'] = intval($statbuf[3], 16);
}
if( $has_switch_status ) {
// remote control and switch 3000 status
$status['mcu']['remote control status'] = intval($mcu_state[2], 16);
$status['mcu']['switch3000 status'] = intval($mcu_state[3], 16);
}
// measurement info
$status['detection']['error'] = ($status['mcu']['status'] & 0x0004) != 0;
$status['detection']['calibrated'] = ($status['mcu']['persistent'] & 0x0001) != 0;
$status['detection']['b/a limit'] = floatval($statbuf[4]);
$status['detection']['freq'] = floatval($statbuf[5]);
if( $status['mcu']['status'] & 0x0001 ) {
$status['detection']['active'] = TRUE;
$status['detection']['rms'] = intval($statbuf[6], 10);
$status['detection']['b/a'] = floatval($statbuf[7]);
$status['detection']['quality'] = zkl_measurement_quality($status['detection']['b/a'], $status['detection']['b/a limit']);
$status['detection']['ok'] = ($status['mcu']['status'] & 0x0002) != 0;
}
else {
$status['detection']['active'] = FALSE;
$status['detection']['rms'] = 0;
$status['detection']['b/a'] = 0;
$status['detection']['quality'] = 0;
$status['detection']['ok'] = FALSE;
}
// special, transient status flags
$status['detection']['sweep']['enabled'] = ($status['mcu']['local state'] & 0x0003) != 0;
$status['detection']['auto-cal']['enabled'] = ($status['mcu']['local state'] & 0x0100) != 0;
$status['detection']['auto-cal']['failure'] = ($status['mcu']['local state'] & 0x0200) != 0;
// power management and switch 3000
if( $has_switch_status ) {
if( $status['mcu']['persistent'] & MCUFLG_PM3000_AVAILABLE ) {
$status['pm3000']['switch3000']['on'] = ($status['mcu']['remote control status'] & 0x0100) != 0;
$status['pm3000']['charger']['on'] = ($status['mcu']['remote control status'] & 0x0400) != 0;
$status['pm3000']['switch #2']['on'] = ($status['mcu']['remote control status'] & 0x0200) != 0;
}
if( $status['mcu']['persistent'] & MCUFLG_SW3000_AVAILABLE ) {
$status['switch3000']['on'] = ($status['mcu']['switch3000 status'] & 0x0009) == 0x0009
|| ($status['mcu']['switch3000 status'] & 0x0006) == 0x0006;
$status['switch3000']['key']['on'] = ($status['mcu']['remote control status'] & 0x0004) != 0;
$status['switch3000']['key']['off'] = ($status['mcu']['remote control status'] & 0x0008) != 0;
$status['switch3000']['detection']['ok'] = ($status['mcu']['remote control status'] & 0x0002) != 0;
$status['switch3000']['enabled'] = ($status['mcu']['persistent'] & MCUFLG_SW3000_ENABLED) != 0;
// switch 3000 batteries
$status['batt']['switch3000']['sec1']['status'] = ($status['mcu']['switch3000 status'] & 0x0100) != 0 ? "ok" : "alarm";
$status['batt']['switch3000']['sec2']['status'] = ($status['mcu']['switch3000 status'] & 0x0200) != 0 ? "ok" : "alarm";
$status['batt']['switch3000']['sec3']['status'] = ($status['mcu']['switch3000 status'] & 0x0400) != 0 ? "ok" : "alarm";
$status['batt']['switch3000']['sec4']['status'] = ($status['mcu']['switch3000 status'] & 0x0800) != 0 ? "ok" : "alarm";
$status['batt']['switch3000']['emer']['status'] = ($status['mcu']['switch3000 status'] & 0x8000) != 0 ? "ok" : "alarm";
}
}
// relay
$status['relay']['open'] = ($status['mcu']['status'] & 0x0008) != 0;
$status['relay']['enabled'] = ($status['mcu']['local state'] & 0x0800) != 0;
// external contact, led intensity and lantern
$status['external contact']['enabled'] = ($status['mcu']['local state'] & 0x0400) != 0;
$status['led']['high'] = ($status['mcu']['local state'] & 0x0040) != 0;
$status['led']['intensity'] = ($status['mcu']['local state'] & 0x0040) != 0 ? "high" : "low";
$status['lantern']['enabled'] = ($status['mcu']['local state'] & 0x0080) != 0;
// batteries; battery number is zero-based
$status['batt']['select'] = intval($statbuf[8], 10);
if( $status['batt']['select'] > 0 ) $status['batt']['select'] -= 1;
$status['batt'][0]['V'] = floatval($statbuf[9]);
$status['batt'][1]['V'] = floatval($statbuf[10]);
if( $status['mcu']['status'] & 0x0400 )
$status['batt'][0]['status'] = "removed";
else if( $status['mcu']['status'] & 0x0200 )
$status['batt'][0]['status'] = "empty";
else if( $status['mcu']['status'] & 0x0100 )
$status['batt'][0]['status'] = "alarm";
else
$status['batt'][0]['status'] = "ok";
if( $status['mcu']['status'] & 0x4000 )
$status['batt'][1]['status'] = "removed";
else if( $status['mcu']['status'] & 0x2000 )
$status['batt'][1]['status'] = "empty";
else if( $status['mcu']['status'] & 0x1000 )
$status['batt'][1]['status'] = "alarm";
else
$status['batt'][1]['status'] = "ok";
// temperature
$status['temp']['on-board']['C'] = (strlen($statbuf[11]) > 0) ? floatval($statbuf[11]) : NULL;
$status['temp']['ext1']['C'] = (strlen($statbuf[12]) > 0) ? floatval($statbuf[12]) : NULL;
$status['temp']['ext2']['C'] = FALSE;
$status['temp']['switch3000']['C'] = FALSE;
$status['temp']['on-board']['ok'] = (($status['wcpu']['status'] & WCPUERR_TEMP_ONBOARD) == 0);
$status['temp']['ext1']['ok'] = (($status['wcpu']['status'] & WCPUERR_TEMP_EXT1) == 0);
$status['temp']['ext2']['ok'] = (($status['wcpu']['status'] & WCPUERR_TEMP_EXT2) == 0);
$status['temp']['switch3000']['ok'] = (($status['wcpu']['status'] & WCPUERR_TEMP_SW3000) == 0);
// motion
$status['motion']['active'] = ($status['mcu']['status'] & 0x00800000) != 0;
$status['motion']['start'] = ($status['mcu']['status'] & 0x00C00000) == 0x00C00000;
$status['motion']['stop'] = ($status['mcu']['status'] & 0x00C00000) == 0x00400000;
// GSM reception
$status['gsm']['enabled'] = ($status['mcu']['local state'] & 0x2000) != 0;
$status['gsm']['connected'] = ($status['wcpu']['status'] & 0x0100) != 0;
$status['gsm']['gprs connected'] = ($status['wcpu']['status'] & 0x0400) != 0;
$status['gsm']['tcp/ip connected'] = ($status['wcpu']['status'] & 0x0800) != 0;
$status['gsm']['sim initialized'] = ($status['wcpu']['status'] & 0x0200) != 0;
$status['gsm']['received signal strength'] = floatval($statbuf[13]);
$status['gsm']['bit error rate'] = intval($statbuf[14], 10);
if( $status['gsm']['bit error rate'] != -1 ) {
if( $status['gsm']['bit error rate'] == 0 )
$status['gsm']['min error pct'] = 0;
else
$status['gsm']['min error pct'] = pow(2.0, $status['gsm']['bit error rate']) / 10;
$status['gsm']['max error pct'] = pow(2.0, $status['gsm']['bit error rate'] + 1) / 10;
}
// GPS reception
$status['gps']['enabled'] = TRUE; // obsolete
$status['gps']['latitude'] = floatval($statbuf[15]);
$status['gps']['longitude'] = floatval($statbuf[16]);
$status['gps']['altitude'] = floatval($statbuf[17]);
$status['gps']['hdop'] = floatval($statbuf[18]);
$status['gps']['speed'] = floatval($statbuf[19]);
$status['gps']['heading'] = floatval($statbuf[20]);
$status['gps']['tijd'] = intval($statbuf[21]);
$status['gps']['fix'] = ($status['wcpu']['status'] & 0x8000) != 0;
switch( substr($statbuf[18], -1) ) {
case 'A': // fix: autonomous
case 'D': // fix: differential (DGPS)
case 'E': // fix: estimated (dead reckoning)
case 'I': // no fix: initialized from MCU
case 'N': // no fix
case 'U': // no fix, device is initializing
$status['gps']['mode'] = substr($statbuf[18], -1);
break;
default:
unset($status['gps']['mode']);
break;
}
$status['gps']['track&trace'] = ($status['mcu']['local state'] & 0x0008) != 0;
$status['geofence']['inside'] = ($status['wcpu']['status'] & 0x80000) != 0;
// subsystem errors
$status['system']['error']['i2c'] = ($status['mcu']['status'] & 0x0010) != 0
|| ($status['wcpu']['status'] & 0x0040) != 0;
$status['system']['error']['log'] = ($status['mcu']['status'] & 0x0020) != 0;
$status['system']['error']['gsm'] = ($status['mcu']['status'] & 0x0040) != 0;
$status['system']['error']['sms'] = ($status['mcu']['status'] & 0x0080) != 0;
$status['system']['error']['wcpu'] = ($status['wcpu']['status'] & 0x0001) != 0;
$status['system']['error']['gprs'] = ($status['wcpu']['status'] & 0x0002) != 0;
$status['system']['error']['tcp'] = ($status['wcpu']['status'] & 0x0002) != 0;
$status['system']['error']['uart'] = ($status['wcpu']['status'] & 0x0010) != 0;
$status['system']['error']['gps'] = ($status['wcpu']['status'] & 0x0020) != 0; // UART2 actually
$status['system']['error']['mcu'] = ($status['wcpu']['status'] & 0x0080) != 0;
if( $has_switch_status ) {
$status['system']['error']['swi2c'] = ($status['mcu']['remote control status'] & 0x0010) != 0;
$status['system']['error']['sw1w'] = ($status['mcu']['remote control status'] & 0x0020) != 0;
}
$status['system']['ok'] =
($status['mcu']['status'] & 0x00F0) == 0 &&
($status['wcpu']['status'] & 0x00FF) == 0 &&
(!$has_switch_status || ($status['mcu']['remote control status'] & 0x00F0) == 0);
// in initialisation phase?
$status['system']['init'] = ($status['mcu']['local state'] & 0x8000) == 0;
return $status;
}
//
// Quality of the measurement (rather arbitrary calculation)
//
function zkl_measurement_quality($b_a_measurement, $b_a_autocal)
{
require_once("utilities.php");
// the ZKL is calibrated at 150 mOhm, with a margin of 1.5% (1/64)
// in the calculation below we'll use a margin of 3%; the closer
// b/a diverges to the calibrated value, the less the quality will
// be; above the calibrate value the quality is 0.0, below the
// mentioned 3% is 1.0 (100%)
// measuring with a 150 mOhm resistor will therefore show a quality
// of 50%
// the 3% margin is chosen rather arbitrarily and it indicates to
// the user that the measurment is near the detection margin and that
// he/she should try to place the ZKL anew.
// the percentage at with quality is 100% can be adjusted below by
// setting the correct value for $q_good.
return CalcShortCircuitQuality($b_a_autocal, $b_a_measurement);
}
//
// Get a (well, the last) measurement
//
function zkl_get_measurement($socket, $timeout = NULL)
{
$buffer = zkl_retrieve($socket, "DEBUG[MEASUREMENT]", $timeout);
if( $buffer === FALSE ) return FALSE;
$buffer = explode(",", $buffer);
$samples = array();
$samples['count'] = intval($buffer[0]);
$samples['b/a'] = floatval($buffer[1]) / 65536.0;
$samples['t_lat'] = floatval($buffer[4]);
$samples['t_seq'] = floatval($buffer[5]);
$samples['v_ref']['rms'] = intval($buffer[2]);
$samples['v_meas']['rms'] = intval($buffer[3]);
$buffer_ptr = 6;
for( $array = 0; $array < 4; $array++ ) {
// determine in which array to put the samples
switch( $array ) {
case 0:
$signal = 'v_ref';
$type = 'raw';
break;
case 1:
$signal = 'v_meas';
$type = 'raw';
break;
case 2:
$signal = 'v_ref';
$type = 'filtered';
break;
case 3:
$signal = 'v_meas';
$type = 'filtered';
break;
}
// collect the samples
$sample_array = array();
for( $sample = 0; $sample < $samples['count']; $sample++ ) {
$val = intval($buffer[$buffer_ptr++], 16);
if( $val & 0x8000 ) {
// 2's complement negative number
$val -= 0x10000;
}
array_push($sample_array, $val);
}
// save the samples in the main array
$samples[$signal][$type] = $sample_array;
}
// done
return $samples;
}
/*
** Functions for real-time status updates
*/
//
// Wait until one of the ZKLs has a new status
// Input:
// $sockets - array of sockets, opened with a function call like
// 'zkl_tcplogin($id, $tcp_server, $priv_key, $pub_key, TRUE)'
// $timeout - time-out in seconds (floating point number)
// Output:
// - array with one or more items as below; each ZKL that has a
// status update will be in this array; ZKLs that didn't present
// a new status will _not_ be in the array
// items in entry of the array
// - ['zkl'] - database ID of the ZKL
// - ['flags'] - which data is updated; bit field with one or more
// 'STATUPDATE_xxx' bits set.
// - ['stat'] - status array; see 'zkl_interpret_status()'
// - empty array when the time-out expired
// - FALSE on error
//
function zkl_wait($sockets, $timeout = NULL)
{
$n = wait_for_sockets($sockets, $timeout);
// check the result
if( !$n )
// error
return FALSE;
else {
$result = array();
if( $n ) foreach( $sockets as $socket ) {
$reply = @cp3000_read_data($socket, $timeout);
if( $reply === FALSE ) {
$zkl_status = TCPERR_SOCKET;
$zkl_error = "socket_read(): " . cp3000_error_string($socket, $zkl_status);
continue;
}
// DEBUGGING: else echo "--[" . microtime() . "]-- " . $reply . "\n";
$tokens = cp3000_cmd2tokens($reply);
array_push($result,
array(
'zkl' => $tokens['client'],
'flags' => intval($tokens['args'][0], 10),
'stat' => zkl_interpret_status($tokens['args'][1])
)
);
}
return $result;
}
}
/*
** Send an SMS
*/
//
// Send an SMS
// Input:
// $server - the SMS server
// $da - destination address (the phone number) _or_ an array with
// (at least) 'telefoonnr' and 'imsi' to send an SMS to a
// device (M2M API aware)
// $msg - the message to send
// $wait - wait for reply if TRUE
// Output:
// FALSE if an error occured, reply from the SMS server it $wait is TRUE
// of simply TRUE when $wait is FALSE.
//
// The globals $zkl_status and $zkl_error will be set when an error occurs
//
function zkl_send_sms($server, $da, $msg, $wait = TRUE)
{
global $zkl_status, $zkl_error;
if( strlen($msg) > 160 ) {
$zkl_status = TCPERR_INVAL;
return FALSE;
}
require_once("m2mapi.php");
$success = TRUE;
if( !is_array($da) || !m2mapi_enabled_for_device($da) ) {
// open the socket to the server
$socket = cp3000_connect($server);
if( !$socket ) {
$zkl_status = TCPERR_CONNECT;
return FALSE;
}
if( !zkl_send_command($socket, "\$SENDSMS,\"%s\",\"%s\"", array($da, $msg)) ) {
// $zkl_status set by 'zkl_send_command()'
$success = FALSE;
}
else if( $wait ) {
$success = ((zkl_read_result($socket, $zkl_status, 15.0) == TCPSTAT_OK) && ($zkl_status == TCPSTAT_OK));
}
// done
cp3000_close($socket);
}
else {
// send to the device using the M2M API (Vodafone Smart Services)
$success = m2mapi_send_sms($da['imsi'], $msg);
}
return $success;
}
//
// Send SMS messages to multiple recipients on the same server
// Input:
// $server - the SMS server
// $msgs - array with destiantion address (key 'da') and messages (key 'msg')
// as:
// $msgs = array(
// array('da' => '+31612345678', 'msg' => 'Text for the first recipient'),
// array('da' => '+31687654321', 'msg' => 'Text for the second recipient'),
// // etc
// );
// $wait - wait for reply if TRUE
// Output:
// FALSE if an error occured, TRUE otherwise (_not_ a message index on success)
//
function zkl_send_multi_sms($server, $msgs, $wait = FALSE)
{
global $zkl_status, $zkl_error;
$success = TRUE;
// open the socket to the server
$socket = cp3000_connect($server);
if( !$socket ) {
$zkl_status = TCPERR_CONNECT;
return FALSE;
}
foreach( $msgs as $msg ) {
if( !$msg['da'] || !$msg['msg'] || strlen($msg['msg']) > 160 ) {
$zkl_status = TCPERR_INVAL;
$success = FALSE;
}
else if( !zkl_send_command($socket, "\$SENDSMS,\"%s\",\"%s\"", array($msg['da'], $msg['msg'])) ) {
$success = FALSE;
}
else if( $wait && !zkl_read_result($socket, $reply, 15.0) ) {
$success = FALSE;
}
}
// done
cp3000_close($socket);
return $success;
}
//
// Build an array with version information from '$str' (which is retrieved from the database)
//
function zkl_get_fw_version($str)
{
$p = 0;
$ver = array();
$ver['major'] = 0;
while( ctype_digit(substr($str, $p, 1)) )
$ver['major'] = $ver['major'] * 10 + (ord(substr($str, $p++, 1)) - ord("0"));
$ver['minor'] = 0;
if( ord(substr($str, $p, 1)) == ord(".") ) {
$p++;
while( ctype_digit(substr($str, $p, 1)) )
$ver['minor'] = $ver['minor'] * 10 + (ord(substr($str, $p++, 1)) - ord("0"));
}
$ver['release'] = 0;
if( ord(substr($str, $p, 1)) == ord(".") ) {
$p++;
while( ctype_digit(substr($str, $p, 1)) )
$ver['release'] = $ver['release'] * 10 + (ord(substr($str, $p++, 1)) - ord("0"));
}
$ver['update'] = 0;
if( ord(substr($str, $p, 1)) == ord(".") ) {
$p++;
while( ctype_digit(substr($str, $p, 1)) )
$ver['update'] = $ver['update'] * 10 + (ord(substr($str, $p++, 1)) - ord("0"));
}
$ver['datecode'] = 0;
if( ord(substr($str, $p, 1)) == ord("-") ) {
$p++;
while( ctype_digit(substr($str, $p, 1)) )
$ver['datecode'] = $ver['datecode'] * 16 + (ord(substr($str, $p++, 1)) - ord("0"));
}
$ver['devtype'] == "";
if( ord(substr($str, $p, 1)) == ord("-") ) {
$p++;
$ver['devtype'] = substr($str, $p);
}
return $ver;
}
?>