#!/usr/bin/python    
#-*- coding=utf-8 -*-
from __future__ import print_function
import sys
from time import *
import socket
import json
import os
import operator
import struct
import logging
import logging.config

appPath="./upsMonitor.d"
config = {}
defConfigValue=("127.0.0.1",300,60,180,"on","shutdown -h now")
keyNames = ("Ip Address","ShutDown Delay for Ac Power Failed(s)",
                  "ShutDown Delay for Battery Low(s)",
                  "UPS Turn Off Delay Time(s)",                  
                  "Turn off UPS after System Shutdown",
                  "ShutDown cmd"
)
keys=("IpAddress","ShutDownDelayForAcFail","ShutDownDelayForBatteryLow","UPSTurnOffDelay","TurnOffUPS","shutDownCmd")
modbusPort = 502
index = 0

def shutDownSystem():
    on=getSettings("Turn off UPS after System Shutdown")
    if on == "on":
        setUpsShutDown(getSettings("Ip Address"),modbusPort)

    os.system("sync")
    os.system("sync")
    sleep(2)
    cmd=getSettings("ShutDown cmd")
    os.system(cmd)
     
def getTimeStr(t=localtime()):
    return strftime("%Y-%m-%d %X", t)

InputAcCount = 0
preTime = time()
batteryLowTime = time()
isHasPreTime = 0
PreStatusAlarm = 0
isHasBatteryLowTime = 0
Snmp_Comm_Timeout = 0
upsAlarmCommunicationLostCount = 0
upsAlarmCommunicationLost = 0
currentStateLabel = "Status: Normal"  # 系统当前状态显示

def StateLog(info):
    log_name = appPath+"/ups.log"
    logger = logging.getLogger("upsMonitor")
    logger.setLevel(logging.DEBUG)
    handle = logging.handlers.RotatingFileHandler(
    log_name, maxBytes=512*1024, backupCount=1)
    logger.addHandler(handle)
    logger.debug(info)
    logger.removeHandler(handle)

def ErrorLog(info):
    errlog_name=appPath+"/error.log"
    errlogger = logging.getLogger("upsError")
    errlogger.setLevel(logging.ERROR)
    errhandle = logging.handlers.RotatingFileHandler(
    errlog_name, maxBytes=512*1024, backupCount=1)
    errlogger.addHandler(errhandle)
    errlogger.error(info)
    errlogger.removeHandler(errhandle)

