#!/usr/bin/env python

SoftwareInfo = \
    { 'Function' : 'PP TRAFFICLIGHT CONTROLLER',
      'Design'   : 'ASTLAB c 2012',
      'Version'  : '1.0',
    }  

from cpphlib import *
import getopt
import sys

class RELAY:

    def __init__(self, switchposition):
       self.switchposition = switchposition
       self.BM = PPBITMANIPULATOR() 
       
    def on(self, stateholder):
       return self.BM.setbitstate(stateholder,1,self.switchposition)
        
    def off(self, stateholder):
       return self.BM.setbitstate(stateholder,0,self.switchposition)  
       
class BSRELAY:

    def __init__(self, switchonposition, switchoffposition):
       self.switchonposition = switchonposition
       self.switchoffposition = switchoffposition
       self.BM = PPBITMANIPULATOR() 
       self.ReleaseTimer = PPPASSIVETIMER()
       
    def on(self, stateholder):
       self.ReleaseTimer.set(0.40)
       return self.BM.setbitstate(stateholder,1,self.switchonposition)
        
    def off(self, stateholder):
       self.ReleaseTimer.set(0.40)
       return self.BM.setbitstate(stateholder,1,self.switchoffposition) 
       
    def release(self, stateholder):
        if self.ReleaseTimer.isexpired():
           if self.BM.getbitstate(stateholder,self.switchonposition) == "1":
              stateholder = self.BM.setbitstate(stateholder,0,self.switchonposition) 
           if self.BM.getbitstate(stateholder,self.switchoffposition) == "1":
              stateholder = self.BM.setbitstate(stateholder,0,self.switchoffposition)      
        return stateholder       
        
class TRAFICLIGHTCONTROLLER:

    def __init__(self, CAEIF, IOIF, werbose):
        self.werbose = werbose
        self.CAEIF = CAEIF
        self.IOIF = IOIF
        self.IOCG = PPIOCARDGADGETS(self.CAEIF, self.CAEIF)
        self.BM = PPBITMANIPULATOR()   
        if not(self.hardwarecheck()):
            if self.werbose:
                print "Error: Hardware is not compatible!"
                print 
            return
        if self.werbose:
            print "Hardware is OK"    
        self.run()    
         
    def hardwarecheck(self):
        """Identify used cards address by discover dict"""
        for addr in range(16):
            if self.IOCG.DiscoverDict.get(addr) == "16 channel DIGITAL INPUT":
                self.DGIAddr = addr
            if self.IOCG.DiscoverDict.get(addr) == "16 channel DIGITAL OUTPUT":
                self.DGOAddr = addr
        if self.DGOAddr == -1 or self.DGIAddr == -1:            
            return False
        else:
            return True           

    def run(self):
        if self.werbose:
            print "Switch off pollings by discover dict"
                
        for addr in range(16):
            if self.IOCG.DiscoverDict.get(addr) != "EMPTY":
                if self.IOCG.CardIOTypes.get(self.IOCG.DiscoverDict.get(addr)) == "INP":
                    self.CAEIF.commandstatemachine("POLLOFF:%X\r\n"%addr,"CIA: POLLOFF OK at address %X\r\n>\r\n"%addr,500)
        if self.werbose:
            print "Switch On pollings on used cards"
        self.CAEIF.commandstatemachine("POLLON:%X\r\n"%self.DGIAddr,"CIA: POLLON OK at address %X\r\n>\r\n"%addr,500)
        self.CAEIF.commandstatemachine("ENABLEPOLL\r\n",">",500)    
        if self.werbose:
            print "Puts everything into root position"
        self.IOIF.send("a%Xb0dFF\r\n"%self.DGOAddr)
        self.IOIF.send("a%Xb1dFF\r\n"%self.DGOAddr)
        TRLstatus = "00"
        prevTRLstatus = "00"
        REDLIGHT = BSRELAY(0,1)
        YELLOWLIGHT = RELAY(2)
        GREENLIGHT = RELAY(3)        
        TCRGENERATOR = PPPASSIVETIMER(0.25, True) #0.25
        delay = 4 
        TCR = -2
        TcrLock = False
        Mode = "Normal"
        while(1):   
            if TCRGENERATOR.isexpired():
                TCR += 1
                if self.werbose:
                    print "TCR: %d"%TCR
                TCRGENERATOR.reset()
            if self.IOIF.getinputflag():
                data = self.IOIF.getinputdata()
                if "a%Xb0"%self.DGIAddr in data:
                    if self.BM.getbitstate(data[5:7],0) == "1":
                        if self.werbose:
                            print "Change to normal mode"
                        Mode = "Normal"
                        TCR = -2
                        TcrLock = False
                        TRLstatus = GREENLIGHT.off(TRLstatus) 
                        TRLstatus = REDLIGHT.off(TRLstatus) 
                        self.IOIF.send("a%Xb0d%s\r\n"%(self.DGOAddr, self.BM.inverse(TRLstatus)))
                        time.sleep(0.4)                        
                        self.IOIF.send("a%Xb0dFF\r\n"%self.DGOAddr)
                    if self.BM.getbitstate(data[5:7],1) == "1":
                        if self.werbose:
                            print "Change to blinking mode"
                        Mode = "Blinking"
                        TCR = -2
                        TcrLock = False
                        TRLstatus = GREENLIGHT.off(TRLstatus) 
                        TRLstatus = REDLIGHT.off(TRLstatus) 
                        self.IOIF.send("a%Xb0d%s\r\n"%(self.DGOAddr, self.BM.inverse(TRLstatus)))
                        time.sleep(0.4)                        
                        self.IOIF.send("a%Xb0dFF\r\n"%self.DGOAddr)
                    if self.BM.getbitstate(data[5:7],3) == "1":
                        break
            
            if Mode == "Normal":               
                if TCR == 0*delay and not(TcrLock):
                    TRLstatus = YELLOWLIGHT.off(TRLstatus)
                    TRLstatus = REDLIGHT.on(TRLstatus)
                    TcrLock = True
                if TCR == 1*delay:
                    TcrLock = False
                if TCR == 10*delay and not(TcrLock):
                   TRLstatus = YELLOWLIGHT.on(TRLstatus)
                   TcrLock = True 
                if TCR == 11*delay:
                    TcrLock = False
                if TCR == 12*delay and not(TcrLock):
                   TRLstatus = REDLIGHT.off(TRLstatus)
                   TRLstatus = YELLOWLIGHT.off(TRLstatus)
                   TRLstatus = GREENLIGHT.on(TRLstatus)
                   TcrLock = True
                if TCR == 13*delay:
                    TcrLock = False   
                if TCR == 22*delay and not(TcrLock):
                   TRLstatus = GREENLIGHT.off(TRLstatus)
                   TRLstatus = YELLOWLIGHT.on(TRLstatus)
                   TcrLock = True    
                if TCR == 23*delay:
                    TcrLock = False
                if TCR == 24*delay:
                    TCR = -1
            
            if Mode == "Blinking":               
                if TCR == 0 and not(TcrLock):
                    TRLstatus = YELLOWLIGHT.on(TRLstatus)
                    TcrLock = True
                if TCR == 1:
                    TcrLock = False
                if TCR == 2 and not(TcrLock):
                   TRLstatus = YELLOWLIGHT.off(TRLstatus)
                   TcrLock = True 
                if TCR == 3:
                    TcrLock = False    
                    TCR = -1  
                    
            TRLstatus = REDLIGHT.release(TRLstatus)                       
            if prevTRLstatus != TRLstatus:
                self.IOIF.send("a%Xb0d%s\r\n"%(self.DGOAddr, self.BM.inverse(TRLstatus)))
                #self.IOIF.send("a%Xb0d%s\r\n"%(self.DGOAddr, TRLstatus))
                prevTRLstatus = TRLstatus
        if self.werbose:
            print "Switch off all relays"                  
        TRLstatus = REDLIGHT.off(TRLstatus) 
        self.IOIF.send("a%Xb0d%s\r\n"%(self.DGOAddr, self.BM.inverse(TRLstatus)))
        time.sleep(0.4)                        
        self.IOIF.send("a%Xb0dFF\r\n"%self.DGOAddr)
        self.IOIF.send("a%Xb1dFF\r\n"%self.DGOAddr)   
        if self.werbose:
            print "Disable global poll"               
        self.CAEIF.commandstatemachine("DISABLEPOLL\r\n",">",500)
        if self.werbose:
            print "Switch Off pollings on used cards"
        self.CAEIF.commandstatemachine("POLLOFF:%X\r\n"%self.DGIAddr,"CIA: POLLOFF OK at address %X\r\n>\r\n"%addr,500) 
        if self.werbose:
            print "Exit from working loop" 
            print 

