#!/usr/local/bin/perl


# MicroDowell SpA Community License
# 
# Preamble:
# 
# The intent of this document is to state the conditions under which the 
# Package (ups.pl) may be copied and distributed, such that MicroDowell SpA 
# maintains control over the development and distribution of the Package, 
# while allowing the users of the Package to use the Package in a variety of 
# ways.  
# 
# Authorized electronic distributors of the Package are:
# 
#   * Any MicroDowell SpA site
# 
# Definitions:
# 
# "MicroDowell" refers to MicroDowell SpA., the Copyright Holder of the 
# Package.
# 
# "Package" refers to those files, including, but not limited to, source 
# code, binary executables, images, and scripts, which are distributed by the 
# Copyright Holder.
# 
# "You" is you, if you are thinking about copying or distributing this 
# Package.
# 
# Terms:
# 
# 1. You may use this Package for commercial or non-commercial purposes 
# without charge.
# 
# 2. You may make and give away verbatim copies of this Package for personal 
# use, or for use within your organization, provided that you duplicate all 
# of the original copyright notices and associated disclaimers.  You may not 
# distribute copies of this Package, or copies of packages derived from this 
# Package, to others outside your organization without specific prior written 
# permission from MicroDowell (although you are encouraged to direct them to 
# sources from which they may obtain it for themselves).
# 
# 3. You may apply bug fixes, portability fixes, and other modifications 
# derived from MicroDowell.  A Package modified in such a way shall still be 
# covered by the terms of this license.
# 
# 4. MicroDowell's name and trademarks may not be used to endorse or promote 
# packages derived from this Package without specific prior written 
# permission from ActiveState.
# 
# 5. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED 
# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF 
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
# 
# ==============================================================================

package MyGlobals ;

use strict ;
use POSIX;
use English ;        # for variable extension
use IO::File ;       # 
use IO::Socket ;     # to connect to UpsEye SERVER
use IO::Select;
use Socket;
use Getopt::Long ;   # for reading command-line options
use Fcntl;
use Tie::RefHash;
use Sys::Hostname;
use Getopt::Long ;   # for reading command-line options
use Class::Struct ;


use Exporter ;
use vars qw(@ISA @EXPORT @EXPORT_OK $VERSION $ups $connection $OS_OSX $OS_win %iniConfig 
				$INIFILE $PROGPATH $INTERACTIVE $UpsSchedule) ;

use constant IDENT_1 => "D-2000 %02X \"MicroDowell SpA - TCP/IP - v0.0 gamma\" v0.0 Socket[%d]\r\n" ;
use constant IDENT_2 => "D-2002 IP=%s MASK=0.0.0.0 GATEWAY=0.0.0.0 MASTER\r\n" ;

use constant UPS_COMSTATE_INIT_MODE => 0 ;   # startup
use constant UPS_COMSTATE_POLL_MODE => 1 ;   # polling data
use constant UPS_COMSTATE_COMM_LOST => 2 ;   # no communication

use constant ERR_COM_NO_CHARS      => -999 ; # nessun carattere dalla porta seriale
use constant ERR_MSG_TOO_SHORT     => -998 ; # messaggio troppo breve: non arriva sino al Checksum
use constant ERR_MSG_CHECKSUM      => -997 ; # checksum non valido
use constant ERR_COM_TIMEOUT       => -996 ; # timeout in lettura
use constant COMMAND_NOT_VALID     => -50  ; # comando non valido
use constant PARAMETER_NOT_VALID   => -51  ; # parametro non valido
use constant ERR_UPS_NOT_FOUND     => -10  ; # non trovo un UPS
use constant ERR_COM_PORT_OPEN     => -11  ; # impossibile aprire la porta di comunicazione
use constant ERR_COM_SET_PORT      => -12  ; # impossibile configurare la porta di comunicazione
use constant ERR_COM_UNDEF_PORT    => -20  ; # porta non valida o non esistente
use constant ERR_USB_DRIVER        => -13  ; # impossibile aprire il driver USB
use constant ERR_USB_COMM_KO       => -14  ; # interfaccia USB OK: gruppo spento o guasto
use constant ERR_UPS_UNKNOWN       => -30  ; # impossibile identificare l'UPS
use constant ERR_PRG_THREAD        => -40  ; # impossibile creare uno thread
use constant ERR_PRG_INVALID_DATA  => -60  ; # dati non validi

use constant CMD_ACK              => 0x06 ; # ACK
use constant CMD_NACK             => 0x15 ; # NACK

#-----------------------------------------------------------------------------------
# B.Box interactive old commands
#-----------------------------------------------------------------------------------
use constant CMD_GET_STATUS_OLD   => 0x01 ; # comando di acquisizione STATUS
use constant CMD_GET_MODEL_OLD    => 0x0A ; # comando di acquisizione MODELLO
#-----------------------------------------------------------------------------------

use constant CMD_GET_STATUS       => 0x00 ; # comando di acquisizione STATUS
use constant CMD_GET_MEASURES     => 0x01 ; # comando di acquisizione MISURE
use constant CMD_GET_CONFIG       => 0x02 ; # comando di acquisizione CONFIGURAZIONE
use constant CMD_GET_BATT_STAT    => 0x03 ; # comando di acquisizione Battery+Load Status
use constant CMD_GET_MASK         => 0x07 ; # comando di acquisizione MASCHERA PUNTI
use constant CMD_SET_TIMER        => 0x20 ; # comando di CONFIGURAZIONE TIMER
use constant CMD_BATT_TEST        => 0x21 ; # comando di CONFIGURAZIONE TEST BATTERIA
use constant CMD_GET_BATT_TEST    => 0x22 ; # comando di       LETTURA  TEST BATTERIA
use constant CMD_BUZZER_OFF       => 0x23 ; # comando di SHUTDOWN BUZZER
use constant CMD_SD_ONESHOT       => 0x40 ; # comando di SCRITTURA SHUTDOWN ONESHOT
use constant CMD_GET_SD_ONESHOT   => 0x41 ; # comando di  LETTURA  SHUTDOWN ONESHOT
use constant CMD_SET_SCHEDULE     => 0x42 ; # comando di SCRITTURA SCHEDULE
use constant CMD_GET_SCHEDULE     => 0x43 ; # comando di  LETTURA  SCHEDULE
use constant CMD_GET_EEP_BLOCK    => 0x50 ; # comando di  LETTURA  BLOCCO EEPROM
use constant CMD_SET_EEP_BLOCK    => 0x51 ; # comando di  SCRITTURA  BLOCCO EEPROM
use constant CMD_GET_EEP_SEED     => 0x52 ; # comando di  LETTURA SEED
use constant CMD_INIT             => 0xF0 ; # comando di REINIZIALIZZAZIONE UPS


#-----------------------------------------------------------------------------------
# CYBP constants
#-----------------------------------------------------------------------------------
use constant CMD_GET_CYP_P3     	=> 0x33 ; # 
use constant CMD_GET_CYP_P4     	=> 0x34 ; # 
use constant CMD_GET_CYP_P9     	=> 0x39 ; # 
#-----------------------------------------------------------------------------------

#-----------------------------------------------------------------------------------
# CHR constants
#-----------------------------------------------------------------------------------
use constant CMD_GET_CHR_F     	=> 0x46 ; # 
#-----------------------------------------------------------------------------------

use constant LEN_ACK              => 1 ; # ACK
use constant LEN_NACK             => 2 ; # NACK

use constant LEN_GET_STATUS       => 1 ; # comando di acquisizione STATUS
use constant LEN_GET_MEASURES     => 1 ; # comando di acquisizione MISURE
use constant LEN_GET_CONFIG       => 1 ; # comando di acquisizione CONFIGURAZIONE
use constant LEN_GET_BATT_STAT    => 1 ; # comando di acquisizione Battery+Load Status
use constant LEN_GET_MASK         => 1 ; # comando di acquisizione MASCHERA PUNTI
use constant LEN_SET_TIMER        => 5 ; # comando di CONFIGURAZIONE TIMER
use constant LEN_BATT_TEST        => 4 ; # comando di CONFIGURAZIONE TEST BATTERIA
use constant LEN_GET_BATT_TEST    => 1 ; # comando di       LETTURA  TEST BATTERIA
use constant LEN_BUZZER_OFF       => 1 ; # comando di SHUTDOWN BUZZER
use constant LEN_SD_ONESHOT       => 8 ; # comando di SCRITTURA SHUTDOWN ONESHOT
use constant LEN_GET_SD_ONESHOT   => 1 ; # comando di  LETTURA  SHUTDOWN ONESHOT
use constant LEN_SET_SCHEDULE     => 8 ; # comando di SCRITTURA SCHEDULE
use constant LEN_GET_SCHEDULE     => 2 ; # comando di  LETTURA  SCHEDULE
use constant LEN_GET_EEP_BLOCK    => 3 ; # comando di LETTURA BLOCCO EEPROM
use constant LEN_GET_EEP_SEED     => 1 ; # comando di LETTURA SEED
use constant LEN_SET_EEP_BLOCK    => 12 ; # comando di SCRITTURA BLOCCO EEPROM
use constant LEN_INIT             => 1 ; # comando di REINIZIALIZZAZIONE UPS


use constant ERR_I2C_BUSY       	=> 0x01	; # I²C bus busy (e²prom)
use constant ERR_CMD_CHECKSUM    => 0x10	; # Checksum not valid
use constant ERR_CMD_UNRECOG     => 0x11	; # unrecognized command
use constant ERR_EEP_NOBLOCK    	=> 0x08	; # WRITE: eeprom address not multiple of 8
use constant ERR_EEP_OOBOUND    	=> 0x09	; # READ: eeprom address out of bound (with size)
use constant ERR_EEP_WADDR1     	=> 0x28	; # error writing e²prom address
use constant ERR_EEP_WSADDR1    	=> 0x29	; # error writing e²prom subaddress
use constant ERR_EEP_RDATA      	=> 0x2A	; # error reading e²prom data
use constant ERR_EEP_WADDR2     	=> 0x50	; # error writing e²prom address
use constant ERR_EEP_WSADDR2    	=> 0x51	; # error reading e²prom subaddress
use constant ERR_EEP_WDATA      	=> 0x52	; # error writing e²prom data
use constant ERR_EEP_WADDRVER   	=> 0x53	; # error writing e²prom address during data verification
use constant ERR_EEP_WDATAVER   	=> 0x54	; # error verification e²prom data
use constant ERR_EEP_VERIFY     	=> 0x55	; # e²prom data are different from those in the write buffer
use constant ERR_EEP_CHECKSUM   	=> 0x90	; # e²prom checksum error

#=======================================================
# Indirizzi delle variabili memorizzate nella EEPROM: #
#=======================================================
use constant  EEP_OPTION_BYTE  => 0x00 ; # 8 byte configurazione UPS
use constant  EEP_MAGIC        => 0x00 ; # magic number: deve essere a 0xAA
use constant  EEP_CHECKSUM     => 0x01 ; # XOR longitudinale da 0x?? a 0x??
use constant  EEP_OUT_CONFIG   => 0x02 ; # vedi documentazione
use constant  EEP_STATUS       => 0x04 ; # Status UPS

use constant  EEP_THRESHOLD_0  => 0x08 ; # Blocco 0 con gli threshold
use constant  EEP_MEAN_MIN     => 0x08 ; # UPS minimum AC voltage intervention point 
use constant  EEP_MEAN_MAX     => 0x0A ; # UPS maximum AC voltage intervention point 
use constant  EEP_AVR_MIN      => 0x0C ; # AVR minimum AC voltage intervention point
use constant  EEP_AVR_MAX      => 0x0E ; # AVR maximum AC voltage intervention point

use constant  EEP_THRESHOLD_1  => 0x10 ; # Blocco 1 con gli threshold
use constant  EEP_BLOW_VALUE   => 0x10 ; # Battery LOW threshold voltage
use constant  EEP_BEND_VALUE   => 0x12 ; # Battery END threshold voltage
use constant  EEP_VMETER_0     => 0x14 ; # Led 0 Voltage indicator threshold
use constant  EEP_VMETER_1     => 0x16 ; # Led 1 Voltage indicator threshold

use constant  EEP_THRESHOLD_2  => 0x18 ; # Blocco 2 con gli threshold
use constant  EEP_VMETER_2     => 0x18 ; # Led 2 Voltage indicator threshold
use constant  EEP_VMETER_3     => 0x1A ; # Led 3 Voltage indicator threshold
use constant  EEP_VMETER_4     => 0x1C ; # Led 4 Voltage indicator threshold

use constant  EEP_FAULT_POINTS => 0x20 ; # number of fault points (in an half sine wave) after which USP enter battery mode
use constant  EEP_CHARGER_MIN  => 0x21 ; # Threshold voltage for battery charger

use constant  EEP_TEMP_MAX     => 0x28 ; # Maximum temperature: over this value an alarm will be generated  
use constant  EEP_TEMP_FAULT   => 0x2A ; # Fault temperature: over this value, UPS will shut down immediately 
use constant  EEP_FANSPEED_0   => 0x2C ; # Temperature for fan activation (low speed)
use constant  EEP_FANSPEED_1   => 0x2E ; # Temperature for fan activation (medium speed)

use constant  EEP_FANSPEED_2   => 0x30 ; # Temperature for fan activation (high speed)

use constant  EEP_IOUT         => 0x38 ; # 
use constant  EEP_IOUT_OVRLD   => 0x38 ; # Maximum current: over this value an alarm will be generated 
use constant  EEP_IOUT_FAULT   => 0x3A ; # Fault current: over this value, UPS will shut down immediately
use constant  EEP_PWRMTR_0     => 0x3C ; # Led 0 Power indicator threshold 
use constant  EEP_PWRMTR_1     => 0x3E ; # Led 1 Power indicator threshold 

use constant  EEP_PWRMTR_2     => 0x40 ; # Led 2 Power indicator threshold 
use constant  EEP_PWRMTR_3     => 0x42 ; # Led 3 Power indicator threshold 
use constant  EEP_PWRMTR_4     => 0x44 ; # Led 4 Power indicator threshold 
use constant  EEP_PWRMTR_5     => 0x46 ; # Between "Full power" and "Overload" Power indicator level 

use constant  EEP_INPUT_MASK_0 => 0x48 ; # Half sine input mask (0÷7 of 10 points)
use constant  EEP_INPUT_MASK_8 => 0x50 ; # Half sine input mask (8÷9 of 10 points)

use constant  EEP_MIN_VBATT    => 0x60 ; # Minimum battery voltaged reached from the last power-on
use constant  EEP_RUNTIME_H    => 0x62 ; # Working time in minutes (max ˜ 32 years) (MSB first)
use constant  EEP_BAT_TIME_M   => 0x65 ; # Time in Battery mode: seconds*2 (max ˜ 390 days). (MSB first)


use constant  EEP_UPS_MODEL    => 0x80 ; # Model of the UPS in ASCII. For the Enterprise line, the structure is:
                                    ; #    ENTxxyyy   
                                    ; # where:	 xx = Power rating in watts/100 (zero padded at left)	
                                    ; #        yyy = model variants (if not defined, space padded to the right)

use constant  EEP_NETWRK_ID    => 0x88 ; # Network ID in ASCII (not used) - space padded
use constant  EEP_NETWRK_NAME  => 0x90 ; # Network Name in ASCII (not used) - space padded
use constant  EEP_SERIAL_NUM   => 0x98 ; # Serial Number in ASCII - zero padded (at left)

use constant  EEP_PROD_DATE    => 0xA0 ; # 
use constant  EEP_PROD_YEAR    => 0xA0 ; # Year of production (add 2000)
use constant  EEP_PROD_WEEK    => 0xA1 ; # Week of production [0÷51]
use constant  EEP_PROD_HW_VER  => 0xA2 ; # Hardware version: 2 nibbles ?  Major and Minor number.
use constant  EEP_PROD_BRD_VER => 0xA3 ; # Board version: 2 nibbles ?  Major and Minor number.
use constant  EEP_PROD_FW_VER  => 0xA4 ; # Firmware version: 2 nibbles ?  Major and Minor number.
use constant  EEP_PROD_FW_SVER => 0xA5 ; # Firmware SUBversion: 2 nibbles ?  Major and Minor number.
use constant  EEP_PROD_BTTRS   => 0xA6 ; # number of batteries installed in the UPS


use constant  EEP_BATT_SUBST   => 0xA8 ; # battery replacement block
use constant  EEP_BATT_YEAR_0  => 0xA8 ; # Year of 1st battery replacement (add 2000)
use constant  EEP_BATT_WEEK_0  => 0xA9 ; # Week of 1st battery replacement
use constant  EEP_BATT_YEAR_1  => 0xAA ; # Year of 2nd battery replacement (add 2000)
use constant  EEP_BATT_WEEK_1  => 0xAB ; # Week of 2nd battery replacement
use constant  EEP_BATT_YEAR_2  => 0xAC ; # Year of 3rd battery replacement (add 2000)
use constant  EEP_BATT_WEEK_2  => 0xAD ; # Week of 3rd battery replacement
use constant  EEP_BATT_YEAR_3  => 0xAE ; # Year of 4th battery replacement (add 2000)
use constant  EEP_BATT_WEEK_3  => 0xAF ; # Week of 4th battery replacement


use constant  EEP_SCHEDULE     => 0xC8 ; # Start of SCHEDULING table
use constant  EEP_SCHEDULE_0   => 0xC8 ; # 1st Scheduling slot
use constant  EEP_SCHEDULE_1   => 0xD0 ; # 2nd Scheduling slot
use constant  EEP_SCHEDULE_2   => 0xD8 ; # 3rd Scheduling slot
use constant  EEP_SCHEDULE_3   => 0xE0 ; # 4th Scheduling slot
use constant  EEP_SCHEDULE_4   => 0xE8 ; # 5th Scheduling slot
use constant  EEP_SCHEDULE_5   => 0xF0 ; # 6th Scheduling slot

#use constant DETACHED_PROCESS => 0 ;   
#-----------------------------------------------------------------------------------
# Constants definition
#-----------------------------------------------------------------------------------
use constant CMD_POLL_DATA => 48 ;
use constant CMD_TIMER => 49 ;
use constant CMD_SCHEDULE => 50 ;
use constant CMD_STDBY => 51 ;
use constant CMD_BEND => 52 ;
use constant CMD_BTEST => 53 ;
use constant CMD_BUZOFF => 54 ;
use constant CMD_POLL_MODEL => 58 ;
#-----------------------------------------------------------------------------------
use constant MAX_CLIENTS => 20 ;

#-----------------------------------------------------------------------------------
# Events constants
#-----------------------------------------------------------------------------------
use constant eveSTART => 0 ;
use constant eveSTOP => 1 ;
use constant eveNOC => 2 ;
use constant eveCOMSTART => 3 ;
use constant eveCOMLOST => 4 ;
use constant eveBMODE => 5 ;
use constant eveBLOW => 6 ;
use constant evePOWON => 7 ;
use constant eveOVERLOAD => 8 ;
use constant eveHTEMP => 9 ;
use constant eveGENFAULT => 11 ;
use constant eve10TOSD => 12 ;
use constant eve1TOSD => 13 ;
use constant eveSYSSD => 14 ;
use constant eveSCHESD => 15 ;
#-----------------------------------------------------------------------------------
use constant eveLINEON => 30 ;
use constant eveSTDBY_CHAR => 31 ;
use constant eveSTDBY_DISCH => 32 ;
use constant eveBEND => 33 ;
use constant eveMSG_BMODE => 34 ;
use constant eveMSG_BLOW => 35 ;
use constant eveBMTEST => 36 ;
use constant eveOLTESTOK => 37 ;
use constant eveOLTESTKO => 38 ;
use constant eveCANCELSCHE => 39 ;
use constant eveLOADOK => 40 ;
use constant eveTEMPOK => 41 ;
#-----------------------------------------------------------------------------------
use constant eveERR_INI => 60 ;
use constant eveERR_MODEL => 61 ;
use constant eveERR_GEN => 62 ;
use constant eveNET_CLOSE => 63 ;
use constant eveNET_NEW => 64 ;
use constant eveERR_TCPCFG => 65 ;
#-----------------------------------------------------------------------------------
use constant eveSDATA => 80 ;
use constant eveSCHEON => 81 ;
use constant evePCOFF => 82 ;
#-----------------------------------------------------------------------------------
# memory handling ...
#-----------------------------------------------------------------------------------
#;;;; use less qw(memory) ;
#;;;; pack(chop(chomp($0))) ;
#-----------------------------------------------------------------------------------
# Warning messages notified to all clients
#-----------------------------------------------------------------------------------
my %MESSAGES = 
	(
	  -1 	=> "Resolver internal error",
     0 	=> "* * * UPS Monitoring Started * * *",
     1 	=> "* * * UPS Monitoring Stopped * * *",
     2 	=> "Unable to communicate with UPS ",
     3 	=> "UPS communication established ",
     4 	=> "UPS communication interrupted ",
     5 	=> "UPS on Battery",
     6 	=> "UPS on Battery Low",
     7 	=> "Mains power restored: UPS on Line",
     8 	=> "UPS output Overload: ",
     9 	=> "Maximum internal temperature reached: ",
     10  => "UPS self test failed",
     11 	=> "UPS generic FAULT",
     12 	=> "WARNING !! The SYSTEM will shutdown within ten minutes",
     13 	=> "WARNING !! The SYSTEM will shutdown within one minute. Save Files, quit Applications.",
     14 	=> ": system shutdown procedure started",
     15 	=> "Starting scheduled system shutdown",
##----------------------------------------------------------------------------------------------------------------------
##----------------------------------------------------------------------------------------------------------------------
     30 	=> "UPS on Line",
     31 	=> "UPS on Standby, charging",
     32 	=> "UPS on Standby, discharging",
     33 	=> "Ups on Battery End",
     34 	=> "WARNING !! The UPS is on BATTERY. The SYSTEM will shutdown within %s seconds. Save Files, quit Applications.",
     35 	=> "WARNING !! The UPS is on BATTERY LOW. The SYSTEM will shutdown within %s seconds. Save Files, quit Applications.",
     36 	=> "UPS on battery: battery test",
     37 	=> "UPS on line: battery test OK",
     38 	=> "UPS on line: battery test KO",
     39 	=> "UPS shutdown scheduling cancelled",
     40 	=> "Output overload ended: load OK",
     41 	=> "UPS internal Temperature OK",
##----------------------------------------------------------------------------------------------------------------------
##----------------------------------------------------------------------------------------------------------------------
     60 	=> "Unable to read the UPS configuration file",
     61 	=> "UPS model not recognized",
     62 	=> "UPSEye error ",
     63 	=> "Network connection closed",
     64 	=> "New connection started with UPS at address ",
     65 	=> "Network module not configured ",
     66 	=> "UPS communication port reset",
     67 	=> "Error opening serial port %s"
     ) ;

my $VERSION      = '0.4.0';                   # program version
my $PROGRNAME    = 'UpsEye server (Perl)' ;   # program name

my $SHOW_HELP    = 0  ;  # if TRUE prints help info
my $SHOW_VERSION = 0  ;  # if TRUE prints the program version
my $DEBUG_FLAG   = 0  ;  # if >0 prints some debug informations (
my $VERBOSE      = 0  ;  # if TRUE, aumenta le informazioni di debugging
my $TEST_FLAG    = -1 ;  # if TRUE apre la pagina memorizzata in questo file
my $LOGGING      = 0  ;
my $DUMP_INFO    = 0  ;  # if TRUE the program will dump all config info

@ISA = qw(Exporter) ;
@EXPORT = qw() ;        
@EXPORT_OK = qw(UPSInit HandleSocket HandleUDP HandleSchedule PollUPS ParseCommandLine CloseSockets 
					 $INTERACTIVE $ups $connection $OS_win HandleExit WriteEventToLog) ;

my $Seconds ;
my $Minutes ;
my $Day ;
my $Hours ;
my $WUDay ;
my $WUHour ;
my $WUMin ;
my $PollCicle = 0;

#-----------------------------------------------------------------------------------
# used to handle the connection with the OSX driver tcp interface
#-----------------------------------------------------------------------------------
my $SckClient ;
my $SckSelect ;
my @SckReady ;

#-----------------------------------------------------------------------------------
# used to handle the connection with the OSX driver tcp interface
#-----------------------------------------------------------------------------------
my $UDPClient ;
my $UDPSelect ;
my @UDPReady ;

my %iniTable; 
my $EventLogCounter ;
my $DataLogCounter ;
my $DataSamples ;
my $tmpDataLogString ;
my $tmpDataLogCounter ;

my @comParms ;         	 						# command parameters 
my @retBuffer ;
my @Parms ;
my $CmdString ;
my $CmdSent ;
my $RetBuff ;  

my @EVENTS = qw(E_START E_STOP E_NOC E_COMSTART E_COMLOST E_BAT E_BLOW E_POWON 
					 E_OVERLOAD E_HTEMP E_TESTKO E_FAULT E_10TOSD E_1TOSD E_SYSSD E_SCHESD) ;

my %EventList = () ;						# event list structure

struct _EVENT =>
	{
	ID 			=> '$',					# event id
	SubID 		=> '$',					# event subid
	Enabled 		=> '$',					# 
	FireEvent	=> '$',					# the event is activated
	Notified 	=> '$',					# the event is notified to all clients
	Settings 	=> '$',					# event settings: the string is composed of five characters;
												# each character is binded to an action can assume one of the
												# following values:
												#	- 0/2: the action is disabled
												#	- 1/3: the action is enabled
												# the action are the following:
												#	- char0: log event
												#	- char1: notify event
												#	- char2: execute command file
												#	- char3: email message
												#	- char4: shutdown the system														
	RunTime		=> '$',					
	Processed	=> '$',					# number of events processed
	LogEna 		=> '$',					# log event flag
	MsgEna 		=> '$',					# notify event flag
	MsgUsers 	=> '@',					# list of the users to notify the message
	MsgRepeat	=> '$',
	MsgDelay		=> '$',
	MsgInterval	=> '$',
	CmdEna	=> '$',						# run command file flag
	CmdFile		=> '$',
	CmdDelay		=> '$',
	EmailEna 	=> '$',					# email message flag
	EmailMsg 	=> '$',					# email message
	EmailDelay 	=> '$',
	EmailUsers 	=> '@',
	ShutdownEna 	=> '$',				# system shutdown flag
	ShutdownDelay 	=> '$',
	ShutdownCmd 	=> '$',
	BlockingMode 	=> '$',				# system blocking commands
} ;


struct _CONNECTION =>
	{
	TcpPort 	=> '$',
	Server 	=> '$',
	NumClients	=> '$',
	Select 	=> '$',
	InBuffer 	=> '%',
	OutBuffer 	=> '%',
	Ready 		=> '%',
	ServerAddr 	=> '$',
	ServerName 	=> '$',
	TmpBuffer   => '%', 
} ;


struct _UPSTIME =>
	{
	Day 	=> '$',
	Hour 	=> '$',
	Min 	=> '$',
	Sec 	=> '$',
} ;

#
# Scheduling structure: each array index of the defines a scheduling
#
struct _SCHEDULE =>
	{
	WeekDay 	=> '@',
	HourOn 	=> '@',
	MinOn 	=> '@',
	HourOff 	=> '@',
	MinOff 	=> '@',
	OutPort  => '@', 
	ForceUpsSD => '@',
	ForcePcSD => '@',
	SDDelay => '$'
	} ;