def JudgetThread(inPutAcStatus, batteryStatus,viewMode):
    ACfail_Flag = 0
    RecordBatteryStatus = 0
    global InputAcCount
    global preTime
    global batteryLowTime
    global isHasPreTime
    global PreStatusAlarm
    global isHasBatteryLowTime
    global Snmp_Comm_Timeout
    global currentStateLabel
    # 市电故障判断   

    if inPutAcStatus == 1:
        InputAcCount+=1
        if InputAcCount>=3:
            ACfail_Flag = 1
    else:
        ACfail_Flag = 0
        InputAcCount=0

    if ACfail_Flag:
        if not isHasPreTime:
            preTime = time()
            isHasPreTime = 1
            CurrentStatusAlarm = 0
            if CurrentStatusAlarm != PreStatusAlarm:
                PreStatusAlarm = CurrentStatusAlarm
                if not viewMode:
                    StateLog("%s--%s\r\n" % (getTimeStr(), "ACfail"))
    else:
        isHasPreTime = 0
        currentStateLabel = "Status: Normal"
        if PreStatusAlarm == 0:
            PreStatusAlarm = CurrentStatusAlarm = -1
            if not viewMode:
                StateLog("%s--%s\r\n" % (getTimeStr(), "AC Normal"))

    if(isHasPreTime):
        ACShutdownTime = int(getSettings(
            "ShutDown Delay for Ac Power Failed(s)"))
        curTime = time()

        LastTime = ACShutdownTime - curTime + preTime
        if LastTime <= 0:
            LastTime = 0
            if not viewMode:
                shutDownSystem()
                StateLog("%s--%s\r\n" % (getTimeStr(), "ShutDown by Ac Power Failed"))

        if(ACfail_Flag):
                currentStateLabel="Status: %s  %.1f Min" %("ACFailShutDown  ", LastTime/60)
        else:
                currentStateLabel="%s" % "Status: Normal"

    RecordBatteryStatus = batteryStatus
    
    if RecordBatteryStatus == 3:  # 电池电压低
        if not isHasBatteryLowTime:
            batteryLowTime = time()
            isHasBatteryLowTime = 1
            CurrentStatusAlarm = 1
            if CurrentStatusAlarm != PreStatusAlarm:
                PreStatusAlarm = CurrentStatusAlarm
                if not viewMode:
                    StateLog("%s--%s\r\n" % (getTimeStr(), "BatteryLow"))
    else:
        isHasBatteryLowTime = 0

    if RecordBatteryStatus == 2:  # 电池正常
        if ACfail_Flag:
            currentStateLabel = "Status: %s  %.1f Min" % (
                "ACFailShutDown  ", LastTime/60)
        else:
            currentStateLabel = "Status: %s " % "Normal "

        CurrentStatusAlarm = 2
        if not CurrentStatusAlarm == PreStatusAlarm and (PreStatusAlarm == 1 or PreStatusAlarm == 3 or PreStatusAlarm == 4):
            PreStatusAlarm = CurrentStatusAlarm = -1
            if not viewMode:
                StateLog("%s--%s\r\n" % (getTimeStr(), "BatteryNormal"))

    elif RecordBatteryStatus == 1:  # 电池状态未知
        if(ACfail_Flag):
            currentStateLabel = "Status: %s  %.1f Min; BatteryUnknow!" % (
                "ACFailShutDown  ", LastTime/60)
        else:
            currentStateLabel = "Status: %s " % "BatteryUnknow "
        CurrentStatusAlarm = 3
        if  CurrentStatusAlarm != PreStatusAlarm:
            PreStatusAlarm = CurrentStatusAlarm
            if not viewMode:
                StateLog("%s--%s\r\n" % (getTimeStr(), "BatteryUnknow"))

    elif RecordBatteryStatus == 4:
        if(ACfail_Flag):
            currentStateLabel = "Status: %s  %.1f Min; BatteryDepleted" % (
                "ACFailShutDown", LastTime/60)
        else:
            currentStateLabel = "Status: BatteryDepleted"
        CurrentStatusAlarm = 4
        if  CurrentStatusAlarm != PreStatusAlarm:
            PreStatusAlarm = CurrentStatusAlarm
            if not viewMode:
                StateLog("%s--%s\r\n" % (getTimeStr(), "BatteryDepleted"))

    if isHasBatteryLowTime:
        BatteryLowSysShutDown = int(getSettings(
            "ShutDown Delay for Battery Low(s)"))
        curTime = time()
        BatLowLastTime = BatteryLowSysShutDown - curTime + batteryLowTime
        if BatLowLastTime <= 0:
            BatLowLastTime = 0
            if not viewMode:
                shutDownSystem()
                StateLog("%s--%s\r\n" % (getTimeStr(), "ShutDown by Battery Low"))

        if RecordBatteryStatus == 1:
            if ACfail_Flag:
                currentStateLabel = "Status: %s  %.1f Min; BatteryUnknow!" % (
                    "ACFailShutDown  ", LastTime/60)
            else:
                currentStateLabel = "Status: %s " % "BatteryUnknow "
        elif RecordBatteryStatus == 3:
            if ACfail_Flag:
                currentStateLabel = "Status: %s %.1f Min; BatteryLowShutDown  %1.f Min" % (
                    "ACFailShutDown", LastTime/60, BatLowLastTime/60)
            else:
                currentStateLabel = "Status: %s  %.1f Min" % (
                    "BatteryLowShutDown ", BatLowLastTime/60)

        elif RecordBatteryStatus == 4:
            if ACfail_Flag:
                currentStateLabel = "Status: %s  %.1f Min; BatteryDepleted" % (
                    "ACFailShutDown", LastTime/60)
            else:
                currentStateLabel = "Status: BatteryDepleted"


