#!/usr/bin/python3
# Cloud Display Control - Copyright 2017 Lucid Networks - Stephen Loeckle

import socket
import binascii
import logging
import socketserver
import struct
import os
import sys
import time
import json
import inspect
from time import time, sleep
import datetime
import collections
import configparser
import argparse
import pickle
import urllib.request
import traceback
from multiprocessing import Process, Queue
from cdclib import buildtm, dict_diff, fetchcmd, getinput, getfirmware, getmac, getmodel, getpowermode, getserial, gettmmode, gettmpos, getvolume, sendlog, saveconfig, savestate, sethdmimodes, setinput, setosd, setpower, settm, setvolume, sockclose, Timeout, update_dict, waitfornetwork

PORT = 7142

class LogRecordStreamHandler(socketserver.StreamRequestHandler):
    def handle(self):
        while True:
            chunk = self.connection.recv(4)
            if len(chunk) < 4:
                break
            slen = struct.unpack('>L', chunk)[0]
            chunk = self.connection.recv(slen)
            while len(chunk) < slen:
                chunk = chunk + self.connection.recv(slen - len(chunk))
            obj = self.unPickle(chunk)
            record = logging.makeLogRecord(obj)
            self.handleLogRecord(record)

    def unPickle(self, data):
#        sortedresults = collections.OrderedDict(sorted(pickle.loads(data).items()))
#        for key,val in sortedresults.items():
#            print("{0} = {1}".format(key,val))
        return pickle.loads(data)

    def handleLogRecord(self, record):
        # if a name is specified, we use the named logger rather than the one
        # implied by the record.
        if self.server.logname is not None:
            name = self.server.logname
        else:
            name = record.name
        logger = logging.getLogger(name)
        # N.B. EVERY record gets logged. This is because Logger.handle
        # is normally called AFTER logger-level filtering. If you want
        # to do filtering, do it at the client end to save wasting
        # cycles and network bandwidth!
        logger.handle(record)

class LogRecordSocketReceiver(socketserver.ThreadingTCPServer):
    allow_reuse_address = True

    def __init__(self, host='localhost',
                 port=logging.handlers.DEFAULT_TCP_LOGGING_PORT,
                 handler=LogRecordStreamHandler):
        socketserver.ThreadingTCPServer.__init__(self, (host, port), handler)
        self.abort = 0
        self.timeout = 1
        self.logname = None

    def serve_until_stopped(self):
        import select
        abort = 0
        while not abort:
            rd, wr, ex = select.select([self.socket.fileno()],
                                       [], [],
                                       self.timeout)
            if rd:
                self.handle_request()
            abort = self.abort