struct _MEASURES =>
	{
   NominalPower => '$',                # UPS nominal Power
   CommStatus => '$',                  # Communication Status: <>0 = Error
	Counter => '$',                     # Contatore Polling
	RunTime => '$',							# battery runtime
	State => '$',								# state (long)
	OldState => '$',						   # Old state (long)
	ShortStatus => '$',                 # short status
	OutConf => '$',                     # output status
	WeekDay => '$',                     # Week Day (from UPS)
	Hour => '$',                        # Hours (from UPS)
	Min => '$',                         # Minutes (from UPS)
	Sec => '$',                         # Seconds (from UPS)
	Vin 	=> '$',								# input voltage (VAC)
	Vout 	=> '$',								# output voltage (VAC)
	Vbat 	=> '$',								# battery voltage (VCC)
   Iout => '$',                        # Output current (A)
   Load 	=> '$',								# UPS Load (VA)
   LoadPerc => '$',                    # UPS Load in percent (%)
	InFreq 	=> '$',							# input frequency
	OutFreq 	=> '$',							# output frequency

	Temperature 	=> '$',					# temperature (°C)
   BattMin => '$',                     # Minimum battery level (VCC)
   BattPerc => '$',                    # Battery charge in percent (%)
   BattLowPerc => '$',                 # Battery LOW in percent (%)
   RtimeEmpty => '$',                  # Runtime to Empty (sec)
   PercEmpty => '$',                   # Percent to Empty (%) 0=discharged, 100=Charged
   BattRuntime => '$',                 # UPS time in battery mode (sec)
   UpsRuntime => '$',                  # UPS time in standard mode (min)
   OutStatus => '$',                   # Status of the output ports
   Icharger => '$',                    # Charger current (A)

	RawData => '@',							# 
	} ;

struct _UPSMODEL =>
	{
	EnterpriseFamily => '$',            # 0: MicroDowell, 1: CHROMA, 2: CYBERPOWER, 3: DRY CONTACTS
	IsEnterprise => '$',            		# Enterprise models ST
	IsInteractive => '$',            	# Interactive models ST
	IsHiBox => '$',            			# HiBox models ST
	IsOldModel => '$',            		# Old Microdowell models
	GE2KVA => '$',                      # UPS >= 2000VA
   Model => '$',                       # UPS model
   SerialNumber => '$',                # UPS Serial number
   NetID => '$',                       # network S/N
   NetName => '$',                     # Network name
	Type => '$',								# UPS type
	FwVersion 	=> '$',						# FW release
	FwsubVersion => '$',                # Firmware SUBversion
	FWMajor 	=> '$',							# Major version
	FWMinor 	=> '$',							# Minor version
	BattNumber => '$',   					# battery number
	ExBattNumber => '$',   					# external battery number
	BatteryAH => '$',	   					#CYP, battery AH
	BatteryDC => '$',	   					#CYP, battery rating
	BLowT => '$',	   						#CYP, battery low thr.
	BMaxT => '$',	   						#CYP, battery max thr.
	BatteryPackVoltage => '$',				#CHR battery pack voltage
	VinFault => '$',							#CHR Vin FAULT
	YearProd => '$',     					# year of production
	MonthProd => '$',    					# month of production
	DayProd => '$',      					# day of production
	HWver => '$',        					# Hardware version
	BRDver => '$',       					# Board version
	} ;

struct _UPS =>
	{
	Name			=> '$',						# UPS name
	Line			=> '$',						# UPS Line (Interactive, HiBox, Pro, Enterprise)
	StrStatus   => '$',                 # UPS status as string
	Type			=> '$',						# UPS type
	BatteryType	=> '$',						# battery type
	TScale		=> '$',						# temperature scale 
	Com 			=> '$',						# com port structure reference
	ComState 	=> '$',						# 0: startup, 1: polling data, 2: no communication
	SerialPort	=> '$',
	SerialSpeed	=> '$',
	InBuffer 	=> '$',
	OutBuffer 	=> '$',
	Command 		=> '@',
	LangID		=> '$',						# UPS language interface
	PollState 	=> '$',
	PollCmd 		=> '$',
	PollInterval	=> '$',					# polling interval
	RxTimeout 	=> '$',
	TxTimeout 	=> '$',
	ErrCount		=> '$',						# number of polling without answer

	EventLog	=> '$',
	ELMaxSize   => '$',
	DataLog	=> '$',
	DLMaxSize   => '$',
	DelayReg	=> '$',
 	
 	MsgFile     => '$',         			# file for message delivery
 	MsgCmd      => '$',         			# command to delivery message

	Model			=> '_UPSMODEL',			# UPS model struct reference
	Time 			=> '_UPSTIME',				# UPS timer struct reference
	Schedule 	=> '_SCHEDULE',			# UPS schedule struct reference
	Measures 	=> '_MEASURES',			# UPS measures struct reference
} ;

#-----------------------------------------------------------------------------------
# STARTUP
#-----------------------------------------------------------------------------------
   BEGIN 
      {
      $OS_OSX = ($^O eq "darwin") ? 1 : 0 ;
      $OS_win = ($^O eq "MSWin32") ? 1 : 0 ;
      
      # This must be in a BEGIN in order for the 'use' to be conditional
      if ($OS_OSX) 
         {
         # print "Loading OSX module\n" ;
         eval "use constant DETACHED_PROCESS => 0" ;   
         die "$@\n" if ($@) ;
         }
      else 
         {
         if ($OS_win)
            {
            # print "Loading Windowx module\n" ;
            eval "use Win32::SerialPort" ;
            die "$@\n" if ($@) ;
            eval "use Win32::Process" ;
            die "$@\n" if ($@) ;
            }
         else
            {
            eval "use constant DETACHED_PROCESS => 0" ;   
            die "$@\n" if ($@) ;
         
            # print "Loading Unix module Device::SerilaPort \n" ;
            eval "use Device::SerialPort" ;
            die "$@\n" if ($@) ;
            }
         }
      } # End BEGIN

#-----------------------------------------------------------------------------------
# UPSInit: initializes the UPS master program.
# It returns 1, if everything is good, 0 otherwise
#-----------------------------------------------------------------------------------
sub UPSInit (@) 
{
	my $i;
	my $section ;
	my $Command ;
	
   ($INIFILE, $PROGPATH) = @_ ;
	$ups = _UPS->new ;
	$UpsSchedule = _SCHEDULE->new ;
	$ups->{PollState} = 0 ; 
	#GGGG $ups->{PollInterval} = ((localtime)[0] + 8) % 60 ;
	$ups->{ComState} = UPS_COMSTATE_INIT_MODE ; 
	$ups->{ErrCount} = 0 ;
	$ups->{Measures}->{Counter} = 0 ;
	$ups->{Measures}->{State} = 0x80000000 ;
	$ups->{Measures}->{OutConf} = 0 ;
	$ups->{Time}->{Day} = 0 ;
	$ups->{Time}->{Hour} = 0 ;
	$ups->{Time}->{Min} = 0 ;
	$ups->{Time}->{Sec} = 0 ;

	#UPS model
   $ups->{Model}->{FwMajor}= 0;
   $ups->{Model}->{FwMinor}= 0;
   $ups->{Model}->{Model} = "UNKNOWN " ;
   $ups->{Model}->{NetID} = "UNKNOWN " ;
   $ups->{Model}->{NetName} = "UNKNOWN " ;
   $ups->{Model}->{SerialNumber} = "UNKNOWN " ;
	$ups->{Model}->{YearProd} = 0 ;
	$ups->{Model}->{MonthProd} = 0 ;
	$ups->{Model}->{DayProd}   = 0 ;
	$ups->{Model}->{HWver} = 0 ;
	$ups->{Model}->{BRDver} = 0 ;
	$ups->{Model}->{FwsubVersion} = 0 ;
	$ups->{Model}->{FwVersion} = 0 ;
	$ups->{Model}->{BattNumber} = 0 ; 

   
	$ups->{Model}->{IsEnterprise} = 0 ;
	$ups->{Model}->{IsHiBox} = 0 ;
	$ups->{Model}->{IsInteractive} = 0 ;							
   
	# Measures
   $ups->{Measures}->{ShortStatus} = 0 ;
	$ups->{Measures}->{LoadPerc} = 0 ;
	$ups->{Measures}->{Vin} = 0 ;
	$ups->{Measures}->{Vbat} = 0 ;
	$ups->{Measures}->{Vout} = 0 ;
   $ups->{Measures}->{Iout} = 0 ;
   $ups->{Measures}->{Temperature} = 0 ;
   $ups->{Measures}->{Icharger} = 0 ;
	# Config
   $ups->{Measures}->{BattMin} = 0 ;
   $ups->{Measures}->{UpsRuntime} = 0 ;
   $ups->{Measures}->{BattRuntime} = 0 ;
   $ups->{Measures}->{BattLowPerc} = 0 ;
	$ups->{Measures}->{OutStatus} = 0 ;				
	# Battery status
   $ups->{Measures}->{BattPerc} = 0 ;
   $ups->{Measures}->{RtimeEmpty} = 0 ;
   $ups->{Measures}->{RtimeEmpty} = 0 ;
   $ups->{Measures}->{PercEmpty} = 0 ;
   $ups->{Measures}->{Load} = 0 ;
   $ups->{Measures}->{OutFreq} = 0 ;
   $ups->{Measures}->{InFreq} = 0 ;
		      
	($Seconds, $Minutes, $Hours) = (localtime)[0,1,2] ;
	unless (ReadIniFile(\%iniConfig,$INIFILE))
	   {
	   WriteEventToLog (eveERR_INI, 0) ;
      sleep (2) ;
	   die "Error occured while reading $INIFILE: $@";
   	}
   if ($iniConfig{UpsParams}{Port} =~ m/usb/)
		{ 
		if (($OS_win eq 0) && ($OS_OSX eq 0))
			{
			# OS is Unix/Linux
			if ($PROGPATH)
	   		{
	   		$Command = $PROGPATH . "BBoxDriver";
	   		}
	   	else
	   		{
				$Command = " ./BBoxDriver";
	   		}
			system($Command) ;			
			$OS_OSX = 1 ;
			sleep (5);
			}
		}
	$WUDay = 0 ;
	$WUHour = 0 ;
	$WUMin  = 0 ;

#-----------------------------------------------------------------------------------
# serial port configuration
#-----------------------------------------------------------------------------------
	for ($i=0; $i<6; $i++)
		{
		$section = "Schedule" . $i ;
		$ups->{Schedule}->{WeekDay}[$i] = (split /,/,$iniConfig{Schedule}{"$section"})[0] ;
		$ups->{Schedule}->{HourOn}[$i] = (split /,/,$iniConfig{Schedule}{"$section"})[1] ;
		$ups->{Schedule}->{MinOn}[$i] = (split /,/,$iniConfig{Schedule}{"$section"})[2] ;
		$ups->{Schedule}->{HourOff}[$i] = (split /,/,$iniConfig{Schedule}{"$section"})[3] ;
		$ups->{Schedule}->{MinOff}[$i] = (split /,/,$iniConfig{Schedule}{"$section"})[4] ;
		$ups->{Schedule}->{OutPort}[$i] = (split /,/,$iniConfig{Schedule}{"$section"})[5] ;
		$ups->{Schedule}->{ForceUpsSD}[$i] = (split /,/,$iniConfig{Schedule}{"$section"})[6] ;
		$ups->{Schedule}->{ForcePcSD}[$i] = (split /,/,$iniConfig{Schedule}{"$section"})[7] ;
		}
	$ups->{Schedule}->{SDDelay} = $iniConfig{Schedule}{SDDelay} ;
	$ups->{Name}= $iniConfig{UpsList}{UpsModel} ;
	$ups->{Type}= $iniConfig{UpsParams}{UpsType} ;
	$ups->{BatteryType}= $iniConfig{UpsMDW}{TipoBat} ;
	$ups->{TScale}= $iniConfig{UpsMDW}{TempScale} ;
	$ups->{MsgFile}= $iniConfig{UpsMDW}{MsgFile} ;
	$ups->{MsgCmd}= $iniConfig{UpsMDW}{MsgCmd} ;
	$ups->{EventLog}= $iniConfig{UpsMDW}{EventLog} ;
	if ($PROGPATH)
	   {
	   $ups->{EventLog} = $PROGPATH . $ups->{EventLog}
	   }
	$ups->{ELMaxSize}= $iniConfig{UpsMDW}{ELMaxSize} ;
	$EventLogCounter = 0 ;
	$DataLogCounter = 0 ;
	$DataSamples = 0 ;
	$tmpDataLogCounter = 0 ;
	$ups->{DataLog}= $iniConfig{UpsMDW}{DataLog} ;
	if ($PROGPATH)
	   {
	   $ups->{DataLog} = $PROGPATH . $ups->{DataLog}
	   }
	$ups->{DLMaxSize}= $iniConfig{UpsMDW}{DLMaxSize} ;
	$ups->{DelayReg}= $iniConfig{UpsMDW}{DelayReg} ;
   CheckEventLogSize ();
   CheckDataLogSize ();

   $_ = $iniConfig{UpsParams}{Language} ;
   /I/i && do     				
			{ $ups->{LangID} = 0 ;} ;
   /E/i && do     				
			{ $ups->{LangID} = 1 ;} ;
   /D/i && do     				
			{ $ups->{LangID} = 2 ;} ;
   /F/i && do     				
			{ $ups->{LangID} = 3 ;} ;
         
#-----------------------------------------------------------------------------------
# server socket initialization
#-----------------------------------------------------------------------------------
	$connection = _CONNECTION->new ;
  
	$connection->{Server} = IO::Socket::INET->new(LocalPort => $iniConfig{UpsParams}{TcpIpPort},
										  Timeout   => 5,	
                                Listen    => 10 )
  									or die "Can't make server socket: $@\n";

	$connection->{ServerName} = hostname();
	if (defined (gethostbyname ($connection->{ServerName})))
		{
	   $connection->{ServerAddr} = gethostbyname ($connection->{ServerName});
	   $connection->{ServerAddr} = inet_ntoa ($connection->{ServerAddr});
		}
	else
		{ $connection->{ServerAddr} = "127.0.0.1"; }
		    
	tie %{$connection->{Ready}}, 'Tie::RefHash';

	$connection->{Select} = IO::Select->new($connection->{Server});
	$connection->{NumClients} = 0 ;
   if ($OS_OSX) 
      {
      ConnectHIDDriver () 
      }
	ConnectUDP () ;

#-----------------------------------------------------------------------------------
# ... now i try to identify the UPS connected
#-----------------------------------------------------------------------------------
	LoadEvents() ;
	$EventList{E_START}->{FireEvent} = 1 ;
	CheckEvents ();
	if (not IdentifyUPSModel())
   	{
		#-----------------------------------------------------------------------------------
   	# The UPS has not been detected
		#-----------------------------------------------------------------------------------
		$EventList{E_NOC}->{FireEvent} = 1 ;
		$EventList{E_NOC}->{SubID} = 0 ;
   	} 
   else 
   	{   	   
      # The UPS has been detected, i start configuration ...
		} 	
		
	# check to see if i must daemonize the program
	$ups->{PollInterval} = ((localtime)[0] + 2) % 60 ;
	if ($INTERACTIVE eq 0)
		{
		Demonize() ;
		}
}

#-----------------------------------------------------------------------------------
####################################################################################
# UPS COMMUNICATION
####################################################################################
#-----------------------------------------------------------------------------------

#-----------------------------------------------------------------------------------
# OpenSerialPort: opens a serial port
#	- <param0>: port name
#	- <param1>: baudrate 
#-----------------------------------------------------------------------------------
sub OpenSerialPort ($ $ $)
{
	my $stopbits ;
	
	if ($ups->{Com})
		{
		$ups->{Com}->close ;
		undef $ups->{Com} ;
		}
   $ups->{SerialPort} = $_[0] ;
	$ups->{SerialSpeed} = $_[1] ;
	$stopbits = $_[2] ;
   
   if ($OS_OSX) 
      {
      }
   else 
      {
      if ($OS_win) 
         {
         $ups->{Com} = Win32::SerialPort->new ($ups->{SerialPort},1) ;
         }
      else 
         {
         $ups->{Com} = Device::SerialPort->new ($ups->{SerialPort},1) ;
         }
      die "Can't open serial port $ups->SerialPort: $^E\n" unless ($ups->{Com}) ;
      
      $ups->{Com}->handshake("none")	      	
      			|| die "$ups->{SerialPort}: bad handshake" ;
      $ups->{Com}->baudrate($ups->{SerialSpeed})			
      			|| die "$ups->{SerialPort}: bad baudrate" ;
      $ups->{Com}->parity('none')		      	
      			|| die "$ups->{SerialPort}: bad parity" ;
      $ups->{Com}->databits(8)		        
      			|| die "$ups->{SerialPort}: bad databits" ;

      $ups->{Com}->stopbits($stopbits)		        
      			|| die "$ups->{SerialPort}: bad stopbits" ;

      $ups->{Com}->buffers (1024,1024) 			
      			|| die "$ups->{SerialPort}: bad buffermax" ;
      $ups->{Com}->write_settings		      	
      			|| undef $ups->{Com} ;
      unless ($ups->{Com})                 
      			{ die "$ups->{SerialPort}: couldn't write_settings" ; }
   
   	if ($ups->{Model}->{EnterpriseFamily} eq 2)
			{
			$ups->{Com}->rts_active(0);
			$ups->{Com}->dtr_active(1);
	
			$ups->{Com}->lookclear ;
			$ups->{Com}->write("\r");
			sleepex(150) ;
			$ups->{Com}->lookclear ;
			$ups->{Com}->write("\r");
			sleepex(150) ;
			}
		else
			{
	   	$ups->{Com}->rts_active(0);
	   	$ups->{Com}->dtr_active(0);
	   	sleepex(500) ;
	   	}
   	}
}


#-----------------------------------------------------------------------------------
# ReadWriteUPS: handles the serial communication with the UPS, and decodes 
# received data
#-----------------------------------------------------------------------------------
sub ReadWriteUPS()
{
   my $done = 0 ;
   my ($CmdID, $CmdLen, @Parms, $i, @Cmd, $event, $item, @q) ;
   
   $_ = $ups->{PollState} ;
   
   SWITCH: 
   	{
   	/^0/ && do 
   				{
					if ($ups->{ComState} == UPS_COMSTATE_POLL_MODE)
						{
						# The communication is OK, first of all i check for commands to send, then
						# i poll the UPS.
						@q = () ;
                  foreach $item (@{$ups->{Command}}) 
                     {
                     if ($item eq ":")
                        { 
                        if (SendCmdUPS($q[0], $q[1], @q[2..@q-1]) < 0)
            			   	{ $ups->{ErrCount}++ ; } 
            			   else 
            			   	{ $ups->{ErrCount} = 0 ; }
                        @q = () ;
                        }
                     else
                        {
                        push @q, $item ;
                        }
                     }
                  @{$ups->{Command}} = () ;
						}   				
						
				   # not reached next poll: exit
   				if ((localtime)[0] != $ups->{PollInterval})
   					{ return 1 ; }
   					
   				$ups->{PollInterval} = ((localtime)[0] + 2 ) % 60 ; 
   				#$ups->{PollState}++ ;

               # is the communication OK?
					if ($ups->{ComState} == UPS_COMSTATE_POLL_MODE)
						{
						# communication OK: Poll the UPS
					   if (SendCmdUPS(CMD_GET_STATUS, LEN_GET_STATUS) < 0)
					   	{ $ups->{ErrCount}++ ; } else { $ups->{ErrCount} = 0 ; }
						
						if ($ups->{Model}->{EnterpriseFamily} eq 0)
							{
							if ($ups->{Model}->{IsOldModel} eq 0)
								{
		                  if ($PollCicle == 0)
		                     {
		                     if (SendCmdUPS(CMD_GET_MEASURES, LEN_GET_MEASURES) < 0)
							      	{ $ups->{ErrCount}++ ; } else { $ups->{ErrCount} = 0 ; }
		                     }
		                  elsif ($PollCicle == 1)
		                     {
		                     if (SendCmdUPS(CMD_GET_CONFIG, LEN_GET_CONFIG) < 0)
							      	{ $ups->{ErrCount}++ ; } else { $ups->{ErrCount} = 0 ; }
							      }
		                  elsif ($PollCicle == 2)
		                     {
		                     if (SendCmdUPS(CMD_GET_BATT_STAT, LEN_GET_BATT_STAT) < 0)
							      	{ $ups->{ErrCount}++ ; } else { $ups->{ErrCount} = 0 ; } 
							      }
							   $PollCicle = ($PollCicle + 1) % 3 ;
					   		}
					   	}
                  ComRestored() ;					   
						}
					else
						{
						#-----------------------------------------------------------------------------------
						# There is no comunication; first of all i must detect the UPS connected
						#-----------------------------------------------------------------------------------
						if ($ups->{ComState} eq UPS_COMSTATE_COMM_LOST) 
							{
							if ($ups->{ErrCount}++ % 10 == 0)
								{
	                  	if (IdentifyUPSModel())
	   	                  { 
		                     $ups->{ErrCount} = 0 ; 
		                     ComRestored() ;
									}
								} 
							}
						elsif ($ups->{ComState} eq UPS_COMSTATE_INIT_MODE)
							{
							if ($ups->{ErrCount} eq 0)
								{
	                     $ups->{ErrCount} = 0 ; 
	                     ComRestored() ;
								}
							else
								{
								$ups->{ComState} = UPS_COMSTATE_COMM_LOST ;
								} 
							}
						else
							{
	                  if ($ups->{ErrCount} == 0)
	                     { ComRestored() ; }
							}
						}

   				if ($ups->{ErrCount} > 40)
   					{
						if ($ups->{ComState} eq UPS_COMSTATE_INIT_MODE)
							{
   						$EventList{E_NOC}->{SubID} = eveERR_MODEL ;
							$EventList{E_NOC}->{FireEvent} = 1 ;
							}
						elsif ($ups->{ComState} eq UPS_COMSTATE_POLL_MODE)
							{
							foreach $event (keys %EventList)
								{
								$EventList{$event}->{FireEvent} = 0 ;
								$EventList{$event}->{SubID} = 0 ;
								$EventList{$event}->{RunTime} = 0 ;
								}
   						$EventList{E_COMLOST}->{SubID} = 0 ;
							$EventList{E_COMLOST}->{FireEvent} = 1 ;
							$ups->{Model}->{IsEnterprise} = 0 ;
							$ups->{Model}->{IsHiBox} = 0 ;
							$ups->{Model}->{IsInteractive} = 0 ;							
							}
						elsif ($ups->{ComState} eq UPS_COMSTATE_COMM_LOST)
							{
							}
						$ups->{StrStatus} = getmessage(eveCOMLOST);
						$ups->{ComState} = UPS_COMSTATE_COMM_LOST ;
   					$ups->{Measures}->{State} = 0x80000000 ;   
     					$ups->{PollState}++ ;
   					}												
   				last SWITCH ;
   				} ;
   	/^1/ && do 
   				{
   				$ups->{PollState} = 0 ;
   				last SWITCH ;
   				} ;
   	} 
}

#-----------------------------------------------------------------------------------
#-----------------------------------------------------------------------------------
sub CreateCmd_Enterprise(@)
{
   my $NumParms = @_ - 2 ;
   my $CmdID = $_[0] ;
   my $CmdLen = $_[1] ;
   my $i ;
   my $Xor ;
      
   @Parms = @_[2..@_] ;

   if ($OS_OSX) 
      {
      $CmdString = sprintf("%02X %02X %02X", 91, $CmdLen, $CmdID) ;
      $Xor = $CmdLen;
      $Xor = $Xor ^ $CmdID;
      # aggiungo gli eventuali parametri ausiliari alla stringa del comando da spedire
      for ($i=0 ; $i<$NumParms ; $i++)
         {
         $CmdString = $CmdString . sprintf(" %02X", $Parms[$i]);
         $Xor = $Xor ^ int($Parms[$i]) ;
         }

      $CmdString = $CmdString . sprintf(" %02X\r\n", $Xor) ;
      }
   else
      {
      # creo la stringa col comando da spedire
      $CmdString = "[" . chr($CmdLen) . chr($CmdID) ;
   
      # aggiungo gli eventuali parametri ausiliari alla stringa del comando da spedire
      for ($i=0 ; $i<$NumParms ; $i++)
         {
         $CmdString = $CmdString . chr($Parms[$i]) ;  
         }

      # calcolo il checksum (Xor)
      $Xor = vec($CmdString, 1, 8) ;   # inizializzo il checksum
      for ($i=2 ; $i<$CmdLen+2 ; $i++)
         {
         $Xor = $Xor ^ vec($CmdString, $i, 8) ;
         }
      $CmdString = $CmdString . chr($Xor) ;
      }  
	return ($CmdString) ;
}

