#!/usr/bin/env python

SoftwareInfo = \
    {
      'Function' : 'PROGRAMMING IR RECEIVER',
      'Design'   : 'CreativePeach c 2013',
      'Version'  : '1.0',
    }  

from cpphlib import *
import getopt
import sys
import time
import binascii
import termios, tty 
        
class IRPROG:

    def __init__(self, CAEIF, IOIF):
        self.CAEIF = CAEIF
        self.IOIF = IOIF
        self.IOCG = PPIOCARDGADGETS(self.CAEIF, self.CAEIF)
        self.BM = PPBITMANIPULATOR()
        self.SM = ""
        self.Programtable = [
            {"Index":0,"Name":"WORKMODE 00","Key":"0","Addressed":False,"Frame":"08,D1,PW0,PW1,PW2,PW3,PW4,PW5,CRC","Description":"1 digit, 6 PWM channels"},
            {"Index":1,"Name":"WORKMODE 01","Key":"1","Addressed":False,"Frame":"04,D1,D2,D3(1),CRC","Description":"2 digit channels + 1 bit"},
            {"Index":2,"Name":"WORKMODE 02","Key":"2","Addressed":False,"Frame":"03,D1,D2,CRC","Description":"2 digit channels"},
            {"Index":3,"Name":"WORKMODE 03","Key":"3","Addressed":False,"Frame":"05,D1,SE0,M1PW,MCT,CRC","Description":"1 digit, 1 servo, 1 manual motor control channels "},
            {"Index":4,"Name":"WORKMODE 04","Key":"4","Addressed":False,"Frame":"05,D1,SE0,SE1,SE2,CRC","Description":"1 digit, 3 servo channels "},
            {"Index":5,"Name":"WORKMODE 05","Key":"5","Addressed":False,"Frame":"06,D1,SE0,SE1,M1PW,MCT,CRC","Description":"1 digit, 2 servo, 1 manual motor control channels "},
            {"Index":6,"Name":"WORKMODE 06","Key":"6","Addressed":False,"Frame":"06,D1,SE0,M1PW,M2PW,MCT,CRC","Description":"1 digit, 1 servo, 2 manual motor control channels "},
            {"Index":7,"Name":"WORKMODE 07","Key":"7","Addressed":False,"Frame":"06,SE0,SE1,M1PW,M2PW,MCT,CRC","Description":"2 servo, 2 manual motor control channels "},
            {"Index":8,"Name":"WORKMODE 08","Key":"8","Addressed":False,"Frame":"06,SE0,SE1,SE2,M1PW,MCT,CRC","Description":"3 servo, 1 manual motor control channels "},
            {"Index":9,"Name":"WORKMODE 09","Key":"9","Addressed":False,"Frame":"04,D1,SE0,M1PW,CRC","Description":"1 digit, 1 servo, 1 automatic motor control channels "},
            {"Index":10,"Name":"WORKMODE 0A","Key":"a","Addressed":False,"Frame":"05,D1,SE0,SE1,M1PW,CRC","Description":"1 digit, 2 servo, 1 automatic motor control channels "},
            {"Index":11,"Name":"WORKMODE 0B","Key":"b","Addressed":False,"Frame":"05,D1,SE0,M1PW,M2PW,CRC","Description":"1 digit, 1 servo, 2 automatic motor control channels "},
            {"Index":12,"Name":"WORKMODE 0C","Key":"c","Addressed":False,"Frame":"05,SE0,SE1,M1PW,M2PW,CRC","Description":"2 servo, 2 automatic motor control channels "},
            {"Index":13,"Name":"WORKMODE 0D","Key":"d","Addressed":False,"Frame":"05,SE0,SE1,SE2,M1PW,CRC","Description":"3 servo, 1 automatic motor control channels "},
            {"Index":14,"Name":"WORKMODE 0E","Key":"e","Addressed":False,"Frame":"04,SE0,M1PW,MCT,CRC","Description":"1 servo, 1 manual motor control channels "},
            {"Index":15,"Name":"WORKMODE 0F","Key":"f","Addressed":False,"Frame":"03,SE0,M1PW,CRC","Description":"1 servo, 1 automatic motor control channels "},
            
            {"Index":16,"Name":"WORKMODE 10","Key":"0","Addressed":True,"Frame":"09,ADDR,D1,PW0,PW1,PW2,PW3,PW4,PW5,CRC","Description":"1 digit, 6 PWM channels"},
            {"Index":17,"Name":"WORKMODE 11","Key":"1","Addressed":True,"Frame":"05,ADDR,D1,D2,D3(1),CRC","Description":"2 digit channels + 1 bit"},
            {"Index":18,"Name":"WORKMODE 12","Key":"2","Addressed":True,"Frame":"04,ADDR,D1,D2,CRC","Description":"2 digit channels"},
            {"Index":19,"Name":"WORKMODE 13","Key":"3","Addressed":True,"Frame":"06,ADDR,D1,SE0,M1PW,MCT,CRC","Description":"1 digit, 1 servo, 1 manual motor control channels "},
            {"Index":20,"Name":"WORKMODE 14","Key":"4","Addressed":True,"Frame":"06,ADDR,D1,SE0,SE1,SE2,CRC","Description":"1 digit, 3 servo channels "},
            {"Index":21,"Name":"WORKMODE 15","Key":"5","Addressed":True,"Frame":"07,ADDR,D1,SE0,SE1,M1PW,MCT,CRC","Description":"1 digit, 2 servo, 1 manual motor control channels "},
            {"Index":22,"Name":"WORKMODE 16","Key":"6","Addressed":True,"Frame":"07,ADDR,D1,SE0,M1PW,M2PW,MCT,CRC","Description":"1 digit, 1 servo, 2 manual motor control channels "},
            {"Index":23,"Name":"WORKMODE 17","Key":"7","Addressed":True,"Frame":"07,ADDR,SE0,SE1,M1PW,M2PW,MCT,CRC","Description":"2 servo, 2 manual motor control channels "},
            {"Index":24,"Name":"WORKMODE 18","Key":"8","Addressed":True,"Frame":"07,ADDR,SE0,SE1,SE2,M1PW,MCT,CRC","Description":"3 servo, 1 manual motor control channels "},
            {"Index":25,"Name":"WORKMODE 19","Key":"9","Addressed":True,"Frame":"05,ADDR,D1,SE0,M1PW,CRC","Description":"1 digit, 1 servo, 1 automatic motor control channels "},
            {"Index":26,"Name":"WORKMODE 1A","Key":"a","Addressed":True,"Frame":"06,ADDR,D1,SE0,SE1,M1PW,CRC","Description":"1 digit, 2 servo, 1 automatic motor control channels "},
            {"Index":27,"Name":"WORKMODE 1B","Key":"b","Addressed":True,"Frame":"06,ADDR,D1,SE0,M1PW,M2PW,CRC","Description":"1 digit, 1 servo, 2 automatic motor control channels "},
            {"Index":28,"Name":"WORKMODE 1C","Key":"c","Addressed":True,"Frame":"06,ADDR,SE0,SE1,M1PW,M2PW,CRC","Description":"2 servo, 2 automatic motor control channels "},
            {"Index":29,"Name":"WORKMODE 1D","Key":"d","Addressed":True,"Frame":"06,ADDR,SE0,SE1,SE2,M1PW,CRC","Description":"3 servo, 1 automatic motor control channels "},
            {"Index":30,"Name":"WORKMODE 1E","Key":"e","Addressed":True,"Frame":"05,ADDR,SE0,M1PW,MCT,CRC","Description":"1 servo, 1 manual motor control channels "},
            {"Index":31,"Name":"WORKMODE 1F","Key":"f","Addressed":True,"Frame":"04,ADDR,SE0,M1PW,CRC","Description":"1 servo, 1 automatic motor control channels "},
        ]
        self.ProgramableADDR = 0xFF
        self.AdressedNeeded = False
        self.IRRCAddr = -1
        self.run()    
         
    def getkey(self):
        """Non blocking keyboard reader"""
        file = sys.stdin.fileno()
        mode = termios.tcgetattr(file)
        try:
            ch = None
            inFile = None
            tty.setraw(file, termios.TCSANOW)
            inFile, unused1, unused2 = select.select([sys.stdin],[],[],0.001)
            if inFile:
                ch = sys.stdin.read(1)
        finally:
            termios.tcsetattr(file, termios.TCSANOW, mode)
        return ch         
         
    def hardwarecheck_for_programming(self):
        """Identifying addresses of cards in use based on discover dict"""
        for addr in range(16):
            if self.IOCG.DiscoverDict.get(addr) == "IR REMOTE CONTROLLER":
                self.IRRCAddr = addr
        if self.IRRCAddr == -1 :            
            return False
        else:
            return True
          
    def CRCCalc(self,Data):
         s=binascii.unhexlify(Data)
         b=[ord(x) for x in s]
         CRC="%0.2X"%(sum(b) % 256)    
         return "%0.2X%s%s"%(1+(len(Data)/2),Data,CRC)

    def run(self):
        print "Switching off pollings by the 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,">",500)
        self.CAEIF.commandstatemachine("DISABLEPOLL\r\n",">",500)
        print 
        print "1. Switch off IR receiver board"
        print "(2.) Connect IR receiver board to your PC through"
        print "     RS232 matching and USB serial cable if you want"
        print "3. Put a jumper to JPRG pins"
        print "4. Switch on IR receiver board"
        raw_input("If you have done with these then press <enter>")
        print 
        self.SM = "print_what_do_you_want"
        while(1):   
            ch = self.getkey()
            if "inmediate_exit" in self.SM:
                break
              
            if ch == 'q':
                print "1. Switch off IR receiver board"
                print "(2.) Disconnect IR receiver board from your PC"
                print "     if it was connected before"
                print "3. Remove the jumper from JPRG pins"
                raw_input("If you have done with these then press <enter>")
                print
                print "You are already quit from programming"
                print
                break
                   
            if "print_what_do_you_want" in self.SM:
                print "Press key '1' for Receiver workmode programming"
                print "Press key '2' for RC servo constants programming"
                print "Press key '3' for DC motor silent ranges programming"
                print "Press key 'a' for programming receiver board automatically"
                print "Press key 'r' for reboot receiver board"
                print "Press key 'q' for quit programming"
                print      
                self.SM="chose_what_do_you_want"
            
            if "chose_what_do_you_want" in self.SM and ch == "r":
                print
                print "Rebooting stream is sent"
                snt = "A2"
                self.IOIF.send("a%XsHS%s\r\n"%(self.IRRCAddr, self.CRCCalc(snt)))
                print "Please wait 15 seconds"
                time.sleep(15)
                print
                self.SM = "print_what_do_you_want"
            
            if "chose_what_do_you_want" in self.SM and ch == "a":
                if not self.hardwarecheck_for_programming():
                    print "Hardware problem. IR Remote controller card not found!"
                    print
                    break
                else:
                    print "Hardware is OK for programming"
                    print
                    print "Automatic programming, ADDRESS=01, WORKMODE=12, MSRx=0A, SCAx=20, SCBx=64"
                    print
                    print "Workmode setting stream is sent"
                    snt = "D91201"
                    self.IOIF.send("a%XsHS%s\r\n"%(self.IRRCAddr, self.CRCCalc(snt)))
                    print "Please wait 15 seconds"
                    time.sleep(15)
                    print "Silent ranges setting stream is sent"
                    snt = "5B0A0A"
                    self.IOIF.send("a%XsHS%s\r\n"%(self.IRRCAddr, self.CRCCalc(snt)))
                    print "Please wait 15 seconds"
                    time.sleep(15)
                    print "Servo constants setting stream is sent"
                    snt = "6B206420642064"
                    self.IOIF.send("a%XsHS%s\r\n"%(self.IRRCAddr, self.CRCCalc(snt)))
                    print "Please wait 5 seconds"
                    time.sleep(5)
                    print
                self.SM = "inmediate_exit"
            
            if "chose_what_do_you_want" in self.SM and ch in ["1","2","3"]:
                if not self.hardwarecheck_for_programming():
                    print "Hardware problem. IR Remote controller card not found!"
                    print
                    break
                else:
                    print "Hardware is OK for programming"
                    print
                if ch == "1":
                    self.SM = "print_mode_question"
                if ch == "2":
                    self.SM = "wait_servo_setup_answer"
                if ch == "3":
                    self.SM = "print_msr_menu"
                      
            if "print_msr_menu" in self.SM:
                try:
                    MSRB1 = int(raw_input("Motor1 silent range (01..20) >"),16)
                    MSRB2 = int(raw_input("Motor2 silent range (01..20) >"),16)
                except:
                    MSRB1 = 0
                    MSRB2 = 0
                if MSRB1 in range (1,33) and MSRB2 in range(1,33):
                    print "Silent ranges setting stream is sent"
                    snt = "5B%0.2X%0.2X"%(MSRB1,MSRB2)
                    self.IOIF.send("a%XsHS%s\r\n"%(self.IRRCAddr, self.CRCCalc(snt)))
                    print "Please wait 15 seconds"
                    time.sleep(15)
                    print
                    self.SM = "print_what_do_you_want"
                else:
                    print "Invalid range data"
                    print 
                    self.SM = "print_msr_menu"

            if "wait_servo_setup_answer" in self.SM or "return_servo_setup_byhand" in self.SM:
                TR = True
                try:
                    SCA0 = int(raw_input("Servo0 constant A (00..FF) >"),16)
                    SCB0 = int(raw_input("Servo0 constant B (00..FF) >"),16)
                    SCA1 = int(raw_input("Servo1 constant A (00..FF) >"),16)
                    SCB1 = int(raw_input("Servo1 constant B (00..FF) >"),16)
                    SCA2 = int(raw_input("Servo2 constant A (00..FF) >"),16)
                    SCB2 = int(raw_input("Servo2 constant B (00..FF) >"),16)
                except:
                    TR = False
                if TR and SCA0 in range (0,256) and SCB0 in range (0,256) and SCA1 in range (0,256) and \
                  SCB1 in range (0,256) and SCA2 in range (0,256) and SCB2 in range (0,256):
                    print "Servo constants setting stream is sent"
                    snt = "6B%0.2X%0.2X%0.2X%0.2X%0.2X%0.2X"%(SCA0,SCB0,SCA1,SCB1,SCA2,SCB2)
                    self.IOIF.send("a%XsHS%s\r\n"%(self.IRRCAddr, self.CRCCalc(snt)))
                    print "Please wait 15 seconds"
                    time.sleep(15)
                    print
                    self.SM = "print_what_do_you_want"
                else:
                    print "Invalid servo constant data"
                    print
                    self.SM = "return_servo_setup_byhand"
                    
            if "print_mode_question" in self.SM:
                print "Do you want program addressed mode? (y/n)"
                self.SM = "wait_mode_answer"
            
            if "wait_mode_answer" in self.SM and ch in ["y","n"]:
                if ch == "y":
                    self.AdressedNeeded = True
                    self.ProgramableADDR = int(raw_input("Receiver address(00-FF): "),16)
                else:
                    self.AdressedNeeded = False 
                self.SM = "print_select_workmode"
            
            if "print_select_workmode" in self.SM:        
                print
                print "Please select workmode:"
                selectkeys = []
                modes = [item for item in self.Programtable if item.get("Addressed") == self.AdressedNeeded]
                for item in modes:
                    print "Press key '%s' for programming %s --> %s"%(item.get("Key"),item.get("Name"),item.get("Description"))
                    selectkeys.append(item.get("Key"))
                print "Press 'r' to return to main menu"
                self.SM = "wait_for_select_workmode"
                print
                
            if "wait_for_select_workmode" in self.SM and ch == 'r':
                print
                self.SM = "print_what_do_you_want"    
                
            if "wait_for_select_workmode" in self.SM and ch in selectkeys:
                mode = [item for item in modes if item.get("Key") == ch] 
                print "Your selection: %s --> %s"%(mode[0].get("Name"),mode[0].get("Description"))
                print "Programming %s, programming stream is sent"%(mode[0].get("Name"))
                snt = "D9%0.2X%0.2X"%(mode[0].get("Index"),self.ProgramableADDR)
                self.IOIF.send("a%XsHS%s\r\n"%(self.IRRCAddr, self.CRCCalc(snt)))
                print "Note: You have to use the following stream in this workmode:"
                print "      %s"%mode[0].get("Frame")
                print
                print "Please wait 15 seconds"
                time.sleep(15)
                self.SM="print_select_workmode"
                    
if __name__ == '__main__': 
    """Main program"""
    PPDriver = PPDRIVER();
    PPDriver.registerCAEinterface("localhost", 8000)
    PPDriver.registerIOinterface("localhost", 12000)
    print "Connecting to CAE and IO interfaces of PPDS"
    PPDriver.connectinterfaces()
    CAEIF, IOIF, MONIF = PPDriver.getinterfaces()
    IRProg = IRPROG(CAEIF, IOIF)
    print "Disonnecting from CAE and IO interfaces of PPDS"
    PPDriver.disconnectinterfaces()
    sys.exit(0)
    