def __init__(self, log, peripheralcontroller, localConfig):
     SharedInstances.static_commandProcessor = self;
     self.log = log
     self.peripheralcontroller = peripheralcontroller
     self.localConfig = localConfig;
     self.peri = LocalPeripherals(self.log);
     self.peri.load();
 def threaded_function(self):
     self.localPeripherals = LocalPeripherals(self.log)
     self.localPeripherals.load()
     self.localPeripherals.initSwitchPeripherals(switch_callback);
     self.setLoadSchedules()
     self.exitThread  = False
     self.log.write(self.MOD_NAME, "starting timer thread loop for peripheral controls")
     while self.exitThread == False:
         self.checkSchedules()
         # timer override the schedules in case it is on
         self.checkTimers()
         if(self.dbgcounter == 2):
             self.dbgcounter = 0
             message = ""
             if(self.p_timers == None):
                 message = message + "No timer"
             else:
                 count = len(self.p_timers)
                 message = message + str(count) + " timer(s)"
                 
             self.log.write(self.MOD_NAME, "loop: "+ message)
         self.dbgcounter+= 1 
         time.sleep(5)
     self.log.write(self.MOD_NAME, "exiting thread loop")
class CommandProcessor:
    ST_CMD_NONE = "0"
    ST_CMD_UPDATE_PERIPHERALS_STATUS = "20"
    ST_CMD_GET_PERIPHERALS = "30"
    ST_CMD_UPDATE_COMMAND_STATUS = "50"
    ST_CMD_SET_REBOOT_DEVICE = "60" 
    
    ST_CMD_SET_PERIPHERALS = "200"
    ST_CMD_SET_TIMER = "230"

    ST_CLIENT_ID = "cid"
    ST_CLIENT_PASSWORD = "******"
    ST_CMDC = "cmdc"
    ST_CMDCS = "cmds"
    ST_STI = "sti" #status id
    ST_STM = "stm" #status message
    ST_DATA = "data"
    ST_CMD_REF = "ref"

    ST_SUMMARY = "summary"

    STATUS_CODE_OK = "10"
    STATUS_MESSAGE_OK = "Ok"
    
    STATUS_CODE_UNKNOWN_COMMAND = "20"
    STATUS_MESSAGE_UNKNOWN_COMMAND = "Unknown command"

    STATUS_CODE_PERIPHERAL_NOT_FOUND = "30"
    STATUS_CODE_PERIPHERAL_NOT_FOUND_MESSAGE = "Peripheral not found"

    STATUS_CODE_DATA_NOT_FOUND = "40"
    STATUS_CODE_DATA_NOT_FOUND_MESSAGE = "Data section not found"

    STATUS_CODE_ERROR_SAVE_SCHEDULES = "50"
    STATUS_CODE_ERROR_SAVE_SCHEDULES_MSG = "Unable to save schedules"

    TIMER_MINUTES = "minutes"
    TIMER_DEVICE_ID = "peripheral_id"

    PERIPHERALS = "peri"
    
    

    MODULE_NAME = "CMDPROCESSOR"

    TIMEOUT15 = 15


    ST_DATA_PERIPHERAL = "peripherals"

    log = None
    peripheralcontroller = None
    localConfig = None
    peri = None;

    pendingreplies = []; # there are the statuses awaiting to be sent to the server

    def __init__(self, log, peripheralcontroller, localConfig):
        SharedInstances.static_commandProcessor = self;
        self.log = log
        self.peripheralcontroller = peripheralcontroller
        self.localConfig = localConfig;
        self.peri = LocalPeripherals(self.log);
        self.peri.load();

    def beginPostSwitchEvent(self,bcmchanel, newvalue):
        self.log.write("beginPostSwitchEvent", "readytopost");
        self.posterth = Thread(target = self.posterSwitchEventThread, args = (bcmchanel, newvalue,))
        self.posterth.start()

    FIELD_EVENT_PERIPHERAL_ID = "pid";
    FIELD_EVENT_PERIPHERAL_TYPE = "ptype"
    FIELD_EVENT_TIME = "etime"
    FIELD_EVENT_PERIPHERAL_VALUE = "nvalue"

    def posterSwitchEventThread(self,bcmchanel,newvalue):
        lock = threading.RLock()
        with lock:
            peripheral = self.peri.findPeripheralByTypeAndGpio(LocalPeripherals.PERI_TYPE_SWITCH, bcmchanel);
            if(peripheral != None):

                payload = self.constructPostHeader();
                date = str(datetime.datetime.now());
                eventdata = { ''+self.FIELD_EVENT_PERIPHERAL_ID+'':'' + peripheral.devid
                + '',''+self.FIELD_EVENT_PERIPHERAL_TYPE + '':'' + peripheral.ptype
                + '',''+self.FIELD_EVENT_TIME + '':'' + date
                + '',''+self.FIELD_EVENT_PERIPHERAL_VALUE+ '':'' + newvalue+''}
                
                payload["event"] = eventdata;

                url = self.localConfig.serveraddress +":"+self.localConfig.serverport +"/diversityclient";
                req = urllib.request.Request(url)
                req.add_header('Content-Type', 'application/json')
                self.log.write("POSTING the following: ", payload);
                data = json.dumps(payload)
                databytes = data.encode('utf-8')
     
                req.add_header('Content-Length', len(databytes))
                
                try:
                    response = urllib.request.urlopen(req, databytes, self.TIMEOUT15);

                    self.pendingreplies = [];
                   
                    data = response.read()
                    self.processCommands(data);
                    self.connected_time += 1;
                except urllib.error.HTTPError as e:
                    self.log.write("post", "HTTP Error: %d"% e.code)
                    self.connection_issues += 1;
                except urllib.error.URLError as e:
                    self.log.write("post", "URL Error: %s"% e.args)
                    self.connection_issues += 1;
                except socket.timeout as e:
                    self.log.write("post", "timeout")
                    self.connection_issues += 1;
            else:
                self.log.write("beginPostSwitchEvent", "Peripheral not found");
            #find the id of the peripheral
            


    def beginPost(self,postdata):
        self.posterth = Thread(target = self.posterThread, args = (postdata, ))
        self.posterth.start()

    def addPendingReplies(self, postdata):
        if len(self.pendingreplies) > 0:
            postdata["tasks"] = self.pendingreplies;
        else:
            self.log.write("addpending tasks", "No pending tasks");

        return postdata;
            

    def posterThread(self,postdata):
        lock = threading.RLock()
        with lock:
            postdata = self.addPendingReplies(postdata);
            postdata = self.addPeripheralsStatus(postdata);
            url = self.localConfig.serveraddress +":"+self.localConfig.serverport +"/diversityclient";
            req = urllib.request.Request(url)
            req.add_header('Content-Type', 'application/json')
            self.log.write("POSTING the following: ", postdata);
            data = json.dumps(postdata)
            databytes = data.encode('utf-8')
 
            req.add_header('Content-Length', len(databytes))
            
            try:
                response = urllib.request.urlopen(req, databytes, self.TIMEOUT15);

                self.pendingreplies = [];
               
                data = response.read()
                self.processCommands(data);
                self.connected_time += 1;
            except urllib.error.HTTPError as e:
                self.log.write("post", "HTTP Error: %d"% e.code)
                self.connection_issues += 1;
            except urllib.error.URLError as e:
                self.log.write("post", "URL Error: %s"% e.args)
                self.connection_issues += 1;
            except socket.timeout as e:
                self.log.write("post", "timeout")
                self.connection_issues += 1;

    wantPost = False;

    def wantPostNow(self):
 #       self.log.write("wantpost", self.wantPost)
        return self.wantPost;
        

    def postAlive(self):
        self.wantPost = False;
        result = self.constructPostHeader();
        result[self.ST_SUMMARY] = self.constructSummary();

        self.beginPost(result);

    connected_time = 0;
    connection_issues = 0;
    def constructSummary(self):
        summary = "Connecion success: " +str(self.connected_time)+\
        " Failed: "+ str(self.connection_issues);
        return summary;
        #self.peripheralcontroller.getSummaryVerbose();
        #return "test 123"

    def processCommands(self, commands):