sub CreateCmd_OldModels(@)
{
   my $NumParms = @_ - 2 ;
   my $CmdID = $_[0] ;
   my $CmdLen = $_[1] ;
   my $i ;
   my $Xor ;
      
   @Parms = @_[2..@_] ;
	$CmdString = "" ;
#-----------------------------------------------------------------------------------
# Polling command
#-----------------------------------------------------------------------------------
   if ($CmdID eq CMD_GET_STATUS)
      {
      if ($OS_OSX) 
         {
         $Xor = 1 ^ CMD_POLL_DATA;
         $CmdString = sprintf("%02X %02X %02X %02X\r\n", 91, 1, CMD_POLL_DATA, $Xor) ;
         }
      else
         {
         $CmdString = chr(CMD_POLL_DATA) ;
         }
      }
   
#-----------------------------------------------------------------------------------
# UPS set timer command
#-----------------------------------------------------------------------------------
   if ($CmdID eq CMD_SET_TIMER)
      {
      if ($OS_OSX) 
         {
         $Xor = 4 ^ CMD_TIMER;
         $Xor ^= ($Parms[0] * 24 + $Parms[1]);
         $Xor ^= $Parms[2];
         $Xor ^= $Parms[3];
         $CmdString = sprintf("%02X %02X %02X %02X %02X %02X %02X\r\n", 
                              91, 4, CMD_TIMER, 
                              ($Parms[0] * 24 + $Parms[1]),
                              $Parms[2], $Parms[3], $Xor) ;
         }
      else
         {
         $CmdString = chr(CMD_TIMER) . chr($Parms[0] * 24 + $Parms[1]) .
      					 chr($Parms[2]) . chr($Parms[3]);
         }
      }

#-----------------------------------------------------------------------------------
# UPS scheduling command
#-----------------------------------------------------------------------------------
   if ($CmdID eq CMD_SET_SCHEDULE)
      {
      my $SDDelay  = $ups->{Schedule}->{SDDelay};
      my $FwVersion = $ups->{Model}->{FwMajor} * 10 + $ups->{Model}->{FwMinor} ;

      if ($ups->{Model}->{Model} =~ m/(B5|MD)/i)
         {
         if ($FwVersion >= 30)
            { $SDDelay /= 2 ;}
         }
      else
         {
         if ($FwVersion >= 40)
            { $SDDelay /= 4 ;}
         }			
		
      if ($Parms[2] ne 255)
         {
         if ($OS_OSX) 
            {
	         $Xor = 6 ^ CMD_SCHEDULE;
	         $Xor ^= ($Parms[1] * 24 + $Parms[2]);
	         $Xor ^= $Parms[3];
	         $Xor ^= ($Parms[1] * 24 + $Parms[2] + 1);
	         $Xor ^= $SDDelay;
	         $Xor ^= 48;
            $CmdString = sprintf("%02X %02X %02X %02X %02X %02X %02X %02X %02X\r\n", 
                                 91, 6, CMD_SCHEDULE, ($Parms[1] * 24 + $Parms[2]),
                                 $Parms[3], ($Parms[1] * 24 + $Parms[2] + 1), 
                                 $SDDelay, 48, $Xor) ;
            }
         else
            {
	   	   $CmdString = chr(CMD_SCHEDULE) . chr($Parms[1] * 24 + $Parms[2]) . chr($Parms[3]) .
      					 	 chr($Parms[1] * 24 + $Parms[2] + 1) . chr($SDDelay) . chr(48) ;
            }
         }
      else
         {
         # there is no scheduling
         if ($OS_OSX) 
            {
	         $Xor = 6 ^ CMD_SCHEDULE;
	         $Xor ^= 255;
	         $Xor ^= 0;
	         $Xor ^= 255;
	         $Xor ^= $SDDelay;
	         $Xor ^= 48;
            $CmdString = sprintf("%02X %02X %02X %02X %02X %02X %02X %02X %02X\r\n", 
                                 91, 6, CMD_SCHEDULE, 255, 0, 255, 
                                 $SDDelay, 48, $Xor) ;
            }
         else
            {
            $CmdString = chr(CMD_SCHEDULE) . chr(255) . chr(0) .
      					 	 chr(255) . chr($SDDelay) . chr(48) ;
            }
         }
      }

#-----------------------------------------------------------------------------------
# UPS shutdown command
#-----------------------------------------------------------------------------------
   if ($CmdID eq CMD_SD_ONESHOT)
      {
      if ($Parms[1] eq 2)
         {
         if ($OS_OSX) 
            {
	         $Xor = (2 ^ CMD_STDBY) ^ CMD_BEND;
            $CmdString = sprintf("%02X %02X %02X %02X %02X\r\n", 91, 2, CMD_STDBY, CMD_BEND, $Xor) ;
            }
         else
            {
            $CmdString = chr(CMD_STDBY) . chr(CMD_BEND) ;
            }
         }
      else
         {
         if ($OS_OSX) 
            {
	         $Xor = 1 ^ CMD_STDBY;
            $CmdString = sprintf("%02X %02X %02X %02X\r\n", 91, 1, CMD_STDBY, $Xor) ;
            }
         else
            {
            $CmdString = chr(CMD_STDBY) ;
            }
         }
      }

#-----------------------------------------------------------------------------------
# UPS model detection
#-----------------------------------------------------------------------------------
   if (($CmdID eq CMD_GET_EEP_BLOCK) && ($Parms[0] eq EEP_UPS_MODEL))
      {
      if ($OS_OSX) 
         {
         $Xor = 1 ^ CMD_POLL_MODEL;
         $CmdString = sprintf("%02X %02X %02X %02X\r\n", 91, 1, CMD_POLL_MODEL, $Xor) ;
         }
      else
         {
         $CmdString = chr(CMD_POLL_MODEL) ;
         }
      }
	return ($CmdString) ;
}

sub CreateCmd_CHR(@)
{
   my $NumParms = @_ - 2 ;
   my $CmdID = $_[0] ;
   my $CmdLen = $_[1] ;
   my $i ;
   my $Xor ;
   my $tmpString ;
   my $intValue;
      
   @Parms = @_[2..@_] ;
	$CmdString = "" ;

#-----------------------------------------------------------------------------------
# Polling command
#-----------------------------------------------------------------------------------
   if ($CmdID eq CMD_GET_STATUS)
      {
      $CmdString = "Q1\r" ;
      $CmdLen = 3 ;
      }
#-----------------------------------------------------------------------------------
# UPS model detection
#-----------------------------------------------------------------------------------
   if (($CmdID eq CMD_GET_EEP_BLOCK) && ($Parms[0] eq EEP_UPS_MODEL))
      {
      $CmdString = "I\r" ;
      $CmdLen = 2 ;
      }

#-----------------------------------------------------------------------------------
# UPS rating info
#-----------------------------------------------------------------------------------
   if ($CmdID eq CMD_GET_CHR_F)
      {
      $CmdString = "F\r" ;
      $CmdLen = 2 ;
      }

#-----------------------------------------------------------------------------------
# UPS shutdown command: Parms[0] = Port, Parms[1] = Type, Parms[2] = SDDelay,
# Parms[3] = WUDelay
#-----------------------------------------------------------------------------------
   if ($CmdID eq CMD_SD_ONESHOT)
      {
      my $SDDelay = $Parms[2] ;
      my $WUDelay = $Parms[3] ;

      if ($Parms[1] eq 1)
         {			
			if ($SDDelay >= 60)
				{
				$CmdString = sprintf("S%02dR%04d\r", 
											($SDDelay % 60) ? ($SDDelay / 60 + 1) : ($SDDelay / 60),
											($WUDelay % 60) ? ($WUDelay / 60 + 1) : ($WUDelay / 60)) ;
				}
			else
				{
				if ($SDDelay < 12)
					{ $CmdString = sprintf("S.2R%04d\r", ($WUDelay % 60) ? ($WUDelay / 60 + 1) : ($WUDelay / 60)); }
				else
					{
					if ($SDDelay >= 54)
						{ $CmdString = sprintf("S01R%04d\r", ($WUDelay % 60) ? ($WUDelay / 60 + 1) : ($WUDelay / 60)); }
					else
						{
						$CmdString = sprintf("S.%1dR%04d\r", 
													($SDDelay / 6 + 1),
													($WUDelay % 60) ? ($WUDelay / 60 + 1) : ($WUDelay / 60)) ;
						}
					}
				}
      	$CmdLen = 9 ;
      	}

      if ($Parms[1] eq 2)
         {
			if ($SDDelay >= 60)
				{
				$CmdString = sprintf("S%02d\r", ($SDDelay % 60) ? ($SDDelay / 60 + 1) : ($SDDelay / 60));
      		}
      	else
      		{
				if ($SDDelay < 12)
					{ $CmdString = sprintf("S.2\r"); }
				else
					{
					if ($SDDelay >= 54)
						{ $CmdString = sprintf("S01\r"); }
					else
						{
						$CmdString = sprintf("S.%1d\r", 
													($SDDelay / 6 + 1)) ;
						}
					}
      		}
      		$CmdLen = 4 ;
      	}

      if (($Parms[1] eq 0) || ($Parms[1] eq 255))
			{ 
			$CmdString = sprintf("C\r"); 
			$CmdLen = 2 ;
			}

      }

	if ($OS_OSX) 
   	{
      $tmpString = sprintf("%02X %02X %02X", 91, $CmdLen+1, $CmdID) ;
      $Xor = $CmdLen + 1;
      $Xor = $Xor ^ $CmdID;
      # aggiungo gli eventuali parametri ausiliari alla stringa del comando da spedire
      for ($i=0 ; $i<$CmdLen ; $i++)
         {
         $intValue = vec($CmdString, $i, 8);
         $tmpString = $tmpString . sprintf(" %02X", $intValue);
         $Xor = $Xor ^ $intValue ;
         }

      $CmdString = $tmpString . sprintf(" %02X\r\n", $Xor) ;
		return ($CmdString) ;
   	}
   else   
		{ return ($CmdString) ;}

}

sub CreateCmd_CYP(@)
{
   my $NumParms = @_ - 2 ;
   my $CmdID = $_[0] ;
   my $CmdLen = $_[1] ;
   my $i ;
   my $Xor ;
      
   @Parms = @_[2..@_] ;
	$CmdString = "" ;

#-----------------------------------------------------------------------------------
# Polling command
#-----------------------------------------------------------------------------------
   if ($CmdID eq CMD_GET_STATUS)
      {
      $CmdString = "D\r" ;
      }
#-----------------------------------------------------------------------------------
# UPS model detection
#-----------------------------------------------------------------------------------
   if (($CmdID eq CMD_GET_EEP_BLOCK) && ($Parms[0] eq EEP_UPS_MODEL))
      {
      $CmdString = "P4\r" ;
      }

   if ($CmdID eq CMD_GET_CYP_P3)
      {
      $CmdString = "P3\r" ;
      }

   if ($CmdID eq CMD_GET_CYP_P9)
      {
      $CmdString = "P9\r" ;
      }

#-----------------------------------------------------------------------------------
# UPS shutdown command: Parms[0] = Port, Parms[1] = Type, Parms[2] = SDDelay,
# Parms[3] = WUDelay
#-----------------------------------------------------------------------------------
   if ($CmdID eq CMD_SD_ONESHOT)
      {
      my $SDDelay = $Parms[2] ;
      my $WUDelay = $Parms[3] ;

      if ($Parms[1] eq 1)
         {			
			if ($SDDelay >= 60)
				{
				$CmdString = sprintf("S%02dR%04d\r", 
											($SDDelay % 60) ? ($SDDelay / 60 + 1) : ($SDDelay / 60),
											($WUDelay % 60) ? ($WUDelay / 60 + 1) : ($WUDelay / 60)) ;
				}
			else
				{
				if ($SDDelay < 12)
					{ $CmdString = sprintf("S.2R%04d\r", ($WUDelay % 60) ? ($WUDelay / 60 + 1) : ($WUDelay / 60)); }
				else
					{
					if ($SDDelay >= 54)
						{ $CmdString = sprintf("S01R%04d\r", ($WUDelay % 60) ? ($WUDelay / 60 + 1) : ($WUDelay / 60)); }
					else
						{
						$CmdString = sprintf("S.%1dR%04d\r", 
													($SDDelay / 6 + 1),
													($WUDelay % 60) ? ($WUDelay / 60 + 1) : ($WUDelay / 60)) ;
						}
					}
				}
      	}

      if ($Parms[1] eq 2)
         {
			if ($SDDelay >= 60)
				{
				$CmdString = sprintf("S%02d\r", ($SDDelay % 60) ? ($SDDelay / 60 + 1) : ($SDDelay / 60));
      		}
      	else
      		{
				if ($SDDelay < 12)
					{ $CmdString = sprintf("S.2\r"); }
				else
					{
					if ($SDDelay >= 54)
						{ $CmdString = sprintf("S01\r"); }
					else
						{
						$CmdString = sprintf("S.%1d\r", 
													($SDDelay / 6 + 1)) ;
						}
					}
      		}
      	}

      if (($Parms[1] eq 0) || ($Parms[1] eq 255))
			{ $CmdString = sprintf("C\r"); }
      	
      }

	return ($CmdString) ;
}


sub SendCmd_Enterprise()
{
   my $RxCount ;
   my $done = 0 ;
   my $in ;
   my $DataBuff ;

	$DataBuff = "" ;
   if ($OS_win)   # polling in WINDOWS mode
      { 	
	   $ups->{Com}->lookclear ;                           # flush input buffer
	   $ups->{Com}->write($CmdString) ;                   # send Command
   	$RxCount = $ups->{Com}->read_bg(100);              # read MAX 100 chars
	   $ups->{Com}->read_const_time(250) ;  	            # timeout (100 ms)
      
      # cycle until all chars read (it exits on timeout)
   	for (;;) 
         {
         sleepex(5) ;
         ($done, $RxCount, $DataBuff) = $ups->{Com}->read_done(0);
         last if $done ;
         }
      } 
   else
      {
	   if ($OS_OSX)
         {
   	   $DataBuff = "";
   	   # OSX: the communication layer is the Tcp
	      if ($CmdString && ($SckClient != -1))
		      { 
		      print $SckClient $CmdString ; 
		      my $timeout = 0 ;
            while ($timeout < 20)
               { 
		         my @Ready = $SckSelect->can_read(.01) ;
		         foreach my $fh( @Ready )
      	         { 
			         if ((defined($_ = $fh->getline)))
				         {				
		               # i remove the trailing and leading "\r\n"
	   	            s/\r// ;
   	   	         s/\n// ;
            		   my @Token = split(/ /, $_) ;                 # split of the tokens
                     $DataBuff = "";
                     foreach my $value( @Token )
                        {
                        $DataBuff .= chr(hex($value)); 
                        }
                     }
                  else
                     {
         	   		close ($SckClient) ;   # communication has been lost, i close the socket
   		         	$SckClient = -1 ;
                     }
                  }
               $timeout++ ;
            	}
            }
         }
	   else
         {
   	   # unix but NOT OSX
         $ups->{Com}->lookclear ;
         $ups->{Com}->write($CmdString) ;
         $ups->{Com}->read_const_time(250) ;  				# read timeout 
         $ups->{RxTimeout} = get_tick_count() + 250 ;		# polling timeout (200 ms)
         # cycle until all chars read (it exits on timeout)
         for (;;) 
            {
				sleepex(5) ;
            ($in,$DataBuff) = $ups->{Com}->read(100) ;
            last if (get_tick_count() > $ups->{RxTimeout})
            }
         }
      }
	return $DataBuff ;
}

sub SendCmd_OldModels()
{
   my $RxCount ;
   my $done = 0 ;
   my $in ;
   my $DataBuff ;

   if ($OS_win)   # polling in WINDOWS mode
      { 	
	   $ups->{Com}->lookclear ;                           # flush input buffer
   	$RxCount = $ups->{Com}->read_bg(100);              # read MAX 100 chars
	   $ups->{Com}->write($CmdString) ;                   # send Command
	   my $timeout = get_tick_count() + 150 ;	
	   for (;;) 
         {
         sleepex(5) ;
         ($done, $RxCount, $DataBuff) = $ups->{Com}->read_done(0);
         last if ($DataBuff =~ /^\[(.*)\]/s) ;
         last if (get_tick_count() > $timeout) ;
         }
      } 
   else
      {
	   if ($OS_OSX)
         {
   	   $DataBuff = "";
   	   # OSX: the communication layer is the Tcp
	      if ($CmdString && ($SckClient != -1))
		      { 
		      print $SckClient $CmdString ; 
		      my $timeout = 0 ;
            while ($timeout < 20)
               { 
		         my @Ready = $SckSelect->can_read(.1) ;
		         foreach my $fh( @Ready )
      	         { 
			         if ((defined($_ = $fh->getline)))
				         {				
		               # i remove the trailing and leading "\r\n"
	   	            s/\r// ;
   	   	         s/\n// ;
            		   my @Token = split(/ /, $_) ;                 # split of the tokens
                     $DataBuff = "";
                     foreach my $value( @Token )
                        {
                        $DataBuff .= chr(hex($value)); 
                        }
                     }
                  else
                     {
         	   		close ($SckClient) ;   # communication has been lost, i close the socket
   		         	$SckClient = -1 ;
                     }
                  }
               $timeout++ ;
            	}
            }
         }
	   else
         {
   	   # unix but NOT OSX
         $ups->{Com}->lookclear ;
         $ups->{Com}->write($CmdString) ;
         $ups->{Com}->read_const_time(250) ;  				# read timeout 
         $ups->{RxTimeout} = get_tick_count() + 250 ;		# polling timeout (200 ms)
         # cycle until all chars read (it exits on timeout)
         for (;;) 
            {
				sleepex(5) ;
            ($in,$DataBuff) = $ups->{Com}->read(100) ;
            last if (get_tick_count() > $ups->{RxTimeout})
            }
         }
        #$ups->{Com}->read_const_time(500) ;  			# timeout (500 ms)
      }
	return $DataBuff ;

}

sub SendCmd_CHR()
{
   my $DataBuff ;
   my $i ;
   my $Data ;
	my $foundCR ;

   if ($OS_OSX)
      {
	   $DataBuff = "";
	   # OSX: the communication layer is the Tcp
      if ($CmdString && ($SckClient != -1))
	      { 
	      print $SckClient $CmdString ; 
	      my $timeout = 0 ;
         while ($timeout < 100)
            { 
	         my @Ready = $SckSelect->can_read(.01) ;
	         foreach my $fh( @Ready )
   	         { 
		         if ((defined($_ = $fh->getline)))
			         {				
	               # i remove the trailing and leading "\r\n"
   	            s/\r// ;
	   	         s/\n// ;
         		   $DataBuff = $_ ;
         		   # my @Token = split(/ /, $_) ;                 # split of the tokens
                  # $DataBuff = "";
                  # foreach my $value( @Token )
                  #    {
                  #    $DataBuff .= chr(hex($value)); 
                  #    }
                  }
               else
                  {
      	   		close ($SckClient) ;   # communication has been lost, i close the socket
		         	$SckClient = -1 ;
                  }
               }
            $timeout++ ;
         	}
         }
      }
	else
		{
		$ups->{Com}->lookclear ;                           # flush input buffer
		for ($i=0;$i<length($CmdString);$i++)
			{
			$ups->{Com}->write(substr($CmdString,$i,1)) ;
			sleepex(10) ;
			}
		$ups->{Com}->read_char_time(1) ;  						# read timeout 
		my $timeout = get_tick_count() + 500 ;	
		$DataBuff = "" ;
		for (;;) 
		   {
			$foundCR = 0 ;
		   $Data = $ups->{Com}->read(10) ;
			if (length($Data) > 0)
				{
				$DataBuff .= $Data ;
				for ($i=1; (($i<=length($Data)) && ($foundCR == 0)); $i++)
					{
					if (vec($Data, $i, 8) == 0xd)
						{ $foundCR = 1 ; }
					}
				$ups->{Com}->lookclear ;
				}
			last if ($foundCR == 1) ;
		   last if (get_tick_count() > $timeout) ;
		   sleepex(10) ;
		   }
		}
	return $DataBuff ;
}

sub SendCmd_CYP()
{
   my $DataBuff ;
   my $i ;
   my $Data ;
	my $foundCR ;

	if ($OS_OSX)
   	{
		return "" ; 
		}

	$ups->{Com}->lookclear ;                           # flush input buffer
	for ($i=0;$i<length($CmdString);$i++)
		{
		$ups->{Com}->write(substr($CmdString,$i,1)) ;
		sleepex(10) ;
		}
	$ups->{Com}->read_char_time(1) ;  						# read timeout 
	my $timeout = get_tick_count() + 500 ;	
	$DataBuff = "" ;
	for (;;) 
	   {
		$foundCR = 0 ;
	   $Data = $ups->{Com}->read(10) ;
		if (length($Data) > 0)
			{
			$DataBuff .= $Data ;
			for ($i=1; (($i<=length($Data)) && ($foundCR == 0)); $i++)
				{
				if (vec($Data, $i, 8) == 0xd)
					{ $foundCR = 1 ; }
				}
			$ups->{Com}->lookclear ;
			}
		last if ($foundCR == 1) ;
	   last if (get_tick_count() > $timeout) ;
	   sleepex(10) ;
	   }

	return $DataBuff ;

}

#-----------------------------------------------------------------------------------
# SendCmdUPS: send commands to UPS and decode received data.
#	INPUT
#     - Param0: command type
#	   - Param1: command lenght
#	   - Param2: command parameter list
#
#	OUTPUT
#     - Param0: command lenght
#	   - Param1: command type
#	   - Param2: command parameter list
#-----------------------------------------------------------------------------------
sub SendCmdUPS(@)
{
   @retBuffer = () ;
   $RetBuff = "";
   if ($ups->{Model}->{EnterpriseFamily} eq 0)
      {
		if ($ups->{Model}->{IsOldModel} eq 0)
			{
			$CmdString = CreateCmd_Enterprise (@_) ;
			$RetBuff = SendCmd_Enterprise () ;
			}
		else
			{
			$CmdString = CreateCmd_OldModels (@_) ;
			$RetBuff = SendCmd_OldModels () ;
			}
		}
		
   if ($ups->{Model}->{EnterpriseFamily} eq 1)
      {
		$CmdSent = $_[0] ;
		CreateCmd_CHR (@_) ;
		$RetBuff = SendCmd_CHR () ;
		}

   if ($ups->{Model}->{EnterpriseFamily} eq 2)
      {
		$CmdSent = $_[0] ;
		CreateCmd_CYP (@_) ;
		$RetBuff = SendCmd_CYP () ;
		}

   if (length($RetBuff) > 1)
      {
      if ($ups->{Model}->{EnterpriseFamily} eq 0)
         {
			if ($ups->{Model}->{IsOldModel} eq 0)
         	{
         	# check correct checksum
         	if (CheckCHKSUM(\$RetBuff))          
            	{ 
            	@retBuffer = DecodeCmd(\$RetBuff) ; 
	         	return($retBuffer[0]) ;
            	}
        		else
            	{
            	# uncorrect checksum
            	return(ERR_MSG_CHECKSUM) ; 
            	}
        		}
      	else
         	{
         	@retBuffer = DecodeCmd(\$RetBuff) ;
         	return($retBuffer[0]) ;
         	}
   		}
   	else
   		{
      	@retBuffer = DecodeCmd(\$RetBuff) ;
      	return($retBuffer[0]) ;
   		}
      }	
   else
      { 
		# no chars on the serial port
      return(ERR_COM_NO_CHARS) ; 
      }     
}


#-----------------------------------------------------------------------------------
# CheckCHKSUM
#-----------------------------------------------------------------------------------
sub CheckCHKSUM($)
{
   my $i ;
   my $Xor ;
   my $Buff_r = shift ;   					# received buffer reference
   my $Len = vec($$Buff_r, 1, 8) ;    	# length of buffer

	if ($Len > 0)
		{
	   $Xor = vec($$Buff_r, 1, 8) ;   	# inizializzo il checksum
	   for ($i=2 ; $i<$Len+2 ; $i++)
	      { $Xor = $Xor ^ vec($$Buff_r, $i, 8) ;  }
	   if (vec($$Buff_r, $i, 8) ne $Xor) 
	      { return(0) ; }     	# wrong checksum 
	   else 
	      { return(1) ; }   	# OK
   	}
   else
		{ return(0) ; }     		# wrong checksum 
   
}

#-----------------------------------------------------------------------------------
# DecodeCmd: decodes data received by UPS
#-----------------------------------------------------------------------------------
sub DecodeCmd($)
{
   my $i ;
   my $FwVersion ;
   my $Cmd_r = shift ;    								# command reference
   my $CmdLen = vec($$Cmd_r, 1, 8) ;   			# command length
   my $CmdID = vec($$Cmd_r, 2, 8) ;    			# command ID
   my $NumParms = $CmdLen      ;     				# number of parms in @cmdParms
   my $ge2kVA = $ups->{Model}->{GEK2VA} ; 		# UPS is >= 2000 VA
   
	@comParms = (); 
	if ($ups->{Model}->{EnterpriseFamily} eq 0)
   	{
		if ($ups->{Model}->{IsOldModel} eq 1)
			{
		   if ($OS_OSX)
	         {
			   for ($i = 5 ; $i < length ($$Cmd_r) ; $i++)
			      { push @comParms, vec($$Cmd_r, $i, 8) ; }
	   		$CmdID = vec($$Cmd_r, 2, 8) ;    		# command ID
		   	$NumParms = length($$Cmd_r) - 2 ;		# number of parms in @comParms
		   	if ($CmdID eq CMD_ACK)
		      	{ return(1) ; }
		   	if ($CmdID eq CMD_NACK)
		      	{ return(ERR_PRG_INVALID_DATA, $comParms[0]) ; }
		     	if (DecodeMeasures_OUnits($CmdID) < 0)
		      	{ return(ERR_PRG_INVALID_DATA, $comParms[0]); }	     	
		   	}
		   else
		   	{
			   for ($i = 2 ; $i < length ($$Cmd_r) ; $i++)
			      { push @comParms, vec($$Cmd_r, $i, 8) ; }
				if ((($$Cmd_r =~ /^\[\n(MD|SH|B5)(.*)\]/s) && (length($$Cmd_r) == 8)) ||
					(($$Cmd_r =~/^\[\x01(.*)\]/s) && (length($$Cmd_r) == 16)))
					{
	   			$CmdID = vec($$Cmd_r, 1, 8) ;    		# command ID
		   		$NumParms = length($$Cmd_r) - 2 ;		# number of parms in @comParms
			     	if (DecodeMeasures_OUnits($CmdID) < 0)
			      	{ return(ERR_PRG_INVALID_DATA, $comParms[0]); }	     	
					}
				else
					{
		      	return(ERR_PRG_INVALID_DATA, $comParms[0]) ;
					}
		   	}   
			}
		else
			{
		   # put the parameters in a list...
		   for ($i = 3 ; $i < length ($$Cmd_r) ; $i++)
	   	   { push @comParms, vec($$Cmd_r, $i, 8) ; }
			DecodeCmd_Enterprise ($CmdID) ;
			if ($CmdID eq CMD_NACK)
      		{
      		return(ERR_PRG_INVALID_DATA, @comParms) ;
      		}
			}
		}

	if ($ups->{Model}->{EnterpriseFamily} eq 1)
		{
		push @comParms, $$Cmd_r ;
		if (DecodeCmd_CHR ($CmdSent) < 0)
			{return(ERR_PRG_INVALID_DATA, -1);}
		else
			{
			$NumParms = length ($$Cmd_r) ;
			$CmdID = $CmdSent ;
			}
		}

	if ($ups->{Model}->{EnterpriseFamily} eq 2)
		{
		push @comParms, $$Cmd_r ;
		if (DecodeCmd_CYP ($CmdSent) < 0)
			{return(ERR_PRG_INVALID_DATA, -1);}
		else
			{
			$NumParms = length ($$Cmd_r) ;
			$CmdID = $CmdSent ;
			}
		}

   return($NumParms, $CmdID, @comParms) ;
}