def startupdisplaystatus(ip, port, socketid, q):
    gotthefunc = inspect.stack()[0][3]
    msg = ('Entered {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)
    ts = time()
    s = {}
    s[socketid] = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s[socketid].settimeout(10)
    s[socketid].connect((ip, port))
    s[socketid].send(b"\x01\x30\x2A\x30\x43\x30\x36\x02\x30\x32\x33\x45\x03\x1A\x0D")
    response = s[socketid].recv(27)
    hexresponse = binascii.b2a_hex(response)
    if hexresponse == b'01303041443132023030303233453030303036343030303103700d':
        displayid = 1
    if hexresponse == b'01303042443132023030303233453030303036343030303203700d':
        displayid = 2
    if hexresponse == b'01303043443132023030303233453030303036343030303303700d':
        displayid = 3
    if hexresponse == b'01303044443132023030303233453030303036343030303403700d':
        displayid = 4
    if hexresponse == b'01303045443132023030303233453030303036343030303503700d':
        displayid = 5
    if hexresponse == b'01303046443132023030303233453030303036343030303603700d':
        displayid = 6
    if hexresponse == b'01303047443132023030303233453030303036343030303703700d':
        displayid = 7
    if hexresponse == b'01303048443132023030303233453030303036343030303803700d':
        displayid = 8
    if hexresponse == b'01303049443132023030303233453030303036343030303903700d':
        displayid = 9
    if hexresponse == b'0130304a4431320230303032334530303030363430303041030b0d':
        displayid = 10

    output = {}
    output['D{0}'.format(displayid)] = {}
    output['D{0}'.format(displayid)]['IP'] = ip
    output['D{0}'.format(displayid)]['ID'] = displayid
    output['D{0}'.format(displayid)]['MODEL'] = getmodel(displayid,s[socketid])
    output['D{0}'.format(displayid)]['SERIAL'] = getserial(displayid,s[socketid])
    output['D{0}'.format(displayid)]['F1'],output['D{0}'.format(displayid)]['F2'],output['D{0}'.format(displayid)]['F3'],output['D{0}'.format(displayid)]['F4'] = getfirmware(displayid,s[socketid])
    output['D{0}'.format(displayid)]['INPUT'] = getinput(displayid,s[socketid])
    output['D{0}'.format(displayid)]['TMMODE'] = gettmmode(displayid,s[socketid])
    output['D{0}'.format(displayid)]['TMBC'] = gettmpos(displayid,s[socketid])
    output['D{0}'.format(displayid)]['TMH'] = gettmpos(displayid,s[socketid])
    output['D{0}'.format(displayid)]['TMV'] = gettmpos(displayid,s[socketid])
    output['D{0}'.format(displayid)]['TMP'] = gettmpos(displayid,s[socketid])
    output['D{0}'.format(displayid)]['PWR'] = getpowermode(displayid,s[socketid])
    output['D{0}'.format(displayid)]['VOL'] = getvolume(displayid,s[socketid])
    sockclose(s[socketid],gotthefunc)
    output['D{0}'.format(displayid)]['ST'] = 'Last process time: {0}'.format(time() - ts)
    q.put(output)
    msg = ('Exited {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)

def setdefaults(ip, port, displaynum, socketid, q):
    gotthefunc = inspect.stack()[0][3]
    msg = ('Entered {} function with options {}'.format(gotthefunc, displaynum))
    sendlog(gotthefunc, msg, 10)
    ts = time()
    s = {}
    s[socketid] = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s[socketid].settimeout(10)
    s[socketid].connect((ip, port))
    output = {}
    output['D{0}'.format(displaynum)] = {}
    output['D{0}'.format(displaynum)]['OSD'] = setosd(displaynum,0,s[socketid])
    sockclose(s[socketid],gotthefunc)
    output['D{0}'.format(displaynum)]['FUNCNAME'] = gotthefunc
    output['D{0}'.format(displaynum)]['FUNCTIME'] = 'Last process time: {0}'.format(time() - ts)
    q.put(output)
    msg = ('Exited {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)

def buildtmworker(sysinfo, port, displaynum, hnum, vnum, pos, socketid, q):
    gotthefunc = inspect.stack()[0][3]
    msg = ('Entered {} function with options {} {} {} {}'.format(gotthefunc,displaynum, hnum, vnum, pos))
    sendlog(gotthefunc, msg, 10)
    ts = time()
    output = {} 
    output['D{0}'.format(displaynum)] = {}
    target = sysinfo['SYSTEM']['TARGET']
    s = {}
    s[socketid] = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s[socketid].settimeout(10)
    s[socketid].connect((sysinfo['D{}'.format(displaynum)]['IP'], port))
#    returnbcmode, returnhnum, returnvnum, returnpos = buildtm(target,displaynum,hnum,vnum,pos,sysinfo['WALL']['WIRING'],s[socketid])
    if displaynum != sysinfo['SYSTEM']['TARGET']:
        if sysinfo['SYSTEM']['DPCHAIN'] == 1:
            if sysinfo['D{0}'.format(displaynum)]['TMMODE'] == 0:
               output['D{0}'.format(displaynum)]['LASTINPUT'] = sysinfo['D{0}'.format(displaynum)]['INPUT']
            returninput = setinput(displaynum, 'dp', sysinfo['WALL']['MODEL'], s[socketid])
            msg = returninput
            sendlog(gotthefunc, msg, 10)
            output['D{0}'.format(displaynum)]['INPUT'] = returninput
        else:
            msg = 'D{0}: DP Chain Disabled'.format(displaynum)
            sendlog(gotthefunc, msg, 10)
    else:
        msg = ('displaynum from builttmworker = {}'.format(displaynum))
        sendlog(gotthefunc, msg, 10)
    returnbcmode, returnhnum, returnvnum, returnpos = buildtm(target,displaynum,hnum,vnum,pos,sysinfo['WALL']['WIRING'],s[socketid])
    returntmmode = settm(displaynum, 1, s[socketid])
    output['D{0}'.format(displaynum)]['SAVED']=0
    returnsaved = saveconfig(displaynum, s[socketid])
    output['D{0}'.format(displaynum)]['TMMODE']=returntmmode
    output['D{0}'.format(displaynum)]['TMBC']=returnbcmode
    output['D{0}'.format(displaynum)]['TMH']=returnhnum
    output['D{0}'.format(displaynum)]['TMV']=returnvnum
    output['D{0}'.format(displaynum)]['TMP']=returnpos
    output['D{0}'.format(displaynum)]['SAVED']=returnsaved
    output['D{0}'.format(displaynum)]['TILED']=1
    sockclose(s[socketid],gotthefunc)
    output['D{0}'.format(displaynum)]['TMTIME'] = 'Last process time: {0}'.format(time() - ts)
    q.put(output)
    msg = ('Exited {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)

def buildtmwork(sysinfo,h,w):
    gotthefunc = inspect.stack()[0][3]
    msg = ('Entered {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)
    tsmain = time()
    socketnum = 0
    target = sysinfo['SYSTEM']['TARGET']
    displaynum = sysinfo['SYSTEM']['TARGET']
    end = displaynum + (h*w)
    position = 1
    buildtmq = Queue()
    buildtmqresults = {}
    buildtmprocs = []
    while (displaynum < end):
       buildtmproc = Process(target=buildtmworker, args=(sysinfo, PORT, displaynum, h, w, position, socketnum, buildtmq))
       buildtmprocs.append(buildtmproc)
       socketnum += 1
       displaynum += 1
       position += 1
    for w in buildtmprocs:
        w.start()
    for i in range(socketnum):
        update_dict(buildtmqresults, buildtmq.get())
    for w in buildtmprocs:
        w.join()
    msg = ('Took {}'.format(time() - tsmain))
    sendlog(gotthefunc, msg, 10)
    msg = buildtmqresults
    sendlog(gotthefunc, msg, 10)
    msg = ('Exited {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)
    print('*********************************************************************************************')
    print(buildtmqresults)
    print('*********************************************************************************************')
    return(buildtmqresults)
    
def buildtm2x2(sysinfo):
    gotthefunc = inspect.stack()[0][3]
    msg = ('Entered {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)
    tsmain = time()
    socketnum = 0
    target = sysinfo['SYSTEM']['TARGET']
    displaynum = sysinfo['SYSTEM']['TARGET']
    end = displaynum + 4
    position = 1
    buildtm2x2q = Queue()
    buildtm2x2qresults = {}
    buildtm2x2workers = []
    while (displaynum < end):
       buildtm2x2worker = Process(target=buildtmworker, args=(sysinfo, PORT, displaynum, 2, 2, position, socketnum, buildtm2x2q))
       buildtm2x2workers.append(buildtm2x2worker)
       socketnum += 1
       displaynum += 1
       position += 1
    for w in buildtm2x2workers:
        w.start()
    for i in range(socketnum):
        update_dict(buildtm2x2qresults, buildtm2x2q.get())
    for w in buildtm2x2workers:
        w.join()
    msg = ('Took {}'.format(time() - tsmain))
    sendlog(gotthefunc, msg, 10)
    msg = buildtm2x2qresults
    sendlog(gotthefunc, msg, 10)
    msg = ('Exited {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)
    print('*********************************************************************************************')
    print(buildtm2x2qresults)
    print('*********************************************************************************************')
    return(buildtm2x2qresults)
    
def buildtm3x3(sysinfo):
    gotthefunc = inspect.stack()[0][3]
    msg = ('Entered {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)
    tsmain = time()
    socketnum = 0
    target = sysinfo['SYSTEM']['TARGET']
    displaynum = sysinfo['SYSTEM']['TARGET']
    end = displaynum + 9
    position = 1
    buildtm3x3q = Queue()
    buildtm3x3qresults = {}
    buildtm3x3workers = []
    while (displaynum < end):
       buildtm3x3worker = Process(target=buildtmworker, args=(sysinfo, PORT, displaynum, 3, 3, position, socketnum, buildtm3x3q))
       buildtm3x3workers.append(buildtm3x3worker)
       socketnum += 1
       displaynum += 1
       position += 1
    for w in buildtm3x3workers:
        w.start()
    for i in range(socketnum):
        update_dict(buildtm3x3qresults, buildtm3x3q.get())
    for w in buildtm3x3workers:
        w.join()
    msg = ('Took {}'.format(time() - tsmain))
    sendlog(gotthefunc, msg, 10)
    msg = buildtm3x3qresults
    sendlog(gotthefunc, msg, 10)
    msg = ('Exited {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)
    return(buildtm3x3qresults)

def changeinput(sysinfo, input, displaynum=999):
    gotthefunc = inspect.stack()[0][3]
    wallmodel = sysinfo['WALL']['MODEL']
    ip = sysinfo['D{0}'.format(sysinfo['SYSTEM']['TARGET'])]['IP']
    msg = ('Entered {} function with options {} {} {}'.format(gotthefunc, displaynum, input, wallmodel))
    sendlog(gotthefunc, msg, 10)
    if displaynum == 999:
        displaynum = sysinfo['SYSTEM']['TARGET']
        msg = ('CHANGEINPUT: Fetching displaynum from sysinfo target: {}'.format(displaynum))
        sendlog(gotthefunc, msg, 10)
    else:
        ip = sysinfo['D{0}'.format(displaynum)]['IP']
        msg = ('CHANGEINPUT: Fetching displaynum from arguments passed to function: {}'.format(displaynum))
        sendlog(gotthefunc, msg, 10)
    ts = time()
    output = {}
    output['D{0}'.format(displaynum)] = {}
    output['D{0}'.format(displaynum)]['LASTINPUT'] = sysinfo['D{0}'.format(displaynum)]['INPUT']
    s = {}
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.settimeout(10)
    s.connect((ip, PORT))
    returninputstatus = setinput(displaynum, input, wallmodel, s)
##  Need to store hdmi mode (or any other input) states
    if input == 'hdmi':
        returninputsettings = sethdmimodes(displaynum, s)
    output['D{0}'.format(displaynum)]['SAVED']=0
    returnsaved = saveconfig(displaynum, s)
    output['D{0}'.format(displaynum)]['SAVED']=returnsaved
    output['D{0}'.format(displaynum)]['INPUT']=returninputstatus
    sockclose(s,gotthefunc)
    output['D{0}'.format(displaynum)]['CHANGEINPUTTIME'] = 'Last process time: {0}'.format(time() - ts)
    msg = ('Exited {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)
    return(output)
    

def changeinputprocess(ip, port, displaynum, input, lastinput, wallmodel, socketid, q):
    gotthefunc = inspect.stack()[0][3]
    msg = ('Entered {} function with options {} {} {} {}'.format(gotthefunc, displaynum, input, lastinput, wallmodel))
    sendlog(gotthefunc, msg, 10)
    ts = time()
    output = {}
    output['D{0}'.format(displaynum)] = {}
    s = {}
    s[socketid] = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s[socketid].settimeout(10)
    s[socketid].connect((ip, port))
    returninputstatus = setinput(displaynum, input, wallmodel, s[socketid])
    if input == 'hdmi':
        returninputsettings = sethdmimodes(displaynum, s[socketid])
    output['D{0}'.format(displaynum)]['SAVED']=0
    returnsaved = saveconfig(displaynum, s[socketid])
    output['D{0}'.format(displaynum)]['SAVED']=returnsaved
    output['D{0}'.format(displaynum)]['INPUT']=returninputstatus
    sockclose(s[socketid],gotthefunc)
    output['D{0}'.format(displaynum)]['CHANGEINPUTTIME'] = 'Last process time: {0}'.format(time() - ts)
    # I put the lastinput variable set here, as opposed to changeinputall master function because I don't want it changed unless the new input change was successful
    output['D{0}'.format(displaynum)]['LASTINPUT'] = lastinput
    q.put(output)
    msg = ('Exited {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)

def changeinputall(sysinfo, input):
    gotthefunc = inspect.stack()[0][3]
    msg = ('Entered {} function with options {}'.format(gotthefunc, input))
    sendlog(gotthefunc, msg, 10)
    tsmain = time()
    socketnum = 0
    dpchain = 0
    changeinputallqueue = Queue()
    changeinputallqueueresults = {}
    changeinputallworkers = []
    for key, value in sysinfo.items():
        if key.startswith('D'):
            if sysinfo['SYSTEM']['TILE'] == 4:
                msg = 'Currently in 2x2 tile. Checking for DP chain status'
                sendlog(gotthefunc, msg, 10)
                if sysinfo['SYSTEM']['DPCHAIN'] == 1:
                   if sysinfo['SYSTEM']['TARGET'] == 1:
                       dplist = [2,3,4]
                   if sysinfo['SYSTEM']['TARGET'] == 7:
                       dplist = [8,9,10]
                   if any(x in key for x in dplist):
                       dpchain = 1
                   else:
                       dpchain = 0
            if sysinfo['SYSTEM']['TILE'] == 9:
                msg = 'Currently in 3x3 tile. Checking for DP chain status'
                sendlog(gotthefunc, msg, 10)
                if sysinfo['SYSTEM']['DPCHAIN'] == 1:
                    dplist = [2,3,4,5,6,7,8,9]
                    if any(x in key for x in dplist):
                        dpchain = 1
                    else:
                        dpchain = 0
            if dpchain == 0:
                changeinputallworker = Process(target=changeinputprocess, args=(sysinfo['{0}'.format(key)]['IP'], PORT, sysinfo['{0}'.format(key)]['ID'], input, sysinfo['{0}'.format(key)]['INPUT'], sysinfo['WALL']['MODEL'], socketnum, changeinputallqueue))
                changeinputallworkers.append(changeinputallworker)
                changeinputallworker.start()
                socketnum += 1
            if dpchain == 1:
                changeinputallworker = Process(target=changeinputprocess, args=(sysinfo['{0}'.format(key)]['IP'], PORT, sysinfo['{0}'.format(key)]['ID'], 'dp', sysinfo['{0}'.format(key)]['INPUT'], sysinfo['WALL']['MODEL'], socketnum, changeinputallqueue))
                changeinputallworkers.append(changeinputallworker)
                changeinputallworker.start()
                socketnum += 1
    for i in range(socketnum):
        update_dict(changeinputallqueueresults, changeinputallqueue.get())
    for w in changeinputallworkers:
        w.join()
    msg = ('Took {}'.format(time() - tsmain))
    sendlog(gotthefunc, msg, 10)
    msg = ('Exited {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)
    return(changeinputallqueueresults) 

def changeinputrange(sysinfo, input, quantity):
    gotthefunc = inspect.stack()[0][3]
    msg = ('Entered {} function with options {} {}'.format(gotthefunc, input, quantity))
    sendlog(gotthefunc, msg, 10)
    tsmain = time()
    socketnum = 0
    dpchain = 0
    target = sysinfo['SYSTEM']['TARGET']
    displaynum = sysinfo['SYSTEM']['TARGET']
    end = displaynum + quantity
    changeinputrangequeue = Queue()
    changeinputrangequeueresults = {}
    changeinputrangeworkers = []
    dplist = []
    if sysinfo['SYSTEM']['TILE'] == 4:
        msg = 'Currently in 2x2 tile. Checking for DP chain status'
        sendlog(gotthefunc, msg, 10)
        if sysinfo['SYSTEM']['DPCHAIN'] == 1:
            if sysinfo['SYSTEM']['TARGET'] == 1:
                dplist = [2,3,4]
            if sysinfo['SYSTEM']['TARGET'] == 7:
                dplist = [8,9,10]
    if sysinfo['SYSTEM']['TILE'] == 9:
        msg = 'Currently in 3x3 tile. Checking for DP chain status'
        sendlog(gotthefunc, msg, 10)
        if sysinfo['SYSTEM']['DPCHAIN'] == 1:
            dplist = [2,3,4,5,6,7,8,9]
    while (displaynum < end):
        if displaynum in dplist:
            msg = 'D{0}: Found in dp chain list'.format(displaynum)
            sendlog(gotthefunc, msg, 10)
            changeinputrangeworker = Process(target=changeinputprocess, args=(sysinfo['D{0}'.format(displaynum)]['IP'], PORT, sysinfo['D{0}'.format(displaynum)]['ID'], 'dp', sysinfo['D{0}'.format(displaynum)]['INPUT'], sysinfo['WALL']['MODEL'], socketnum, changeinputrangequeue))
            changeinputrangeworkers.append(changeinputrangeworker)
            changeinputrangeworker.start()
            socketnum += 1
            displaynum += 1
        else:
            msg = 'D{0}: Not found in dp chain list'.format(displaynum)
            sendlog(gotthefunc, msg, 10)
            changeinputrangeworker = Process(target=changeinputprocess, args=(sysinfo['D{0}'.format(displaynum)]['IP'], PORT, sysinfo['D{0}'.format(displaynum)]['ID'], input, sysinfo['D{0}'.format(displaynum)]['INPUT'], sysinfo['WALL']['MODEL'], socketnum, changeinputrangequeue))
            changeinputrangeworkers.append(changeinputrangeworker)
            changeinputrangeworker.start()
            socketnum += 1
            displaynum += 1
    for i in range(socketnum):
        update_dict(changeinputrangequeueresults, changeinputrangequeue.get())
    for w in changeinputrangeworkers:
        w.join()
    msg = ('Took {}'.format(time() - tsmain))
    sendlog(gotthefunc, msg, 10)
    msg = ('Exited {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)
    return(changeinputrangequeueresults)
 
def changepower(sysinfo, power):
    gotthefunc = inspect.stack()[0][3]
    msg = ('Entered {} function with options {}'.format(gotthefunc, power))
    sendlog(gotthefunc, msg, 10)
    tsmain = time()
    socketnum = 0
    powerqueue = Queue()
    powerworkers = []
    for key, value in sysinfo.items():
        if key.startswith('D'):
            powerworker = Process(target=changepowerworker, args=(sysinfo['{0}'.format(key)]['IP'], PORT, sysinfo['{0}'.format(key)]['ID'], power, socketnum, powerqueue))
            powerworkers.append(powerworker)
            powerworker.start()
            socketnum += 1
    powerresults = {}
    for i in range(socketnum):
        update_dict(powerresults, powerqueue.get())
    msg = ('Took {}'.format(time() - tsmain))
    sendlog(gotthefunc, msg, 10)
    # Merge power results into master sysinfo dictionary
    update_dict(sysinfo, powerresults)
    msg = ('Exited {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)

def changepowerworker(ip, port, displaynum, power, socketid, q):
    gotthefunc = inspect.stack()[0][3]
    msg = ('Entered {} function with options {} {}'.format(gotthefunc, displaynum, power))
    sendlog(gotthefunc, msg, 10)
    ts = time()
    s = {}
    s[socketid] = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s[socketid].settimeout(10)
    s[socketid].connect((ip, port))
    output = {}
    output['D{0}'.format(displaynum)] = {}
    output['D{0}'.format(displaynum)]['PWR'] = setpower(displaynum, power,s[socketid])
    sockclose(s[socketid],gotthefunc)
    output['D{0}'.format(displaynum)]['FUNCNAME'] = gotthefunc
    output['D{0}'.format(displaynum)]['FUNCTIME'] = 'Last process time: {0}'.format(time() - ts)
    q.put(output)
    msg = ('Exited {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)

def changevolume(sysinfo,resetvolume=0):
    gotthefunc = inspect.stack()[0][3]
    displaynum = sysinfo['SYSTEM']['TARGET']
    wallmodel = sysinfo['WALL']['MODEL']
    if resetvolume == 0:
        vol = sysinfo['SYSTEM']['COMMAND'].split("_")
    else:
        vol = resetvolume
    ip = sysinfo['D{0}'.format(sysinfo['SYSTEM']['TARGET'])]['IP']
    msg = ('Entered {} function with options {} {} {}'.format(gotthefunc, displaynum, vol[1], wallmodel))
    sendlog(gotthefunc, msg, 10)
    ts = time()
    output = {}
    output['D{0}'.format(displaynum)] = {}
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.settimeout(10)
    s.connect((ip, PORT))
    try:
        with Timeout(3):
            output['D{0}'.format(sysinfo['SYSTEM']['TARGET'])]['VOL'] = setvolume(displaynum,sysinfo['D{0}'.format(sysinfo['SYSTEM']['TARGET'])]['VOL'],vol[1], s)
    except Timeout.Timeout as e:
        pass
        msg = ('Change Volume Timeout')
        sendlog(gotthefunc, msg, 40)
        output['D{0}'.format(sysinfo['SYSTEM']['TARGET'])]['VOL'] = sysinfo['D{0}'.format(sysinfo['SYSTEM']['TARGET'])]['VOL']
    except:
        pass
        msg = ('Unknown Error in {0}'.format(gotthefunc))
        sendlog(gotthefunc, msg, 40)
        err = traceback.format_exc()
        msg = ('Unknown Error in {0} Detail: {1}'.format(gotthefunc,err))
        sendlog(gotthefunc, msg, 10)
        output['D{0}'.format(sysinfo['SYSTEM']['TARGET'])]['VOL'] = sysinfo['D{0}'.format(sysinfo['SYSTEM']['TARGET'])]['VOL']
    returnsaved = saveconfig(displaynum, s)
    sockclose(s,gotthefunc)
    msg = ('Exited {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)
    return(output)
 
def disabletmworker(sysinfo, port, displaynum, input, wallmodel, save, socketid, q):
    gotthefunc = inspect.stack()[0][3]
    msg = ('Entered {} function with options {} {} {}'.format(gotthefunc, displaynum, input, wallmodel))
    sendlog(gotthefunc, msg, 10)
    ts = time()
    output = {}
    output['D{0}'.format(displaynum)] = {}
    target = sysinfo['SYSTEM']['TARGET']
    s = {}
    s[socketid] = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s[socketid].settimeout(10)
    s[socketid].connect((sysinfo['D{}'.format(displaynum)]['IP'], port))
#    returninput = setinput(displaynum, input, wallmodel, s[socketid])
    if displaynum != sysinfo['SYSTEM']['TARGET']:
        returninput = setinput(displaynum, input, wallmodel, s[socketid])
        msg = returninput
        sendlog(gotthefunc, msg, 10)
        output['D{0}'.format(displaynum)]['INPUT'] = returninput
    returntmmode = settm(displaynum, 0, s[socketid])
    output['D{0}'.format(displaynum)]['SAVED'] = 0
    if save == 1:
        returnsaved = saveconfig(displaynum, s[socketid])
        output['D{0}'.format(displaynum)]['SAVED'] = returnsaved
    else:
        output['D{0}'.format(displaynum)]['SAVED'] = 0
    # **** NEED gettm status here, returning bcmode, hnum, vnum, pos *****
    output['D{0}'.format(displaynum)]['TMMODE'] = returntmmode
    sockclose(s[socketid],gotthefunc)
    output['D{0}'.format(displaynum)]['DISABLETMTIME'] = 'Last process time: {0}'.format(time() - ts)
    q.put(output)
    msg = ('Exited {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)
    
def disabletm2x2(sysinfo, save=1):
    gotthefunc = inspect.stack()[0][3]
    msg = ('Entered {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)
    tsmain = time()
    socketnum = 0
    target = sysinfo['SYSTEM']['TARGET']
    displaynum = sysinfo['SYSTEM']['TARGET']
    end = displaynum + 4
    position = 1
    disabletm2x2q = Queue()
    disabletm2x2qresults = {}
    disabletm2x2workers = []
    while (displaynum < end):
#       disabletm2x2worker = Process(target=disabletmworker, args=(sysinfo, PORT, displaynum, sysinfo['SYSTEM']['BASEINPUT'], sysinfo['WALL']['MODEL'] , socketnum, disabletm2x2q))
       disabletm2x2worker = Process(target=disabletmworker, args=(sysinfo, PORT, displaynum, sysinfo['SYSTEM']['DEFAULTINPUT'], sysinfo['WALL']['MODEL'] , save, socketnum, disabletm2x2q))
       disabletm2x2workers.append(disabletm2x2worker)
       socketnum += 1
       displaynum += 1
       position += 1
    for w in disabletm2x2workers:
        w.start()
    for i in range(socketnum):
        update_dict(disabletm2x2qresults, disabletm2x2q.get())
    for w in disabletm2x2workers:
        w.join()
    print('Took {}'.format(time() - tsmain))
    msg = ('Exited {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)
    return(disabletm2x2qresults)
    
def disabletm3x3(sysinfo, save=1):
    gotthefunc = inspect.stack()[0][3]
    msg = ('Entered {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)
    tsmain = time()
    socketnum = 0
    target = sysinfo['SYSTEM']['TARGET']
    displaynum = sysinfo['SYSTEM']['TARGET']
    end = displaynum + 9
    position = 1
    disabletm3x3q = Queue()
    disabletm3x3qresults = {}
    disabletm3x3workers = []
    while (displaynum < end):
#       disabletm3x3worker = Process(target=disabletmworker, args=(sysinfo, PORT, displaynum, sysinfo['SYSTEM']['BASEINPUT'], sysinfo['WALL']['MODEL'] , socketnum, disabletm3x3q))
       disabletm3x3worker = Process(target=disabletmworker, args=(sysinfo, PORT, displaynum, sysinfo['SYSTEM']['DEFAULTINPUT'], sysinfo['WALL']['MODEL'], save, socketnum, disabletm3x3q))
       disabletm3x3workers.append(disabletm3x3worker)
       socketnum += 1
       displaynum += 1
       position += 1
    for w in disabletm3x3workers:
        w.start()
    for i in range(socketnum):
        update_dict(disabletm3x3qresults, disabletm3x3q.get())
    for w in disabletm3x3workers:
        w.join()
    print('Took {}'.format(time() - tsmain))
    msg = ('Exited {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)
    return(disabletm3x3qresults)

def logserver():
    gotthefunc = inspect.stack()[0][3]
    msg = ('Entered {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)
#    logging.basicConfig(format='%(asctime)s %(processName)-10s %(name)s %(levelname)-8s %(message)s',handlers=[logging.handlers.RotatingFileHandler('/var/log/cdc/cdc.log', mode='a', maxBytes=10*1024*1024, backupCount=0, encoding=None, delay=False)])
    logging.basicConfig(format='%(asctime)s %(processName)-10s %(name)s %(levelname)-8s %(message)s',handlers=[logging.handlers.RotatingFileHandler('/var/log/cdc/cdc.log', mode='a', maxBytes=10000*1024, backupCount=30, encoding=None, delay=False)])
#    logging.basicConfig(format='%(asctime)s %(processName)-10s %(name)s %(levelname)-8s %(message)s',filename='/var/log/cdc/cdc.log')
    tcpserver = LogRecordSocketReceiver()
    tcpserver.serve_until_stopped()
    msg = ('Exited {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)

def main():
    gotthefunc = inspect.stack()[0][3]
    msg = ('Entered {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)

    parser = argparse.ArgumentParser(description='Cloud Display Controls')
    parser.add_argument('--console', action="store_true", default=False, dest='consolelogs', help = 'Output logs to console')
    cliargs = parser.parse_args()

    timeout = 10
    socket.setdefaulttimeout(timeout)

    logworker = Process(target=logserver)
    logworker.start()

    # Build master systemwide sysinfo dictionary
    sysinfo = {}
    sysinfo['SYSTEM'] = {}
    sysinfo['WALL'] = {}

    # Read config
    config = configparser.ConfigParser()
    config.read(['/etc/cdc/cdc.conf'])
    # Load license key
    sysinfo['SYSTEM']['LICENSEKEY'] = config.get('system', 'licensekey')
    # Get machine mac address
    sysinfo['SYSTEM']['MAC'] = getmac("eth0")
    sysinfo['SYSTEM']['LOGLEVEL'] = config.get('system', 'loglevel')
    sysinfo['WALL']['MODEL'] = config.get('wall', 'model')
    sysinfo['WALL']['WIRING'] = config.get('wall', 'wiring')
    sysinfo['SYSTEM']['DEFAULTINPUT'] = config.get('wall', 'defaultinput')
    logger = logging.getLogger('main')
    if sysinfo['SYSTEM']['LOGLEVEL'] == '10':
        logger.setLevel(logging.DEBUG)
    if sysinfo['SYSTEM']['LOGLEVEL'] == '20':
        logger.setLevel(logging.INFO)
    if sysinfo['SYSTEM']['LOGLEVEL'] == '30':
        logger.setLevel(logging.WARNING)
    if sysinfo['SYSTEM']['LOGLEVEL'] == '40':
        logger.setLevel(logging.ERROR)
    if sysinfo['SYSTEM']['LOGLEVEL'] == '50':
        logger.setLevel(logging.CRITICAL)
    sock = logging.handlers.SocketHandler('localhost', logging.handlers.DEFAULT_TCP_LOGGING_PORT)
    logger.addHandler(sock)
    if cliargs.consolelogs:
        consolelog = logging.StreamHandler(sys.stdout)
        logger.addHandler(consolelog)
    sleep(2)

#    loglevel = config.get('system', 'loglevel')
    msg = 'Log test'
    sendlog(gotthefunc, msg, 10)

    # Load display IP addresses from configuration
    IP = {}
    ipnum = 0
    for key, val in config.items('network'):
        if 'ip' in key:
            IP[ipnum]=val
            ipnum += 1

    # Test network connection

    msg = waitfornetwork(IP[0])
    sendlog(gotthefunc, msg, 10)
    # Begin startupdisplaystatus to query all displays for current configuration and status.
    # Additionally, ID to IP learning and mapping takes place.
    tsmain = time()
    socketnum = 0
    startupqueue = Queue()
    startupworkers = []
    for ipid, ipaddr in IP.items():
        now = datetime.datetime.now()
        startupworker = Process(target=startupdisplaystatus, args=(ipaddr, PORT, socketnum, startupqueue))
        startupworkers.append(startupworker)
        startupworker.start()
        socketnum += 1
    sysinfo['SYSTEM']['DISPLAYS'] = socketnum
    startupresults = {}
    for i in range(socketnum):
        update_dict(startupresults, startupqueue.get())
    for w in startupworkers:
        w.join()
    sortedresults = collections.OrderedDict(sorted(startupresults.items()))
    for key,val in sortedresults.items():
        msg = ("{0} = {1}".format(key,val))
        sendlog(gotthefunc, msg, 10)
    msg = ('Took {}'.format(time() - tsmain))
    sendlog(gotthefunc, msg, 10)
    # Merge startup results into master sysinfo dictionary
    update_dict(sysinfo, startupresults)

    # Let's set some display defaults, like OSD Off

    tsmain = time()
    socketnum = 0
    setdefaultqueue = Queue()
    setdefaultworkers = []
    for key, value in sysinfo.items():
        if key.startswith('D'):
            setdefaultworker = Process(target=setdefaults, args=(sysinfo['{0}'.format(key)]['IP'], PORT, sysinfo['{0}'.format(key)]['ID'], socketnum, setdefaultqueue))
            setdefaultworkers.append(setdefaultworker)
            setdefaultworker.start()
            socketnum += 1
    setdefaultresults = {}
    for i in range(socketnum):
        update_dict(setdefaultresults, setdefaultqueue.get())
    for w in setdefaultworkers:
        w.join()
    sortedresults = collections.OrderedDict(sorted(setdefaultresults.items()))
    for key,val in sortedresults.items():
        msg = ("{0} = {1}".format(key,val))
        sendlog(gotthefunc, msg, 10)
    msg = ('Took {}'.format(time() - tsmain))
    sendlog(gotthefunc, msg, 10)
    # Merge setdefault results into master sysinfo dictionary
    update_dict(sysinfo, setdefaultresults)

    # If it exists, load old state file in case there was a power issue

    if os.path.exists("/var/lib/cdc/cdc.state"):
        with open('/var/lib/cdc/cdc.state', 'r') as f:
            laststate = json.load(f)
        f.close()
        sysinfo['SYSTEM']['LASTUPDATED'] = laststate['SYSTEM']['LASTUPDATED']
        sysinfo['SYSTEM']['COMMAND'] = laststate['SYSTEM']['COMMAND']
        sysinfo['SYSTEM']['TILE'] = laststate['SYSTEM']['TILE']
        sysinfo['SYSTEM']['BASEINPUT'] = laststate['SYSTEM']['BASEINPUT']
        sysinfo['SYSTEM']['DPCHAIN'] = laststate['SYSTEM']['DPCHAIN']
        for key, value in sysinfo.items():
            if key.startswith('D'):
                sysinfo['{0}'.format(key)]['LASTINPUT'] = laststate['{0}'.format(key)]['LASTINPUT']
    else:
        laststate = {}
        sysinfo['SYSTEM']['LASTUPDATED'] = 0
        sysinfo['SYSTEM']['COMMAND'] = None
        sysinfo['SYSTEM']['TILE'] = 0
        sysinfo['SYSTEM']['BASEINPUT'] = None
        sysinfo['SYSTEM']['DPCHAIN'] = 0
        # Set LASTINPUT to default input from config if LASTINPUT has never been set
        for key, value in sysinfo.items():
            if key.startswith('D'):
                sysinfo['{0}'.format(key)]['LASTINPUT'] = config.get('wall', 'defaultinput')

#    if sysinfo['SYSTEM']['TILE'] == 0:
#        cmdresponse = disabletm2x2(sysinfo)
#        msg = cmdresponse
#        sendlog(gotthefunc, msg, 10)
#        update_dict(sysinfo, cmdresponse)
#
#    if sysinfo['SYSTEM']['TILE'] == 4:
#        cmdresponse = buildtm2x2(sysinfo)
#        msg = cmdresponse
#        sendlog(gotthefunc, msg, 10)
#        update_dict(sysinfo, cmdresponse)


# Unused until 9 display programming compelted
#
#    if sysinfo['SYSTEM']['TILE'] = 9:
#        cmdresponse = buildtm2x2(sysinfo)
#        print(cmdresponse)
#        update(sysinfo, cmdresponse)
        

#    sysinfo['SYSTEM']['GETCMDRUNNING'] = 0

    # Let's compare old state to sysinfo

#    diff,changes = dict_diff(laststate,sysinfo)
#    if diff:
#        sortedresults = collections.OrderedDict(sorted(diff.items()))
#        for key,val in sortedresults.items():
#            print("{0} = {1}".format(key,val))
#        print(diff)
#    if changes:
#        sysinfo.update(changes)
#        savestate(sysinfo)
#        sortedresults = collections.OrderedDict(sorted(changes.items()))
#        for key,val in sortedresults.items():
#            print("{0} = {1}".format(key,val))
#        print(changes)

    print(sysinfo)
    # Begin cloud server query for data
    csloop = 0
    while True:
        try:
            with Timeout(3):
                olddict = sysinfo.copy()
        except Timeout.Timeout as e:
            pass
            msg = ('CSLoop Start Class Timeout')
            sendlog(gotthefunc, msg, 40)
        except:
            pass
            msg = ('Unknown Error 1 in {0}'.format(gotthefunc))
            sendlog(gotthefunc, msg, 40)
            err = traceback.format_exc()
            msg = ('Unknown Error 1 Detail: {0}'.format(err))
            sendlog(gotthefunc, msg, 10)
        try:
            with Timeout(3):
                cmd = fetchcmd(sysinfo)
        except Timeout.Timeout as e:
            pass
            msg = ('CSLoop cmd = fetchcmd Class Timeout')
            sendlog(gotthefunc, msg, 40)
        except:
            pass
            msg = ('Unknown Error 2 in {0}'.format(gotthefunc))
            sendlog(gotthefunc, msg, 40)
            err = traceback.format_exc()
            msg = ('Unknown Error 2 Detail: {0}'.format(err))
            sendlog(gotthefunc, msg, 10)
        # Merge results into master sysinfo dictionary
        msg = ('Entering update_dict function'.format(gotthefunc))
        sendlog(gotthefunc, msg, 10)
        try:
            with Timeout(3):
                update_dict(sysinfo, cmd)
        except Timeout.Timeout as e:
            pass
            msg = ('CSLoop update_dict Class Timeout')
            sendlog(gotthefunc, msg, 40)
        except:
            pass
            msg = ('Unknown Error 3 in {0}'.format(gotthefunc))
            sendlog(gotthefunc, msg, 40)
            err = traceback.format_exc()
            msg = ('Unknown Error 3 Detail: {0}'.format(err))
            sendlog(gotthefunc, msg, 10)
        msg = ('Exited update_dict function'.format(gotthefunc))
        sendlog(gotthefunc, msg, 10)
        cmdresponse = {}
        if sysinfo['SYSTEM']['COMMAND'] != 'null':
            if sysinfo['SYSTEM']['COMMAND'] == 'input_dvi':
                sysinfo['SYSTEM']['DPCHAINPREV'] = sysinfo['SYSTEM']['DPCHAIN']
                sysinfo['SYSTEM']['DPCHAIN'] = 1
                if sysinfo['SYSTEM']['DPCHAINPREV'] == 0:
                    try:
                        with Timeout(10):
                            cmdresponse = changeinputall(sysinfo, 'dvi')
                    except Timeout.Timeout as e:
                        pass
                        msg = ('CSLoop changeinputall dvi Class Timeout')
                        sendlog(gotthefunc, msg, 40)
                    except:
                        pass
                        msg = ('Unknown Error 4 in {0}'.format(gotthefunc))
                        sendlog(gotthefunc, msg, 40)
                        err = traceback.format_exc()
                        msg = ('Unknown Error 4 Detail: {0}'.format(err))
                        sendlog(gotthefunc, msg, 10)
                else:
                    try:
                        with Timeout(10):
                            cmdresponse = changeinput(sysinfo, 'dvi')
                    except Timeout.Timeout as e:
                        pass
                        msg = ('CSLoop changeinput dvi Class Timeout')
                        sendlog(gotthefunc, msg, 40)
                    except:
                        pass
                        msg = ('Unknown Error 5 in {0}'.format(gotthefunc))
                        sendlog(gotthefunc, msg, 40)
                        err = traceback.format_exc()
                        msg = ('Unknown Error 5 Detail: {0}'.format(err))
                        sendlog(gotthefunc, msg, 10)
                update_dict(sysinfo, cmdresponse)
                sysinfo['SYSTEM']['BASEINPUT'] = 'dvi'
            if sysinfo['SYSTEM']['COMMAND'] == 'input_dvi_4':
                sysinfo['SYSTEM']['DPCHAINPREV'] = sysinfo['SYSTEM']['DPCHAIN']
                sysinfo['SYSTEM']['DPCHAIN'] = 1
                try:
                    with Timeout(10):
                        cmdresponse = changeinputrange(sysinfo, 'dvi', 4)
                except Timeout.Timeout as e:
                    pass
                    msg = ('CSLoop changeinputrange dvi Class Timeout')
                    sendlog(gotthefunc, msg, 40)
                except:
                    pass
                    msg = ('Unknown Error 6 in {0}'.format(gotthefunc))
                    sendlog(gotthefunc, msg, 40)
                    err = traceback.format_exc()
                    msg = ('Unknown Error 6 Detail: {0}'.format(err))
                    sendlog(gotthefunc, msg, 10)
                update_dict(sysinfo, cmdresponse)
            if sysinfo['SYSTEM']['COMMAND'] == 'input_dvi_all':
                sysinfo['SYSTEM']['DPCHAINPREV'] = sysinfo['SYSTEM']['DPCHAIN']
                sysinfo['SYSTEM']['DPCHAIN'] = 1
                try:
                    with Timeout(10):
                        cmdresponse = changeinputall(sysinfo, 'dvi')
                except Timeout.Timeout as e:
                    pass
                    msg = ('CSLoop changeinputall dvi 2 Class Timeout')
                    sendlog(gotthefunc, msg, 40)
                except:
                    pass
                    msg = ('Unknown Error 7 in {0}'.format(gotthefunc))
                    sendlog(gotthefunc, msg, 40)
                    err = traceback.format_exc()
                    msg = ('Unknown Error 7 Detail: {0}'.format(err))
                    sendlog(gotthefunc, msg, 10)
                update_dict(sysinfo, cmdresponse)
                sysinfo['SYSTEM']['BASEINPUT'] = 'dvi'
            if sysinfo['SYSTEM']['COMMAND'] == 'input_hdmi':
## Temporary until I can get this to only affect drequested screen numbers
                sysinfo['SYSTEM']['DPCHAINPREV'] = sysinfo['SYSTEM']['DPCHAIN']
                sysinfo['SYSTEM']['DPCHAIN'] = 1
                try:
                    with Timeout(10):
                        cmdresponse = changeinput(sysinfo, 'hdmi')
                except Timeout.Timeout as e:
                    pass
                    msg = ('CSLoop changeinput hdmi Class Timeout')
                    sendlog(gotthefunc, msg, 40)
                except:
                    pass
                    msg = ('Unknown Error 8 in {0}'.format(gotthefunc))
                    sendlog(gotthefunc, msg, 40)
                    err = traceback.format_exc()
                    msg = ('Unknown Error 8 Detail: {0}'.format(err))
                    sendlog(gotthefunc, msg, 10)
                update_dict(sysinfo, cmdresponse)
                sysinfo['SYSTEM']['BASEINPUT'] = 'hdmi'
            if sysinfo['SYSTEM']['COMMAND'] == 'input_hdmi_all':
                sysinfo['SYSTEM']['DPCHAINPREV'] = sysinfo['SYSTEM']['DPCHAIN']
                sysinfo['SYSTEM']['DPCHAIN'] = 1
                try:
                    with Timeout(10):
                        cmdresponse = changeinputall(sysinfo, 'hdmi')
                except Timeout.Timeout as e:
                    pass
                    msg = ('CSLoop changeinputall hdmi Class Timeout')
                    sendlog(gotthefunc, msg, 40)
                except:
                    pass
                    msg = ('Unknown Error 9 in {0}'.format(gotthefunc))
                    sendlog(gotthefunc, msg, 40)
                    err = traceback.format_exc()
                    msg = ('Unknown Error 9 Detail: {0}'.format(err))
                    sendlog(gotthefunc, msg, 10)
                update_dict(sysinfo, cmdresponse)
                sysinfo['SYSTEM']['BASEINPUT'] = 'hdmi'
            if sysinfo['SYSTEM']['COMMAND'] == 'input_hdmi_splitter_4':
                sysinfo['SYSTEM']['DPCHAINPREV'] = sysinfo['SYSTEM']['DPCHAIN']
                sysinfo['SYSTEM']['DPCHAIN'] = 0
                try:
                    with Timeout(10):
                        cmdresponse = changeinputrange(sysinfo, 'hdmi', 4)
                except Timeout.Timeout as e:
                    pass
                    msg = ('CSLoop changeinputrange hdmi Class Timeout')
                    sendlog(gotthefunc, msg, 40)
                except:
                    pass
                    msg = ('Unknown Error 10 in {0}'.format(gotthefunc))
                    sendlog(gotthefunc, msg, 40)
                    err = traceback.format_exc()
                    msg = ('Unknown Error 10 Detail: {0}'.format(err))
                    sendlog(gotthefunc, msg, 10)
                update_dict(sysinfo, cmdresponse)
            if sysinfo['SYSTEM']['COMMAND'] == 'input_hdmi_splitter':
                sysinfo['SYSTEM']['DPCHAINPREV'] = sysinfo['SYSTEM']['DPCHAIN']
                sysinfo['SYSTEM']['DPCHAIN'] = 0
                try:
                    with Timeout(10):
                        cmdresponse = changeinputall(sysinfo, 'hdmi')
                except Timeout.Timeout as e:
                    pass
                    msg = ('CSLoop changeinputall 2 hdmi Class Timeout')
                    sendlog(gotthefunc, msg, 40)
                except:
                    pass
                    msg = ('Unknown Error 11 in {0}'.format(gotthefunc))
                    sendlog(gotthefunc, msg, 40)
                    err = traceback.format_exc()
                    msg = ('Unknown Error 11 Detail: {0}'.format(err))
                    sendlog(gotthefunc, msg, 10)
                update_dict(sysinfo, cmdresponse)
                sysinfo['SYSTEM']['BASEINPUT'] = 'hdmi_splitter'
            if sysinfo['SYSTEM']['COMMAND'] == 'input_option':
                sysinfo['SYSTEM']['DPCHAINPREV'] = sysinfo['SYSTEM']['DPCHAIN']
                sysinfo['SYSTEM']['DPCHAIN'] = 1
                try:
                    with Timeout(10):
                        cmdresponse = changeinput(sysinfo, 'option')
                except Timeout.Timeout as e:
                    pass
                    msg = ('CSLoop changeinput optioni Class Timeout')
                    sendlog(gotthefunc, msg, 40)
                except:
                    pass
                    msg = ('Unknown Error 12 in {0}'.format(gotthefunc))
                    sendlog(gotthefunc, msg, 40)
                    err = traceback.format_exc()
                    msg = ('Unknown Error 12 Detail: {0}'.format(err))
                    sendlog(gotthefunc, msg, 10)
                update_dict(sysinfo, cmdresponse)
            if sysinfo['SYSTEM']['COMMAND'] == 'tile_9_on':
                try:
                    with Timeout(130):
                        cmdresponse = buildtmwork(sysinfo,3,3)
                except Timeout.Timeout as e:
                    pass
                    msg = ('CSLoop buildtmwork 3x3 Class Timeout')
                    sendlog(gotthefunc, msg, 40)
                except:
                    pass
                    msg = ('Unknown Error 13 in {0}'.format(gotthefunc))
                    sendlog(gotthefunc, msg, 40)
                    err = traceback.format_exc()
                    msg = ('Unknown Error 13 Detail: {0}'.format(err))
                    sendlog(gotthefunc, msg, 10)
                msg = cmdresponse
                sendlog(gotthefunc, msg, 10)
                update_dict(sysinfo, cmdresponse)
                sysinfo['SYSTEM']['TILE'] = 9
            if sysinfo['SYSTEM']['COMMAND'] == 'tile_on':
                if sysinfo['SYSTEM']['TILE'] == 9:
                    try:
                        with Timeout(250):
                            cmdresponse = disabletm3x3(sysinfo,0)
                    except Timeout.Timeout as e:
                        pass
                        msg = ('CSLoop disabletmwork 3x3 Class Timeout')
                        sendlog(gotthefunc, msg, 40)
                    except:
                        pass
                        msg = ('Unknown Error 16 in {0}'.format(gotthefunc))
                        sendlog(gotthefunc, msg, 40)
                        err = traceback.format_exc()
                        msg = ('Unknown Error 16 Detail: {0}'.format(err))
                        sendlog(gotthefunc, msg, 10)
                    msg = cmdresponse
                    sendlog(gotthefunc, msg, 10)
                    update_dict(sysinfo, cmdresponse)
                    sysinfo['SYSTEM']['TILE'] = 0
                try:
                    with Timeout(30):
                        cmdresponse = buildtmwork(sysinfo,2,2)
                except Timeout.Timeout as e:
                    pass
                    msg = ('CSLoop buildtmwork 2x2 Class Timeout')
                    sendlog(gotthefunc, msg, 40)
                except:
                    pass
                    msg = ('Unknown Error 14 in {0}'.format(gotthefunc))
                    sendlog(gotthefunc, msg, 40)
                    err = traceback.format_exc()
                    msg = ('Unknown Error 14 Detail: {0}'.format(err))
                    sendlog(gotthefunc, msg, 10)
                msg = cmdresponse
                sendlog(gotthefunc, msg, 10)
                update_dict(sysinfo, cmdresponse)
                sysinfo['SYSTEM']['TILE'] = 4
            if sysinfo['SYSTEM']['COMMAND'] == 'tile_off':
                if sysinfo['SYSTEM']['TILE'] == 4:
                    try:
                        with Timeout(20):
                            cmdresponse = disabletm2x2(sysinfo)
                    except Timeout.Timeout as e:
                        pass
                        msg = ('CSLoop disabletmwork 2x2 Class Timeout')
                        sendlog(gotthefunc, msg, 40)
                    except:
                        pass
                        msg = ('Unknown Error 15 in {0}'.format(gotthefunc))
                        sendlog(gotthefunc, msg, 40)
                        err = traceback.format_exc()
                        msg = ('Unknown Error 15 Detail: {0}'.format(err))
                        sendlog(gotthefunc, msg, 10)
                    msg = cmdresponse
                    sendlog(gotthefunc, msg, 10)
                    update_dict(sysinfo, cmdresponse)
                    sysinfo['SYSTEM']['TILE'] = 0
                if sysinfo['SYSTEM']['TILE'] == 9:
                    try:
                        with Timeout(250):
                            cmdresponse = disabletm3x3(sysinfo)
                    except Timeout.Timeout as e:
                        pass
                        msg = ('CSLoop disabletmwork 3x3 Class Timeout')
                        sendlog(gotthefunc, msg, 40)
                    except:
                        pass
                        msg = ('Unknown Error 16 in {0}'.format(gotthefunc))
                        sendlog(gotthefunc, msg, 40)
                        err = traceback.format_exc()
                        msg = ('Unknown Error 16 Detail: {0}'.format(err))
                        sendlog(gotthefunc, msg, 10)
                    msg = cmdresponse
                    sendlog(gotthefunc, msg, 10)
                    update_dict(sysinfo, cmdresponse)
                    sysinfo['SYSTEM']['TILE'] = 0
            if sysinfo['SYSTEM']['COMMAND'].startswith('volume'):
                vol = sysinfo['SYSTEM']['COMMAND'].split("_")
                try:
                    with Timeout(10):
                        cmdresponse = changevolume(sysinfo)
                except Timeout.Timeout as e:
                    pass
                    msg = ('CSLoop changevolume Class Timeout')
                    sendlog(gotthefunc, msg, 40)
                except:
                    pass
                    msg = ('Unknown Error 17 in {0}'.format(gotthefunc))
                    sendlog(gotthefunc, msg, 40)
                    err = traceback.format_exc()
                    msg = ('Unknown Error 17 Detail: {0}'.format(err))
                    sendlog(gotthefunc, msg, 10)
                update_dict(sysinfo, cmdresponse)
                msg = cmdresponse
                sendlog(gotthefunc, msg, 10)
            if sysinfo['SYSTEM']['COMMAND'] == 'display_all_power_off':
                try:
                    with Timeout(10):
                        changepower(sysinfo, 0)
                except Timeout.Timeout as e:
                    pass
                    msg = ('CSLoop changepower off Class Timeout')
                    sendlog(gotthefunc, msg, 40)
                except:
                    pass
                    msg = ('Unknown Error 18 in {0}'.format(gotthefunc))
                    sendlog(gotthefunc, msg, 40)
                    err = traceback.format_exc()
                    msg = ('Unknown Error 18 Detail: {0}'.format(err))
                    sendlog(gotthefunc, msg, 10)
            if sysinfo['SYSTEM']['COMMAND'] == 'display_all_power_on':
                try:
                    with Timeout(10):
                        changepower(sysinfo, 1)
                except Timeout.Timeout as e:
                    pass
                    msg = ('CSLoop changepower on Class Timeout')
                    sendlog(gotthefunc, msg, 40)
                except:
                    pass
                    msg = ('Unknown Error 19 in {0}'.format(gotthefunc))
                    sendlog(gotthefunc, msg, 40)
                    err = traceback.format_exc()
                    msg = ('Unknown Error 19 Detail: {0}'.format(err))
                    sendlog(gotthefunc, msg, 10)
            if sysinfo['SYSTEM']['COMMAND'] == 'nexp_reboot':
                msg = 'Reboot function disabled'
                sendlog(gotthefunc, msg, 10)
#                controller_reboot() 
                
            sortedresults = collections.OrderedDict(sorted(sysinfo.items()))
            for key,val in sortedresults.items():
                msg = ("{0} = {1}".format(key,val))
                sendlog(gotthefunc, msg, 10)
            savestate(sysinfo)
        else:
            msg = "Command is null. Pass."
            sendlog(gotthefunc, msg, 10)
            pass
            
#        diff = dict_diff(olddict,sysinfo)
#        if diff:
#            print(diff)
        sleep(1)
        if csloop >= 60:
            savestate(sysinfo)
            csloop = 0
        else:
            csloop += 1
    
    msg = ('Exited {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)
    logworker.join()


if __name__ == '__main__':
   main()