#        self.log.write("----- processCommands ------", commands);       
        jcmd = json.loads(commands.decode());
        retvalcommands = [];
        if(self.ST_CMDCS in jcmd):
            cmds = jcmd[self.ST_CMDCS];
            
            if(cmds != None):
                for cmd in cmds:
                    onecmd = self.processCommand(cmd);
                    if(onecmd != None):
                        self.log.write("  Message","Adding command to array");
                        retvalcommands.append(onecmd);
                    else:
                        self.log.write("  Error","command not created");
        else:
            self.log.write("  Error","cmds not found");
                

 #       self.log.write("***** processCommands  end ******",retvalcommands );
                
        self.pendingreplies.append(retvalcommands);
        
        return None;
        
    def processCommand(self, command):
 #       self.log.write("------ processCommand ------", command);
  
        jcmd = command;
        commandid = "-1"
        if(jcmd != None):
            if(self.ST_CLIENT_ID in jcmd):
                commandid = jcmd[self.ST_CLIENT_ID]
            
        result = None;
        if(commandid == self.ST_CMD_GET_PERIPHERALS):
            result = self.cmdGetPeripherals(jcmd)
            self.wantPost = True;
        elif(commandid == self.ST_CMD_SET_TIMER):
            result =    self.cmdSetTimer(jcmd)
            self.wantPost = True;
        elif(commandid == self.ST_CMD_SET_REBOOT_DEVICE):
            self.log.write(self.MODULE_NAME, "Will reboot")
 
            result = self.cmdReboot(jcmd);
            self.wantPost = True;
        else:
            self.log.write(self.MODULE_NAME, "unknown command")
            #self.ST_CMD_SET_TIMER, statuscode, statusmessage
            result = self.constructBaseCommand(commandid,
                                               self.STATUS_CODE_UNKNOWN_COMMAND,
                                               self.STATUS_MESSAGE_UNKNOWN_COMMAND);

        return result
            

    def cmdReboot(self,jcmd):
        OSCommands.rebootDevice();
        
        response = { ''+self.ST_CMDC +'':'' + self.ST_CMD_UPDATE_COMMAND_STATUS 
        + '',''+self.ST_CMD_REF + '':'' + jcmd[self.ST_CMD_REF]}
                     
        return response;

    def addPeripheralsStatus(self, postdata):

        #peri = LocalPeripherals(self.log)
        #self.peri.load();
        #jval = peri.toJSON();
        postdata["peripherals_stat"] = self.peri.getPeripheralsStatus(self.peripheralcontroller);
        
        return postdata;

  
    def cmdGetPeripherals(self, jcmd):
        self.log.write(self.MODULE_NAME, "***********get peripherals************")
        #peri = LocalPeripherals(self.log)
        #peri.load();
        jval = self.peri.toJSON();
        self.log.write(self.MODULE_NAME, "- Create response")
        retval = self.createResponseSetPeripherals(jcmd[self.ST_CMD_REF], jval);
        return retval

    def createResponseSetPeripherals(self, reference, locper):
        # add aray of peripherals (locper) to response
        response = { ''+self.ST_CMDC +'':'' + self.ST_CMD_SET_PERIPHERALS
        + '',''+self.ST_CMD_REF + '':'' + reference
        + '',''+self.PERIPHERALS + '': locper }

   
        return response; #json.dumps(response)

        
    def cmdSetTimer(self, jcmd):
        self.log.write(self.MODULE_NAME, "*********** Set timer ************")
        datasection = jcmd[self.ST_DATA]
 #       pdb.set_trace()
        minutes = None
        deviceid = None
        statuscode = None
        statusmessage = None
        
        if(datasection != None):
            minutes = datasection[self.TIMER_MINUTES]

            iminutes = 0
            try:
                iminutes = int(minutes)
            except ValueError:
                iminutes = 0
                pass
    
            deviceid = datasection[self.TIMER_DEVICE_ID]

            self.log.write(self.MODULE_NAME, "minutes: " + str(minutes) + " deviceid: " + deviceid);

                 
            #peri = LocalPeripherals(self.log)
            #peri.load();
 #           pdb.set_trace()
            peripheralobject = self.peri.findPeripheral(deviceid)
            if(peripheralobject != None):
                #make sure that this peripheral support this service
                if(peripheralobject.ptype == LocalPeripherals.PERI_TYPE_OUT_SAINTSMART_RELAY or
                   peripheralobject.ptype == LocalPeripherals.PERI_TYPE_OUT_PIFACE_RELAY):
                    # set timer
                    if(iminutes == 0):
                        self.peripheralcontroller.turnOffPeripheral(peripheralobject)
                    else:
                        self.peripheralcontroller.addOneTimer(peripheralobject, iminutes)
                    statuscode = self.STATUS_CODE_OK
                    statusmessage = self.STATUS_MESSAGE_OK
                else:
                    statuscode = self.STATUS_CODE_PERIPHERAL_NOT_FOUND
                    statusmessage = self.STATUS_CODE_PERIPHERAL_NOT_FOUND_MESSAGE 
            else:
                statuscode = self.STATUS_CODE_PERIPHERAL_NOT_FOUND
                statusmessage = self.STATUS_CODE_PERIPHERAL_NOT_FOUND_MESSAGE    
                   
        else:
            statuscode = self.STATUS_CODE_DATA_NOT_FOUND
            statusmessage = self.STATUS_CODE_DATA_NOT_FOUND_MESSAGE
 
        # create confirmation message
        jbase = self.constructBaseCommand(self.ST_CMD_SET_TIMER, statuscode, statusmessage);
        retval = self.addToData(jbase, self.ST_DATA_PERIPHERAL, deviceid)
        retval = self.createResponseSetTimerStatus(jcmd[self.ST_CMD_REF]);
        
        return retval

    def createResponseSetTimerStatus(self, reference):
        # add aray of peripherals (locper) to response
        response = { ''+self.ST_CMDC +'':'' + self.ST_CMD_UPDATE_COMMAND_STATUS 
        + '',''+self.ST_CMD_REF + '':'' + reference
         }
        return response;
                
    def constructPostHeader(self):
        
        base = { ''+self.ST_CLIENT_ID +'':'' + self.localConfig.clientid
            + '',''+self.ST_CLIENT_PASSWORD + '':'' + self.localConfig.password + ''}
        return base

    def constructBaseCommand(self, commandid, statid, statmsg):
        base = { ''+self.ST_CMDC+'':'' + commandid
            + '',''+self.ST_STI + '':'' + statid
            + '',''+self.ST_STM + '':'' + statmsg+''}
        return json.dumps(base)
        
    def addToData(self, jbase, stkeyname, jdata):
        self.log.write(self.MODULE_NAME, "- addToData")
        basedic = json.loads(jbase)
        
        if(self.ST_DATA not in basedic):
            basedic[self.ST_DATA] = {}
            
        basedic[self.ST_DATA].update({ stkeyname : jdata })


        return json.dumps(basedic)