def printdocument():
    sys.stdout.write(""" \
    
Traffichlight controller via high level pheripherials.
                    DOCUMENTATION                                                                                                                                      
INPUTS: 
  - BLUE BUTTON connects to channel 0 at PD-INP8-A matching unit
  BLUE BUTTON selects Normal mode 
  - RED BUTTON connects to channel 1 at PD-INP8-A matching unit
  RED BUTTON selects Blinking mode 
  - GREEN BUTTON connects to channel 3 at PD-INP8-A matching unit
  GREEN BUTTON switch off all, and exit from controller program
  
  All buttons are powered by 12V DC
  PD-INP8-A matching unit connected to BL0E at PP-DI16-A input card
  
OUTPUTS:
  - BISTABLE RELAY switch on pin connects to channel 0 at PD-OSW8-A matching unit
  - BISTABLE RELAY switch off pin connects to channel 1 at PD-OSW8-A matching unit
  BISTABLE RELAY powered by 12V DC, and switch RED light on and off
  - RELAY switch pin connects to channel 2 at PD-OSW8-A matching unit
  RELAY powered by 5V DC, and switch YELLOW light on and off
  - RELAY switch pin connects to channel 3 at PD-OSW8-A matching unit
  RELAY powered by 5V DC, and switch GREEN light on and off 
  
  PD-OSW8-A matching unit connected to BL0E at PP-DO16-A output card 
  
  Software supposes that only one input and one output card there are in your system.
  Discover of card's addresses automatic.
  
""")

def usage():
    sys.stdout.write("""Traffichlight controller via high level pheripherials. %s %s \

USAGE: ./trafficlight.py [options]  

    options:
    --------
    -h  : print this help and exit
    -d  : print documentation end exit
    -w  : werbose mode
          
"""%(SoftwareInfo.get('Version'), SoftwareInfo.get('Design')))

if __name__ == '__main__': 
    """Main program"""
    Options, Arguments = getopt.getopt(sys.argv[1:], "dvh")
    verbose = False
    for option in Options:            
        if option[0] == "-v":    
            verbose = True
        if option[0] == "-h":
            usage()
            sys.exit(0) 
        if option[0] == "-d":
            printdocument()
            sys.exit(0)     
    PPDriver = PPDRIVER();
    PPDriver.registerCAEinterface("localhost", 8000)
    PPDriver.registerIOinterface("localhost", 12000)
    PPDriver.connectinterfaces()
    CAEIF, IOIF, MONIF = PPDriver.getinterfaces()
    TrafficLightController = TRAFICLIGHTCONTROLLER(CAEIF, IOIF, verbose)   
    PPDriver.disconnectinterfaces()
    sys.exit(0)
    