sub DecodeCmd_CYP($)
{
   my $CmdID = $_[0] ;    								# command ID
	my @DataList = () ;

#-----------------------------------------------------------------------------------
# Polling command "D"
#-----------------------------------------------------------------------------------
   if (($CmdID eq CMD_GET_STATUS) || ($CmdID eq CMD_GET_MEASURES) || ($CmdID eq CMD_GET_CONFIG) || ($CmdID eq CMD_GET_BATT_STAT))
      {
		# skip "#" leading char
		$comParms[0] = substr ($comParms[0], 1) ;
		if ($comParms[0] =~ /^I\d{3}\.\dO\d{3}\.\dL/)
			{ 
			DecodeMeasures_CYP($CmdID) ; 
			return(1) ;
			}
		else
		   { return(ERR_PRG_INVALID_DATA, -1); }
     	}

#-----------------------------------------------------------------------------------
# Polling command "P4"
#-----------------------------------------------------------------------------------
   if (($CmdID eq CMD_GET_EEP_BLOCK) && ($Parms[0] eq EEP_UPS_MODEL))
      {
		@DataList = split /,/, $comParms[0] ;
		if ($DataList[0] =~ /^#(\d{3,})VA/)
			{
			$ups->{Measures}->{NominalPower} = int($1) ;
			$ups->{Model}->{Model} = sprintf ("CYP%02d   ", $ups->{Measures}->{NominalPower}/100) ;
			}
		if ($DataList[1] =~ /(\d{1,})\.(\d{1,})/)
			{
         $ups->{Model}->{NetID} = "********" ;
         $ups->{Model}->{NetName} = "********" ;
         $ups->{Model}->{SerialNumber} = "********" ;
      	$ups->{Model}->{YearProd} = 0 ;  					# Year
      	$ups->{Model}->{MonthProd} = 0 ;			      	# Month
      	$ups->{Model}->{DayProd}   = 0 ;       			# Day
      	$ups->{Model}->{HWver} = 0 ;
      	$ups->{Model}->{BRDver} = 0 ;
      	$ups->{Model}->{FwMajor} = $1 ;
      	$ups->{Model}->{FwMinor} = $2 ;
      	$ups->{Model}->{FwsubVersion} = 0 ;
      	$ups->{Model}->{FwVersion} = 0 ;
			}
		return(1) ;
      }

#-----------------------------------------------------------------------------------
# Polling command "P3"
#-----------------------------------------------------------------------------------
   if ($CmdID eq CMD_GET_CYP_P3)
      {
		@DataList = split /,/, substr ($comParms[0], 1) ;
		$ups->{Model}->{BatteryDC} = $DataList[0] ; 
		$ups->{Model}->{BatteryAH} = $DataList[2] ; 
		$ups->{Model}->{BattNumber} = $DataList[1] ; 
		$ups->{Model}->{ExBattNumber} = $DataList[3] ; 
		$ups->{Model}->{BLowT} = $ups->{Model}->{BattNumber} * 11.0 ;
		$ups->{Model}->{BMaxT} = $ups->{Model}->{BattNumber} * 12.8 ;
		return(1) ;
      }

#-----------------------------------------------------------------------------------
# Polling command "P9"
#-----------------------------------------------------------------------------------
   if ($CmdID eq CMD_GET_CYP_P9)
      {
		return(1) ;
      }

#-----------------------------------------------------------------------------------
	return(ERR_PRG_INVALID_DATA, -1);   
}


sub DecodeCmd_CHR($)
{
   my $CmdID = $_[0] ;    								# command ID

#-----------------------------------------------------------------------------------
# Polling command "Q1"
#-----------------------------------------------------------------------------------
   if (($CmdID eq CMD_GET_STATUS) || ($CmdID eq CMD_GET_MEASURES) || ($CmdID eq CMD_GET_CONFIG) || ($CmdID eq CMD_GET_BATT_STAT))
      {
		if ($comParms[0] =~ /^\(\w+/)
			{ 
			# skip "(" leading char
			$comParms[0] = substr ($comParms[0], 1) ;
			DecodeMeasures_CHR($CmdID) ; 
			return(1) ;
			}
		else
		   { return(ERR_PRG_INVALID_DATA, -1); }
     	}
#-----------------------------------------------------------------------------------
# Polling command "I"
#-----------------------------------------------------------------------------------
   if (($CmdID eq CMD_GET_EEP_BLOCK) && ($Parms[0] eq EEP_UPS_MODEL))
      {
		if (length($comParms[0]) < 15)
		   { return(ERR_PRG_INVALID_DATA, -1); }
		
		my $CompanyName =  substr ($comParms[0], 1, 15) ;
		my $UPSModel = substr ($comParms[0], 17, 10) ;
		my $Version = substr ($comParms[0], 28, 10) ;
		if ($UPSModel =~ /ProBox(\D{1,2})(\d{1,})/)
			{
			$ups->{Measures}->{NominalPower} = int($2) * 100 ;
			$ups->{Model}->{Model} = sprintf ("CHR%02d   ", int($2)) ;
			}
		else
		   { return(ERR_PRG_INVALID_DATA, -1); }
		
		if ($Version =~ /Ver.\s(\d{1,})\.(\d{1,})/)
			{
         $ups->{Model}->{NetID} = "********" ;
         $ups->{Model}->{NetName} = "********" ;
         $ups->{Model}->{SerialNumber} = "********" ;
      	$ups->{Model}->{YearProd} = 0 ;  					# Year
      	$ups->{Model}->{MonthProd} = 0 ;			      	# Month
      	$ups->{Model}->{DayProd}   = 0 ;       			# Day
      	$ups->{Model}->{HWver} = 0 ;
      	$ups->{Model}->{BRDver} = 0 ;
      	$ups->{Model}->{FwMajor} = $1 ;
      	$ups->{Model}->{FwMinor} = $2 ;
      	$ups->{Model}->{FwsubVersion} = 0 ;
      	$ups->{Model}->{FwVersion} = 0 ;
			}
		return(1) ;
      }

#-----------------------------------------------------------------------------------
# Polling command "F"
#-----------------------------------------------------------------------------------
   if ($CmdID eq CMD_GET_CHR_F)
      {
		my @DataList = split / /, substr ($comParms[0], 1) ;
		$ups->{Model}->{BatteryPackVoltage} = $DataList[2] ; 
		$ups->{Model}->{BattNumber} = $ups->{Model}->{BatteryPackVoltage} / 12 ;
		$ups->{Measures}->{OutFreq} = $DataList[3] ;
		return(1) ;
      }

	return(ERR_PRG_INVALID_DATA, -1);
}

sub DecodeCmd_Enterprise($)
{
   my $i ;
   my $CmdID = $_[0] ;    								# command ID

   if ($CmdID eq CMD_ACK)
      {
      return(0) ;
      }
   if ($CmdID eq CMD_NACK)
      {
      return(ERR_PRG_INVALID_DATA, @comParms) ;
      }
	
   if (($CmdID eq CMD_GET_STATUS) || ($CmdID eq CMD_GET_MEASURES) || ($CmdID eq CMD_GET_CONFIG) || ($CmdID eq CMD_GET_BATT_STAT))
      {
     	if ($ups->{Model}->{IsEnterprise} eq 1)
     		{
     		DecodeMeasures_ENT($CmdID) ;
     		}
     	if ($ups->{Model}->{IsInteractive} eq 1)
     		{
     		DecodeMeasures_INT($CmdID) ;
     		}
     	if ($ups->{Model}->{IsHiBox} eq 1)
     		{
     		DecodeMeasures_HIB($CmdID) ;
     		}
		}
   if ($CmdID eq CMD_GET_BATT_TEST)
      {
      ;
      }
   if ($CmdID eq CMD_GET_SCHEDULE)
      {
      ;
      }
 
   # ______________________________________________     
   # ---------------- EEPROM read -----------------
   if ($CmdID eq CMD_GET_EEP_BLOCK)
      {
      if ($comParms[0] eq EEP_OPTION_BYTE) # -------- Option Byte -------
         { ; }
         
      if ($comParms[0] eq EEP_UPS_MODEL)  # ------- UPS model -------
         {
         my $pattern ;
         
         $ups->{Model}->{Model} = "" ;
         $ups->{Model}->{GEK2VA} = 0 ;   # default: < 2000 VA
         for ($i=2 ; $i < 10 ; $i++)
            { $ups->{Model}->{Model} = $ups->{Model}->{Model} . chr($comParms[$i]) ; }
         # identify type of UPS
         $pattern = "ENT" ;
         if ($ups->{Model}->{Model} =~ m/$pattern/)
         	{ 
         	$ups->{Model}->{IsEnterprise} = 1;
				$ups->{Model}->{IsHiBox} = 0 ;
				$ups->{Model}->{IsInteractive} = 0 ;							
         	}
         else
         	{
	         $pattern = "INT" ;
	         if ($ups->{Model}->{Model} =~ m/$pattern/)
	         	{ 
	         	$ups->{Model}->{IsEnterprise} = 0 ;
					$ups->{Model}->{IsHiBox} = 0 ;
					$ups->{Model}->{IsInteractive} = 1 ;							
	         	}
	         else
	         	{
		         $pattern = "HIB" ;
	         	if ($ups->{Model}->{Model} =~ m/$pattern/)
		         	{ 
		         	$ups->{Model}->{IsEnterprise} = 0 ;
						$ups->{Model}->{IsHiBox} = 1 ;
						$ups->{Model}->{IsInteractive} = 0 ;							
	      	   	}
	         	}
         	}
         if (chr($comParms[5]) ge '2') 
            { $ups->{Model}->{GEK2VA} = 1 ; }
         }
      if ($comParms[0] eq EEP_NETWRK_ID)  # -------- Network ID (not used) -------
         {
         $ups->{Model}->{NetID} = "" ;
         for ($i=2 ; $i < 10 ; $i++)
            { $ups->{Model}->{NetID} = $ups->{Model}->{NetID} . chr($comParms[$i]) ; }
         }
      if ($comParms[0] eq EEP_NETWRK_NAME) # -------- Network Name (not used) -------
         {
         $ups->{Model}->{NetName} = "" ;
         for ($i=2 ; $i < 10 ; $i++)
            { $ups->{Model}->{NetName} = $ups->{Model}->{NetName} . chr($comParms[$i]) ; }
         }
      if ($comParms[0] eq EEP_SERIAL_NUM) # -------- UPS Serial Number -------
         {
         $ups->{Model}->{SerialNumber} = "" ;
         for ($i=2 ; $i < 10 ; $i++)
            { $ups->{Model}->{SerialNumber} = $ups->{Model}->{SerialNumber} . chr($comParms[$i]) ; }
      
         if (hex($ups->{Model}->{SerialNumber}) == 0)
				{$ups->{Model}->{SerialNumber} = "        ";}
         }
      
      if ($comParms[0] eq EEP_PROD_DATE) # -------- Production Date -------
         {
         $ups->{Model}->{YearProd}  = 2000+$comParms[2] ;  # Year
         $ups->{Model}->{MonthProd} = $comParms[3] ;       # Month
         $ups->{Model}->{DayProd}   = $comParms[4] ;       # Day
         $ups->{Model}->{HWver} = int($comParms[5]/16) + ($comParms[5] % 16) / 10;
         $ups->{Model}->{BRDver} = int($comParms[6]/16) + ($comParms[6] % 16) / 10;
         $ups->{Model}->{FwMajor} = int($comParms[7]/16) ;
         $ups->{Model}->{FwMinor} = ($comParms[7] % 16) ;
         $ups->{Model}->{FwsubVersion} = $comParms[8] ;
         $ups->{Model}->{BattNumber} = $comParms[9] ;
         $ups->{Line}= "Enterprise N".substr($ups->{Model}->{Model},3,2)." - ".$ups->{Model}->{HWver}.", ".$ups->{Model}->{BRDver}
                       .", ".$ups->{Model}->{FwMajor}.".".$ups->{Model}->{FwMinor}.".".$ups->{Model}->{FwsubVersion};
         }
      if ($comParms[0] eq EEP_BATT_SUBST) # -------- Battery substitution -------
         { ; }
         
      # schedule
      if (($comParms[0] eq EEP_SCHEDULE_0) or ($comParms[0] eq EEP_SCHEDULE_1) or ($comParms[0] eq EEP_SCHEDULE_2) or 
          ($comParms[0] eq EEP_SCHEDULE_3) or ($comParms[0] eq EEP_SCHEDULE_4) or ($comParms[0] eq EEP_SCHEDULE_5) )
         {
			# i get the index of the schedule data
			$i = ($comParms[0] - EEP_SCHEDULE_0) / 8 ;		
			$UpsSchedule->{WeekDay}[$i] = $comParms[2] ;
			$UpsSchedule->{HourOn}[$i] = $comParms[3] ;
			$UpsSchedule->{MinOn}[$i] = $comParms[4] ;
			$UpsSchedule->{HourOff}[$i] = ($comParms[5] & 0x7F) ;
			$UpsSchedule->{MinOff}[$i] = $comParms[6] ;
			$UpsSchedule->{OutPort}[$i] = $comParms[7] ;
			$UpsSchedule->{ForceUpsSD}[$i] = (($comParms[5] & 0xf0) ? 1 : 0) ;
         }
      }
}

sub DecodeMeasures_OUnits($)
{
	my $CmdID = $_[0] ;
   my $FwVersion ;
   my $i ;

	if ($CmdID eq CMD_GET_STATUS_OLD)
   	{
   	# long status
		my $upsstate = 0 ;
		$upsstate = $comParms[0] & 0x07 ;
		if ($comParms[0] & 0x08)
			{ $upsstate |= 0x80 ; }
		if ($comParms[0] & 0x10)
			{ $upsstate |= 0x1000 ; }
		if ($comParms[0] & 0x20)
			{ $upsstate |= 0x2000 ; }
		if (!($comParms[0] & 0x40))
			{
			$upsstate |= 0x10 ;
			if (!($comParms[0] & 0x1))
				{
				$upsstate |= 0x08 ;
				}
			}
		if (!($comParms[0] & 0x80))
			{ $upsstate |= 0x80000 ; }
			
		# Status
   	$ups->{Measures}->{OldState} = $ups->{Measures}->{State} ;   		# save old state
   	$ups->{Measures}->{State} = $upsstate ;
	   if ($ups->{Measures}->{OldState} ne $ups->{Measures}->{State})
         { CheckUPSState($ups->{Measures}->{State}); }

   	$ups->{Measures}->{OutConf} = 0 ;                        			# OutConfig
   	$ups->{Time}->{Day} = $ups->{Measures}->{WeekDay} = int ($comParms[4]/24) ;    # UPS day of week
   	$ups->{Time}->{Hour} = $ups->{Measures}->{Hour} = $comParms[4] % 24 ; # UPS Hour
   	$ups->{Time}->{Min} = $ups->{Measures}->{Min} = $comParms[5] ;        # UPS Min
   	$ups->{Time}->{Sec} = $ups->{Measures}->{Sec} = $comParms[6] ;        # UPS Sec
		
		# Measures
		$FwVersion = $ups->{Model}->{FwMajor} * 10 + $ups->{Model}->{FwMinor} ;
      $ups->{Measures}->{ShortStatus} = ($upsstate & 0xffff) ;     		# short status
		$ups->{Measures}->{Vbat} = $comParms[2] / 16.82 ;								# Bbat
		if ($ups->{Model}->{Model} =~ m/(B5|MD)/i)
			{
			# B.Box Interactive models
      	$ups->{Measures}->{LoadPerc} = -1 ;           						# Load %
			if ($FwVersion < 50)
				{ $ups->{Measures}->{Vin} =  $comParms[1] * 1.69 }
			else
				{
      		if ($upsstate | 0x80000)
      			{
      			# Output 60 Hz
      			$ups->{Measures}->{Vin} =  $comParms[1] * 1.84 ;	      			# Vinput
      			}
      		else
      			{ $ups->{Measures}->{Vin} =  $comParms[1] / 1.215 ; }				# Vinput 
      		}
			if (($FwVersion < 60) && ($FwVersion >= 50))
      	   { $ups->{Measures}->{Vbat} *= 2.0; }
			}
		else
			{
			#B.Box Pro models
      	$ups->{Measures}->{Vbat} *= 2.0 ; 									
   		if ($upsstate | 0x80000)
   			{
   			# Output 60 Hz
   			$ups->{Measures}->{Vin} =  $comParms[1] * 1.84 ;	      			# Vinput
   			}
   		else
   			{ $ups->{Measures}->{Vin} =  $comParms[1] / 1.215 ; }				# Vinput 
			
			$ups->{Measures}->{LoadPerc} = BBoxProLoadPerc($comParms[3]) ;
			}
		
      if ($upsstate & 0x1)
      	{ $ups->{Measures}->{Vout} = 230.0 ; }						# Voutput
      else
      	{ $ups->{Measures}->{Vout} = $ups->{Measures}->{Vin} ; }	# Voutput
      $ups->{Measures}->{Iout} = -1 ;  							# Ioutput
      $ups->{Measures}->{Temperature} = -1 ;  					# Temperature
      $ups->{Measures}->{Icharger} = -1 ;  						# Icharger

		# Config
      $ups->{Measures}->{BattMin} = -1 ; 						# BattMin
      $ups->{Measures}->{UpsRuntime} = -1 ;      				# Runtime in minutes
      $ups->{Measures}->{BattRuntime} = -1 ; 					# Battery time in seconds
      $ups->{Measures}->{BattLowPerc} = -1 ;  					# battery LOW in %
      if ($upsstate & 0x8)
      	{ $ups->{Measures}->{OutStatus} = 0 ; } 				# Output status
		else
      	{ $ups->{Measures}->{OutStatus} = 1 ; }
		
		# Battery status
      $ups->{Measures}->{BattPerc} = int (($comParms[2] - 167) * 100.0 / (218 - 167)) ;           # Battery charge in %
      if ($ups->{Measures}->{BattPerc} > 100)
      	{ $ups->{Measures}->{BattPerc} = 100 ; }
      $ups->{Measures}->{RtimeEmpty} = -1 ;   					# Runtime to empty (sec)
      $ups->{Measures}->{RtimeEmpty} = -1 ; 
      $ups->{Measures}->{PercEmpty} = -1 ;                 # Perc to empty 
      $ups->{Measures}->{Load} = -1 ;         					# Load (VA)
      $ups->{Measures}->{OutFreq} = 50.0 ;   					# Output Frequency (Hz)
      if ($upsstate & 0x1)
      	{ $ups->{Measures}->{InFreq} = 0.0 ; }  				# Input Frequency (Hz)
      else
      	{ $ups->{Measures}->{InFreq} = $ups->{Measures}->{OutFreq} ; }
      
   	$ups->{Measures}->{Counter}++ ;
   	return(1) ;
		}
	else
		{
		if ($CmdID eq CMD_GET_MODEL_OLD)
			{
      	$ups->{Model}->{Model} = "" ;
      	$ups->{Model}->{GEK2VA} = 0 ;   		# default: < 2000 VA
      	for ($i=0 ; $i < 5 ; $i++)
         	{ $ups->{Model}->{Model} = $ups->{Model}->{Model} . chr($comParms[$i]) ; }
         $ups->{Model}->{Model} .= "   " ;
         $ups->{Model}->{NetID} = "********" ;
         $ups->{Model}->{NetName} = "********" ;
         $ups->{Model}->{SerialNumber} = "********" ;
      	$ups->{Model}->{YearProd} = 0 ;  					# Year
      	$ups->{Model}->{MonthProd} = 0 ;			      	# Month
      	$ups->{Model}->{DayProd}   = 0 ;       			# Day
      	$ups->{Model}->{HWver} = 0 ;
      	$ups->{Model}->{BRDver} = 0 ;
      	$ups->{Model}->{FwMajor} = $comParms[2] - 48 ;
      	$ups->{Model}->{FwMinor} = $comParms[4] - 48 ;
      	$ups->{Model}->{FwsubVersion} = 0 ;
      	$ups->{Model}->{FwVersion} = 0 ;
      	$ups->{Model}->{BattNumber} = 2 ; 
      	if ($ups->{Model}->{Model} =~ m/B5/i)
      	   { $ups->{Line}= "HiBox USB - ".$ups->{Model}->{FwMajor}.".".$ups->{Model}->{FwMinor}; }
      	elsif ($ups->{Model}->{Model} =~ m/MD/i)
      	   { $ups->{Line}= "B.Box Interactive - ".$ups->{Model}->{FwMajor}.".".$ups->{Model}->{FwMinor}; }
			else
      	   { $ups->{Line}= "B.Box Pro - ".$ups->{Model}->{FwMajor}.".".$ups->{Model}->{FwMinor}; }
	   	return(1) ;
			}
		else
			{
			return(-1) ;
			}
		}
}

sub DecodeMeasures_CHR($)
{
	my $CmdID = $_[0] ;
	my $Status ;
	my @DataList = split / /, $comParms[0] ;

   $ups->{Measures}->{Vin} = $DataList[0] ;      			# Vinput
   $ups->{Measures}->{VinFault} = $DataList[1] ;      	# Vinput Fault value
   $ups->{Measures}->{Vout} = $DataList[2] ; 				# Voutput
   $ups->{Measures}->{LoadPerc} = $DataList[3];       	# Load %
   $ups->{Measures}->{Load} = $ups->{Measures}->{NominalPower} * 
   									($ups->{Measures}->{LoadPerc} / 100) ; # Load (VA)
	$ups->{Measures}->{InFreq} = $DataList[4] ;				# Input Frequency
   $ups->{Measures}->{Vbat} = $DataList[5] ;					# Battery cell voltage
   if ($ups->{Measures}->{Vbat} > 2.283)
		{ $ups->{Measures}->{BattPerc} = 100 ; }
	else
		{
		if ($ups->{Measures}->{Vbat} < 1.667)
			{ $ups->{Measures}->{BattPerc} = 0 ; }
		else
			{$ups->{Measures}->{BattPerc} = (100.0 * (($ups->{Measures}->{Vbat} - 1.667) / (0.616)))} ; 
		}
   $ups->{Measures}->{Vbat} = $DataList[5] * 6 *
   								$ups->{Model}->{BattNumber};	# Battery voltage    
   $ups->{Measures}->{Temperature} = $DataList[6] ;  		# Temperature
   $ups->{Measures}->{Iout} = -1 ;  											# Ioutput
   $ups->{Measures}->{Icharger} = -1 ;  										# Icharger
   $ups->{Measures}->{OutFreq} = 50.0 ;

   $ups->{Measures}->{OldState} = $ups->{Measures}->{State} ;   		# save old state
	$ups->{Measures}->{State} = 0;
	$Status = unpack("C", pack("B8", $DataList[7])) ;
	if ($Status & 0x80)
		{
		$ups->{Measures}->{State} |= 0x1 ;
		if ($Status & 0x40)
			{ $ups->{Measures}->{State} |= 0x2; }
		}
	else
		{ 
		if ($Status & 0x2)
			{
			$ups->{Measures}->{State} |= 0xc0; 
			if ($ups->{Measures}->{Vin} > 160)
				{ $ups->{Measures}->{State} |= 0x10; }
			}
		else
			{ $ups->{Measures}->{State} |= 0x18; }
		}
	
   $ups->{Measures}->{ShortStatus} = $ups->{Measures}->{State} ;     		# short status
   if ($ups->{Measures}->{OldState} ne $ups->{Measures}->{State})
      { CheckUPSState($ups->{Measures}->{State}); }

   $ups->{Measures}->{OutConf} = 255 ;                        	 			# OutConfig
	my ($sec,$min,$hour,$wday) = get_time() ;

   $ups->{Time}->{Day} = $ups->{Measures}->{WeekDay} = $wday ;   		# UPS day of week
   $ups->{Time}->{Hour} = $ups->{Measures}->{Hour} = $hour ;     		# UPS Hour
   $ups->{Time}->{Min} = $ups->{Measures}->{Min} = $min ;       		# UPS Min
   $ups->{Time}->{Sec} = $ups->{Measures}->{Sec} = $sec ;       		# UPS Sec

   $ups->{Measures}->{BattMin} = -1 ; 											# BattMin
   $ups->{Measures}->{UpsRuntime} = -1 ;      								# Runtime in minutes
   $ups->{Measures}->{BattRuntime} = -1 ; 									# Battery time in seconds
   $ups->{Measures}->{BattLowPerc} = -1 ;   									# battery LOW in %
   $ups->{Measures}->{OutStatus} = -1 ;    									# Output status

	# Runtime to empty (sec)
	$ups->{Measures}->{RtimeEmpty} = -1 ;
   $ups->{Measures}->{Counter}++ ;

}

sub DecodeMeasures_CYP($)
{
	my $CmdID = $_[0] ;
   my $i ;
	my $strPolling = $comParms[0] ;
	my $HiStatus ;
	my $LoStatus ;
	my $delta ;	

	$HiStatus = vec(substr($strPolling, 31, 1), 0, 8) ;
	$LoStatus = vec(substr($strPolling, 32, 1), 0, 8) ;

   $ups->{Measures}->{Vin} =  substr($strPolling, 1, 5) ;      		# Vinput
	$delta = ($ups->{Model}->{BMaxT} - $ups->{Model}->{BLowT}) / 100.0 ;
	$ups->{Measures}->{BattPerc} = substr($strPolling, 17, 3) ;			# Battery charge in %   
   $ups->{Measures}->{Vbat} = $ups->{Model}->{BLowT} + 
   									($ups->{Measures}->{BattPerc} * $delta) ;   # Vbattery (V)
   $ups->{Measures}->{Vout} = substr($strPolling, 7, 5) ; 				# Voutput
   $ups->{Measures}->{Iout} = -1 ;  											# Ioutput
   $ups->{Measures}->{Temperature} = substr($strPolling, 21, 3) ;  	# Temperature
   $ups->{Measures}->{Icharger} = -1 ;  										# Icharger
   $ups->{Measures}->{LoadPerc} = substr($strPolling, 13, 3) ;       # Load %
   $ups->{Measures}->{Load} = $ups->{Measures}->{NominalPower} * 
   									($ups->{Measures}->{LoadPerc} / 100) ; # Load (VA)
	$ups->{Measures}->{InFreq} = substr($strPolling, 25, 5) ;
   $ups->{Measures}->{OutFreq} = 50.0 ;
   $ups->{Measures}->{OldState} = $ups->{Measures}->{State} ;   		# save old state
	$ups->{Measures}->{State} = 0 ;
	if ($HiStatus & 0x60)
		{
		if ($HiStatus & 0x40)
			{ $ups->{Measures}->{State} |= 0x1; }
		if ($HiStatus & 0x20)
			{ $ups->{Measures}->{State} |= 0x2; }
		}
	else
		{ $ups->{Measures}->{State} |= 0x18; }
	
   $ups->{Measures}->{ShortStatus} = $ups->{Measures}->{State} ;     		# short status
   if ($ups->{Measures}->{OldState} ne $ups->{Measures}->{State})
      { CheckUPSState($ups->{Measures}->{State}); }

   $ups->{Measures}->{OutConf} = 255 ;                        	 			# OutConfig
	my ($sec,$min,$hour,$wday) = get_time() ;

   $ups->{Time}->{Day} = $ups->{Measures}->{WeekDay} = $wday ;   		# UPS day of week
   $ups->{Time}->{Hour} = $ups->{Measures}->{Hour} = $hour ;     		# UPS Hour
   $ups->{Time}->{Min} = $ups->{Measures}->{Min} = $min ;       		# UPS Min
   $ups->{Time}->{Sec} = $ups->{Measures}->{Sec} = $sec ;       		# UPS Sec

   $ups->{Measures}->{BattMin} = -1 ; 											# BattMin
   $ups->{Measures}->{UpsRuntime} = -1 ;      								# Runtime in minutes
   $ups->{Measures}->{BattRuntime} = -1 ; 									# Battery time in seconds
   $ups->{Measures}->{BattLowPerc} = -1 ;   									# battery LOW in %
   $ups->{Measures}->{OutStatus} = 0 ;    										# Output status

	# Runtime to empty (sec)
	$ups->{Measures}->{RtimeEmpty} = (($ups->{Model}->{BatteryAH} * $ups->{Model}->{BattNumber}) / 9.0) *
												($ups->{Measures}->{BattPerc} / 100.0) * ((100.0 - $ups->{Measures}->{LoadPerc}) * 18.0) ;

	if ($ups->{Measures}->{RtimeEmpty} < 0)
		{$ups->{Measures}->{RtimeEmpty} = 0; }


   $ups->{Measures}->{Counter}++ ;

}

sub DecodeMeasures_ENT($)
{
	my $CmdID = $_[0] ;
   my $ge2kVA = $ups->{Model}->{GEK2VA} ; 	# UPS is >= 2000 VA
   my $i ;
	
	if ($CmdID eq CMD_GET_STATUS)
	   {
	   $ups->{Measures}->{OldState} = $ups->{Measures}->{State} ;   		 # save old state
	   $ups->{Measures}->{State} = $comParms[0] + $comParms[1]*256 + $comParms[2]*65536 + $comParms[3]*16777216 ;
	   if ($ups->{Measures}->{OldState} ne $ups->{Measures}->{State})
	      { CheckUPSState($ups->{Measures}->{State}); }
	
	   $ups->{Measures}->{OutConf} = $comParms[4] ;                        	 # OutConfig
	   $ups->{Time}->{Day} = $ups->{Measures}->{WeekDay} = $comParms[5] ;    # UPS day of week
	   $ups->{Time}->{Hour} = $ups->{Measures}->{Hour} = $comParms[6] ;      # UPS Hour
	   $ups->{Time}->{Min} = $ups->{Measures}->{Min} = $comParms[7] ;        # UPS Min
	   $ups->{Time}->{Sec} = $ups->{Measures}->{Sec} = $comParms[8] ;        # UPS Sec
	   $ups->{Measures}->{Counter}++ ;
   	}
   if ($CmdID eq CMD_GET_MEASURES)
      {
      $ups->{Measures}->{ShortStatus} = $comParms[0] + $comParms[1]*256 ;     		# short status
      $ups->{Measures}->{Vin} =  ($comParms[2]*256 + $comParms[3]) / 36.4 ;      	# Vinput
      $ups->{Measures}->{Vbat} = ($comParms[4]*256 + $comParms[5]) / (($ge2kVA) ? 329 : 585) ;   # Vbattery (V)
      $ups->{Measures}->{Vout} = ($comParms[6]*256 + $comParms[7]) / (($ge2kVA) ? 63.8 : 36.4) ; # Voutput
      $ups->{Measures}->{Iout} = ($comParms[8]*256 + $comParms[9]) / (($ge2kVA) ? 635 : 1350) ;  # Ioutput
      $ups->{Measures}->{Temperature} = (($comParms[10]*256 + $comParms[11]) - 202.97) / 1.424051 ;  # Temperature
      $ups->{Measures}->{Icharger} = ($comParms[12]*256 + $comParms[13]) ;  			# Icharger
      $ups->{Measures}->{Counter}++ ;
      }
   if ($CmdID eq CMD_GET_CONFIG)
      {
      $ups->{Measures}->{BattMin} = ($comParms[0] + $comParms[1]*256) / (($ge2kVA) ? 329 : 585) ; # BattMin
      $ups->{Measures}->{UpsRuntime} = ($comParms[2]*65536 + $comParms[3]*256 + $comParms[4]) ;      # Runtime in minutes
      $ups->{Measures}->{BattRuntime} = ($comParms[5]*65536 + $comParms[6]*256 + $comParms[7]) * 2 ; # Battery time in seconds
      $ups->{Measures}->{BattLowPerc} = $comParms[8] ;   	# battery LOW in %
      $ups->{Measures}->{OutStatus} = $comParms[9] ;    		# Output status
      $ups->{Measures}->{Counter}++ ;
      }
   if ($CmdID eq CMD_GET_BATT_STAT)
      {
      $ups->{Measures}->{BattPerc} = $comParms[0] ;                       # Battery charge in %
      $ups->{Measures}->{RtimeEmpty} = ($comParms[1]*256 + $comParms[2]) ;   # Runtime to empty (sec)
      if ($ups->{Measures}->{RtimeEmpty} eq 65535)
         { $ups->{Measures}->{RtimeEmpty} = -1 ; }
      $ups->{Measures}->{PercEmpty} = $comParms[3] ;                      # Perc to empty 
      $ups->{Measures}->{Load} = ($comParms[4]*256 + $comParms[5]) ;         # Load (VA)
      $ups->{Measures}->{LoadPerc} = $comParms[6] ;                       # Load %
      $i = ($comParms[7]*256 + $comParms[8]) ;  # no "div by 0" !!!
      if ($i ne 0)
         { $ups->{Measures}->{InFreq} = 50000 / $i ; } # Frequency (Hz)
      else
         { $ups->{Measures}->{InFreq} = 0 } ; # Frequency (Hz)
      $ups->{Measures}->{OutFreq} = 50.0 ;
      $ups->{Measures}->{Counter}++ ;
      }
}

sub DecodeMeasures_INT($)
{
	my $CmdID = $_[0] ;
   my $i ;

	if ($CmdID eq CMD_GET_STATUS)
	   {
	   $ups->{Measures}->{OldState} = $ups->{Measures}->{State} ;   		 # save old state
	   $ups->{Measures}->{State} = $comParms[0] + $comParms[1]*256 + $comParms[2]*65536 + $comParms[3]*16777216 ;
	   if ($ups->{Measures}->{OldState} ne $ups->{Measures}->{State})
	      { CheckUPSState($ups->{Measures}->{State}); }
	
	   $ups->{Measures}->{OutConf} = $comParms[4] ;                        	 # OutConfig
	   $ups->{Time}->{Day} = $ups->{Measures}->{WeekDay} = $comParms[5] ;    # UPS day of week
	   $ups->{Time}->{Hour} = $ups->{Measures}->{Hour} = $comParms[6] ;      # UPS Hour
	   $ups->{Time}->{Min} = $ups->{Measures}->{Min} = $comParms[7] ;        # UPS Min
	   $ups->{Time}->{Sec} = $ups->{Measures}->{Sec} = $comParms[8] ;        # UPS Sec
	   $ups->{Measures}->{Counter}++ ;
   	}
   if ($CmdID eq CMD_GET_MEASURES)
      {
      $ups->{Measures}->{ShortStatus} = $comParms[0] + $comParms[1]*256 ;     		# short status
      $ups->{Measures}->{Vin} =  ($comParms[2]*256 + $comParms[3]) / 37.1 ;      	# Vinput
      $ups->{Measures}->{Vbat} = ($comParms[4]*256 + $comParms[5]) / 610 ;   			# Vbattery (V)
      if ($ups->{Measures}->{State} & 0x01)
      	{ $ups->{Measures}->{Vout} = 223 ; }
      else
      	{ $ups->{Measures}->{Vout} = $ups->{Measures}->{Vin}; }
      $ups->{Measures}->{Iout} = ($comParms[8]*256 + $comParms[9]) / (1100) ;  		# Ioutput
      $ups->{Measures}->{Temperature} = -1 ;  											# Temperature
      $ups->{Measures}->{Icharger} = ($comParms[12]*256 + $comParms[13]) ;  			# Icharger
      $ups->{Measures}->{Counter}++ ;
      }
   if ($CmdID eq CMD_GET_CONFIG)
      {
      $ups->{Measures}->{BattMin} = ($comParms[0] + $comParms[1]*256) ; 				# BattMin (battery percent)
      $ups->{Measures}->{UpsRuntime} = ($comParms[2]*65536 + $comParms[3]*256 + $comParms[4]) ;      # Runtime in minutes
      $ups->{Measures}->{BattRuntime} = ($comParms[5]*65536 + $comParms[6]*256 + $comParms[7]) * 2 ; # Battery time in seconds
      $ups->{Measures}->{BattLowPerc} = $comParms[8] ;   								# battery LOW in %
      $ups->{Measures}->{OutStatus} = $comParms[9] ;    									# Output status
      $ups->{Measures}->{Counter}++ ;
      }
   if ($CmdID eq CMD_GET_BATT_STAT)
      {
      $ups->{Measures}->{BattPerc} = $comParms[0] ;                       # Battery charge in %
      $ups->{Measures}->{RtimeEmpty} = ($comParms[1]*256 + $comParms[2]) ;   # Runtime to empty (sec)
      if ($ups->{Measures}->{RtimeEmpty} eq 65535)
         { $ups->{Measures}->{RtimeEmpty} = -1 ; }
      $ups->{Measures}->{PercEmpty} = $comParms[3] ;                      # Perc to empty 
      $ups->{Measures}->{Load} = ($comParms[4]*256 + $comParms[5]) ;         # Load (VA)
      $ups->{Measures}->{LoadPerc} = $comParms[6] ;                       # Load %
      $i = ($comParms[7]*256 + $comParms[8]) ;  # no "div by 0" !!!
      if ($i ne 0)
         { $ups->{Measures}->{InFreq} = 50000 / $i ; } # Frequency (Hz)
      else
         { $ups->{Measures}->{InFreq} = 0 } ; # Frequency (Hz)
      $ups->{Measures}->{OutFreq} = 50.0 ;
      $ups->{Measures}->{Counter}++ ;
      }

}

sub DecodeMeasures_HIB($)
{
	my $CmdID = $_[0] ;

	if ($CmdID eq CMD_GET_STATUS)
	   {
	   $ups->{Measures}->{OldState} = $ups->{Measures}->{State} ;   		 # save old state
	   $ups->{Measures}->{State} = $comParms[0] + $comParms[1]*256 + $comParms[2]*65536 + $comParms[3]*16777216 ;
	   if ($ups->{Measures}->{OldState} ne $ups->{Measures}->{State})
	      { CheckUPSState($ups->{Measures}->{State}); }
	
	   $ups->{Measures}->{OutConf} = $comParms[4] ;                        	 # OutConfig
	   $ups->{Time}->{Day} = $ups->{Measures}->{WeekDay} = $comParms[5] ;    # UPS day of week
	   $ups->{Time}->{Hour} = $ups->{Measures}->{Hour} = $comParms[6] ;      # UPS Hour
	   $ups->{Time}->{Min} = $ups->{Measures}->{Min} = $comParms[7] ;        # UPS Min
	   $ups->{Time}->{Sec} = $ups->{Measures}->{Sec} = $comParms[8] ;        # UPS Sec
	   $ups->{Measures}->{Counter}++ ;
   	}
   if ($CmdID eq CMD_GET_MEASURES)
      {
      $ups->{Measures}->{ShortStatus} = $comParms[0] + $comParms[1]*256 ;     		# short status
      $ups->{Measures}->{Vin} =  ($comParms[2]*256 + $comParms[3]) / 36.4 ;      	# Vinput
      $ups->{Measures}->{Vbat} = ($comParms[4]*256 + $comParms[5]) / 58 ;   			# Vbattery (V)
      if ($ups->{Measures}->{State} & 0x01)
      	{ $ups->{Measures}->{Vout} = 223; }
      else
      	{ $ups->{Measures}->{Vout} = $ups->{Measures}->{Vin}; }

      $ups->{Measures}->{Iout} = -1 ;  				# Ioutput
      $ups->{Measures}->{Temperature} = -1 ;  		# Temperature
      $ups->{Measures}->{Icharger} = -1 ;  			# Icharger
      $ups->{Measures}->{Counter}++ ;
      }
   if ($CmdID eq CMD_GET_CONFIG)
      {
      $ups->{Measures}->{BattMin} = -1 ;							# BattMin (battery percent)
      $ups->{Measures}->{UpsRuntime} = -1 ;      				# Runtime in minutes
      $ups->{Measures}->{BattRuntime} = -1 ; 					# Battery time in seconds
      $ups->{Measures}->{BattLowPerc} = -1 ;   					# battery LOW in %
      $ups->{Measures}->{OutStatus} = -1 ;    					# Output status
      $ups->{Measures}->{Counter}++ ;
      }
   if ($CmdID eq CMD_GET_BATT_STAT)
      {
      $ups->{Measures}->{BattPerc} = $comParms[0] ;                  # Battery charge in %
      $ups->{Measures}->{RtimeEmpty} = -1 ;   							# Runtime to empty (sec)
      $ups->{Measures}->{PercEmpty} = -1 ;                      	# Perc to empty 
      $ups->{Measures}->{Load} = -1 ;         							# Load (VA)
      $ups->{Measures}->{LoadPerc} = -1 ;                       	# Load %
      $ups->{Measures}->{InFreq} = 50.0 ;									# Frequency (Hz)
      $ups->{Measures}->{OutFreq} = 50.0 ;
      $ups->{Measures}->{Counter}++ ;
      }
}

sub BBoxProLoadPerc($ @)
{
	my $loadvalue = shift ;
	
	if ($loadvalue <= 3)															# Load %
		{ return(0) ; }           				
	elsif (($loadvalue > 3) && ($loadvalue <= 10))
		{ return(25) ; }           				
	elsif (($loadvalue > 10) && ($loadvalue <= 22))
		{ return(50) ; }           				
	elsif (($loadvalue > 22) && ($loadvalue <= 37))
		{ return(75) ; }           				
	elsif (($loadvalue > 37) && ($loadvalue <= 47))
		{ return(100) ; }           				
	else
		{ return(120) ; }           				
}

#-----------------------------------------------------------------------------------
# Identify Enterprise models
#-----------------------------------------------------------------------------------
sub Identify_Enterprise()
{
	my $ErrCount ;
	my $ErrFlg = 0 ;
   my @tmpResult ;

	$ups->{Model}->{EnterpriseFamily} = 0 ;
	$ups->{Model}->{IsOldModel} = 0 ;
	if (not $OS_OSX) 
		{ 	
		OpenSerialPort($iniConfig{UpsParams}{Port}, 19200,1) ;
		}
	$ups->{ErrCount} = 1 ;
	for ($ErrCount=0; $ErrCount<5; $ErrCount++) 
		{	
		$ErrFlg = 0 ;
      if (SendCmdUPS(CMD_GET_EEP_BLOCK, LEN_GET_EEP_BLOCK, EEP_UPS_MODEL,   8) < 0)  { $ErrFlg++ ; }
   	sleepex(150) ;
     
      $ups->{Model}->{NetID} = "00000000" ;
      $ups->{Model}->{NetName} = "00000000" ;

         
      if ($ErrFlg eq 0) 
         { 
	      if (SendCmdUPS(CMD_GET_EEP_BLOCK, LEN_GET_EEP_BLOCK, EEP_SERIAL_NUM,  8) < 0)
		   	{ $ups->{ErrCount}++ ; } else { $ups->{ErrCount} = 0 ; }
   		sleepex(150) ;
      	if (SendCmdUPS(CMD_GET_EEP_BLOCK, LEN_GET_EEP_BLOCK, EEP_PROD_DATE,   8) < 0)
		   	{ $ups->{ErrCount}++ ; } else { $ups->{ErrCount} = 0 ; }
   		sleepex(150) ;
      	if (SendCmdUPS(CMD_GET_EEP_BLOCK, LEN_GET_EEP_BLOCK, EEP_BATT_SUBST,  8) < 0)
		   	{ $ups->{ErrCount}++ ; } else { $ups->{ErrCount} = 0 ; }
			
			if (SendCmdUPS(CMD_GET_STATUS, LEN_GET_STATUS) < 0) 
		   	{ $ups->{ErrCount}++ ; } else { $ups->{ErrCount} = 0 ; }
  	   	sleepex(150) ;
         if (SendCmdUPS(CMD_GET_MEASURES, LEN_GET_MEASURES) < 0)
		   	{ $ups->{ErrCount}++ ; } else { $ups->{ErrCount} = 0 ; }
	      sleepex(150) ;
         if (SendCmdUPS(CMD_GET_CONFIG, LEN_GET_CONFIG) < 0)
		   	{ $ups->{ErrCount}++ ; } else { $ups->{ErrCount} = 0 ; }
  	      sleepex(150) ;
         if (SendCmdUPS(CMD_GET_BATT_STAT, LEN_GET_BATT_STAT) < 0)
		   	{ $ups->{ErrCount}++ ; } else { $ups->{ErrCount} = 0 ; }                  
      	#-----------------------------------------------------------------------------------
         # i disable the dry contacts shutdown options
         #-----------------------------------------------------------------------------------
         if (SendCmdUPS(CMD_GET_EEP_BLOCK, LEN_GET_EEP_BLOCK, EEP_OPTION_BYTE, 8) < 0)  
         	{ $ErrFlg++ ; } 
         else 
            { 
	         @tmpResult = @retBuffer ;
	         if ($tmpResult[6] & 0x60) 
	            {
               if (SendCmdUPS(CMD_GET_EEP_SEED, LEN_GET_EEP_SEED) < 0)
               	{ $ErrFlg++ ; } 
               else
                  { 
                  my $eepSeed = ($retBuffer[2] * 256) + ($retBuffer[3] & 0xff);
                  $eepSeed = ~($eepSeed) & 0xffff;
                  if (SendCmdUPS(CMD_SET_EEP_BLOCK, LEN_SET_EEP_BLOCK, 
                                       (($eepSeed >> 8) & 0xff), ($eepSeed & 0xff),
                                       0, $tmpResult[4], $tmpResult[5], ($tmpResult[6] & 0x9f),
                                       $tmpResult[7], $tmpResult[8], $tmpResult[9], $tmpResult[10],
                                       $tmpResult[11]) < 0)  
                  	{ $ErrFlg++ ; } 
                  else
                     {
                     SendCmdUPS(CMD_INIT, LEN_INIT); 
                     return(1);
                     }
                  }
	            }
	         else
               { return(1); }
            }
         }   
   	}
	return(0); 

}

#-----------------------------------------------------------------------------------
# Identify older UPS models
#-----------------------------------------------------------------------------------
sub Identify_OldModels()
{
	my $ErrCount ;
	my $ErrFlg = 0 ;

	$ups->{Model}->{EnterpriseFamily} = 0 ;
	$ups->{Model}->{IsOldModel} = 1 ;
	if (not $OS_OSX) 
		{ 	
		OpenSerialPort($iniConfig{UpsParams}{Port}, 1200,2) ;
		}

	for ($ErrCount=0; $ErrCount<10; $ErrCount++) 
		{	
		$ErrFlg = 0 ;
      if (SendCmdUPS(CMD_GET_EEP_BLOCK, LEN_GET_EEP_BLOCK, EEP_UPS_MODEL, 8) < 0)
      	{ $ErrFlg++ ; }
      if ($ErrFlg eq 0) 
         { 
		   sleepex(250) ;
			if (SendCmdUPS(CMD_GET_STATUS, LEN_GET_STATUS) < 0)
				{ $ups->{ErrCount}++ ; } else { $ups->{ErrCount} = 0 ; } 
		   sleepex(250) ;
			if (SendCmdUPS(CMD_GET_STATUS, LEN_GET_STATUS) < 0)
				{ $ups->{ErrCount}++ ; } else { $ups->{ErrCount} = 0 ; } 
         return(1) ; # UPS identified
         }   
   	sleepex(500) ;
   	}
	return(0); 
}


#-----------------------------------------------------------------------------------
# Identify CHROMA models
#-----------------------------------------------------------------------------------
sub Identify_CHR()
{

	my $ErrCount ;
	my $ErrFlg = 0 ;

	$ups->{Model}->{EnterpriseFamily} = 1 ;
	$ups->{Model}->{IsOldModel} = 0 ;
	if (not $OS_OSX) 
		{ OpenSerialPort($iniConfig{UpsParams}{Port}, 2400,1); }

	for ($ErrCount=0; $ErrCount<3; $ErrCount++) 
		{	
		$ErrFlg = 0 ;
      if (SendCmdUPS(CMD_GET_EEP_BLOCK, LEN_GET_EEP_BLOCK, EEP_UPS_MODEL, 8) < 0)
      	{ $ErrFlg++ ; }
      if ($ErrFlg eq 0) 
         { 
		   sleepex(50) ;
			if (SendCmdUPS(CMD_GET_CHR_F) < 0)
				{ $ups->{ErrCount}++ ; } else { $ups->{ErrCount} = 0 ; } 
		   sleepex(50) ;
			if (SendCmdUPS(CMD_GET_STATUS, LEN_GET_STATUS) < 0)
				{ $ups->{ErrCount}++ ; } 
			else 
				{ 
				$ups->{ErrCount} = 0 ; 
	         return(1) ; # UPS identified
				} 
         }   
   	}

	return (0) ;
}

#-----------------------------------------------------------------------------------
# Identify CYBERPOWER models
#-----------------------------------------------------------------------------------
sub Identify_CYP()
{
	my $ErrCount ;
	my $ErrFlg = 0 ;

	$ups->{Model}->{EnterpriseFamily} = 2 ;
	$ups->{Model}->{IsOldModel} = 0 ;
	if (not $OS_OSX) 
		{ 	
		OpenSerialPort($iniConfig{UpsParams}{Port}, 2400,1) ;
		}

	for ($ErrCount=0; $ErrCount<3; $ErrCount++) 
		{	
		$ErrFlg = 0 ;
      if (SendCmdUPS(CMD_GET_EEP_BLOCK, LEN_GET_EEP_BLOCK, EEP_UPS_MODEL, 8) < 0)
      	{ $ErrFlg++ ; }
      if ($ErrFlg eq 0) 
         { 
		   sleepex(50) ;
			if (SendCmdUPS(CMD_GET_CYP_P3) < 0)
				{ $ups->{ErrCount}++ ; } else { $ups->{ErrCount} = 0 ; } 
		   sleepex(50) ;
			if (SendCmdUPS(CMD_GET_STATUS, LEN_GET_STATUS) < 0)
				{ $ups->{ErrCount}++ ; } else { $ups->{ErrCount} = 0 ; } 
		   sleepex(50) ;
			if (SendCmdUPS(CMD_GET_CYP_P9) < 0)
				{ $ups->{ErrCount}++ ; } 
			else 
				{ 
				$ups->{ErrCount} = 0 ; 
	         return(1) ; # UPS identified
				} 
         }   
   	}
	return (0) ;
}

#-----------------------------------------------------------------------------------
# IdentifyUPSModel: identifies the UPS model connected
#-----------------------------------------------------------------------------------
sub IdentifyUPSModel()
{
	my $Vid;
	my $Pid;
	
	if ($OS_OSX) 
		{ 	
		# Get the VID/PID ofthe UPS connected
		my $Xor;
		my $CmdID = 0xff;
		my $CmdLen = 2;
		my $CmdP0 = 0x01;
		my @VIDList;
      
      $Xor = $CmdLen;
      $Xor = $Xor ^ $CmdID;
      $Xor = $Xor ^ $CmdP0;

      $CmdString = sprintf("%02X %02X %02X %02X %02X\r\n", 91, $CmdLen, $CmdID, $CmdP0, $Xor) ;
		$RetBuff = "";
		$RetBuff = SendCmd_CHR () ;
		@VIDList = split / /, $RetBuff ;
		if (hex($VIDList[2]) != 0xff)
			{ return (0); }  
		$Vid = (hex($VIDList[3]) * 256) + hex($VIDList[4]);
		$Pid = (hex($VIDList[5]) * 256) + hex($VIDList[6]);

		if ($Vid eq 0x75d)
			{
			if ($Pid >= 0x200)
				{
				if (Identify_Enterprise() > 0)
					{ return (1); }
				}
			else
				{
				if (Identify_OldModels() > 0)
					{ return (1); }
				}
			}
		
		if ($Vid eq 0x665)
			{
			if (Identify_CHR() > 0)
				{ return (1) ;}
			}

	   return(0) ; # errors in the UPS identification

		}
	else
		{
		if (Identify_Enterprise() > 0)
			{ return (1); }
	
		if (Identify_CHR() > 0)
			{ return (1); }
	
		if (Identify_CYP() > 0)
			{ return (1); }
	
		if (Identify_OldModels() > 0)
			{ return (1); }
	
	   return(0) ; # errors in the UPS identification
   	}
}

#-----------------------------------------------------------------------------------
# GetScheduleUPS: reads the the UPS scheduling data (for Enterprise models only)
#-----------------------------------------------------------------------------------
sub GetScheduleUPS()
{
	my $ErrFlag = 0 ;

	$ErrFlag = 0 ;
   if (SendCmdUPS(CMD_GET_EEP_BLOCK, LEN_GET_EEP_BLOCK, EEP_SCHEDULE_0, 8) < 0)
   	{ $ErrFlag++ ; } 
   sleepex(150) ;
   if (SendCmdUPS(CMD_GET_EEP_BLOCK, LEN_GET_EEP_BLOCK, EEP_SCHEDULE_1, 8) < 0)
   	{ $ErrFlag++ ; } 
   sleepex(150) ;
   if (SendCmdUPS(CMD_GET_EEP_BLOCK, LEN_GET_EEP_BLOCK, EEP_SCHEDULE_2, 8) < 0) 
   	{ $ErrFlag++ ; } 
   sleepex(150) ;
   if (SendCmdUPS(CMD_GET_EEP_BLOCK, LEN_GET_EEP_BLOCK, EEP_SCHEDULE_3, 8) < 0)
   	{ $ErrFlag++ ; } 
   sleepex(150) ;
   if (SendCmdUPS(CMD_GET_EEP_BLOCK, LEN_GET_EEP_BLOCK, EEP_SCHEDULE_4, 8) < 0)
   	{ $ErrFlag++ ; } 
   sleepex(150) ;
   if (SendCmdUPS(CMD_GET_EEP_BLOCK, LEN_GET_EEP_BLOCK, EEP_SCHEDULE_5, 8) < 0)
   	{ $ErrFlag++ ; } 
   if ($ErrFlag eq 0)
      { return(1) ; }    # UPS identified

   return(0) ; # errors in the UPS identification   
}

#-----------------------------------------------------------------------------------
# CheckScheduling: checks if the UPS scheduling info is the same as the UPS 
# scheduling. The function is called at program startup or when the UPS 
# communication is restored.
#-----------------------------------------------------------------------------------
sub CheckScheduling()
{
	my $i ;
	my $Data ;
	
   if ($ups->{Model}->{EnterpriseFamily} eq 0)
      {
		if ($ups->{Model}->{IsOldModel} eq 0)
			{
	      if (GetScheduleUPS())
	      	{
	      	for ($i=0; $i<6; $i++)
	      		{
					if (($UpsSchedule->{WeekDay}[$i] ne $ups->{Schedule}->{WeekDay}[$i]) or
						 ($UpsSchedule->{HourOn}[$i] ne $ups->{Schedule}->{HourOn}[$i]) or
						 ($UpsSchedule->{MinOn}[$i] ne $ups->{Schedule}->{MinOn}[$i]) or
						 ($UpsSchedule->{HourOff}[$i] ne $ups->{Schedule}->{HourOff}[$i]) or
						 ($UpsSchedule->{MinOff}[$i] ne $ups->{Schedule}->{MinOff}[$i]) or
						 ($UpsSchedule->{OutPort}[$i] ne $ups->{Schedule}->{OutPort}[$i]))
						{
				      if (SendCmdUPS(CMD_SET_SCHEDULE, LEN_SET_SCHEDULE, $i,
				      							$ups->{Schedule}->{WeekDay}[$i],
				      							$ups->{Schedule}->{HourOn}[$i],
				      							$ups->{Schedule}->{MinOn}[$i],
				      							$ups->{Schedule}->{HourOff}[$i],
				      							$ups->{Schedule}->{MinOff}[$i],
				      							$ups->{Schedule}->{OutPort}[$i]) > 0)
	      				{ 
							$UpsSchedule->{WeekDay}[$i] = $ups->{Schedule}->{WeekDay}[$i] ;
						 	$UpsSchedule->{HourOn}[$i] = $ups->{Schedule}->{HourOn}[$i] ;
						 	$UpsSchedule->{MinOn}[$i] = $ups->{Schedule}->{MinOn}[$i] ;
						 	$UpsSchedule->{HourOff}[$i] = $ups->{Schedule}->{HourOff}[$i] ;
						 	$UpsSchedule->{MinOff}[$i] = $ups->{Schedule}->{MinOff}[$i] ;
						 	$UpsSchedule->{OutPort}[$i] = $ups->{Schedule}->{OutPort}[$i] ;
							$Data = sprintf("D-201%d %02X %02d %02d %02d %02d %02X %01d\r\n", $i,
												$ups->{Schedule}->{WeekDay}[$i],
												$ups->{Schedule}->{HourOn}[$i],
												$ups->{Schedule}->{MinOn}[$i],
												$ups->{Schedule}->{HourOff}[$i],
												$ups->{Schedule}->{MinOff}[$i],
												$ups->{Schedule}->{OutPort}[$i],
												$ups->{Schedule}->{ForceUpsSD}[$i]) ;
							NotifyClients (\$Data) ;
	      				} 
						}					 
	      		}
	      	} 
			}
		else
			{
			# the older B.Box don't have an E2Rom to store scheduling
			SetOldSchedule() ;
			}
		}

   if ($ups->{Model}->{EnterpriseFamily} eq 1)
      {
		# CHR
		}

   if ($ups->{Model}->{EnterpriseFamily} eq 2)
      {
		# CYP
		}
}

#-----------------------------------------------------------------------------------
# SetOldSchedule: defines the scheduling for the older UPS models
#-----------------------------------------------------------------------------------
sub SetOldSchedule()
{
	my ($i, $j, $k) ;
	my($ss,$mm,$hh, $dd) = get_time() ;
	my $weekmin ;
	$weekmin = $dd * 1440 + $hh * 60 + $mm ;
	my $minpos = 10080 ;
	my $maxneg = 0 ;
	my $mintowu ;
	
	for ($i=0; $i<6; $i++)
		{
		$k = 1 ;
		for ($j=0; $j<7; $j++)
			{
			if ($ups->{Schedule}->{WeekDay}[$i] & $k)
				{
				$mintowu = $j * 1440 + $ups->{Schedule}->{HourOn}[$i] * 60 +
							  $ups->{Schedule}->{MinOn}[$i] - $weekmin	;
				if ($mintowu eq 0)
					{ return (1) ;}
				if ($mintowu < 0)
					{
					if ($maxneg > $mintowu) { $maxneg = $mintowu ; }
					}
				else
					{
					if ($minpos > $mintowu) { $minpos = $mintowu ; }
					}
				}
			$k = $k * 2 ;
			}
		}
	if ($minpos < 10080)
		{
		# the reboot will be one of the next days of the week
		$dd = int(($weekmin + $minpos) / 1440) ;
		$hh = int((($weekmin + $minpos) % 1440) / 60) ;			
		$mm = int(($weekmin + $minpos) % 60) ;			
		}
	elsif ($maxneg < 0)
		{
		# the reboot will be one of the days of the next week
		$dd = int(($weekmin + $maxneg) / 1440) ;
		$hh = int((($weekmin + $maxneg) % 1440) / 60) ;
		$mm = int(($weekmin + $maxneg) % 60) ;
		}
	else
		{
		$dd = 0 ;
		$hh = 255 ;
		$mm = 0 ;
		# no scheduling active
		}
   if (($dd ne $WUDay) or ($hh ne $WUHour) or ($mm ne $WUMin))
   	{
   	SendCmdUPS(CMD_SET_SCHEDULE, LEN_SET_SCHEDULE, 0,
   							$dd,
   							$hh,
   							$mm,
   							$hh,
   							0,
   							0) ;
		$WUDay = $dd ;
		$WUHour = $hh ;
		$WUMin = $mm ;		
		}
}

sub Get_CHR_CYP_WUDelay()
{
	my ($i, $j, $k) ;
	my($ss,$mm,$hh, $dd) = get_time() ;
	my $weekmin ;
	$weekmin = $dd * 1440 + $hh * 60 + $mm ;
	my $minpos = 10080 ;
	my $maxneg = 0 ;
	my $mintowu ;
	
	for ($i=0; $i<6; $i++)
		{
		$k = 1 ;
		for ($j=0; $j<7; $j++)
			{
			if ($ups->{Schedule}->{WeekDay}[$i] & $k)
				{
				$mintowu = $j * 1440 + $ups->{Schedule}->{HourOn}[$i] * 60 +
							  $ups->{Schedule}->{MinOn}[$i] - $weekmin	;
				if ($mintowu < 0)
					{
					if ($maxneg > $mintowu) { $maxneg = $mintowu ; }
					}
				else
					{
					if ($minpos > $mintowu) { $minpos = $mintowu ; }
					}
				}
			$k = $k * 2 ;
			}
		}
	if ($minpos < 10080)
		{
		# the reboot will be one of the next days of the week
		return (60 * $minpos) ;
		}
	elsif ($maxneg < 0)
		{
		# the reboot will be one of the days of the next week
		return (60 * (10080 + $maxneg)) ;
		}
	else
		{
		# no scheduling active
		return(0);
		}
}

#-----------------------------------------------------------------------------------
# PollUPS: executes a timed polling of the UPS
#-----------------------------------------------------------------------------------
sub PollUPS()
{
   ReadWriteUPS() ;
}

#-----------------------------------------------------------------------------
# HandleSchedule: checks and handles the received COMMAND
#-----------------------------------------------------------------------------
sub HandleSchedule()
{
	my ($sec,$min,$hour,$wday) = (localtime)[0,1,2,6] ;
	
	if ($Seconds != $sec)
		{
		# one second scheduling
		$Seconds = $sec ;
		if ($Seconds %2 == 0)
		{ UpdateClientInfo (0) ;}

	   if ($OS_OSX)
         {
      	if ($SckClient eq -1)
	      	{
			   ConnectHIDDriver();
   		   }
		   }
   	if ($UDPClient eq -1)
      	{
		   ConnectUDP();
		   }

		CheckEvents();
      if ($ups->{ComState} == UPS_COMSTATE_POLL_MODE)
         { WriteDataToLog ();}
		if ($Minutes != $min)
			{
			# one minute scheduling
			$Minutes = $min ;
			CheckUPSShutdown() ;
			if ($ups->{Model}->{EnterpriseFamily} eq 0)
				{
				if ($ups->{Model}->{IsOldModel} eq 1)
					{
					# for older B.Box models i check every minute the scheduling
					SetOldSchedule() ;
					}
				}
			if ($Hours != $hour)
				{
				# one hour scheduling
				if ($ups->{ComState} == UPS_COMSTATE_POLL_MODE)
					{
			   	my($ss,$mm,$hh, $dd) = get_time() ;
   				push @{$ups->{Command}}, (CMD_SET_TIMER, LEN_SET_TIMER, $dd, $hh, $mm, $ss, ":") ;
					}
				$Hours = $hour ;					
				}
			}
		}
}


#-----------------------------------------------------------------------------
# CheckUPSShutdown: checks the UPS switching OFF scheduling 
#-----------------------------------------------------------------------------
sub CheckUPSShutdown ()
{
	my $mintosd ;
	my $daymin ;
	my $dayofweek ;
	my ($i, $j, $k) ;
	my $weekmin ;
	
	# i control the scheduling only if the UPS communication is OK
	if ($ups->{ComState} == UPS_COMSTATE_POLL_MODE)
		{
		my($ss,$mm,$hh, $dd) = get_time() ;
		$weekmin = $dd * 1440 + $hh * 60 + $mm ;
		for ($i=0; $i<6; $i++)
			{
			$k = 1 ;
			for ($j=0; $j<7; $j++)
				{
				if ($ups->{Schedule}->{WeekDay}[$i] & $k)
					{
					$mintosd = $j * 1440 + $ups->{Schedule}->{HourOff}[$i] * 60 +
								  $ups->{Schedule}->{MinOff}[$i] - $weekmin	;
					if ($ups->{Schedule}->{ForcePcSD}[$i] eq 1)
						{
						# the scheduling forces the PC shutdown
						if ($mintosd eq 10)
							{
							# ... 10 minutes to shutdown enabling
							$EventList{E_10TOSD}->{FireEvent} = 1 ;
							$EventList{E_10TOSD}->{SubID} = 0 ;
							}
						elsif ($mintosd eq 1)
							{
							# ... 1 minute to shutdown enabling
							$EventList{E_1TOSD}->{FireEvent} = 1 ;
							$EventList{E_1TOSD}->{SubID} = 0 ;
							}
						elsif ($mintosd eq 0)
							{
							# ... shutdown enabled
							$EventList{E_SCHESD}->{FireEvent} = 1 ;
							$EventList{E_SCHESD}->{SubID} = 0 ;
							}
						}
					else
						{
						# the scheduling does not force the PC shutdown; it is possible
						# for Enterprise models only
						if ($ups->{Model}->{EnterpriseFamily} eq 0)
							{
							if ($ups->{Model}->{IsOldModel} eq 0)							
								{
								if ($mintosd eq 0)
									{
									# ... i enable ONESHOT shutdown
				               if (SendCmdUPS(CMD_SD_ONESHOT, LEN_SD_ONESHOT, 
				               				   ($ups->{Schedule}->{OutPort}[$i], 
				               				   0x4, 0, 1, 0, 0, 0)) < 0)
	         			         { ; } 
									}
								}
							}							
						}
					}
				$k = $k * 2 ;
				}
			}
		}	
}


#-----------------------------------------------------------------------------------
####################################################################################
# EVENTS
####################################################################################
#-----------------------------------------------------------------------------------

#-----------------------------------------------------------------------------
# CheckEventLogSize: if the eventlog file is greater then ELMaxSize in KBytes, 
#                    tiles the file
#-----------------------------------------------------------------------------
sub CheckEventLogSize()
{
   my $neweventlog = "/tmp/tmpEVENTLOG.DAT";
   my $tmpString ;

   my $fsize = (stat($ups->{EventLog}))[7] ;
   if (!$fsize) { return 0; }
   
   if ($fsize > ($ups->{ELMaxSize} * 1024))
      {
      unless (open(EVENTLOG, "< $ups->{EventLog}")) {return 0} ;
      unless (open(NEW_EVENTLOG, "> $neweventlog")) {return 0} ;
      seek (EVENTLOG, ($fsize-$ups->{ELMaxSize} * 1024), 0) ;
      select(NEW_EVENTLOG);
      $tmpString = <EVENTLOG> ;
      while (<EVENTLOG>)
         {
         print NEW_EVENTLOG $_ ;
         }
      close (EVENTLOG) ;
      close (NEW_EVENTLOG) ;
      unlink ($ups->{EventLog}) ;
      rename ($neweventlog, $ups->{EventLog}); 
   }
}

#-----------------------------------------------------------------------------
# CheckDataLogSize: if the datalog file is greater then DLMaxSize in KBytes, 
#                   tiles the file
#-----------------------------------------------------------------------------
sub CheckDataLogSize()
{
   my $newdatalog = "/tmp/tmpDATALOG.DAT";
   my $tmpString ;

   my $fsize = (stat($ups->{DataLog}))[7] ;
   if (!$fsize) { return 0 }
   if ($fsize > ($ups->{DLMaxSize} * 1024))
      {
      unless (open(LOG, "< $ups->{DataLog}")) {return 0} ;
      unless (open(NEW_LOG, "> $newdatalog")) {return 0} ;
      seek (LOG, ($fsize-$ups->{DLMaxSize} * 1024), 0) ;
      select(NEW_LOG);
      $tmpString = <LOG> ;
      while (<LOG>)
         { print NEW_LOG $_ ; }
      close (LOG) ;
      close (NEW_LOG) ;
      unlink ($ups->{DataLog}) ;
      rename ($newdatalog, $ups->{DataLog}); 
   }
}

#-----------------------------------------------------------------------------
# WriteDataToLog: writes sampled data in the datalog file
#-----------------------------------------------------------------------------
sub WriteDataToLog()
{   
   my $TimeOfLog ;
   my $DataToLog ;
   my $sec; my $min; my $hour; my $mday; 
   my $mon; my $year; my $wday; my $yday; my $isdst;
   
   if (++$DataSamples != $ups->{DelayReg}) {return 0} ;
   $DataSamples = 0 ;
   ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
   $TimeOfLog = sprintf("%02d/%02d/%04d %02d:%02d:%02d", $mon+1, $mday, $year+1900, $hour, $min, $sec) ;
   $DataToLog = sprintf("%08X\t%02X\t%5.1f\t%5.1f\t%5.1f\t%05d\t%05d\t%05d\t%5.1f\t%5.1f\t%5.1f",
                     $ups->{Measures}->{State}, $ups->{Measures}->{OutConf}, 
                     $ups->{Measures}->{Vin}, $ups->{Measures}->{Vout}, $ups->{Measures}->{Vbat},
                     $ups->{Measures}->{BattPerc}, $ups->{Measures}->{Load},
                     $ups->{Measures}->{LoadPerc},    
                     $ups->{Measures}->{Temperature}, $ups->{Measures}->{InFreq},
                     $ups->{Measures}->{OutFreq} ) ;

   $tmpDataLogString .= "$TimeOfLog $DataToLog\n" ;
   if (++$tmpDataLogCounter eq 10)
   	{
	   unless (open(DATALOG, ">> $ups->{DataLog}")) {return 0} ;
	   print DATALOG $tmpDataLogString ;
   	close(DATALOG) ;
	   $tmpDataLogString = "" ;
	   $tmpDataLogCounter = 0 ;
   	}
   if ($DataLogCounter++ > 10000)
      {
      CheckDataLogSize () ;
      $DataLogCounter = 0 ;
      }
}


#-----------------------------------------------------------------------------
# WriteEventToLog: writes an event in the eventlog file
#-----------------------------------------------------------------------------
sub WriteEventToLog(@)
{
   my $TimeOfLog ;
   my $EventToLog ;
   my $EventDescription ;
	my ($eventID, $eventSubID) = @_ ;
   my $sec; my $min; my $hour; my $mday; 
   my $mon; my $year; my $wday; my $yday; my $isdst;
   
   ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
   $TimeOfLog = sprintf("%02d/%02d/%04d %02d:%02d:%02d", $mon+1, $mday, $year+1900, $hour, $min, $sec) ;
   $EventToLog = sprintf("%08d %08d %08d %08d %08d %08d", $eventID, $eventSubID, 0, 0, 0, 0) ;

	if ($eventSubID)
		{								
      if ($eventID eq eveSYSSD)
         {
   	   $EventDescription = getmessage ($eventSubID) . getmessage ($eventID) ;
         }
      else
         {
   	   $EventDescription = getmessage ($eventID) . getmessage ($eventSubID) ;
         }

	   }
   else
      {
   	$EventDescription = getmessage ($eventID) ;
      }

   unless (open(EVENTLOG, ">> $ups->{EventLog}")) {return 0} ;
   print EVENTLOG "$TimeOfLog $EventToLog \"$EventDescription\"\n" ;
   if ($eventID eq eveCOMSTART)
      {
      if ($ups->{Measures}->{State} & 0x8)
         {
         $EventToLog = sprintf("%08d %08d %08d %08d %08d %08d", eveLINEON, 0, 0, 0, 0, 0) ;
	      $EventDescription = getmessage (eveLINEON) ;
         print EVENTLOG "$TimeOfLog $EventToLog \"$EventDescription\"\n" ;
         }
      }
   close(EVENTLOG) ;
   if ($EventLogCounter++ > 1000)
      {
      CheckEventLogSize () ;
      $EventLogCounter = 0 ;
      }
}

#-----------------------------------------------------------------------------
# LoadEvents: loads the event information in the corresponding event list
#-----------------------------------------------------------------------------
sub LoadEvents()
{
	my ($event,$eventid) ;
	my $users ;
	foreach $eventid (@EVENTS)
		{
		$event = _EVENT->new ;
		$event->{ID} = $iniConfig{$eventid}{EventID} ;
		$event->{FireEvent} = 0 ;
		$event->{Processed} = 0 ;
		$event->{RunTime} = 0 ;
		$event->{Notified} = 0 ;
		$event->{SubID} = 0 ;
		$event->{Enabled} = $iniConfig{$eventid}{Enabled} ;
		$event->{Settings} = $iniConfig{$eventid}{Settings} ;
		$event->{BlockingMode} = $iniConfig{$eventid}{BlockingMode} ;
		$event->{LogEna} = substr($event->{Settings},0,1) ;
		$event->{MsgEna} = substr($event->{Settings},1,1) ;
		@{$event->{MsgUsers}} = split (/,/,$iniConfig{$eventid}{MsgUsers}) ;
		$event->{MsgRepeat} = $iniConfig{$eventid}{MsgRepeat} ;
		$event->{MsgInterval} = $iniConfig{$eventid}{MsgInterval} ;
		$event->{MsgDelay} = $iniConfig{$eventid}{MsgDelay} ;
		$event->{CmdEna} = substr($event->{Settings},2,1) ;
		$event->{CmdFile} = $iniConfig{$eventid}{CmdFile} ;
		if ($event->{BlockingMode} == 0)
			{
			# ... the command file will be forked
			if (!$OS_win)
				{
				$_ = $event->{CmdFile} ;
				s/ *$//g ;
				if ( length(chop($event->{CmdFile})) )
					{
					$event->{CmdFile} = $_ . " &" ;
					}
				}
			}
		$event->{CmdDelay} = $iniConfig{$eventid}{CmdFileDelay} ;
		$event->{EmailEna} = substr($event->{Settings},3,1) ;
		@{$event->{EmailUsers}} = split (/,/,$iniConfig{$eventid}{EmailUsers}) ;
		$event->{EmailMsg} = $iniConfig{$eventid}{EmailMsg} ;
		$event->{EmailDelay} = $iniConfig{$eventid}{EmailDelay} ;
		$event->{ShutdownEna} = substr($event->{Settings},4,1) ;
		if (($eventid eq "E_SYSSD") || ($eventid eq "E_SCHESD"))
			{
			$event->{ShutdownCmd} = $iniConfig{$eventid}{ShutdownCmd} ;
			if ($event->{BlockingMode} == 0)
				{
				# ... the command file will be forked
				if (!$OS_win)
					{
					$_ = $event->{ShutdownCmd} ;
					s/ *$//g ;
					if ( length(chop($event->{ShutdownCmd})) )
						{
						$event->{ShutdownCmd} = $_ . " &" ;
						}
					}
				}
			}
		$event->{ShutdownDelay} = $iniConfig{$eventid}{ShutdownDelay} ;
		$EventList{$eventid} = $event ;		
		}
}

#-----------------------------------------------------------------------------
# ComRestored: handles UPS events, state variations, communication faults, ...
#-----------------------------------------------------------------------------
sub ComRestored ($)
{
	my @Data ;
	my $Notify = 0 ;
	my $i ;
	my($ss,$mm,$hh, $dd) = get_time() ;
	
	if ($ups->{ComState} == UPS_COMSTATE_INIT_MODE)
		{
		$Notify = 1 ;
		# ... starting UPS communication
		$EventList{E_COMLOST}->{FireEvent} = 0 ;
		$EventList{E_COMSTART}->{FireEvent} = 1 ;
		$EventList{E_COMSTART}->{SubID} = 0 ;
		# ... i update the timer/schedule data
		ReadIniFile(\%iniConfig,$INIFILE) ;
   	@Data = (CMD_SET_TIMER, LEN_SET_TIMER, $dd, $hh, $mm, $ss, ":") ;
   	push @{$ups->{Command}}, @Data ;
   	CheckScheduling() ;
		UpdateClientInfo (1) ;
		$ups->{PollInterval} = ((localtime)[0] + 2 ) % 60 ; 
		}
	elsif ($ups->{ComState} == UPS_COMSTATE_COMM_LOST)
		{
		$Notify = 1 ;
		# ... the UPS communication is restored
		$EventList{E_COMLOST}->{FireEvent} = 0 ;
		$EventList{E_COMSTART}->{FireEvent} = 1 ;
		$EventList{E_COMSTART}->{SubID} = 0 ;
		ReadIniFile(\%iniConfig,$INIFILE) ;
		LoadIniData() ;
		# ... i update the timer/schedule data
   	@Data = (CMD_SET_TIMER, LEN_SET_TIMER, $dd, $hh, $mm, $ss, ":") ;
   	push @{$ups->{Command}}, @Data ;
   	CheckScheduling() ;
		UpdateClientInfo (1) ;
		$ups->{PollInterval} = ((localtime)[0] + 2 ) % 60 ; 
		}
	$ups->{ComState} = UPS_COMSTATE_POLL_MODE ;	
}

#-----------------------------------------------------------------------------
# UpdateClientInfo: sends the UPS data to all the network clients
#-----------------------------------------------------------------------------
sub UpdateClientInfo ($)
{
   my $i ;
   my $DataToSend ;

	# state
   $DataToSend = sprintf("D-2100 %d %02X %08X %04X %02X %03d %02d %02d %d %02d %02d %02d\r\n",
                     $ups->{Measures}->{Counter}, ($ups->{ComState} eq 1) ? 0 : 1, 
                     $ups->{Measures}->{State}, $ups->{Measures}->{ShortStatus}, 
                     $ups->{Measures}->{OutConf}, 0, 0, 0, $ups->{Time}->{Day},
                     $ups->{Time}->{Hour}, $ups->{Time}->{Min}, $ups->{Time}->{Sec} ) ;

	# measures
   $DataToSend .= sprintf("D-2101 %.1f %.1f %.1f %.1f %.1f %.1f %.1f\r\n",
                     $ups->{Measures}->{Vin}, $ups->{Measures}->{Vout}, $ups->{Measures}->{Vbat},
                     $ups->{Measures}->{Temperature}, $ups->{Measures}->{Iout}, 
                     $ups->{Measures}->{InFreq}, $ups->{Measures}->{OutFreq} ) ;

	# configuration
   $DataToSend .= sprintf("D-2102 %d %d %d %d %d %d %d %d\r\n",
                     $ups->{Measures}->{LoadPerc}, $ups->{Measures}->{Load}, 
                     $ups->{Measures}->{BattPerc}, $ups->{Measures}->{RtimeEmpty},
                     $ups->{Measures}->{PercEmpty}, $ups->{Measures}->{UpsRuntime},
                     $ups->{Measures}->{BattRuntime}, $ups->{Measures}->{BattLowPerc} ) ;

	# init
	if (shift eq 1)
		{
		$DataToSend .= sprintf(IDENT_1, ($ups->{ComState} eq 1) ? 0 : 1, $connection->{NumClients}  ) ;
		$DataToSend .= sprintf("D-2001 \"%s\" \"%s\" \"%08X\" \"%08X\" %04d/%02d/%02d %3.1f %3.1f %d.%d.%d %d\r\n", 
           				         $ups->{Model}->{Model}, $ups->{Model}->{SerialNumber},
           				         vec($ups->{Model}->{NetID},0,32), vec($ups->{Model}->{NetName},0,32),
         				         $ups->{Model}->{YearProd}, $ups->{Model}->{MonthProd},
         				         $ups->{Model}->{DayProd}, $ups->{Model}->{HWver},
         				         $ups->{Model}->{BRDver}, $ups->{Model}->{FwMajor},
         				         $ups->{Model}->{FwMinor},$ups->{Model}->{FwsubVersion},
				                  $ups->{Model}->{BattNumber} ) ;
		$DataToSend .= sprintf(IDENT_2, $connection->{ServerAddr}) ;
		for ($i=0; $i<6; $i++)
			{
			$DataToSend .= sprintf("D-201%d %02X %02d %02d %02d %02d %02X %01d\r\n", $i,
											$ups->{Schedule}->{WeekDay}[$i],
											$ups->{Schedule}->{HourOn}[$i],
											$ups->{Schedule}->{MinOn}[$i],
											$ups->{Schedule}->{HourOff}[$i],
											$ups->{Schedule}->{MinOff}[$i],
											$ups->{Schedule}->{OutPort}[$i],
											$ups->{Schedule}->{ForceUpsSD}[$i]) ;
			}		
		}		
	NotifyClients(\$DataToSend) ;	
}

#-----------------------------------------------------------------------------
# CheckUPSState: controls the UPS commutations
#-----------------------------------------------------------------------------
sub CheckUPSState ($)
{
	my $state = shift ;

	#-----------------------------------------------------------------------------
	# ... check the state if the value is changed, ...
	#-----------------------------------------------------------------------------	
	if ($state & 0x80)   # UPS in Standby mode
		{
		# ... UPS is in standby
	   if ($state & 0x01)
         {
		   NotifyEvent(eveSTDBY_DISCH) ;
		   $ups->{StrStatus} = getmessage(eveSTDBY_DISCH);
		   }
		else
		   {
		   NotifyEvent(eveSTDBY_CHAR) ;
		   $ups->{StrStatus} = getmessage(eveSTDBY_CHAR);
		   }
		}
	elsif ($state & 0x01) # UPS in battery mode
		{
		if ($state & 0x04) # UPS in Battery END
			{
			# ... ups batteries discharged
			NotifyEvent(eveBEND) ;
		   $ups->{StrStatus} = getmessage(eveBEND);
   		WriteEventToLog (eveBEND, 0) ;
			}
		elsif ($state & 0x02) # UPS in Battery LOW
			{
			# ... ups low batteries charge
			$EventList{E_BLOW}->{FireEvent} = 1 ;
			$EventList{E_BLOW}->{SubID} = 0 ;
			$ups->{StrStatus} = getmessage(eveBLOW);
			}
		else
			{
			$ups->{StrStatus} = getmessage(eveBMODE);
			if (not ($state & 0x10))   # Mains NOT present
				{
				# ... UPS on battery, mains off
				$EventList{E_BAT}->{FireEvent} = 1 ;
				$EventList{E_BAT}->{SubID} = 0 ;
				}
			else
				{
				# ... UPS on battery, mains on
				}
			}
		}
	else 
		{
		if ($ups->{Measures}->{OldState} == 0x80000000)
			{
			# ... the UPS communication has been restored/started, the mains is OK
			NotifyEvent(eveLINEON) ;
			$ups->{StrStatus} = getmessage(eveLINEON);
			}
		else
			{
			$EventList{E_BAT}->{FireEvent} = 0 ;
			$EventList{E_BLOW}->{FireEvent} = 0 ;
			$EventList{E_SYSSD}->{FireEvent} = 0 ;
			$EventList{E_POWON}->{FireEvent} = 1 ;
			$ups->{StrStatus} = getmessage(evePOWON);
			}
		}
	if (($state & 0x1000) or ($state & 0x2000))  # temperature fault
		{
		$EventList{E_HTEMP}->{FireEvent} = 1 ;
		if ($state & 0x01)
			{
			$EventList{E_HTEMP}->{SubID} = eveBMODE ;
			$ups->{StrStatus} = getmessage(eveHTEMP).getmessage(eveBMODE);
			}
		else
			{
			$EventList{E_HTEMP}->{SubID} = eveLINEON ;
			$ups->{StrStatus} = getmessage(eveHTEMP).getmessage(eveLINEON);
			}
		}
	else
	   {
      if (($ups->{Measures}->{OldState} & 0x1000) or ($ups->{Measures}->{OldState} & 0x2000))
         {
			NotifyEvent(eveTEMPOK) ;
		   $ups->{StrStatus} = getmessage(eveTEMPOK);
   		WriteEventToLog (eveTEMPOK, 0) ;
         }
	   }
	if (($state & 0x8000) or ($state & 0x4000))  # Load fault
		{
		$EventList{E_OVERLOAD}->{FireEvent} = 1 ;
		if ($state & 0x01)
			{
			$EventList{E_OVERLOAD}->{SubID} = eveBMODE ;
			$ups->{StrStatus} = getmessage(eveOVERLOAD).getmessage(eveBMODE);
			}
		else
			{
			$EventList{E_OVERLOAD}->{SubID} = eveLINEON ;
			$ups->{StrStatus} = getmessage(eveOVERLOAD).getmessage(eveLINEON);
			}
		}
	else
	   {
      if (($ups->{Measures}->{OldState} & 0x8000) or ($ups->{Measures}->{OldState} & 0x4000))  
	      {
			NotifyEvent(eveLOADOK) ;
		   $ups->{StrStatus} = getmessage(eveLOADOK);
   		WriteEventToLog (eveLOADOK, 0) ;
	      }
	   }
}


#-----------------------------------------------------------------------------------
# LoadIniData: reads the new configuration data of the UPS
#-----------------------------------------------------------------------------------
sub LoadIniData
{ 
	my $i ;
	my $section ;
	for ($i=0; $i<6; $i++)
		{
		$section = "Schedule" . $i ;
		$ups->{Schedule}->{WeekDay}[$i] = (split /,/,$iniConfig{Schedule}{"$section"})[0] ;
		$ups->{Schedule}->{HourOn}[$i] = (split /,/,$iniConfig{Schedule}{"$section"})[1] ;
		$ups->{Schedule}->{MinOn}[$i] = (split /,/,$iniConfig{Schedule}{"$section"})[2] ;
		$ups->{Schedule}->{HourOff}[$i] = (split /,/,$iniConfig{Schedule}{"$section"})[3] ;
		$ups->{Schedule}->{MinOff}[$i] = (split /,/,$iniConfig{Schedule}{"$section"})[4] ;
		$ups->{Schedule}->{OutPort}[$i] = (split /,/,$iniConfig{Schedule}{"$section"})[5] ;
		$ups->{Schedule}->{ForceUpsSD}[$i] = (split /,/,$iniConfig{Schedule}{"$section"})[6] ;
		$ups->{Schedule}->{ForcePcSD}[$i] = (split /,/,$iniConfig{Schedule}{"$section"})[7] ;
		}

	$_ = $iniConfig{UpsParams}{Language} ;
   /I/i && do     				
			{ $ups->{LangID} = 0 ;} ;
   /E/i && do     				
			{ $ups->{LangID} = 1 ;} ;
   /D/i && do     				
			{ $ups->{LangID} = 2 ;} ;
   /F/i && do     				
			{ $ups->{LangID} = 3 ;} ;
	$ups->{Name}= $iniConfig{UpsList}{UpsModel} ;
	$ups->{Type}= $iniConfig{UpsParams}{UpsType} ;
	$ups->{BatteryType}= $iniConfig{UpsMDW}{TipoBat} ;
	$ups->{TScale}= $iniConfig{UpsMDW}{TempScale} ;
	$ups->{MsgFile}= $iniConfig{UpsMDW}{MsgFile} ;
	$ups->{MsgCmd}= $iniConfig{UpsMDW}{MsgCmd} ;
}

#-----------------------------------------------------------------------------------
# CheckEvents: handles all the events enabled and fired
#-----------------------------------------------------------------------------------
sub CheckEvents()
{
	my ($event,$interval,$module) ;
	my $eventcount ;
	my $message ;
	my $Delay ;
		
	foreach $event (keys %EventList)
		{
		if (($EventList{$event}->{Enabled} == 1) && ($EventList{$event}->{FireEvent} == 1))
			{
	      $eventcount = 0 ;
			#-----------------------------------------------------------------------------
			# i analize all the events fired and enabled
			#-----------------------------------------------------------------------------
			# first of all i notify the event to all clients, ...
			if ($EventList{$event}->{Notified} == 0)
				{
				$EventList{$event}->{Notified} = 1 ;
				#if ($event eq "E_SYSSD")
				#	{
				#	NotifyEvent ($EventList{$event}->{SubID},$EventList{$event}->{ID}) ;
				#	}
				#else
				#	{
					NotifyEvent ($EventList{$event}->{ID},$EventList{$event}->{SubID}) ;
				#	}
				}
			
			# ... log event section
			if (($EventList{$event}->{LogEna} == 1) && ($EventList{$event}->{RunTime} == 0))
				{
				$EventList{$event}->{Processed}++ ;
				#if ($event eq "E_SYSSD")
				#	{
				#	NotifyEvent ($EventList{$event}->{SubID},$EventList{$event}->{ID}) ;
				#	}
				#else
				#	{
					NotifyEvent ($EventList{$event}->{ID},$EventList{$event}->{SubID}) ;
				#	}
				#########################################
				# DA FARE: SCRITTURA EVENTO IN LOG FILE #
				#########################################
				WriteEventToLog ($EventList{$event}->{ID},$EventList{$event}->{SubID}) ;
				}
			elsif (($EventList{$event}->{LogEna} == 0) || ($EventList{$event}->{LogEna} == 2))
				{
				$eventcount++ ;
				}

			# ... message event section
			if ($EventList{$event}->{MsgEna} == 1) 
				{
				if ($EventList{$event}->{RunTime} == $EventList{$event}->{MsgDelay})
					{
					#################################
					# DA FARE: INVIO MESSAGGI POPUP #
					#################################
					if ($event eq "E_SYSSD")
						{
						$message = getmessage ($EventList{$event}->{SubID}).getmessage ($EventList{$event}->{ID}) ;
						}
					else
						{
						$message = getmessage ($EventList{$event}->{ID}) ;
						if ($EventList{$event}->{SubID})
							{								
							$message .= getmessage ($EventList{$event}->{SubID}) ;
							}
						}
					NotifyMessage ($message) ;
					if ($EventList{$event}->{MsgRepeat} == 0)
						{
						$EventList{$event}->{Processed}++ ;
						}
					}
				elsif (($EventList{$event}->{RunTime} > $EventList{$event}->{MsgDelay}) &&
						 ($EventList{$event}->{MsgRepeat} == 1))
					{
					$interval = $EventList{$event}->{RunTime} - $EventList{$event}->{MsgDelay};
					$module = $EventList{$event}->{MsgInterval} ;					
					if (($interval % $module) == 0)
						{
						#################################
						# DA FARE: INVIO MESSAGGI POPUP #
						#################################
						if ($event eq "E_BAT")
							{
							$message = getmessage (eveMSG_BMODE,
														  $EventList{$event}->{ShutdownDelay}-$EventList{$event}->{RunTime}) ;
							$EventList{$event}->{ID} = eveBMODE ;							  		
							$EventList{$event}->{SubID} = $EventList{$event}->{ShutdownDelay}-$EventList{$event}->{RunTime} ;							  		
							}
						elsif ($event eq "E_BLOW")
							{
							$message = getmessage (eveMSG_BLOW,30) ;
							}
						elsif ($event eq "E_SYSSD")
							{
							$message = getmessage ($EventList{$event}->{SubID}).getmessage ($EventList{$event}->{ID}) ;
							}
						else
							{
							$message = getmessage ($EventList{$event}->{ID}) ;
							if ($EventList{$event}->{SubID})
								{								
								$message .= getmessage ($EventList{$event}->{SubID}) ;
								}
							}												
							NotifyEvent ($EventList{$event}->{ID},$EventList{$event}->{SubID}) ;
							NotifyMessage ($message) ;
						}
					}
				}
			elsif (($EventList{$event}->{MsgEna} == 0) || ($EventList{$event}->{MsgEna} == 2))
				{
				$eventcount++ ;
				}

			# ... command file event section
			if (($EventList{$event}->{CmdEna} == 1) && 
				 ($EventList{$event}->{RunTime} == $EventList{$event}->{CmdDelay}))
				{
				RunCommand($EventList{$event}->{CmdFile}) ;
				$EventList{$event}->{Processed}++ ;
				}			
			elsif (($EventList{$event}->{CmdEna} == 0) || ($EventList{$event}->{CmdEna} == 2))
				{
				$eventcount++ ;
				}
			
			# ... email event section
			if (($EventList{$event}->{EmailEna} == 1) && 
				 ($EventList{$event}->{RunTime} == $EventList{$event}->{EmailDelay}))
				{
				#################################
				# DA IMPLEMENTARE               #
				#################################
				$EventList{$event}->{Processed}++ ;
				}
			elsif (($EventList{$event}->{EmailEna} == 0) || ($EventList{$event}->{EmailEna} == 2))
				{
				$eventcount++ ;
				}
			# ... shutdown event section
			if ((($EventList{$event}->{ShutdownEna} == 1) || ($EventList{$event}->{ShutdownEna} == 3)) && 
				 ($EventList{$event}->{RunTime} == $EventList{$event}->{ShutdownDelay}))
				{
				if ($event eq "E_BAT")
					{
					$EventList{E_BAT}->{FireEvent} = 0 ;
					$EventList{E_SYSSD}->{FireEvent} = 1 ;
					$EventList{E_SYSSD}->{SubID} = eveBMODE ;
					}
				elsif ($event eq "E_BLOW")
					{
					$EventList{E_BLOW}->{FireEvent} = 0 ;
					$EventList{E_SYSSD}->{FireEvent} = 1 ;
					$EventList{E_SYSSD}->{SubID} = eveBLOW ;
					$message = getmessage (eveMSG_BLOW,30) ;
					}
				elsif ($event eq "E_COMLOST")
					{
					$EventList{E_COMLOST}->{FireEvent} = 0 ;
					$EventList{E_SYSSD}->{FireEvent} = 1 ;
					$EventList{E_SYSSD}->{SubID} = eveCOMLOST ;
					}
				elsif ($event eq "E_SCHESD")
					{
					# OSX SetReport(chr(CMD_STDBY)) ;
					$Delay = $ups->{Schedule}->{SDDelay} ;
               if ($Delay < 60)
               	{ $Delay = 60 ; }
					if ($ups->{Model}->{EnterpriseFamily} eq 0)
						{					
               	if (SendCmdUPS(CMD_SD_ONESHOT, LEN_SD_ONESHOT, (255, 1, int($Delay/256), 
               				  ($Delay % 256), 0, 0, 0)) < 0)
                  	{ ; } 
						}
					if ($ups->{Model}->{EnterpriseFamily} eq 1)
						{					
						# CHR
               	my $WUDelay = Get_CHR_CYP_WUDelay () ; 
               	if (SendCmdUPS(CMD_SD_ONESHOT, LEN_SD_ONESHOT, (255, 1, $Delay, $WUDelay)) < 0)
                  	{ ; } 
						}
					if ($ups->{Model}->{EnterpriseFamily} eq 2)
						{					
						# CYP
               	my $WUDelay = Get_CHR_CYP_WUDelay () ; 
               	if (SendCmdUPS(CMD_SD_ONESHOT, LEN_SD_ONESHOT, (255, 1, $Delay, $WUDelay)) < 0)
                  	{ ; } 
						}
					RunCommand($EventList{$event}->{ShutdownCmd}) ;

					SystemSD(10) ;
					}
				elsif ($event eq "E_SYSSD")
					{
					if ($ups->{Measures}->{State} & 0x1)
						{
						$Delay = $ups->{Schedule}->{SDDelay} ;
   	            if ($Delay < 60)
      	         	{ $Delay = 60 ; }
						if ($ups->{Model}->{EnterpriseFamily} eq 0)
							{					
		               if (SendCmdUPS(CMD_SD_ONESHOT, LEN_SD_ONESHOT, (255, 2, int($Delay/256), 
		               				  ($Delay % 256), 0, 0, 0)) < 0)
   		               { ; } 
							}
						if ($ups->{Model}->{EnterpriseFamily} eq 1)
							{					
							# CHR
	               	if (SendCmdUPS(CMD_SD_ONESHOT, LEN_SD_ONESHOT, (255, 2, $Delay, 0)) < 0)
   	               	{ ; } 
							}
						if ($ups->{Model}->{EnterpriseFamily} eq 2)
							{					
							# CYP
	               	if (SendCmdUPS(CMD_SD_ONESHOT, LEN_SD_ONESHOT, (255, 2, $Delay, 0)) < 0)
   	               	{ ; } 
							}

						RunCommand($EventList{$event}->{ShutdownCmd}) ;

						SystemSD(10) ;
						}
					else
						{
						# OSX SetReport(chr(CMD_STDBY)) ;
						$Delay = $ups->{Schedule}->{SDDelay} ;
   	            if ($Delay < 60)
      	         	{ $Delay = 60 ; }
						if ($ups->{Model}->{EnterpriseFamily} eq 0)
							{					
	                  if (SendCmdUPS(CMD_SD_ONESHOT, LEN_SD_ONESHOT, (255, 1, int($Delay/256), 
   	               				  ($Delay % 256), 0, 0, 0)) < 0)
      	               { ; } 
							}
							
						if ($ups->{Model}->{EnterpriseFamily} eq 1)
							{					
							# CHR
	               	if (SendCmdUPS(CMD_SD_ONESHOT, LEN_SD_ONESHOT, (255, 1, $Delay, 0)) < 0)
   	               	{ ; } 
							}
						if ($ups->{Model}->{EnterpriseFamily} eq 2)
							{					
							# CYP
	               	if (SendCmdUPS(CMD_SD_ONESHOT, LEN_SD_ONESHOT, (255, 1, $Delay, 0)) < 0)
   	               	{ ; } 
							}

						RunCommand($EventList{$event}->{ShutdownCmd}) ;
						SystemSD(10) ;
						}
					}												
				else
					{
					# OSX SetReport(chr(CMD_STDBY)) ;
					$Delay = $ups->{Schedule}->{SDDelay} ;
	            if ($Delay < 60)
   	         	{ $Delay = 60 ; }

					if ($ups->{Model}->{EnterpriseFamily} eq 0)
						{					
                  if (SendCmdUPS(CMD_SD_ONESHOT, LEN_SD_ONESHOT, (255, 1, int($Delay/256), 
	               				  ($Delay % 256), 0, 0, 0)) < 0)
   	               { ; } 
						}
						
					if ($ups->{Model}->{EnterpriseFamily} eq 1)
						{					
						# CHR
               	if (SendCmdUPS(CMD_SD_ONESHOT, LEN_SD_ONESHOT, (255, 1, $Delay, 0)) < 0)
	               	{ ; } 
						}
					if ($ups->{Model}->{EnterpriseFamily} eq 2)
						{					
						# CYP
               	if (SendCmdUPS(CMD_SD_ONESHOT, LEN_SD_ONESHOT, (255, 1, $Delay, 0)) < 0)
	               	{ ; } 
						}

					RunCommand($EventList{$event}->{ShutdownCmd}) ;
					SystemSD(10) ;
					}
				$EventList{$event}->{Processed}++ ;
				}
			elsif (($EventList{$event}->{ShutdownEna} == 0) || ($EventList{$event}->{ShutdownEna} == 2))
				{
				$eventcount++ ;
				}

			$EventList{$event}->{RunTime}++ ;
			# ... if all events has been notified, disable
			if (($eventcount + $EventList{$event}->{Processed}) == 5)
				{
				$EventList{$event}->{FireEvent} = 0 ;
				$EventList{$event}->{RunTime} = 0 ;
				}
			}
		else
			{
			$EventList{$event}->{Processed} = 0 ;
			$EventList{$event}->{RunTime} = 0 ;
			$EventList{$event}->{Notified} = 0 ;
			$EventList{$event}->{SubID} = 0 ;
			}
		}
}

#-----------------------------------------------------------------------------------
# NotifyEvent: notifies to all clients an UPS event
#-----------------------------------------------------------------------------------
sub NotifyEvent(@)
{
	my ($evid, $evsubid) = @_ ;
	my $evstring  = "E-";
	if ($evsubid)
		{
		$evstring .= $evid . "-" . $evsubid . "\r\n" ;
		}
	else
		{
		$evstring .= $evid . "\r\n" ;
		}
	NotifyClients (\$evstring) ;
}

#-----------------------------------------------------------------------------------
# NotifyMessage: notifies to all clients a message
#-----------------------------------------------------------------------------------
sub NotifyMessage($)
{
	my $message = shift ;

	if (!$OS_win)
		{
	  	# if message file is != ""
  		if ($ups->{MsgFile})
    		{
		   open FH, "> $ups->{MsgFile}" ;  						# open file
 			print FH "#!/bin/sh\n" ;
		 	print FH "echo \$1 > $ups->{MsgFile}.msg\n" ;
 			print FH "$ups->{MsgCmd} < $ups->{MsgFile}.msg\n" ;
 			close(FH) ;                     						# close file
 			chmod 0777, $ups->{MsgFile} ;   						# change permissions

 			# run the command only if defined...    
 			if ($ups->{MsgCmd})      
   			{
   			RunCommand("$ups->{MsgFile} \"$message\" &") ;
   			}
   		}
   	}    
	else
	   {
	   printf ("\n Message from the UPS service@%s \n\n\t$message \n\n", $connection->{ServerAddr});
	   }
	$message  = "M-".$message."\r\n" ;
	NotifyClients (\$message) ;
}

####################################################################################
#-----------------------------------------------------------------------------------

#-----------------------------------------------------------------------------------
####################################################################################
# SOCKETS
####################################################################################
#-----------------------------------------------------------------------------------

#-----------------------------------------------------------------------------
# HandleSocket: handles socket connection
#-----------------------------------------------------------------------------
sub HandleSocket ()
{
	my $client;
	my $rv;
	my $data;

	#-----------------------------------------------------------------------------
   # Check for new information on the connections we have; anything to read or 
   # accept?
	#-----------------------------------------------------------------------------
	foreach $client ($connection->{Select}->can_read(0.01)) 
		{
		if ($client == $connection->{Server}) 
        	{
			#-----------------------------------------------------------------------------
         # accept a new connection
			#-----------------------------------------------------------------------------
			$connection->{NumClients} = $connection->{Select}-> count() ;
         
			if ($connection->{NumClients} <= MAX_CLIENTS) 
         	{
            $connection->{client} = $connection->{Server}->accept();
            $connection->{Select}->add($connection->{client});
            $data = "C-2299" ;
         	$connection->TmpBuffer->{$client} .= $data;
            push( @{$connection->Ready->{$client}}, $data );
         	} 
         else 
         	{
				#-----------------------------------------------------------------------------
				# we accept at least MAX_CLIENTS connections
				#-----------------------------------------------------------------------------
            $client = $connection->{Server}->accept();
            $connection->{Select}->remove($client);
            close $client;            
            }
        	} 
		else 
			{
			#-----------------------------------------------------------------------------
         # read data
			#-----------------------------------------------------------------------------
         $data = '';
         $rv   = $client->recv($data, POSIX::BUFSIZ, 0);
         unless (defined($rv) && length $data) 
         	{
				#-----------------------------------------------------------------------------
            # This would be the end of file and/or connection, so close the client
				#-----------------------------------------------------------------------------
	         delete $connection->TmpBuffer->{$client};
	         delete $connection->InBuffer->{$client};
	         delete $connection->OutBuffer->{$client};
	         delete $connection->Ready->{$client};
	
	         $connection->{Select}->remove($client);
	         close($client);
	         next;
         	}
         	
         # put the data in a temporary buffer
         if (length($data))
            {
            $connection->TmpBuffer->{$client} .= $data ;
            }
         
			#-----------------------------------------------------------------------------
   		# test whether the data in the buffer or the data we just read means there is 
   		# a complete request waiting to be fulfilled.  
   		# If there is, set $ready{$client} to the requests waiting to be fulfilled.
			#-----------------------------------------------------------------------------
			if ($connection->TmpBuffer->{$client} =~ m/^(.*)(\r\n)/)
				{
				$_ = $connection->TmpBuffer->{$client} ;
            s/\r//g ;
            s/\n//g ;
            $connection->TmpBuffer->{$client} = $_ ;
            push( @{$connection->Ready->{$client}}, $connection->TmpBuffer->{$client} );
       		}
       	}
		}
	#-----------------------------------------------------------------------------
   # any complete requests to process?
	#-----------------------------------------------------------------------------
   foreach $client (keys %{$connection->Ready}) 
   	{
      HandleSckData($client);
    	}

	#-----------------------------------------------------------------------------
   # buffers to flush?
	#-----------------------------------------------------------------------------
	foreach $client ($connection->{Select}->can_write(0.01)) 
		{
		#-----------------------------------------------------------------------------
      # Skip this client if we have nothing to say
		#-----------------------------------------------------------------------------
      next unless exists $connection->OutBuffer->{$client};
		$rv = $client->send($connection->OutBuffer->{$client}, 0);
      unless (defined $rv) 
      	{
         warn "I was told I could write, but I can't.\n";
         next;
			}
		if ($rv == length $connection->OutBuffer->{$client} || $! == POSIX::EWOULDBLOCK) 
			{
         substr($connection->OutBuffer->{$client}, 0, $rv) = '';
         delete $connection->OutBuffer->{$client} unless length $connection->OutBuffer->{$client};
        	} 
      else 
      	{
			#-----------------------------------------------------------------------------
         # Couldn't write all the data, and it wasn't because it would have blocked.  
         # Shutdown and move on.
			#-----------------------------------------------------------------------------
         delete $connection->InBuffer->{$client};
         delete $connection->OutBuffer->{$client};
         delete $connection->Ready->{$client};

         $connection->{Select}->remove($client);
         close($client);
         next;
        	}
    	}

	foreach $client ($connection->{Select}->handles) 
		{
      delete $connection->OutBuffer->{$client};
		}


	#-----------------------------------------------------------------------------
   # Out of band data?
	#-----------------------------------------------------------------------------
	foreach $client ($connection->{Select}->has_exception(0)) 
		{  
		#-----------------------------------------------------------------------------
		# arg is timeout, deal with out-of-band data here, if you want to.
		#-----------------------------------------------------------------------------
		}

	$connection->{NumClients} = $connection->{Select}-> count() ;

}

#-----------------------------------------------------------------------------
# HandleSckData: deals with all pending requests for $client
#-----------------------------------------------------------------------------
sub HandleSckData ($) 
{
   # requests are in $connection->Ready->{$client};
   # send output to $connection->OutBuffer->{$client}
	my $client = shift;
   my $request;
   my $Data ;					# record data from the client
   my @Token ;    			# contains splitted record data
   my @Token1 ;
   my $NumTokens ;   		# number of parameters in the message
	my $all = 0;

   foreach $request (@{$connection->Ready->{$client}}) 
   	{
      # $request is the text of the request
      # put text of reply into $connection->OutBuffer->{$client}
    	$Data = $connection->TmpBuffer->{$client} ;
      $Data =~ s/\s\s/ /g ;
      $Data =~ s/\s$//g ;
      $Data =~ s/-{1}/ / ;
      # @Token contains the different tokens of the message (separated by ' ')
      @Token = split(/ /, $Data) ;           # split of the tokens
      $NumTokens = @Token ;

      $_ = $Token[0] ;  			            # @Token[0] is the letter which identifies the command
   
      /M/i && do     				            # looking for 'M' message (MESSAGE)
      	{ $Data = 0 ; } ;
      /D/i && do     				            # looking for 'D' message (DATA)
      	{ $Data = 0 ; } ;
         
      /C/i && do     				            # looking for 'C' message (COMMAND)
      	{ ($Data, $all) = CheckCommand(@Token); } ;
         
      /E/i && do     				            # looking for 'E' message (EVENT)
      	{ $Data = 0 ; } ;

      /RS/i && do     				            # looking for 'RS' message (Read sections message)
      	{ 
      	$Data = "";
      	foreach my $section (keys %iniConfig)
            {
      	   $Data .= sprintf("%s ", $section) ;
      	   }
         $Data = $Data . chr(13) . chr(10);
         };

      /RK/i && do     				            # looking for 'RK' message (Read section keys message)
      	{ 
      	my $section = $Token[1];
         $Data = "RK-" . $section . "\r\n";
         foreach my $subkey (keys %{$iniConfig{$section}}) 
            {
      	   $Data .= sprintf("%s=%s \r\n", $subkey, $iniConfig{$section}{$subkey}) ;
            }
      	} ;
      
      /RD/i && do     				            # looking for 'RD' message (Dump INI file message)
      	{ 
      	$Data = "";
      	foreach my $section (keys %iniConfig)
            {
            $Data .= "RK-" . $section . "\r\n";
            foreach my $subkey (keys %{$iniConfig{$section}}) 
               {
      	      $Data .= sprintf("%s=%s \r\n", $subkey, $iniConfig{$section}{$subkey}) ;
               }
            }
      	} ;

      /WK/i && do     				            # looking for 'WS' message (Write section keys message)
      	{ 
      	my $section = $Token[1];
         my $subkey = (split(/=/,$Token[2]))[0];
      	my $value = (split(/=/, $Data))[1] ;
      	$iniConfig{$section}{$subkey} = $value;
   		WriteIniFile(\%iniConfig,$INIFILE) ;
      	$Data = "WK-" . $section . " " . $subkey . "=" . $value . "\r\n";
      	LoadEvents() ;
			CheckEvents ();
      	} ;

      /WD/i && do     				            # looking for 'WD' message (Delete section keys message)
      	{ 
      	my $section = $Token[1];
         my $subkey = $Token[2];
         delete $iniConfig{$section}{$subkey};
   		WriteIniFile(\%iniConfig,$INIFILE) ;
      	$Data = "WD-" . $section . " " . $subkey . "\r\n";
      	printf "$Data";
      	} ;

 		if ($all == 0)
 			{ $connection->OutBuffer->{$client} .= $Data; }
 		else
 			{ NotifyClients(\$Data); }
    	delete $connection->InBuffer->{$client} ;
    	delete $connection->TmpBuffer->{$client} ;
    	}
	delete $connection->Ready->{$client};
}

#-----------------------------------------------------------------------------
# CheckCommand: checks and handles the received COMMAND on the socket 
# connection
#-----------------------------------------------------------------------------
sub CheckCommand(@)
{
   my $NumParms = @_ - 2 ;
   my $CmdID = $_[1] ;
   my @Parms = @_[2..@_] ;
   my @Data ;
   my $ii ;
   my $Data ;
   my $Cmd ;
   my $i ;
   
   $_ = $CmdID ;
 
	/^2200$/i && do
	      {
         if (SendCmdUPS(CMD_GET_STATUS, LEN_GET_STATUS) < 0)
            { $Data = "D-" . $CmdID . " " . $retBuffer[0] . "\r\n" ; } 
         else
	         {
	         $Data = "D-" . $CmdID . " 0 5B" . sprintf(" %02X", $retBuffer[0]) ;
	         for ($i=1 ; $i < $retBuffer[0]+2 ; $i++)
	            {
	            $Data .= sprintf(" %02X", $retBuffer[$i]) ;
	            }
	         $Data .= "\r\n" ;
	         }
  			return ($Data,1) ;
	      } ;

	/^2201$/i && do
	      {
         if (SendCmdUPS(CMD_GET_MEASURES, LEN_GET_MEASURES) < 0)
            { $Data = "D-" . $CmdID . " " . $retBuffer[0] . "\r\n" ; } 
         else
	         {
	         $Data = "D-" . $CmdID . " 0 5B" . sprintf(" %02X", $retBuffer[0]) ;
	         for ($i=1 ; $i < $retBuffer[0]+2 ; $i++)
	            {
	            $Data .= sprintf(" %02X", $retBuffer[$i]) ;
	            }
	         $Data .= "\r\n" ;
	         }
  			return ($Data,1) ;
	      } ;

	/^2202$/i && do
	      {
         if (SendCmdUPS(CMD_GET_CONFIG, LEN_GET_CONFIG) < 0)
            { $Data = "D-" . $CmdID . " " . $retBuffer[0] . "\r\n" ; } 
         else
	         {
	         $Data = "D-" . $CmdID . " 0 5B" . sprintf(" %02X", $retBuffer[0]) ;
	         for ($i=1 ; $i < $retBuffer[0]+2 ; $i++)
	            {
	            $Data .= sprintf(" %02X", $retBuffer[$i]) ;
	            }
	         $Data .= "\r\n" ;
	         }
  			return ($Data,1) ;
	      } ;

	/^2203$/i && do
	      {
         if (SendCmdUPS(CMD_GET_BATT_STAT, LEN_GET_BATT_STAT) < 0)
            { $Data = "D-" . $CmdID . " " . $retBuffer[0] . "\r\n" ; } 
         else
	         {
	         $Data = "D-" . $CmdID . " 0 5B" . sprintf(" %02X", $retBuffer[0]) ;
	         for ($i=1 ; $i < $retBuffer[0]+2 ; $i++)
	            {
	            $Data .= sprintf(" %02X", $retBuffer[$i]) ;
	            }
	         $Data .= "\r\n" ;
	         }
  			return ($Data,1) ;
	      } ;

	/^2222$/i && do
	      {
         if (SendCmdUPS(CMD_GET_BATT_TEST, LEN_GET_BATT_TEST) < 0)
            { $Data = "D-" . $CmdID . " " . $retBuffer[0] . "\r\n" ; } 
         else
	         {
	         $Data = "D-" . $CmdID . " 0 5B" . sprintf(" %02X", $retBuffer[0]) ;
	         for ($i=1 ; $i < $retBuffer[0]+2 ; $i++)
	            {
	            $Data .= sprintf(" %02X", $retBuffer[$i]) ;
	            }
	         $Data .= "\r\n" ;
	         }
  			return ($Data,1) ;
	      } ;

	/^2223$/i && do
	      {
         if (SendCmdUPS(CMD_BUZZER_OFF, LEN_BUZZER_OFF) < 0)
            { $Data = "D-" . $CmdID . " " . $retBuffer[0] . "\r\n" ; } 
         else
	         {
	         $Data = "D-" . $CmdID . " 0 5B" . sprintf(" %02X", $retBuffer[0]) ;
	         for ($i=1 ; $i < $retBuffer[0]+2 ; $i++)
	            {
	            $Data .= sprintf(" %02X", $retBuffer[$i]) ;
	            }
	         $Data .= "\r\n" ;
	         }
  			return ($Data,1) ;
	      } ;

	/^2241$/i && do
	      {
         if (SendCmdUPS(CMD_GET_SD_ONESHOT, LEN_GET_SD_ONESHOT) < 0)
            { $Data = "D-" . $CmdID . " " . $retBuffer[0] . "\r\n" ; } 
         else
	         {
	         $Data = "D-" . $CmdID . " 0 5B" . sprintf(" %02X", $retBuffer[0]) ;
	         for ($i=1 ; $i < $retBuffer[0]+2 ; $i++)
	            {
	            $Data .= sprintf(" %02X", $retBuffer[$i]) ;
	            }
	         $Data .= "\r\n" ;
	         }
  			return ($Data,1) ;
	      } ;


	/^2243$/i && do
	      {
         if (SendCmdUPS(CMD_GET_SCHEDULE, LEN_GET_SCHEDULE, @Parms) < 0)
            { $Data = "D-" . $CmdID . " " . $retBuffer[0] . "\r\n" ; } 
         else
	         {
	         $Data = "D-" . $CmdID . " 0 5B" . sprintf(" %02X", $retBuffer[0]) ;
	         for ($i=1 ; $i < $retBuffer[0]+2 ; $i++)
	            {
	            $Data .= sprintf(" %02X", $retBuffer[$i]) ;
	            }
	         $Data .= "\r\n" ;
	         }
  			return ($Data,1) ;
	      } ;


	/^2250$/i && do
	      {
         if (SendCmdUPS(CMD_GET_EEP_BLOCK, LEN_GET_EEP_BLOCK, @Parms) < 0)
            { $Data = "D-" . $CmdID . " " . $retBuffer[0] . "\r\n" ; } 
         else
	         {
	         $Data = "D-" . $CmdID . " 0 5B" . sprintf(" %02X", $retBuffer[0]) ;
	         for ($i=1 ; $i < $retBuffer[0]+2 ; $i++)
	            {
	            $Data .= sprintf(" %02X", $retBuffer[$i]) ;
	            }
	         $Data .= "\r\n" ;
	         }
  			return ($Data,1) ;
	      } ;


	/^2207$/i && do      # command not valid
	      { $Data = "D-" . $CmdID . " 5B 02 15 11 06\r\n" ; return ($Data,1) ;  } ;
	/^2220$/i && do      # command not valid
	      { $Data = "D-" . $CmdID . " 5B 02 15 11 06\r\n" ; return ($Data,1) ;  } ;
	/^2221$/i && do      # command not valid
	      { $Data = "D-" . $CmdID . " 5B 02 15 11 06\r\n" ; return ($Data,1) ;  } ;
	/^2240$/i && do      # command not found
	      { $Data = "D-" . $CmdID . " 5B 02 15 11 06\r\n" ; return ($Data,1) ;  } ;
	/^2242$/i && do      # command not found
	      { $Data = "D-" . $CmdID . " 5B 02 15 11 06\r\n" ; return ($Data,1) ;  } ;
	/^2251$/i && do      # command not found
	      { $Data = "D-" . $CmdID . " 5B 02 15 11 06\r\n" ; return ($Data,1) ;  } ;
	/^2252$/i && do      # command not found
	      { $Data = "D-" . $CmdID . " 5B 02 15 11 06\r\n" ; return ($Data,1) ;  } ;
	/^2290$/i && do      # command not found
	      { $Data = "D-" . $CmdID . " 5B 02 15 11 06\r\n" ; return ($Data,1) ;  } ;

   /^2299$/i && do     				# UPS configuration parameters (GET)
			{ 
   		# ups model identification
  			$Data = sprintf(IDENT_1, ($ups->{ComState} eq 1) ? 0 : 1, $connection->{NumClients}  ) ;
			if ($ups->{ComState} eq UPS_COMSTATE_POLL_MODE)
				{
				$Data = $Data . sprintf("D-2001 \"%s\" \"%s\" \"%08X\" \"%08X\" %04d/%02d/%02d %3.1f %3.1f %d.%d.%d %d\r\n", 
               				         $ups->{Model}->{Model}, $ups->{Model}->{SerialNumber},
               				         vec($ups->{Model}->{NetID},0,32), vec($ups->{Model}->{NetName},0,32),
               				         $ups->{Model}->{YearProd}, $ups->{Model}->{MonthProd}, $ups->{Model}->{DayProd}, 
                                    $ups->{Model}->{HWver}, $ups->{Model}->{BRDver}, 
		         	   				   $ups->{Model}->{FwMajor},$ups->{Model}->{FwMinor},$ups->{Model}->{FwsubVersion},
	   				                  $ups->{Model}->{BattNumber} ) ;

				$Data = $Data . sprintf(IDENT_2, $connection->{ServerAddr}) ;
				for ($i=0; $i<6; $i++)
					{
					$Data = $Data . sprintf("D-201%d %02X %02d %02d %02d %02d %02X %01d\r\n", $i,
													$ups->{Schedule}->{WeekDay}[$i],
													$ups->{Schedule}->{HourOn}[$i],
													$ups->{Schedule}->{MinOn}[$i],
													$ups->{Schedule}->{HourOff}[$i],
													$ups->{Schedule}->{MinOff}[$i],
													$ups->{Schedule}->{OutPort}[$i],
													$ups->{Schedule}->{ForceUpsSD}[$i]) ;
					}
   		   }
  			return ($Data,1) ;
			} ;
}

#-----------------------------------------------------------------------------
# NotifyClients: sends to all the program cleints, the <param> data
#-----------------------------------------------------------------------------
sub NotifyClients ($) 
{
   my $client;
   my $Data_r = shift ;
   
	foreach $client ($connection->{Select}->handles) 
		{
		$connection->OutBuffer->{$client} .= $$Data_r ;
		}
}

#-----------------------------------------------------------------------------
# CloseSockets: closes all the opened sockets
#-----------------------------------------------------------------------------
sub CloseSockets()
{
	my $client ;
	if ($connection)
		{
	   foreach $client ($connection->{Select}->handles) 
   		{
			$client->close() ;
			}
   	$connection->{Server}->close() ;
   	undef $connection ;
   	}
}


#-----------------------------------------------------------------------------------
####################################################################################
# UDP
####################################################################################

#-----------------------------------------------------------------------------
# HandleUDP: handles UDP connection on port 18880
#-----------------------------------------------------------------------------
sub HandleUDP ()
{
	my $strToSend;
	if ($UDPClient != -1)
		{
		my @Ready = $UDPSelect->can_read(.1) ;
		foreach my $fh( @Ready )
      	{ 
    		my $UDPData ;
    		$UDPClient->recv($UDPData, 512);
    		my ($rport,$ripaddr) = sockaddr_in($UDPClient->peername);
			$_ = $UDPData;
			
      	/GetInfo/i && do     								      
         	{ 
				#-----------------------------------------------------------------------------
				# If available, the infos sent will be the following:
				#		Info: this field is necessary, and represents general Company onformations
				#		MyIP: PC IP address
				#		MasterIP: Master IP address
				#		Gway: default gateway
				#		DHCP: (for tcpIP modules only)
				#		FWtcp: (for tcpIP modules only)
				#		Flags: (for tcpIP modules only)
				#		MAC: network adapter MAC address
				#		OS: operating system
				#		FW: UPS firmware release
				#		M/S: master or slave application
				#		Model: UPS model
				#		S/N: serial number
				#		nMame: PC network name
				#		ComPort: PC/UPS communication port
				#-----------------------------------------------------------------------------
				if ($ups->{ComState} eq UPS_COMSTATE_POLL_MODE)
					{				
					$strToSend = sprintf("Info=Copyright 2004 - MicroDowell S.p.A.\r\nMyIP=%s\r\nFW=%d.%d.%d\r\nnName=%s\r\nS/N=%s\r\nModel=%s\r\nM/S=M\r\nComPort=%s\r\n",
											$connection->{ServerAddr},
								      	$ups->{Model}->{FwMajor},
      									$ups->{Model}->{FwMinor},
      									$ups->{Model}->{FwsubVersion},
											$connection->{ServerName},							
								         $ups->{Model}->{SerialNumber},
											$ups->{Model}->{Model},											
											$iniConfig{UpsParams}{Port});
					}
				else
					{
					$strToSend = sprintf("Info=Copyright 2004 - MicroDowell S.p.A.\r\nMyIP=%s\r\nnName=%s\r\nM/S=M\r\nComPort=%s\r\n",
											$connection->{ServerAddr},
											$connection->{ServerName},							
											$iniConfig{UpsParams}{Port});
					}
				if ($^O eq "darwin")
					{
					$strToSend .= "OS=Mac OSX\r\n"
					}
 				else
 					{
					$strToSend .= "OS=Unix/Linux\r\n"
 					}
				$UDPClient->send($strToSend);
				} ;

      	/GetStatus/i && do     								      
				{ 
				#-----------------------------------------------------------------------------
				# If available, the infos sent will be the following:
				#		Status: this field is necessary, and represents the UPS status
				#		Batt: battery voltage/percent
				#		Vac: Input/output voltage
				#		Temp: UPS internal temperature
				#		Load: load and load percent
				#		Freq: input/output frequency
				#		RunT: battery run time
				#		CommOK: UPS communication status (1=OK, 0=KO)
				#-----------------------------------------------------------------------------
				if ($ups->{ComState} eq UPS_COMSTATE_POLL_MODE)
					{
   				$strToSend = sprintf("StatusUPS=%08X\r\nBatt=%5.1f %d\r\nVac=%5.1f %5.1f\r\nTemp=%5.1f\r\nLoad=%d %d\r\nFreq=%5.1f %5.1f\r\nRunT=0\r\nCommOK=1\r\n",
                     				$ups->{Measures}->{State}, 
                     				$ups->{Measures}->{Vbat}, $ups->{Measures}->{BattPerc},
                     				$ups->{Measures}->{Vin}, $ups->{Measures}->{Vout},
                     				$ups->{Measures}->{Temperature}, 
                     				$ups->{Measures}->{Load}, $ups->{Measures}->{LoadPerc},    
                     				$ups->{Measures}->{InFreq},$ups->{Measures}->{OutFreq}) ;
					}
				else
					{
   				$strToSend = sprintf("StatusUPS=00000000\r\nBatt=0.0 0\r\nVac=0.0 0.0\r\nTemp=0.0\r\nLoad=0 0\r\nFreq=0.0 0.0\r\nRunT=0\r\nCommOK=0\r\n");
					}
				$UDPClient->send($strToSend);
         	} ;

			} 
		}
}
#-----------------------------------------------------------------------------------

#-----------------------------------------------------------------------------------
####################################################################################
# READ/WRITE configuration file
####################################################################################
#-----------------------------------------------------------------------------------

#-----------------------------------------------------------------------------------
# ReadIniFile (iniTable, iniFile)  
#		- iniTable: reference to the hash table used to push the INI data
#		- iniFile: INI file name
# It returns 1, if everything is good, 0 otherwise
#-----------------------------------------------------------------------------------
sub ReadIniFile {
	local *FILE ;
  	my (@ini,$section,$name,$value,$i) ;
  	my $href = $_[0] ; 		# reference to hash table
  	my $file = $_[1] ; 		# the INI file name  
  
  	foreach$i(keys%$href){delete${$href}{$i}} ;  # we clear all the hash elements
  	unless(open FILE,$file){return 0} ;
  	@ini=<FILE> ;
  	close FILE ;
  	chomp @ini ;
  	$section = '' ;
  
  	foreach $i (@ini)
  	{ 
  		while($i =~ s/^\s+|\s+$//){} ;
    	if($i =~ /^\[(.+)\]$/)
    	{ 
    		# the beginning of section  
      	$section = $1 ;
    	}
    	elsif($i =~ /^[;#]/)
    	{ 
    		# comments
    	}
    	elsif( ($i =~ /=/) && $section)
    	{ 
    		# the parameters  
      	($name,$value) = split(/=/,$i,2) ;
      	$value = $2 if($value =~ /^(["'])(.*)\1$/) ;
      	if($name =~ /^[a-zA-Z_][a-zA-Z0-9_]*$/)
      	{ 
      		# save the value in the hash table
        		$$href{$section}{$name} = $value ;
      	}
    	}
  	}
  	return 1 ;
}

#-----------------------------------------------------------------------------------
# WriteIniFile (iniTable, iniFile)  
#		- iniTable: reference to the hash table used to push the INI data
#		- iniFile: INI file name
# It returns 1, if everything is good, 0 otherwise
#-----------------------------------------------------------------------------------
sub WriteIniFile
{ 
	local *FILE ;
  	my (@ini,$section,$name,$value,$i) ;
  	my %ls ; 					# hash:  key/wrench - section, value - line  
  	my $href = $_[0] ; 		# reference to hash table  
  	my $file = $_[1] ; 		# the INI file name
  	my $brackets ;
  	
  	unless(open FILE,$file){return 0} ;
  	@ini=<FILE> ;
  	close FILE ;
  	chomp @ini ;
  	$section = '' ;

  	for($i=0;$i<=$#ini;$i++)
  	{ 
  		while($ini[$i]=~s/^\s+|\s+$//){} ;
    	if($ini[$i]=~/^\[(.+)\]$/)
    	{ 
    		# the beginning of section if remained the elements of the foregoing section
      	foreach $name(keys %{$$href{$section}})
      	{ 
      		$ini[$ls{$section}].="\n$name=\"$$href{$section}{$name}\"" ;
        		delete $$href{$section}{$name} ;
      	}
      	delete $$href{$section} ;
      	$ls{$1} = $i ;
      	$section = $1 ;
      	
      	# if the obtained section is removed   
      	$ini[$i] = '; '.$ini[$i] unless(exists $$href{$section}) ;
    	}
    	elsif($ini[$i] =~ /^[;#]/)
    	{ 
    		# comments
    	}
    	elsif( ($ini[$i]=~/=/) && $section)
    	{ 
    		# the parameters  
      	if(!exists($$href{$section}))
      	{ 
      		# section is removed - remarim all parameters  
        		$ini[$i] = '; '.$ini[$i] ;
      	}
      	else
      	{ 
      		# section is not removed - we analyze the parameters  
        		($name,$value) = split(/=/,$ini[$i],2) ;
        		$value =~ /^(["']{0,1})(.*)\1$/;
        		$value = $2 ;
        		$brackets = $1 ;
        		if($name =~ /^[a-zA-Z_][a-zA-Z0-9_]*$/)
        		{ 
        			# if $name generally exists and it does not contain left symbols
          		if($$href{$section}{$name} ne $value)
          		{ 
          			# if value of name changed or removed   
            		if(exists $$href{$section}{$name})
            		{ 
            			# value of name changed   
              			$ini[$i] = "$name=$brackets$$href{$section}{$name}$brackets" ;
              			delete $$href{$section}{$name} ;
            		}
            		else
            		{ 
            			# value of to name is removed   
              			$ini[$i] = '; '.$ini[$i] ;
            		}
          		}
          		else
          		{ 
          			# if value of name did not change   
            		delete $$href{$section}{$name} ;
          		}
        		}
        		else
        		{ 
        			# $name contain symbols different from ' A- z ', ' A- Z ', " 0-9 ' and " _ '
          		foreach$i(keys%$href){delete${$href}{$i}} ;  # we move away all elements of hash  
          		return 0 ;
        		}
      	}
    	}
  	}

  	# if the elements of last section remained   
  	foreach $name(keys %{$$href{$section}})
  	{ 
  		$ini[$ls{$section}] .= "\n$name=\"${$$href{$section}}{$name}\"" ;
    	delete $$href{$section}{$name} ;
  	}
  	delete $$href{$section} ;

  	# we add new sections  
  	foreach $section(keys %$href)
  	{ 
  		# in $section new section  
    	@ini=(@ini,"\n[$section]") ;
    	foreach $name(keys %{$$href{$section}})
    	{ 
    		@ini =(@ini,"$name=\"${$$href{$section}}{$name}\"") ;
      	delete ${$$href{$section}}{$name} ;
    	}
    	delete $$href{$section} ;
  	}

  	# we write the changed Ini- file  
  	unless(open FILE,">$file"){return 0} ;
  	foreach$i(@ini)
  	   { 	
  		print FILE "$i\n" ;
  	   }
  	close FILE;
  	unless( ReadIniFile($href,$file) ){return 0} ;
  	return 1 ;
}
####################################################################################
#-----------------------------------------------------------------------------------


#-----------------------------------------------------------------------------------
####################################################################################
# UTILITY FUNCTIONS
####################################################################################
#-----------------------------------------------------------------------------------
sub get_tick_count ()
{
  	if ($OS_win) 
  	   { 
  		return Win32::GetTickCount() ;
      }
   else
      {
   	my ($real2, $user2, $system2, $cuser2, $csystem2) = POSIX::times();
   	$real2 *= 10.0;
   	return int $real2;
      }
}

#-----------------------------------------------------------------------------------
# get_time: returns the week time (i.e.: current hour, min, sec and the weekday
# starting from monday (= 0))
#-----------------------------------------------------------------------------------
sub get_time ()
{
	
	my ($sec,$min,$hour,$wday) = (localtime)[0,1,2,6] ;
	$wday = ($wday + 6) % 7 ;
   return ($sec,$min,$hour, $wday) ;
}

#-----------------------------------------------------------------------------------
# ParseCommandLine() - parse the command line options
#
# This function reads the configuration file for WebAssay. There are
# three possible sources, which are tried in the following order:
#	1. -f switch on the command-line
#	2. $HOME/.webassay
#	3. local site configuration file
#-----------------------------------------------------------------------------------
sub ParseCommandLine()
{
    my @switches = (
                    'help',         \$SHOW_HELP,
                    'version',      \$SHOW_VERSION,
                    'i',        		\$INTERACTIVE,
                    'log=s',        \$LOGGING,
                    'test=i',       \$TEST_FLAG,
                    'debug=i',      \$DEBUG_FLAG, 
                    'verbose',      \$VERBOSE,
                    'config=s',     \$INIFILE,
                    'dump',         \$DUMP_INFO,
                   );

    &GetOptions(@switches) || die "use -help switch to display brief help\n";

    if ($SHOW_VERSION)
       {
       print "This is $PROGRNAME, version $VERSION\n\n";
       exit 0;
       }

    if ($SHOW_HELP)
      {
      print <<EofHelp;
$PROGRNAME, v$VERSION

Usage: UpsEye.pl [options]

   -help            : shows this help message
   -version         : shows program $PROGRNAME version
   -i          	  : upseye is interactive (not demonized)
   -test=#          : tests the event numbered '#'
   -dump            : dumps all config info
   -debug=#         : '#'=[1..4] prints debugging informations
                      higher number: more informations...
   -verbose         : shows more informations (debug mode)
   -config=File     : sets the configuration file
EofHelp
      exit 0;
      }
   
}

#-----------------------------------------------------------------------------------
# sleepex: sleeps for <Param> milliseconds
#-----------------------------------------------------------------------------------
sub sleepex($)
{
	my $timeout = shift ;
	select undef, undef, undef, 0.001 * $timeout ;
}

#-----------------------------------------------------------------------------
# getmessage: returns a message string related to a particular event
#-----------------------------------------------------------------------------
sub getmessage ($;$)
{
  	my ($msgid,$optional) = @_;
	my $message ;
	
	if ($optional)
		{
		$message = $MESSAGES{$msgid} ;
		$message =~ s/%s/$optional/g ;
		return $message ;
		}
	else
		{
  		return $MESSAGES{$msgid};
		}

}

sub HandleExit()
{
   if (! $OS_OSX)
      {
      $ups->{Com}->close || die "\nclose problem with $ups->{SerialPort}\n" ;
      }
   undef $ups ;
   undef $connection ;
   sleep (2) ;
}

#-----------------------------------------------------------------------------
# PrintDebug: PrintDebug(DebugLevel, parms)  if '$DEBUG_FLAG' is set, prints 
# debug informations 
#-----------------------------------------------------------------------------
sub PrintDebug($@)
{
   my $sec; my $min; my $hour; my $mday; 
   my $mon; my $year; my $wday; my $yday; my $isdst;
   ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
   my $Date = sprintf "%02d/%02d/%02d-%02d:%02d:%02d", 1900+$year, $mon+1, $mday, $hour, $min, $sec ;
   
   if ($DEBUG_FLAG >= shift)  # check if DEBUG_FLAG >= DebugLevel
      {
      print "$Date\t@_" ;
      }
}

#-----------------------------------------------------------------------------
# RunCommand(Command) is a subroutine which runs the command passed as 
#           argument. 
#           Please remember: the standard 'system' command waits until the
#           completition of the spawned program. Somtimes this is not the
#           requested behaviour. The particular Windows routines used run
#           the command and immediately return to the program.
#           If you want to implement such behaviour on Unix/Linux systems,
#           you have to append an ' &' to the command, so it will be run
#           in background and the control will return to the calling program
#           immediately.
#-----------------------------------------------------------------------------
sub RunCommand($)
{
   my $Process ;
   my $Command ;      # command to run
   my $Parms = "" ;
   my @Tmp ;
   my @Cmds ;
   my $i ;

   @Cmds = split(/\\@/, $_[0]) ;

   for ($i=0 ; $i < @Cmds ; $i++)
      {
      $_ = $Cmds[$i] ;

      if ($_ eq "")
         {
         PrintDebug(1, "Command not defined!\n") ;
         return ;
         }

      s/^\s+//gi ;
      s/\s+$//gi ;
      @Tmp = split(/ /, $_) ;
      $Command = $Tmp[0] ;
      if ($OS_win)
         {
         if (@Tmp > 1)
            { 
            $Parms = join(' ', $Command, @Tmp[1..@Tmp-1]) ; 
	         }
         else
            { $Parms = $Command ; }
         }
      else
         {
         if (@Tmp > 1)
            { 
         	 $Parms = join(' ', @Tmp[1..@Tmp-1]) ;
         	 }
         else
            { $Parms = "" ; }
         }

         # if we are running this script on a Windows PC, we have to use a 
         # different function to exec a command.
         if ($OS_win)   #are we on Windows?
            {
            # i'm using a particular call to implement a "spawn"
            Win32::Process::Create($Process, 
                     $Command,
                     $Parms,
                     0,
                     DETACHED_PROCESS,
                     ".") or print STDERR "Unable to run command: $Command [$Process]\n" ;
                     # ".") || die "Create: $!" ;
                     
            }
         else  # no, we are using a "real" computer (unix, ...)
            {
            $Command .= " " . $Parms ;
            system($Command) == 0 
               or print STDERR "Unable to run command: [$Command] Err=$?\n" ;
            }
      }
}


#-----------------------------------------------------------------------------
# This function demonizes the UpsEye program
#-----------------------------------------------------------------------------
sub Demonize
{
   my $thePID ;
	if (!$OS_win)
		{
		open STDIN, '/dev/null' or die "Can't read /dev/null: $!" ;
		open STDOUT, '>>/dev/null' or die "Can't write to /dev/null: $!" ;
		open STDERR, '>>/dev/null' or die "Can't write to /dev/null: $!" ;
		defined (my $pid = fork) or die "Can't fork: $!" ;
		exit if $pid ;
		setsid or die "Can't start a new session: $!";
		$thePID = $PROGPATH . "thepid" ;
		open(PIDFILE,">  $thePID") or die "can't create PID-File !";
		printf PIDFILE "%d\n", getpid();
		close PIDFILE;
		setsid or die "Can't start a new session: $!";
		umask 0 ;
		}
}

#-----------------------------------------------------------------------------
# This function shutdowns the system after $_[0] seconds
#-----------------------------------------------------------------------------
sub SystemSD($)
{
    my $status ;
    NotifyEvent (evePCOFF) ;

}

####################################################################################

#-----------------------------------------------------------------------------------
####################################################################################
# OSX
####################################################################################
#-----------------------------------------------------------------------------------

#-----------------------------------------------------------------------------
# ConnectDriver: tries to connect with the OSX HID driver tcp interface 
#-----------------------------------------------------------------------------
sub ConnectHIDDriver () 
{
	$SckClient = IO::Socket::INET->new(PeerAddr => "127.0.0.1",
                          PeerPort => 18890,
                          Proto    => "tcp",
                          Type     => SOCK_STREAM,
                          Timeout  => 5)
							or $SckClient = -1 ;   # was unable to open it...
	if ($SckClient ne -1)
		{
		$SckSelect = IO::Select->new() ;
		$SckSelect->add($SckClient) ;
      return(1) ;
		}
	else
		{ return(0) ; }

}

#-----------------------------------------------------------------------------
# ConnectUDP: adds an UDP layer for the broadcast messages
#-----------------------------------------------------------------------------
sub ConnectUDP () 
{
	$UDPClient = IO::Socket::INET->new(LocalPort => 18880,
                          				  Proto    => "udp")
							or $UDPClient = -1 ;   # was unable to open it...
	if ($UDPClient ne -1)
		{
		$UDPSelect = IO::Select->new() ;
		$UDPSelect->add($UDPClient) ;
      return(1);
		}
	else
		{ 
		return(0); 
		}
}




####################################################################################
#-----------------------------------------------------------------------------------

warn "started...\n";
1 ;