class PeripheralController:
    exitThread = False
    tlock = None
    log = None
    MOD_NAME = "PERCON"
    dbgcounter = 0;
    piFaceController = None
    
    peripheralThread = None

    loadSchedule = False
    localPeripherals = None


    p_timers = []
    p_pschedules = []  #PeripheralSchedulesObject

    
    def __init__(self):
        self.log = Logger("logs/controllerlog","txt", True)
        self.piFaceController = RPIFaceDigitalController()
        self.tlock = threading.Lock()




    def join(self):
        if(self.peripheralThread != None):
            self.peripheralThread.join()
        
    def threaded_function(self):
        self.localPeripherals = LocalPeripherals(self.log)
        self.localPeripherals.load()
        self.localPeripherals.initSwitchPeripherals(switch_callback);
        self.setLoadSchedules()
        self.exitThread  = False
        self.log.write(self.MOD_NAME, "starting timer thread loop for peripheral controls")
        while self.exitThread == False:
            self.checkSchedules()
            # timer override the schedules in case it is on
            self.checkTimers()
            if(self.dbgcounter == 2):
                self.dbgcounter = 0
                message = ""
                if(self.p_timers == None):
                    message = message + "No timer"
                else:
                    count = len(self.p_timers)
                    message = message + str(count) + " timer(s)"
                    
                self.log.write(self.MOD_NAME, "loop: "+ message)
            self.dbgcounter+= 1 
            time.sleep(5)
        self.log.write(self.MOD_NAME, "exiting thread loop")

 #       self.testCallback();


    def testCallback(self):
        switch_callback(4);

    
    def getPeripheralStatus(self, port):
        retval = "off"
        if(self.piFaceController.getPortStatus(int(port))):
            retval = "on"
        return retval;
    
    def getSummaryVerbose(self):
        text = "";
        port1 = self.piFaceController.getPortStatus(0);
        port2 = self.piFaceController.getPortStatus(1);
        if(port1):
            text = "Valve 1 is on.";
        else:
            text = "Valve 1 is off.";

        if(port2):
            text += " Valve 2 is on.";
        else:
            text += " Valve 2 is off.";

        return text;

    def turnOffPeripheral(self, peripheralObject):
        # remove all timers associate to this peripheral
        self.tlock.acquire()
        if(self.p_timers != None):
            try:
                self.log.write(self.MOD_NAME, "turn off peripheral");
                count = len(self.p_timers);
                for i in range(count-1,-1,-1):
                    pto = self.p_timers[i];
                    portid = pto.getPeripheralSerialID();