def getUpsShutDownTimeDelay(ipAddr, port):
    try:
        socket.setdefaulttimeout(1)
        s = socket.socket()
        s.connect((ipAddr, port))
        global index
        if index > 1000:
            index = 0
        else:
            index += 1
        data = struct.pack("3H6B", socket.htons(index), 0,
                           socket.htons(6), 0x01, 0x03, 0x00, 0x00, 0x00, 0x07)
        s.send(data)
        data = s.recv(12)
        print(repr(data))
    except Exception, e:
        ErrorLog("%s -- %s"%(getTimeStr(),str(e)))

def setUpsShutDown(ipAddr,port):
    try:
        socket.setdefaulttimeout(1)
        s = socket.socket()
        s.connect((ipAddr, port))
        global index
        if index > 1000:
            index = 0
        else:
            index += 1
        turnOffTime=int(getSettings("UPS Turn Off Delay Time(s)"))
        data = struct.pack("3H6B", socket.htons(index), 0,
                           socket.htons(6), 0x01, 0x06, 0x00, 0x07, turnOffTime>>8&0xff,turnOffTime&0xff)
        s.send(data)
        data = s.recv(12)
        
        s.close()
    except Exception, e:
        ErrorLog("%s -- %s"%(getTimeStr(),str(e)))

    
def getUpsAlarm(ipAddr, port):
    try:
        socket.setdefaulttimeout(1)
        s = socket.socket()
        s.connect((ipAddr, port))
        global index
        if index > 1000:
            index = 0
        else:
            index += 1
        data = struct.pack("3H6B", socket.htons(index), 0,
                           socket.htons(6), 0x01, 0x02, 0x01, 0x01, 0x00, 0x18)
        s.send(data)
        data = s.recv(12)
        _, alarm1,_,_ = struct.unpack("9s3B", data)
        if len(data)!=12:
            return []
        alarm=[]
        for i in range(0, 8):
            alarm.append((alarm1 >> i & 0x1))
        
        s.close()
        return alarm
    except Exception, e:
        ErrorLog("%s -- %s"%(getTimeStr(),str(e)))
    return []


def dataProcess(viewMode):
    #viewMode,查看模式不会记录也不会关机等操作
    i = 0
    global currentStateLabel
    global isHasBatteryLowTime
    global isHasPreTime
    global PreStatusAlarm
    upsAlarmCommunicationLostCount=0
    upsAlarmCommunicationLost=0
    snmpCommunicationCount=0
    printConfig()
    while True:
        sleep(1)
        ip = getSettings("Ip Address")
        alarm = getUpsAlarm(ip, modbusPort)
      #  getUpsShutDownTimeDelay(ip, modbusPort)
        i += 1
        if len(alarm)==0:
            currentStateLabel = "%s" % "Status: TimeOut(snmp)"
            CurrentStatusAlarm = 5
            snmpCommunicationCount+=1
            if snmpCommunicationCount>=3:
                if CurrentStatusAlarm != PreStatusAlarm:
                    PreStatusAlarm = CurrentStatusAlarm
                    if not viewMode:
                        StateLog("%s--%s\n\r" % (getTimeStr(), "TimeOut(snmp)"))

                isHasBatteryLowTime = 0
                isHasPreTime = 0
        else:
            if PreStatusAlarm == 5:
                PreStatusAlarm = CurrentStatusAlarm = -1
                if not viewMode:
                    StateLog("%s--%s\n\r" % (getTimeStr(), "Status: Normal"))
            snmpCommunicationCount=0
            upsAlarmCommunicationLost=alarm[0]
            inputAcState=alarm[6]
            if alarm[3]==1:
                batteryState=3#电池低电压
            elif alarm[4] ==1:
                batteryState=4#电池低容量
            else:
                batteryState=2#电池正常
            if upsAlarmCommunicationLost !=1:
                JudgetThread(inputAcState,batteryState,viewMode)
        
        if upsAlarmCommunicationLost:
            currentStateLabel = "%s" % "Status: ups communication lost"
            CurrentStatusAlarm = 6
            upsAlarmCommunicationLostCount+=1
            if upsAlarmCommunicationLostCount>=3:
                if  CurrentStatusAlarm != PreStatusAlarm:
                    PreStatusAlarm = CurrentStatusAlarm
                    if not viewMode:
                        StateLog("%s--%s\r\n" %
                                (getTimeStr(), "ups communication lost"))
                isHasBatteryLowTime = 0
                isHasPreTime = 0
        else:
            if PreStatusAlarm == 6:
                PreStatusAlarm = CurrentStatusAlarm = -1
                if not viewMode:
                    StateLog("%s--%s\r\n" %
                                (getTimeStr(), "ups communication connect"))
            upsAlarmCommunicationLostCount=0
        if viewMode:    
            sys.stdout.write("                                                            ")
            sys.stdout.write("\r")
            sys.stdout.write(currentStateLabel)
            sys.stdout.write("\r")
            sys.stdout.flush()
    