#                    self.log.write(self.MOD_NAME, "here");
                    self.log.write(self.MOD_NAME,type(pto.peripheral));
                    self.log.write(self.MOD_NAME,type(peripheralObject));
                    if(peripheralObject.devid == pto.peripheral.devid):
                        #delete
                        self.log.write(self.MOD_NAME, "turn off (deleting)");
                        self.turnPeripheralOff(pto.peripheral);
 #                       if(pto.getPeripheralType() == LocalPeripherals.PERI_TYPE_OUT_RELAY):
#                            setGPIOLow(self.pto.getPeripheralGPIO());
#                        elif(pto.getPeripheralType() == LocalPeripherals.PERI_TYPE_OUT_PIFACE_RELAY):
#                            self.piFaceController.turnOffOutput(int(portid))
                        del self.p_timers[i]
            except:
                self.log.write(self.MOD_NAME, "turn off peripheral. exception");
                
        self.tlock.release()


    def addOneTimer(self, peripheralObject, duration):
        pto = PeripheralTimerObject(peripheralObject, duration)
        self.log.write(self.MOD_NAME, "init relay timer for: "+ str(duration));
        self.tlock.acquire()
        self.p_timers.append(pto)
 #       pdb.set_trace()
        self.tlock.release()


    def checkTimers(self):
 #       pdb.set_trace()
        self.tlock.acquire()
        if(self.p_timers != None):
 #           self.log.write(self.MOD_NAME, "got timer");
            count = len(self.p_timers);
            for i in range(count-1,-1,-1):
                pto = self.p_timers[i];
                portid = pto.getPeripheralSerialID();
                if(pto.isExpired() == True):
                    #delete
                    self.log.write(self.MOD_NAME, "Turning off (expired)");
                    self.turnPeripheralOff(pto.peripheral);
                   # if(pto.getPeripheralType() == LocalPeripherals.PERI_TYPE_OUT_RELAY):
#                        setGPIOLow(pto.getPeripheralGPIO());
#                    elif(pto.getPeripheralType() == LocalPeripherals.PERI_TYPE_OUT_PIFACE_RELAY):
#                        self.piFaceController.turnOffOutput(int(portid))
#                    else:
#                        self.log.write(self.MOD_NAME, "Not action (A) for peipheral type:" + pto.getPeripheralType());
                        
                    del self.p_timers[i]
                else:
                    self.turnPeripheralOn(pto.peripheral);
                    #if(pto.getPeripheralType() == LocalPeripherals.PERI_TYPE_OUT_RELAY):
#                        self.log.write(self.MOD_NAME, "Turning on GPIO ");
#                        self.log.write(self.MOD_NAME, pto.getPeripheralGPIO());
#                        setGPIOHigh(pto.getPeripheralGPIO());
#                    elif(pto.getPeripheralType() == LocalPeripherals.PERI_TYPE_OUT_PIFACE_RELAY):
#                        self.log.write(self.MOD_NAME, "Turning on (PIFACE) ");
#                        self.piFaceController.turnOnOutput(int(portid))
#                    else:
#                        self.log.write(self.MOD_NAME, "Not action (B) for peipheral type:" + pto.getPeripheralType());
                    
                    #turn on
        self.tlock.release()

    def setLoadSchedules(self):
        self.tlock.acquire()
        self.loadSchedule = True
        self.tlock.release()


    _schedulesmessagecounter = 0
    
    def checkSchedules(self):
        
        if(self.loadSchedule == True):
            self.loadSchedule = False
            self.loadSchedules()
        self.tlock.acquire()
        for periSched in self.p_pschedules:
 #           pdb.set_trace()
            if(periSched != None):
                periobj = self.localPeripherals.findPeripheral(periSched._peripheralID)
                if(periSched.isItTime() == True):
                    if(self._schedulesmessagecounter == 20):
                        self._schedulesmessagecounter = 0;
                        self.log.write(self.MOD_NAME, "**** schedule to open peripheral ****")
                    self._schedulesmessagecounter = self._schedulesmessagecounter + 1

                    self.log.write(self.MOD_NAME, "schedule turning valve on" +  periobj.serialid)
                    self.turnPeripheralOn(periobj);