def printConfig():
    #keys = sorted(config.items(), key=operator.itemgetter(0))
    for key in keyNames:
        i=keyNames.index(key)
        k=keys[i]
        print(str(i+1)+"."+key, ":", config[k])
    print("")

def saveSettings(key, value):
    i=-1
    if key in keyNames:
        i=keys.index(key)
        key=keys[i]
    if key in ("1","2","3","4","5","6"):
        key=keys[int(key)-1]
    if value != config[key]:
            config[key] = value
            fd = open(appPath+"/config.json", "w")
            fd.write(json.dumps(config))
            os.fsync(fd)
            fd.close()


def getSettings(key):
    value = ""
    i=-1
    if key in keyNames:
        i=keyNames.index(key)
        key=keys[i]
    if key in config:
        value = config[key]
    return value

def printHelp():
    print("upsMonitor config    view the settings")
    print("upsMonitor view      view the state")
    print("upsMonitor start     start monitor")
    print("upsMonitor kill      stop monitor")
    print("upsMonitor config 1=127.0.0.1     set the snmp ip")
    print("upsMonitor config 6=\"shutdown -h now\"     set the shutdown cmd")

def upsMonitor():
    dataProcess(0)

def initCfg(cfg):
    toWrite = 0
    if os.path.exists(appPath+"/config.json"):
        cfg=json.loads(open(appPath+"/config.json","r").read())
        if not cfg:
            cfg=[]
    for key in keys:
        if key not in cfg:
            i=keys.index(key)
            cfg[key] = defConfigValue[i]
            toWrite = 1
    if toWrite:
        fd = open(appPath+"/config.json", "w")
        fd.write(json.dumps(cfg))
        os.fsync(fd)
        fd.close()
    return cfg


if __name__ == '__main__':
    if not os.path.exists(appPath):
        os.makedirs(appPath)
    config = initCfg(config)
    if len(sys.argv) == 2:
        if sys.argv[1] == "start":
            open(appPath+"/pid","w").write(str(os.getpid()))
            upsMonitor()
        if sys.argv[1] == "config":
            printConfig()
            print("")
            print("example:upsMonitor config 1=127.0.0.1")
            print("        upsMonitor config 2=180")
        if sys.argv[1] == "kill":
            if os.path.exists(appPath+"/pid"):
                pid=open(appPath+"/pid","r").read()
                os.system("kill -9 %s"%pid)
        if sys.argv[1] == "help":
            printHelp()
        if sys.argv[1] == "view":
            dataProcess(1)

    elif len(sys.argv) == 3:
        tmp = sys.argv[2].split("=")
        if len(tmp) == 2:
            saveSettings(tmp[0], tmp[1])
            printConfig()
    else:
        printHelp()