#                    self.rpiController.turnOnOutput(int(periobj.serialid)) 
                else:
                    self.turnPeripheralOff(periobj);
#                    self.rpiController.turnOffOutput(int(periobj.serialid))

        self.tlock.release()

    def turnPeripheralOn(self, periobj):
        if(periobj.ptype == LocalPeripherals.PERI_TYPE_OUT_SAINTSMART_RELAY):
            self.log.write(self.MOD_NAME, "Turning on GPIO " + periobj.pgpio);
            self.log.write(self.MOD_NAME, periobj.pgpio);
            #Saintsmart relay turn on when gpio is low
            setGPIOLow(periobj.pgpio);
        elif(periobj.ptype == LocalPeripherals.PERI_TYPE_OUT_PIFACE_RELAY):
            self.log.write(self.MOD_NAME, "Turning on (PIFACE relay) ");
            self.piFaceController.turnOnOutput(int(periobj.serialid))
        else:
            self.log.write(self.MOD_NAME, "Not action (B) for peipheral type:" + pto.getPeripheralType());

    def turnPeripheralOff(self, periobj):
        if(periobj.ptype == LocalPeripherals.PERI_TYPE_OUT_SAINTSMART_RELAY):
            self.log.write(self.MOD_NAME, "Turning OFF GPIO "+ periobj.pgpio);
            #Saintsmart relay turn off when gpio is high
            setGPIOHigh(periobj.pgpio);
        elif(periobj.ptype == LocalPeripherals.PERI_TYPE_OUT_PIFACE_RELAY):
            self.log.write(self.MOD_NAME, "Turning OFF (PIFACE relay) ");
            self.piFaceController.turnOffOutput(int(periobj.serialid))
        else:
            self.log.write(self.MOD_NAME, "Not action (A) for peipheral type:" + pto.getPeripheralType());

    def loadSchedules(self):
        self.tlock.acquire()
        self.p_pschedules = []
        self.log.write(self.MOD_NAME, "----  load schedules")
        peripherals = self.localPeripherals.getPeripherals()
        sce = Schedules(self.log)
        for periObj in peripherals:
            schedules = sce.loadSchedulesForPeripheral(periObj.devid)
            self.p_pschedules.append(schedules)
            
            
            

        self.tlock.release()
            

    def startThread(self):
        if(self.peripheralThread == None):
            self.peripheralThread = Thread(target = self.threaded_function, args = [])
            self.peripheralThread.start()
            
    def stopThread(self):
        self.piFaceController.stop();
        if(self.peripheralThread != None):
            with self.tlock:
                self.exitThread  = True