#!/usr/bin/python3
# coding: utf8
# LAVA Controls Copyright 2022 LAVA Controls - Stephen Loeckle


import argparse
import binascii
import collections
import configparser
import datetime
import inspect
import json
from lavacontrolscorelib import api, checkmake, configbuilder, fetchcmdDE, fetchcmdLAVA, gettoken, jobs, Matrix, netinfo, ping, QueryAPI, sendlog, savestate, sendemail, setmatrixroute, update_dict, UpdateApi, UpdateDatabase, version
import lavacontrolscorelib as lccore
import lavacontrolslglib as lclg
import lavacontrolsneclib as lcnec
import lavacontrolssamsunglib as lcsam
import logging
from multiprocessing import Process, Queue, freeze_support
import netifaces
import os
import pickle
import platform
import serial
import socket
import socketserver
import sqlite3
import struct
import subprocess
import sys
from time import time, sleep
import traceback

osplatform = platform.system().lower()

if osplatform == 'linux':
    from lavacontrolscorelib import Timeout
elif osplatform == 'windows':
    import zc.lockfile
    from lavacontrolscorelib import wineventlog
    
PORT = 7142

class LogRecordStreamHandler(socketserver.StreamRequestHandler):
    def handle(self):
        while True:
            try:
                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)
            except KeyboardInterrupt:
                break

    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
    lavacontrolsloggingport = 9042
    def __init__(self, host='localhost',
                 port=lavacontrolsloggingport,
                 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:
            try:
                rd, wr, ex = select.select([self.socket.fileno()],
                                           [], [],
                                           self.timeout)
                if rd:
                    self.handle_request()
                abort = self.abort
            except KeyboardInterrupt:
                break
        
def startupdisplaystatus(sysinfo, ip=None, socketid=0, q=None, id=None):
    gotthefunc = inspect.stack()[0][3]
    if osplatform == 'windows':
        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', 9042)
        logger.addHandler(sock)
    
    msg = ('Entered {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)
    ts = time()
    s = {}
    output = {}
    
    try:
        displayid, make = checkmake(sysinfo, ip, id)
    except:
        err = traceback.format_exc()
        msg = ('Error in {} Detail: {}'.format(gotthefunc,err))
        sendlog(gotthefunc, msg, 10)
        print(msg)
        sys.exit()
    else:
        msg = ('D{}: Make is {}'.format(displayid,make))
        sendlog(gotthefunc, msg, 10)
        print(msg)
    
    if make == 'unknown':
        msg = 'Fatal error: Cannot determine make of display or communications to display has failed'
        sys.exit()
            
    output['D{0}'.format(displayid)] = {}
    sysinfo['D{0}'.format(displayid)] = {}
    if sysinfo['SYSTEM']['CONNECTION'] == 'ip':
        output['D{0}'.format(displayid)]['IP'] = ip
        sysinfo['D{0}'.format(displayid)]['IP'] = ip
    else:
        output['D{0}'.format(displayid)]['IP'] = 'RS-232'
        sysinfo['D{0}'.format(displayid)]['IP'] = 'RS-232'
    output['D{0}'.format(displayid)]['ID'] = displayid
    
    if make == 'nec':
        dispconn = lcnec.DisplayInfo(sysinfo, displayid)
        output['D{0}'.format(displayid)]['MODEL'] = dispconn.getmodel()
        output['D{0}'.format(displayid)]['SERIAL'] = dispconn.getserialnumber()
        output['D{0}'.format(displayid)]['F1'],output['D{0}'.format(displayid)]['F2'],output['D{0}'.format(displayid)]['F3'],output['D{0}'.format(displayid)]['F4'] = dispconn.getfirmware()
        output['D{0}'.format(displayid)]['TEMP1'],output['D{0}'.format(displayid)]['TEMP2'],output['D{0}'.format(displayid)]['TEMP3'] = dispconn.gettemperature()
        inputconn = lcnec.Input(sysinfo, displayid)
        inputname, output['D{0}'.format(displayid)]['INPUT'] = inputconn.getinput()
        if sysinfo['SYSTEM']['MODE'] == 'wall':
            tmconn = lcnec.Tiling(sysinfo, displayid)
            output['D{0}'.format(displayid)]['TMMODE'] = tmconn.getmode()
            output['D{0}'.format(displayid)]['TMBC'] = tmconn.getbcmode()
            output['D{0}'.format(displayid)]['TMH'] = tmconn.gethsize()
            output['D{0}'.format(displayid)]['TMV'] = tmconn.getvsize()
            output['D{0}'.format(displayid)]['TMP'] = tmconn.getpos()
        coreconn = lcnec.CoreDisplaySettings(sysinfo,displayid)
        output['D{0}'.format(displayid)]['PWR'] = coreconn.getpower()
        volconn = lcnec.Volume(sysinfo, displayid)
        output['D{0}'.format(displayid)]['VOL'] = volconn.getvolume()
    if make == 'lg':
        dispconn = lclg.DisplayInfo(sysinfo, displayid)
        coreconn = lclg.CoreDisplaySettings(sysinfo, displayid)
        output['D{0}'.format(displayid)]['PWR'] = coreconn.power('get')
        if output['D{0}'.format(displayid)]['PWR'] == 1:
            output['D{0}'.format(displayid)]['MODEL'] = dispconn.model('get')
            output['D{0}'.format(displayid)]['SERIAL'] = dispconn.serialnumber('get')
            output['D{0}'.format(displayid)]['F1'],output['D{0}'.format(displayid)]['F2'],output['D{0}'.format(displayid)]['F3'],output['D{0}'.format(displayid)]['F4'] = dispconn.firmware('get')
            output['D{0}'.format(displayid)]['TEMP1'],output['D{0}'.format(displayid)]['TEMP2'],output['D{0}'.format(displayid)]['TEMP3'] = dispconn.temperature('get')
            inputconn = lclg.Input(sysinfo,displayid)
            inputname, output['D{0}'.format(displayid)]['INPUT'] = inputconn.input('get')
            if sysinfo['SYSTEM']['MODE'] == 'wall':
                tmconn = lclg.Tiling(sysinfo, displayid)
                tmmode = tmconn.tmmode('get')
                output['D{0}'.format(displayid)]['TMMODE'] = tmmode[0]
            
                #output['D{0}'.format(displayid)]['TMBC'] = tmconn.getbcmode()
                output['D{0}'.format(displayid)]['TMH'] = tmmode[1]
                output['D{0}'.format(displayid)]['TMV'] = tmmode[2]
                output['D{0}'.format(displayid)]['TMP'] = tmconn.tmid('get')
        msg = 'D{}: {}'.format(displayid,output)
        sendlog(gotthefunc, msg, 10)
        
        #volconn = lclg.Volume(sysinfo, displayid)
        #output['D{0}'.format(displayid)]['VOL'] = volconn.volume('get')
    if make == 'sam':
        dispconn = lcsam.DisplayInfo(sysinfo, displayid)
        output['D{0}'.format(displayid)]['MODEL'] = dispconn.getmodel()
        output['D{0}'.format(displayid)]['SERIAL'] = dispconn.getserialnumber()
        output['D{0}'.format(displayid)]['F1'],output['D{0}'.format(displayid)]['F2'],output['D{0}'.format(displayid)]['F3'],output['D{0}'.format(displayid)]['F4'] = dispconn.getfirmware()
        output['D{0}'.format(displayid)]['TEMP1'],output['D{0}'.format(displayid)]['TEMP2'],output['D{0}'.format(displayid)]['TEMP3'] = dispconn.gettemperature()
        inputconn = lcsam.Input(sysinfo,displayid)
        inputname, output['D{0}'.format(displayid)]['INPUT'] = inputconn.getinput()
        coreconn = lcsam.CoreDisplaySettings(sysinfo, displayid)
        output['D{0}'.format(displayid)]['PWR'] = coreconn.getpower()
        #volconn = lcsam.Volume(sysinfo, displayid)
        #output['D{0}'.format(displayid)]['VOL'] = volconn.getvolume()

    output['D{0}'.format(displayid)]['MAKE'] = make
    output['D{0}'.format(displayid)]['ST'] = 'Last process time: {0}'.format(time() - ts)
    output['D{0}'.format(displayid)]['status'] = 'Ok'
    msg = ('Exited {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)
    if sysinfo['SYSTEM']['CONNECTION'] == 'ip':
        q.put(output)
    elif sysinfo['SYSTEM']['CONNECTION'] == 'serial':
        return(output)
    
def setdefaults(sysinfo, displayid=1, socketid=0, q=None):
    gotthefunc = inspect.stack()[0][3]
    if osplatform == 'windows':
        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', 9042)
        logger.addHandler(sock)
    
    msg = ('Entered {} function with options {}'.format(gotthefunc, displayid))
    sendlog(gotthefunc, msg, 10)
    ts = time()
    s = {}
    output = {}
    #if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'lg':
    #    coreconn = lclg.CoreDisplaySettings(sysinfo, displayid)
    if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'nec':
        coreconn = lcnec.CoreDisplaySettings(sysinfo, displayid)
    if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'sam':
        coreconn = lcsam.CoreDisplaySettings(sysinfo, displayid)
    output['D{0}'.format(displayid)] = {}
    output['D{0}'.format(displayid)]['SERIAL'] = sysinfo['D{0}'.format(displayid)]['SERIAL']
    output['D{0}'.format(displayid)]['FUNCNAME'] = gotthefunc
    output['D{0}'.format(displayid)]['FUNCTIME'] = 'Last process time: {0}'.format(time() - ts)
    if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'nec':
        output['D{0}'.format(displayid)]['OSD'] = coreconn.setosd(0)
    #if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'lg':
    #    coreconn.setdpm(0)
    #    coreconn.setnetworkstandby(1)
    #    coreconn.setstandbycontrol(0)
    if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'sam':
        coreconn.setenergysavinglfd(0)
        coreconn.setnetworkstandby(1)
        coreconn.setstandbycontrol(0)
        output['D{0}'.format(displayid)]['OSD'] = coreconn.setosd(0)
    if q:
        q.put(output)
    else:
        msg = ('Exited {} function'.format(gotthefunc))
        sendlog(gotthefunc, msg, 10)
        return(output)
    msg = ('Exited {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)

def buildtmworker(sysinfo, tileid, displayid, hnum, vnum, pos, port, socketid, q):
    gotthefunc = inspect.stack()[0][3]
    if osplatform == 'windows':
        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', 9042)
        logger.addHandler(sock)
    
    msg = ('Entered {} function with options {} {} {} {} {}'.format(gotthefunc,displayid, hnum, vnum, pos, port))
    sendlog(gotthefunc, msg, 10)

    ts = time()
    output = {} 
    output['D{0}'.format(displayid)] = {}
    target = sysinfo['SYSTEM']['TARGET']
    s = {}
    if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'nec':
        returninput = lcnec.Input(sysinfo, displayid)
    if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'lg':
        returninput = lclg.Input(sysinfo, displayid)
#    returnbcmode, returnhnum, returnvnum, returnpos = buildtm(target,displayid,hnum,vnum,pos,sysinfo['WALL']['NUMBERING'],s[socketid])
    if displayid != sysinfo['SYSTEM']['TARGET']:
        if sysinfo['SYSTEM']['CHAIN']['ON'] == 1:
            # if sysinfo['D{0}'.format(displayid)]['TMMODE'] == 0:
            # commented out for one test 29 march 2023 - reindent the next two if statements
            if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'nec':
                inputname, output['D{0}'.format(displayid)]['LASTINPUT'] = returninput.getinput()
            if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'lg':
                inputname, output['D{0}'.format(displayid)]['LASTINPUT'] = returninput.input('get')
            if port == 'dp':
                cmdsuccess = 0
                cmdloop = 0
                while cmdsuccess == 0 and cmdloop <= 5:
                    try:
                        if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'nec':
                            inputname, output['D{0}'.format(displayid)]['INPUT'] = returninput.setinput(15)
                        if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'lg':
                            inputname, output['D{0}'.format(displayid)]['INPUT'] = returninput.input('set','d0')
                    except:
                        pass
                        err = traceback.format_exc()
                        msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
                        sendlog(gotthefunc, msg, 10)
                        sleep(0.5)
                        cmdloop += 1
                    else:
                        cmdsuccess = 1
                        if cmdloop > 5:
                            msg = '{}: Reached maxiumum cmdloop in {} dp setinput'.format(displayid,gotthefunc)
                            sendlog(gotthefunc,msg, 10)
            elif port == 'hdmi':
                cmdsuccess = 0
                cmdloop = 0
                while cmdsuccess == 0 and cmdloop <= 5:
                    try:
                        if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'nec':
                            inputname, output['D{0}'.format(displayid)]['INPUT'] = returninput.setinput(17)
                        if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'lg':
                            inputname, output['D{0}'.format(displayid)]['INPUT'] = returninput.input('set','a0')
                    except:
                        pass
                        err = traceback.format_exc()
                        msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
                        sendlog(gotthefunc, msg, 10)
                        sleep(0.5)
                        cmdloop += 1
                    else:
                        cmdsuccess = 1
                        if cmdloop > 5:
                            msg = '{}: Reached maxiumum cmdloop in {} hdmi setinput'.format(displayid,gotthefunc)
                            sendlog(gotthefunc,msg, 10)
            msg = output['D{0}'.format(displayid)]['INPUT']
            sendlog(gotthefunc, msg, 10)
            if 'lava' in sysinfo['SYSTEM']['PLATFORM'].lower() or sysinfo['SYSTEM']['PLATFORM'].lower() == 'onprem' or sysinfo['SYSTEM']['PLATFORM'].lower() == 'on-prem':
                apidata = {}
                apidata['id'] = displayid
                apidata['lava_id'] = sysinfo['D{0}'.format(displayid)]['lava_id']
                apidata['input'] = inputname
                au = UpdateApi(sysinfo)
                au.displayUpdate(apidata)
        else:
            msg = 'D{0}: DP Chain Disabled'.format(displayid)
            sendlog(gotthefunc, msg, 10)
    else:
        if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'nec':
            inputname, output['D{0}'.format(displayid)]['LASTINPUT'] = returninput.getinput()
        if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'lg':
            inputname, output['D{0}'.format(displayid)]['LASTINPUT'] = returninput.input('get')
        output['D{0}'.format(displayid)]['INPUT'] = output['D{0}'.format(displayid)]['LASTINPUT']
        msg = ('displayid from builttmworker = {}'.format(displayid))
        sendlog(gotthefunc, msg, 10)
    cmdsuccess = 0
    cmdloop = 0
    while cmdsuccess == 0 and cmdloop <= 5:
        try:
            if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'nec':
                returnbcmode, returnhnum, returnvnum, returnpos = lcnec.buildtm(sysinfo,target,displayid,hnum,vnum,pos,sysinfo['WALL']['NUMBERING'],sysinfo['WALL']['LAYOUT'])
            if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'lg':
                returnbcmode, returnhnum, returnvnum, returnpos = lclg.buildtm(sysinfo,target,displayid,hnum,vnum,pos,sysinfo['WALL']['NUMBERING'],sysinfo['WALL']['LAYOUT'])
        except:
            pass
            err = traceback.format_exc()
            msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
            sendlog(gotthefunc, msg, 10)
            sleep(0.5)
            cmdloop += 1
        else:
            cmdsuccess = 1
            if cmdloop > 5:
                msg = '{}: Reached maxiumum cmdloop in {} buildtm'.format(displayid,gotthefunc)
                sendlog(gotthefunc,msg, 10)
    #sleep(0.75)
    # Commented out by sloeckle 27/3/2023 to speed up system processing
    if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'nec':
        tmconn = lcnec.Tiling(sysinfo,displayid)
        cmdsuccess = 0
        cmdloop = 0
        while cmdsuccess == 0 and cmdloop <= 5:
            try:
                returntmmode = tmconn.setmode(1)
            except:
                pass
                err = traceback.format_exc()
                msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
                sendlog(gotthefunc, msg, 10)
                sleep(0.5)
                cmdloop += 1
            else:
                cmdsuccess = 1
                if cmdloop > 5:
                    msg = '{}: Reached maxiumum cmdloop in {} setmode'.format(displayid,gotthefunc)
                    sendlog(gotthefunc,msg, 10)
        output['D{0}'.format(displayid)]['SAVED']=0
        coreconn = lcnec.CoreDisplaySettings(sysinfo,displayid)
        cmdsuccess = 0
        cmdloop = 0
        while cmdsuccess == 0 and cmdloop <= 5:
            try:
                returnsaved = coreconn.saveconfig(2)
            except:
                pass
                err = traceback.format_exc()
                msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
                sendlog(gotthefunc, msg, 10)
                sleep(0.5)
                cmdloop += 1
            else:
                cmdsuccess = 1
                if cmdloop > 5:
                    msg = '{}: Reached maxiumum cmdloop in {} saveconfig'.format(displayid,gotthefunc)
                    sendlog(gotthefunc,msg, 10)
                returnsaved=1
    if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'lg':
        returntmmode=1
        returnsaved=1
    output['D{0}'.format(displayid)]['TMMODE']=returntmmode
    output['D{0}'.format(displayid)]['TMBC']=returnbcmode
    output['D{0}'.format(displayid)]['TMH']=returnhnum
    output['D{0}'.format(displayid)]['TMV']=returnvnum
    output['D{0}'.format(displayid)]['TMP']=returnpos
    output['D{0}'.format(displayid)]['SAVED']=returnsaved
    output['D{0}'.format(displayid)]['TILED']=1
    output['D{0}'.format(displayid)]['TMTIME'] = 'Last process time: {0}'.format(time() - ts)
    if sysinfo['SYSTEM']['CONNECTION'] == 'ip':
        q.put(output)
    db1success = 0
    while db1success == 0:
        try:
            conn = {}
            c = {}
            conn[socketid] = sqlite3.connect(sysinfo['SYSTEM']['DATABASEFILE'])
            c[socketid] = conn[socketid].cursor()
            c[socketid].execute('INSERT into tiles (tileid, member, size, pos) values (?, ?, ?, ?)', (tileid, displayid, str(hnum)+'x'+str(vnum), pos))
            c[socketid].execute('UPDATE displays SET input = ?, lastinput = ? WHERE lava_id = ?', (output['D{0}'.format(displayid)]['INPUT'],output['D{0}'.format(displayid)]['LASTINPUT'],sysinfo['D{0}'.format(displayid)]['lava_id']))
        except sqlite3.OperationalError:
            pass
            msg = ('{}: Database locked (8). Trying again.',format(gotthefunc))
            sendlog(gotthefunc, msg, 10)
            sleep(0.5)
        except:
            pass
            err = traceback.format_exc()
            msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
            sendlog(gotthefunc, msg, 10)
            sleep(0.5)
        else:
            db1success = 1
            db2success = 0
            while db2success == 0:
                try:
                    conn[socketid].commit()
                except sqlite3.OperationalError:
                    pass
                    msg = ('{}: Database locked (10). Trying again.',format(gotthefunc))
                    sendlog(gotthefunc, msg, 10)
                    sleep(0.5)
                except:
                    pass
                    err = traceback.format_exc()
                    msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
                    sendlog(gotthefunc, msg, 10)
                    sleep(0.5)
                else:
                    db2success = 1
                    conn[socketid].close()
                    
    msg = ('Exited {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)
    if sysinfo['SYSTEM']['CONNECTION'] == 'serial':
        return(output)

def buildtmfactory(sysinfo,h,w,tileid,displaylist=None):
    gotthefunc = inspect.stack()[0][3]
    msg = ('Entered {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)
    tsmain = time()
    socketnum = 0
    targetdisplay = sysinfo['SYSTEM']['TARGET']
    targetinput = sysinfo['D{0}'.format(targetdisplay)]['INPUT']
    targetmodel = sysinfo['D{0}'.format(targetdisplay)]['MODEL'].lower()
    position = 1
    buildtmq = Queue()
    buildtmqresults = {}
    buildtmprocs = []
    if sysinfo['D{0}'.format(targetdisplay)]['MAKE'] == 'nec':
        necmodelswithhdmichain = ['un492', 'un552']
        if any(x in targetmodel for x in necmodelswithhdmichain) and targetinput in (3, 17):
            port = 'hdmi'
        elif any(x in targetmodel for x in necmodelswithhdmichain) and targetinput == 15:
            port = 'dp'
        else:
            port = 'dp'
    else:
        port = 'dp'
    msg = 'Target display {} is of model {} with source input {} and will be using {} chain.'.format(targetdisplay, targetmodel, targetinput, port)
    sendlog(gotthefunc, msg, 10)
    msg = ('DisplayList: ',displaylist)
    sendlog(gotthefunc, msg, 10)
    if sysinfo['SYSTEM']['CONNECTION'] == 'ip':
        if displaylist:
            for displayid in displaylist:
                buildtmproc = Process(target=buildtmworker, args=(sysinfo, tileid, displayid, h, w, position, port, socketnum, buildtmq))
                buildtmprocs.append(buildtmproc)
                socketnum += 1
                position += 1
        else:
            displayid = sysinfo['SYSTEM']['TARGET']
            msg = 'TARGET: {} H: {} V: {}'.format(displayid,h,w)
            sendlog(gotthefunc, msg, 10)
            end = displayid + (h*w)
            msg = 'END: {}'.format(end)
            sendlog(gotthefunc, msg, 10)
            while (displayid < end):
                buildtmproc = Process(target=buildtmworker, args=(sysinfo, tileid, displayid, h, w, position, port, socketnum, buildtmq))
                buildtmprocs.append(buildtmproc)
                msg = 'SOCKETID: {} DISPLAYID: {} POSITION: {}'.format(socketnum,displayid,position)
                sendlog(gotthefunc, msg, 10)
                socketnum += 1
                displayid += 1
                position += 1
    #    if sysinfo['SYSTEM']['USEMATRIX'] == 1:
    #        buildtmproc = Process(target=setmatrixroute, args=(sysinfo['SYSTEM']['MATRIXIP'],sysinfo['SYSTEM']['MYIP'],sysinfo['SYSTEM']['MATRIXCOMMANDS']))
    #        buildtmprocs.append(buildtmproc)
    #        socketnum += 1
        for w in buildtmprocs:
            w.start()
        for i in range(socketnum):
            update_dict(buildtmqresults, buildtmq.get())
        for w in buildtmprocs:
            w.join()
    if sysinfo['SYSTEM']['CONNECTION'] == 'serial':
        if displaylist:
            for displayid in displaylist:
                buildtmresult = buildtmworker(sysinfo, tileid, displayid, h, w, position, port, socketnum, buildtmq)
                msg = 'D{}: buildtm result: {}'.format(displayid, buildtmresult)
                sendlog(gotthefunc, msg, 10)
                update_dict(buildtmqresults, buildtmresult)
                socketnum += 1
                position += 1
        else:
            displayid = sysinfo['SYSTEM']['TARGET']
            msg = 'TARGET: {} H: {} V: {}'.format(displayid,h,w)
            sendlog(gotthefunc, msg, 10)
            end = displayid + (h*w)
            msg = 'END: {}'.format(end)
            sendlog(gotthefunc, msg, 10)
            while (displayid < end):
                buildtmproc = buildtmworker(sysinfo, tileid, displayid, h, w, position, port, socketnum, buildtmq)
                update_dict(buildtmqresults, buildtmproc)
                msg = 'SOCKETID: {} DISPLAYID: {} POSITION: {}'.format(socketnum,displayid,position)
                sendlog(gotthefunc, msg, 10)
                socketnum += 1
                displayid += 1
                position += 1
    msg = ('Took {}'.format(time() - tsmain))
    sendlog(gotthefunc, msg, 10)
    msg = buildtmqresults
    sendlog(gotthefunc, msg, 10)
    msg = ('Exited {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)
    return(buildtmqresults)

def changeinput(sysinfo, newinput, displayid=999, save=1):
    gotthefunc = inspect.stack()[0][3]
    msg = ('Entered {} function with options {} {}'.format(gotthefunc, displayid, newinput))
    sendlog(gotthefunc, msg, 10)
    if displayid == 999:
        displayid = sysinfo['SYSTEM']['TARGET']
        msg = ('CHANGEINPUT: Fetching displayid from sysinfo target: {}'.format(displayid))
        sendlog(gotthefunc, msg, 10)
    else:
        msg = ('CHANGEINPUT: Fetching displayid from arguments passed to function: {}'.format(displayid))
        sendlog(gotthefunc, msg, 10)
    ts = time()
    output = {}
    output['D{0}'.format(displayid)] = {}
    output['D{0}'.format(displayid)]['SERIAL'] = sysinfo['D{0}'.format(displayid)]['SERIAL']
    output['D{0}'.format(displayid)]['LASTINPUT'] = sysinfo['D{0}'.format(displayid)]['INPUT']
    if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'lg':
        inputconn = lclg.Input(sysinfo, displayid)
    if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'nec':
        inputconn = lcnec.Input(sysinfo, displayid)
    if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'sam':
        inputconn = lcsam.Input(sysinfo, displayid)
    cmdsuccess = 0
    cmdloop = 0
    while cmdsuccess == 0 and cmdloop <= 5:
        try:
            if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'lg':
                inputname, output['D{0}'.format(displayid)]['LASTINPUT'] = inputconn.input('get')
            if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'nec':
                inputname, output['D{0}'.format(displayid)]['LASTINPUT'] = inputconn.getinput()
        except:
            pass
            err = traceback.format_exc()
            msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
            sendlog(gotthefunc, msg, 10)
            sleep(0.5)
            cmdloop += 1
        else:
            cmdsuccess = 1
            if cmdloop > 5:
                msg = '{}: Reached maxiumum cmdloop in {} getinput'.format(displayid,gotthefunc)
                sendlog(gotthefunc,msg, 10)
    cmdsuccess = 0
    cmdloop = 0
    while cmdsuccess == 0 and cmdloop <= 5:
        try:
            if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'lg':
                inputname, output['D{0}'.format(displayid)]['INPUT']=inputconn.input('set',newinput)
            if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'nec':
                inputname, output['D{0}'.format(displayid)]['INPUT']=inputconn.setinput(newinput)
        except:
            pass
            err = traceback.format_exc()
            msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
            sendlog(gotthefunc, msg, 10)
            sleep(0.5)
            cmdloop += 1
        else:
            cmdsuccess = 1
            if cmdloop > 5:
                msg = '{}: Reached maxiumum cmdloop in {} {} setinput'.format(displayid,newinput,gotthefunc)
                sendlog(gotthefunc,msg, 10)
##  Need to store hdmi mode (or any other input) states
    if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'nec':
        if input == 'hdmi':
            returninputsettings = lcnec.sethdmimodes(displayid, sysinfo)
    if 'lava' in sysinfo['SYSTEM']['PLATFORM'].lower() or sysinfo['SYSTEM']['PLATFORM'].lower() == 'onprem' or sysinfo['SYSTEM']['PLATFORM'].lower() == 'on-prem':
        apidata = {}
        apidata['id'] = displayid
        apidata['lava_id'] = sysinfo['D{0}'.format(displayid)]['lava_id']
        apidata['input'] = inputname
        au = UpdateApi(sysinfo)
        au.displayUpdate(apidata)
    output['D{0}'.format(displayid)]['SAVED']=0
    if save == 1:
        if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'nec':
            coreconn = lcnec.CoreDisplaySettings(sysinfo,displayid)
            cmdsuccess = 0
            cmdloop = 0
            while cmdsuccess == 0 and cmdloop <= 5:
                try:
                    returnsaved = coreconn.saveconfig(1)
                except:
                    pass
                    err = traceback.format_exc()
                    msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
                    sendlog(gotthefunc, msg, 10)
                    sleep(0.5)
                    cmdloop += 1
                else:
                    cmdsuccess = 1
                    if cmdloop > 5:
                        msg = '{}: Reached maxiumum cmdloop in {} saveconfig'.format(displayid,gotthefunc)
                        sendlog(gotthefunc,msg, 10)
        if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'lg':
            returnsaved=1
                    
        output['D{0}'.format(displayid)]['SAVED']=returnsaved
    else:
        output['D{0}'.format(displayid)]['SAVED']=0
    output['D{0}'.format(displayid)]['CHANGEINPUTTIME'] = 'Last process time: {0}'.format(time() - ts)
    msg = ('D{}: Exited {} function'.format(displayid,gotthefunc))
    sendlog(gotthefunc, msg, 10)
    UpdateDatabase(sysinfo,output)
    return(output)
    
def changeinputprocess(sysinfo, ip, displayid, newinput, socketid, q):
    gotthefunc = inspect.stack()[0][3]
    if osplatform == 'windows':
        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', 9042)
        logger.addHandler(sock)
    
    msg = ('Entered {} function with options {} {}'.format(gotthefunc, displayid, newinput))
    sendlog(gotthefunc, msg, 10)
    ts = time()
    output = {}
    output['D{0}'.format(displayid)] = {}
    output['D{0}'.format(displayid)]['SERIAL'] = sysinfo['D{0}'.format(displayid)]['SERIAL']
    if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'lg':
        inputconn = lclg.Input(sysinfo, displayid)
    if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'nec':
        inputconn = lcnec.Input(sysinfo, displayid)
    if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'sam':
        inputconn = lcsam.Input(sysinfo, displayid)
    
    if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'lg':
        inputname, output['D{0}'.format(displayid)]['LASTINPUT'] = inputconn.input('get')
        inputname, output['D{0}'.format(displayid)]['INPUT']=inputconn.input('set',newinput)
    if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'nec':
        inputname, output['D{0}'.format(displayid)]['LASTINPUT'] = inputconn.getinput()
        inputname, output['D{0}'.format(displayid)]['INPUT']=inputconn.setinput(newinput)
        if inputname == 'hdmi' or output['D{0}'.format(displayid)]['INPUT'] == 17:
            returninputsettings = lcnec.sethdmimodes(displayid, sysinfo)
    if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'sam':
        inputname, output['D{0}'.format(displayid)]['LASTINPUT'] = inputconn.getinput()
        inputname, output['D{0}'.format(displayid)]['INPUT']=inputconn.setinput(newinput)
        
    if 'lava' in sysinfo['SYSTEM']['PLATFORM'].lower() or sysinfo['SYSTEM']['PLATFORM'].lower() == 'onprem' or sysinfo['SYSTEM']['PLATFORM'].lower() == 'on-prem':
        apidata = {}
        apidata['id'] = displayid
        apidata['lava_id'] = sysinfo['D{0}'.format(displayid)]['lava_id']
        apidata['input'] = inputname
        au = UpdateApi(sysinfo)
        au.displayUpdate(apidata)
    
    output['D{0}'.format(displayid)]['SAVED']=0
    if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'lg':
        returnsaved=1
    if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'nec':
        coreconn = lcnec.CoreDisplaySettings(sysinfo,displayid)
        returnsaved = coreconn.saveconfig(1)
    if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'sam':
        coreconn = lcsam.CoreDisplaySettings(sysinfo,displayid)
        returnsaved = coreconn.saveconfig(1)

    output['D{0}'.format(displayid)]['SAVED']=returnsaved
    output['D{0}'.format(displayid)]['CHANGEINPUTTIME'] = 'Last process time: {0}'.format(time() - ts)       
    q.put(output)
    msg = ('Exited {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)

def changeinputall(sysinfo, newinput):
    gotthefunc = inspect.stack()[0][3]
    msg = ('Entered {} function with options {}'.format(gotthefunc, newinput))
    sendlog(gotthefunc, msg, 10)
    tsmain = time()
    socketnum = 0
    dpchain = 0
    changeinputallqueue = Queue()
    changeinputallqueueresults = {}
    changeinputallworkers = []
    for key, value in sysinfo.items():
        if key.startswith('D') and key != 'DNone':
            """
                Disabling legacy dpchain checking through sysinfo. It's broken with all the new changes. Use changeinputall with care.
            """
#            if sysinfo['SYSTEM']['TILE'] == 4:
#                msg = 'Currently in 2x2 tile. Checking for DP chain status'
#                sendlog(gotthefunc, msg, 10)
#                if sysinfo['SYSTEM']['CHAIN']['ON'] == 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']['CHAIN']['ON'] == 1:
#                    dplist = [2,3,4,5,6,7,8,9]
#                    if any(x in key for x in dplist):
#                        dpchain = 1
#                    else:
#                        dpchain = 0
            dpchain = 0
            if dpchain == 0:
                changeinputallworker = Process(target=changeinputprocess, args=(sysinfo, sysinfo['{0}'.format(key)]['IP'], sysinfo['{0}'.format(key)]['ID'], newinput, socketnum, changeinputallqueue))
                changeinputallworkers.append(changeinputallworker)
                changeinputallworker.start()
                socketnum += 1
            if dpchain == 1:
                changeinputallworker = Process(target=changeinputprocess, args=(sysinfo, sysinfo['{0}'.format(key)]['IP'], sysinfo['{0}'.format(key)]['ID'], 'dp', socketnum, changeinputallqueue))
                changeinputallworkers.append(changeinputallworker)
                changeinputallworker.start()
                socketnum += 1
    for i in range(socketnum):
        processresults = changeinputallqueue.get()
        update_dict(changeinputallqueueresults, processresults)
    for w in changeinputallworkers:
        w.join()
    UpdateDatabase(sysinfo,changeinputallqueueresults)
    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']
    displayid = sysinfo['SYSTEM']['TARGET']
    end = displayid + 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']['CHAIN']['ON'] == 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']['CHAIN']['ON'] == 1:
            dplist = [2,3,4,5,6,7,8,9]
    while (displayid < end):
        if displayid in dplist:
            msg = 'D{0}: Found in dp chain list'.format(displayid)
            sendlog(gotthefunc, msg, 10)
            changeinputrangeworker = Process(target=changeinputprocess, args=(sysinfo, sysinfo['D{0}'.format(displayid)]['IP'], sysinfo['D{0}'.format(displayid)]['ID'], 'dp', socketnum, changeinputrangequeue))
            changeinputrangeworkers.append(changeinputrangeworker)
            changeinputrangeworker.start()
            socketnum += 1
            displayid += 1
        else:
            msg = 'D{0}: Not found in dp chain list'.format(displayid)
            sendlog(gotthefunc, msg, 10)
            changeinputrangeworker = Process(target=changeinputprocess, args=(sysinfo, sysinfo['D{0}'.format(displayid)]['IP'], sysinfo['D{0}'.format(displayid)]['ID'], input, socketnum, changeinputrangequeue))
            changeinputrangeworkers.append(changeinputrangeworker)
            changeinputrangeworker.start()
            socketnum += 1
            displayid += 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
    powerresults = {}
    if sysinfo['SYSTEM']['CONNECTION'] == 'ip':
        powerqueue = Queue()
        powerworkers = []
        for key, value in sysinfo.items():
            if key.startswith('D') and key != 'DNone':
                powerworker = Process(target=changepowerworker, args=(sysinfo, sysinfo['{0}'.format(key)]['ID'], power, sysinfo['{0}'.format(key)]['IP'], socketnum, powerqueue))
                powerworkers.append(powerworker)
                powerworker.start()
                socketnum += 1
        for i in range(socketnum):
            update_dict(powerresults, powerqueue.get())
    elif sysinfo['SYSTEM']['CONNECTION'] == 'serial':
        if sysinfo['SYSTEM']['MODE'] == 'single':
            powerresult = changepowerworker(sysinfo, x, power)
        elif sysinfo['SYSTEM']['MODE'] == 'wall':
            if sysinfo['D1']['MAKE'] == 'lg':
#                x = 1
#                changepowerworker(sysinfo, x, power)
                layout = sysinfo['WALL']['LAYOUT'].split('x')
                displaycount = int(int(layout[0])*int(layout[1]))+1
                x = 1
                y = 1
                powerresults = {}
                output = {}
                while x < displaycount:
                    coreconn = lclg.CoreDisplaySettings(sysinfo,x)
#                    output['D{0}'.format(x)] = {}
#                    output['D{0}'.format(x)]['PWR'] = int(power)
                    powerresult = changepowerworker(sysinfo, x, power)
#                    update_dict(powerresults, output)
                    update_dict(powerresults, powerresult)
                    x += 1
                    y += 1
            elif sysinfo['D1']['MAKE'] == 'nec':
                layout = sysinfo['WALL']['LAYOUT'].split('x')
                displaycount = int(int(layout[0])*int(layout[1]))+1
                x = 1
                y = 1
                powerresults = {}
                while x < displaycount:
                    powerresult = changepowerworker(sysinfo, x, power)
                    update_dict(powerresults, powerresult)
                    x += 1
                    y += 1
        sortedresults = collections.OrderedDict(sorted(powerresults.items()))
        msg = sortedresults
        sendlog(gotthefunc, msg, 10)
    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(sysinfo, displayid, power, ip=None, socketid=None, q=None):
    gotthefunc = inspect.stack()[0][3]
    if osplatform == 'windows':
        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', 9042)
        logger.addHandler(sock)
    
    msg = ('Entered {} function with options {} {}'.format(gotthefunc, displayid, power))
    sendlog(gotthefunc, msg, 10)
    ts = time()
    output = {}
    output['D{0}'.format(displayid)] = {}
    if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'lg':
        coreconn = lclg.CoreDisplaySettings(sysinfo,'0')
        output['D{0}'.format(displayid)]['PWR'] = coreconn.power('set', str(power))
    if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'nec':
        coreconn = lcnec.CoreDisplaySettings(sysinfo,displayid)
        output['D{0}'.format(displayid)]['PWR'] = coreconn.setpower(power)
    if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'sam':
        coreconn = lcsam.CoreDisplaySettings(sysinfo,displayid)
        output['D{0}'.format(displayid)]['PWR'] = coreconn.setpower(power)
    if displayid != 0:
        if 'lava' in sysinfo['SYSTEM']['PLATFORM'].lower() or sysinfo['SYSTEM']['PLATFORM'].lower() == 'onprem' or sysinfo['SYSTEM']['PLATFORM'].lower() == 'on-prem':
            apidata = {}
            apidata['id'] = displayid
            apidata['lava_id'] = sysinfo['D{0}'.format(displayid)]['lava_id']
            apidata['powermode'] = output['D{0}'.format(displayid)]['PWR']
            au = UpdateApi(sysinfo)
            au.displayUpdate(apidata)
    
    output['D{0}'.format(displayid)]['FUNCNAME'] = gotthefunc
    output['D{0}'.format(displayid)]['FUNCTIME'] = 'Last process time: {0}'.format(time() - ts)
    if sysinfo['SYSTEM']['CONNECTION'] == 'ip':
        q.put(output)
    elif sysinfo['SYSTEM']['CONNECTION'] == 'serial':
        return(output)
    msg = ('Exited {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)

def changevolume(sysinfo,resetvolume=0):
    gotthefunc = inspect.stack()[0][3]
    if isinstance( sysinfo['SYSTEM']['TARGET'], int):
        if sysinfo['SYSTEM']['TARGET'] == 0 or not sysinfo['SYSTEM']['TARGET']:
            displayid = 1
        else:
            displayid = sysinfo['SYSTEM']['TARGET']
    else:
        displayid = 1
    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, displayid, vol[1]))
    sendlog(gotthefunc, msg, 10)
    ts = time()
    output = {}
    output['D{0}'.format(displayid)] = {}
    try:
        if osplatform == 'linux':
            with Timeout(3):
                if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'nec':
                    volconn = lcnec.Volume(sysinfo, displayid)
                elif sysinfo['D{0}'.format(displayid)]['MAKE'] == 'sam':
                    volconn = lcsam.Volume(dsysinfo, displayid)
                volconn = lcnec.Volume(sysinfo, displayid)
                output['D{0}'.format(sysinfo['SYSTEM']['TARGET'])]['VOL'] = volconn.setvolume(int(vol[1]))
        else:
            if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'nec':
                volconn = lcnec.Volume(sysinfo, displayid)
            elif sysinfo['D{0}'.format(displayid)]['MAKE'] == 'sam':
                volconn = lcsam.Volume(dsysinfo, displayid)
            volconn = lcnec.Volume(sysinfo, displayid)
            output['D{0}'.format(sysinfo['SYSTEM']['TARGET'])]['VOL'] = volconn.setvolume(int(vol[1]))
    except:
        pass
        msg = ('Error in {0}'.format(gotthefunc))
        sendlog(gotthefunc, msg, 40)
        err = traceback.format_exc()
        msg = ('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']
    coreconn = lcnec.CoreDisplaySettings(sysinfo,displayid)
    returnsaved = coreconn.saveconfig(1)
    msg = ('Exited {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)
    return(output)
 
def disabletmworker(sysinfo, tileid, displayid, lastinput, save, socketid, q):
    gotthefunc = inspect.stack()[0][3]
    if osplatform == 'windows':
        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', 9042)
        logger.addHandler(sock)
    
    msg = ('Entered {} function with options {} {}'.format(gotthefunc, displayid, lastinput))
    sendlog(gotthefunc, msg, 10)

    ts = time()
    ip = sysinfo['D{}'.format(displayid)]['IP']
    output = {}
    output['D{0}'.format(displayid)] = {}
    target = sysinfo['SYSTEM']['TARGET']
    if displayid != sysinfo['SYSTEM']['TARGET']:
        
        cmdsuccess = 0
        cmdloop = 0
        while cmdsuccess == 0 and cmdloop <= 5:
            try:
                if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'lg':
                    inputconn = lclg.Input(sysinfo, displayid)
                    inputname, output['D{0}'.format(displayid)]['INPUT'] = inputconn.input('set',lastinput)
                if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'nec':
                    inputconn = lcnec.Input(sysinfo, displayid)
                    inputname, output['D{0}'.format(displayid)]['INPUT'] = inputconn.setinput(lastinput)
            except:
                pass
                err = traceback.format_exc()
                msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
                sendlog(gotthefunc, msg, 10)
                sleep(0.5)
                cmdloop += 1
            else:
                cmdsuccess = 1
                if cmdloop > 5:
                    msg = '{}: Reached maxiumum cmdloop in {} setinput'.format(displayid,gotthefunc)
                    sendlog(gotthefunc,msg, 10)
        msg = output['D{0}'.format(displayid)]['INPUT']
        sendlog(gotthefunc, msg, 10)
        if 'lava' in sysinfo['SYSTEM']['PLATFORM'].lower() or sysinfo['SYSTEM']['PLATFORM'].lower() == 'onprem' or sysinfo['SYSTEM']['PLATFORM'].lower() == 'on-prem':
            apidata = {}
            apidata['id'] = displayid
            apidata['lava_id'] = sysinfo['D{0}'.format(displayid)]['lava_id']
            apidata['input'] = inputname
            au = UpdateApi(sysinfo)
            au.displayUpdate(apidata)

#    returnbcmode, returnhnum, returnvnum, returnpos = buildtm(target,displayid,1,1,1,sysinfo['WALL']['NUMBERING'],sysinfo['WALL']['LAYOUT'],s[socketid])
    
    # sleep(0.75)
    # Commented out by sloeckle 27/3/2023 to speed up system processing
    if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'lg':
        tmconn = lclg.Tiling(sysinfo,displayid)
    if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'nec':
        tmconn = lcnec.Tiling(sysinfo,displayid)
    cmdsuccess = 0
    cmdloop = 0
    while cmdsuccess == 0 and cmdloop <= 5:
        try: 
            if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'lg':
                returntmmode = tmconn.tmmode('set','0')
            if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'nec':
                returntmmode = tmconn.setmode(0)
        except:
            pass
            err = traceback.format_exc()
            msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
            sendlog(gotthefunc, msg, 10)
            sleep(0.5)
            cmdloop += 1
        else:
            cmdsuccess = 1
            if cmdloop > 5:
                msg = '{}: Reached maxiumum cmdloop in {} setmode'.format(displayid,gotthefunc)
                sendlog(gotthefunc,msg, 10)
            output['D{0}'.format(displayid)]['TMMODE'] = returntmmode
#    returntmmode = settm(displayid, 0, s[socketid])
    output['D{0}'.format(displayid)]['SAVED'] = 0
    if save == 1:
        if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'lg':
            output['D{0}'.format(displayid)]['SAVED'] = 1
        if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'nec':
            coreconn = lcnec.CoreDisplaySettings(sysinfo,displayid)
            cmdsuccess = 0
            cmdloop = 0
            while cmdsuccess == 0 and cmdloop <= 5:
                try:            
                    returnsaved = coreconn.saveconfig(2)
                except:
                    pass
                    err = traceback.format_exc()
                    msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
                    sendlog(gotthefunc, msg, 10)
                    sleep(0.5)
                    cmdloop += 1
                else:
                    cmdsuccess = 1
                    if cmdloop > 5:
                        msg = '{}: Reached maxiumum cmdloop in {} saveconfig'.format(displayid,gotthefunc)
                        sendlog(gotthefunc,msg, 10)
            output['D{0}'.format(displayid)]['SAVED'] = returnsaved
    else:
        msg='D{}: Skipping Save for Quick Transition.'.format(displayid)
        sendlog(gotthefunc, msg, 10)
        output['D{0}'.format(displayid)]['SAVED'] = 0
    # **** NEED gettm status here, returning bcmode, hnum, vnum, pos *****
    
    output['D{0}'.format(displayid)]['DISABLETMTIME'] = 'Last process time: {0}'.format(time() - ts)
    if sysinfo['SYSTEM']['CONNECTION'] == 'ip':
        q.put(output)
    db1success = 0
    while db1success == 0:
        try:
            conn = {}
            c = {}
            conn[socketid] = sqlite3.connect(sysinfo['SYSTEM']['DATABASEFILE'])
            c[socketid] = conn[socketid].cursor()
            c[socketid].execute('DELETE from tiles WHERE member=?', (displayid, ))
        except sqlite3.OperationalError:
            pass
            msg = ('{}: Database locked (11) during delete in disabletmworker. Trying again.',format(displayid))
            sendlog(gotthefunc, msg, 10)
            sleep(0.5)
        except:
            pass
            err = traceback.format_exc()
            msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
            sendlog(gotthefunc, msg, 10)
            sleep(0.5)
        else:
            db1success = 1                        
            db2success = 0
            while db2success == 0:
                try:
                    conn[socketid].commit()
                except sqlite3.OperationalError:
                    pass
                    msg = ('{}: Database locked (12) during commit in disabletmworker. Trying again.',format(displayid))
                    sendlog(gotthefunc, msg, 10)
                    sleep(0.5)
                except:
                    pass
                    err = traceback.format_exc()
                    msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
                    sendlog(gotthefunc, msg, 10)
                    sleep(0.5)
                else:
                    db2success = 1        
                    conn[socketid].close()
                    
    msg = ('Exited {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)
    if sysinfo['SYSTEM']['CONNECTION'] == 'serial':
        return(output)

def disabletmfactory(sysinfo,tileid,save=1):
    gotthefunc = inspect.stack()[0][3]
    msg = ('Entered {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)
    displaylist = []
    dbsuccess = 0
    while dbsuccess == 0:
        try:
            conn = sqlite3.connect(sysinfo['SYSTEM']['DATABASEFILE'])
            c = conn.cursor()
            c.execute('select * from tiles inner join displays on displays.id = tiles.member where tileid=?', (tileid, ))
        except sqlite3.OperationalError:
            pass
            msg = ('{}: Database locked (13). Trying again.',format(gotthefunc))
            sendlog(gotthefunc, msg, 10)
            sleep(0.5)
        except:
            pass
            err = traceback.format_exc()
            msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
            sendlog(gotthefunc, msg, 10)
            sleep(0.5)
        else:
            dbsuccess = 1      
            rows = c.fetchall()
        finally:
            conn.close()  
#    c.execute('select member from tiles where tileid=?', (tileid, ))
    
    msg = '{}: Disable tileid {} with members {}'.format(gotthefunc,tileid,repr(rows))
    sendlog(gotthefunc, msg, 10)
    for row in rows:
        displaylist.append(row)
    tsmain = time()
    socketnum = 0
    disabletmq = Queue()
    disabletmqresults = {}
    disabletmprocs = []
    
    for displaydata in displaylist:
        displayid = displaydata[2]
        msg = '{}: displayid: {}'.format(gotthefunc,displayid)
        sendlog(gotthefunc, msg, 10)
        lastinput = displaydata[18]
        msg = '{}: lastinput: {}'.format(gotthefunc,lastinput)
        sendlog(gotthefunc, msg, 10)
        if not lastinput or lastinput == None:
            if sysinfo['SYSTEM']['DEFAULTINPUT'].lower() == 'dvi':
                if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'lg':
                    lastinput = 'a5'
                if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'nec':
                    lastinput = 3
            if sysinfo['SYSTEM']['DEFAULTINPUT'].lower() == 'hdmi':
                if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'lg':
                    lastinput = 'a0'
                if sysinfo['D{0}'.format(displayid)]['MAKE'] == 'nec':
                    lastinput = 7
            msg = '{}: lastinput was null, None or nonexistant. Defaulting to: {}'.format(gotthefunc,lastinput)
            sendlog(gotthefunc, msg, 10)
        if sysinfo['SYSTEM']['CONNECTION'] == 'ip':
            disabletmproc = Process(target=disabletmworker, args=(sysinfo, tileid, displayid, lastinput, save, socketnum, disabletmq))
            disabletmprocs.append(disabletmproc)
        if sysinfo['SYSTEM']['CONNECTION'] == 'serial':
            disabletmproc = disabletmworker(sysinfo, tileid, displayid, lastinput, save, socketnum, disabletmq)
            msg = 'D{}: buildtm result: {}'.format(displayid, disabletmproc)
            sendlog(gotthefunc, msg, 10)
            update_dict(disabletmqresults, disabletmproc)
        socketnum += 1
    if sysinfo['SYSTEM']['CONNECTION'] == 'ip':
        for w in disabletmprocs:
            w.start()
        for i in range(socketnum):
            update_dict(disabletmqresults, disabletmq.get())
        for w in disabletmprocs:
            w.join()
    msg = ('Took {}'.format(time() - tsmain))
    sendlog(gotthefunc, msg, 10)
    msg = disabletmqresults
    sendlog(gotthefunc, msg, 10)
    msg = ('Exited {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)
    return(disabletmqresults)
    
def logserver(sysinfo):
    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/lavacontrols/lavacontrols.log', mode='a', maxBytes=10*1024*1024, backupCount=0, encoding=None, delay=False)])
    try:
        logging.basicConfig(format='%(asctime)s %(processName)-10s %(name)s %(levelname)-8s %(message)s',handlers=[logging.handlers.RotatingFileHandler(sysinfo['SYSTEM']['LAVACONTROLSLOGFILE'], mode='a', maxBytes=10000*1024, backupCount=30, encoding=None, delay=False)])
    except:
        err = traceback.format_exc()
        msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
        sendlog(gotthefunc, msg, 10)
#    logging.basicConfig(format='%(asctime)s %(processName)-10s %(name)s %(levelname)-8s %(message)s',filename='/var/log/lavacontrols/lavacontrols.log')
    try:
        tcpserver = LogRecordSocketReceiver()
    except:
        err = traceback.format_exc()
        msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
        sendlog(gotthefunc, msg, 10)
    try:
        tcpserver.serve_until_stopped()
    except:
        err = traceback.format_exc()
        msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
        sendlog(gotthefunc, msg, 10)
    msg = ('Exited {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)
    
def hardware(sysinfo,test):
    gotthefunc = inspect.stack()[0][3]
    if osplatform == 'windows':
        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', 9042)
        logger.addHandler(sock)
    
    msg = ('Entered {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)
    csloop = 0
    au = UpdateApi(sysinfo)
    while True:
        displaylist = None
        try:
            if osplatform == 'linux':
                with Timeout(3):
                    olddict = sysinfo.copy()
            else:
                olddict = sysinfo.copy()
        except:
            pass
            msg = ('Error 1 in {0}'.format(gotthefunc))
            sendlog(gotthefunc, msg, 40)
            err = traceback.format_exc()
            msg = ('Error 1 Detail: {0}'.format(err))
            sendlog(gotthefunc, msg, 10)
        try:
            if osplatform == 'linux':
                with Timeout(3):
                    if sysinfo['SYSTEM']['PLATFORM'].lower() == 'de':
                        cmd = fetchcmdDE(sysinfo)                        
                    else:
                        cmd = fetchcmdLAVA(sysinfo)
            else:
                if sysinfo['SYSTEM']['PLATFORM'].lower() == 'de':
                    cmd = fetchcmdDE(sysinfo)                        
                else:
                    cmd = fetchcmdLAVA(sysinfo)
        except:
            pass
            msg = ('Error 2 in {0}'.format(gotthefunc))
            sendlog(gotthefunc, msg, 40)
            err = traceback.format_exc()
            msg = ('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:
            if osplatform == 'linux':
                with Timeout(3):
                    update_dict(sysinfo, cmd)
            else:
                update_dict(sysinfo, cmd)
        except:
            pass
            msg = ('Error 3 in {0}'.format(gotthefunc))
            sendlog(gotthefunc, msg, 40)
            err = traceback.format_exc()
            msg = ('Error 3 Detail: {0}'.format(err))
            sendlog(gotthefunc, msg, 10)
        msg = ('Exited update_dict function'.format(gotthefunc))
        sendlog(gotthefunc, msg, 10)
        # Drain ONE queued command per poll. fetchcmdLAVA enqueued the whole
        # batch in order; popping one command per ~1s poll turns the
        # single-command dispatch below into an ordered consumer of the batch,
        # with the poll interval acting as a settle gap between dependent
        # commands (e.g. power before input). The dispatch block is unchanged —
        # each popped command flows through it exactly as a lone command does.
        cmdqueue = sysinfo['SYSTEM'].get('COMMANDQUEUE') or []
        sysinfo['SYSTEM']['CURRENTITEM'] = None
        if cmdqueue:
            nextitem = cmdqueue.pop(0)
            sysinfo['SYSTEM']['COMMANDQUEUE'] = cmdqueue
            if nextitem.get('command') == 'reboot':
                msg = ('Dequeued reboot command; rebooting controller')
                sendlog(gotthefunc, msg, 10)
                sleep(2)
                lccore.devicereboot()
            else:
                sysinfo['SYSTEM']['COMMAND'] = nextitem['command']
                sysinfo['SYSTEM']['TARGET'] = nextitem['target']
                sysinfo['SYSTEM']['CURRENTITEM'] = nextitem   # confirmed after dispatch below
                msg = ('Dequeued command {} (target {}); {} remaining in queue'.format(nextitem['command'], nextitem['target'], len(cmdqueue)))
                sendlog(gotthefunc, msg, 10)
        else:
            sysinfo['SYSTEM']['COMMAND'] = 'null'
        cmdresponse = {}
        if sysinfo['SYSTEM']['COMMAND'] != 'null' or sysinfo['SYSTEM']['COMMAND'] is not None:
            msg = 'Received command from API: {}'.format(sysinfo['SYSTEM']['COMMAND'])
            sendlog(gotthefunc, msg, 10)
            if 'input' in sysinfo['SYSTEM']['COMMAND']:
                x,y = sysinfo['SYSTEM']['COMMAND'].split('_')
                if sysinfo['D1']['MAKE'] == 'lg':
                    if y.lower() == 'dvi' or y.lower() == 'dvi-all':
                        inputcode = 'a5'
                    elif y.lower() == 'displayport' or y.lower() == 'dp':
                        inputcode = 'd0'
                    elif y.lower() == 'hdmi' or y.lower() == 'hdmi1' or y.lower == 'hdmi-all':
                        inputcode = 'a0'
                    elif y.lower() == 'hdmi2':
                        inputcode = 'a1'
                    elif y.lower() == 'webos':
                        inputcode = 'e0'
                    
                if sysinfo['D1']['MAKE'] == 'nec':
                    if y.lower() == 'vga':
                        inputcode = 1
                    elif y.lower() == 'dvi' or y.lower() == 'dvi-all':
                        inputcode = 3
                    elif y.lower() == 'video1':
                        inputcode = 5
                    elif y.lower() == 'video2':
                        inputcode = 6
                    elif y.lower() == 's-video':
                        inputcode = 7
                    elif y.lower() == 'dvd/hd1':
                        inputcode = 12
                    elif y.lower() == 'option':
                        inputcode = 13
                    elif y.lower() == 'dvd/hd2':
                        inputcode = 14
                    elif y.lower() == 'displayport' or y.lower() == 'dp':
                        inputcode = 15
                    elif y.lower() == 'displayport2':
                        inputcode = 16
                    elif y.lower() == 'hdmi' or y.lower() == 'hdmi1' or y.lower == 'hdmi-all':
                        inputcode = 17
                    elif y.lower() == 'hdmi2':
                        inputcode = 18
                    elif y.lower() == 'displayport3':
                        inputcode = 128
                    elif y.lower() == 'hdmi3':
                        inputcode = 130
                    elif y.lower() == 'hdmi4':
                        inputcode = 131
                    elif y.lower() == 'mp':
                        inputcode = 135
                    elif y.lower() == 'computemodule':
                        inputcode = 136
#                sysinfo['SYSTEM']['CHAINPREVIOUS']['ON'] = sysinfo['SYSTEM']['CHAIN']['ON']
#                sysinfo['SYSTEM']['CHAIN']['ON'] = 1
#                if sysinfo['SYSTEM']['CHAINPREVIOUS']['ON'] == 0:
#                    try:
#                        with Timeout(10):
#                            cmdresponse = changeinputall(sysinfo, inputcode)
#                    except Timeout.Timeout as e:
#                        pass
#                        msg = ('CSLoop changeinputall {} Class Timeout'.format(y.lower()))
#                        sendlog(gotthefunc, msg, 40)
#                    except:
#                        pass
#                        msg = ('Error 4 in {0}'.format(gotthefunc))
#                        sendlog(gotthefunc, msg, 40)
#                        err = traceback.format_exc()
#                        msg = ('Error 4 Detail: {0}'.format(err))
#                        sendlog(gotthefunc, msg, 10)
#                else:
#                    try:
#                        with Timeout(10):
#                            cmdresponse = changeinput(sysinfo, inputcode)
#                    except Timeout.Timeout as e:
#                        pass
#                        msg = ('CSLoop changeinput {} Class Timeout'.format(y.lower()))
#                        sendlog(gotthefunc, msg, 40)
#                    except:
#                        pass
#                        msg = ('Error 5 in {0}'.format(gotthefunc))
#                        sendlog(gotthefunc, msg, 40)
#                        err = traceback.format_exc()
#                        msg = ('Error 5 Detail: {0}'.format(err))
#                        sendlog(gotthefunc, msg, 10)
#                update_dict(sysinfo, cmdresponse)
#                sysinfo['SYSTEM']['BASEINPUT'] = y.lower()

            if sysinfo['SYSTEM']['COMMAND'].startswith('input_apantac'):
                x,y = sysinfo['SYSTEM']['COMMAND'].split('-')
                matrixconn = lccore.Matrix(sysinfo)
                matrixconn.setapantaclink(y)
                
            if sysinfo['SYSTEM']['COMMAND'] == 'input_computemodule':
                sysinfo['SYSTEM']['CHAINPREVIOUS']['ON'] = sysinfo['SYSTEM']['CHAIN']['ON']
                sysinfo['SYSTEM']['CHAIN']['ON'] = 1
                if sysinfo['SYSTEM']['CHAINPREVIOUS']['ON'] == 0:
                    try:
                        if osplatform == 'linux':
                            with Timeout(10):
                                cmdresponse = changeinputall(sysinfo, inputcode)
                        else:
                            cmdresponse = changeinputall(sysinfo, inputcode)
                    except:
                        pass
                        msg = ('Error 4 in {0}'.format(gotthefunc))
                        sendlog(gotthefunc, msg, 40)
                        err = traceback.format_exc()
                        msg = ('Error 4 Detail: {0}'.format(err))
                        sendlog(gotthefunc, msg, 10)
                else:
                    try:
                        if osplatform == 'linux':
                            with Timeout(10):
                                cmdresponse = changeinput(sysinfo, inputcode)
                        else:
                            cmdresponse = changeinput(sysinfo, inputcode)
                    except:
                        pass
                        msg = ('Error 5 in {0}'.format(gotthefunc))
                        sendlog(gotthefunc, msg, 40)
                        err = traceback.format_exc()
                        msg = ('Error 5 Detail: {0}'.format(err))
                        sendlog(gotthefunc, msg, 10)
                update_dict(sysinfo, cmdresponse)
            if sysinfo['SYSTEM']['COMMAND'] == 'input_option':
                sysinfo['SYSTEM']['CHAINPREVIOUS']['ON'] = sysinfo['SYSTEM']['CHAIN']['ON']
                sysinfo['SYSTEM']['CHAIN']['ON'] = 1
                if sysinfo['SYSTEM']['CHAINPREVIOUS']['ON'] == 0:
                    try:
                        if osplatform == 'linux':
                            with Timeout(10):
                                cmdresponse = changeinputall(sysinfo, inputcode)
                        else:
                            cmdresponse = changeinputall(sysinfo, inputcode)
                    except:
                        pass
                        msg = ('Error 4 in {0}'.format(gotthefunc))
                        sendlog(gotthefunc, msg, 40)
                        err = traceback.format_exc()
                        msg = ('Error 4 Detail: {0}'.format(err))
                        sendlog(gotthefunc, msg, 10)
                else:
                    try:
                        if osplatform == 'linux':
                            with Timeout(10):
                                cmdresponse = changeinput(sysinfo, inputcode)
                        else:
                            cmdresponse = changeinput(sysinfo, inputcode)
                    except:
                        pass
                        msg = ('Error 5 in {0}'.format(gotthefunc))
                        sendlog(gotthefunc, msg, 40)
                        err = traceback.format_exc()
                        msg = ('Error 5 Detail: {0}'.format(err))
                        sendlog(gotthefunc, msg, 10)
                update_dict(sysinfo, cmdresponse)
                sysinfo['SYSTEM']['BASEINPUT'] = 'dvi'
            if sysinfo['SYSTEM']['COMMAND'] == 'input_dvi':
                sysinfo['SYSTEM']['CHAINPREVIOUS']['ON'] = sysinfo['SYSTEM']['CHAIN']['ON']
                sysinfo['SYSTEM']['CHAIN']['ON'] = 1
                if sysinfo['SYSTEM']['CHAINPREVIOUS']['ON'] == 0:
                    try:
                        if osplatform == 'linux':
                            with Timeout(10):
                                cmdresponse = changeinputall(sysinfo, inputcode)
                        else:
                            cmdresponse = changeinputall(sysinfo, inputcode)
                    except:
                        pass
                        msg = ('Error 4 in {0}'.format(gotthefunc))
                        sendlog(gotthefunc, msg, 40)
                        err = traceback.format_exc()
                        msg = ('Error 4 Detail: {0}'.format(err))
                        sendlog(gotthefunc, msg, 10)
                else:
                    try:
                        if osplatform == 'linux':
                            with Timeout(10):
                                cmdresponse = changeinput(sysinfo, inputcode)
                        else:
                            cmdresponse = changeinput(sysinfo, inputcode)
                    except:
                        pass
                        msg = ('Error 5 in {0}'.format(gotthefunc))
                        sendlog(gotthefunc, msg, 40)
                        err = traceback.format_exc()
                        msg = ('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']['CHAINPREVIOUS']['ON'] = sysinfo['SYSTEM']['CHAIN']['ON']
                sysinfo['SYSTEM']['CHAIN']['ON'] = 1
                try:
                    if osplatform == 'linux':
                        with Timeout(10):
                            cmdresponse = changeinputrange(sysinfo, inputcode, 4)
                    else:
                        cmdresponse = changeinputrange(sysinfo, inputcode, 4)
                except:
                    pass
                    msg = ('Error 6 in {0}'.format(gotthefunc))
                    sendlog(gotthefunc, msg, 40)
                    err = traceback.format_exc()
                    msg = ('Error 6 Detail: {0}'.format(err))
                    sendlog(gotthefunc, msg, 10)
                update_dict(sysinfo, cmdresponse)
            if sysinfo['SYSTEM']['COMMAND'] == 'input_dvi-all':
                sysinfo['SYSTEM']['CHAINPREVIOUS']['ON'] = sysinfo['SYSTEM']['CHAIN']['ON']
                sysinfo['SYSTEM']['CHAIN']['ON'] = 1
                try:
                    if osplatform == 'linux':
                        with Timeout(10):
                            cmdresponse = changeinputall(sysinfo, inputcode)
                    else:
                        cmdresponse = changeinputall(sysinfo, inputcode)
                except:
                    pass
                    msg = ('Error 7 in {0}'.format(gotthefunc))
                    sendlog(gotthefunc, msg, 40)
                    err = traceback.format_exc()
                    msg = ('Error 7 Detail: {0}'.format(err))
                    sendlog(gotthefunc, msg, 10)
                update_dict(sysinfo, cmdresponse)
                sysinfo['SYSTEM']['BASEINPUT'] = 'dvi'
            if sysinfo['SYSTEM']['COMMAND'] == 'input_dp':
                sysinfo['SYSTEM']['CHAINPREVIOUS']['ON'] = sysinfo['SYSTEM']['CHAIN']['ON']
                sysinfo['SYSTEM']['CHAIN']['ON'] = 1
                if sysinfo['SYSTEM']['CHAINPREVIOUS']['ON'] == 0:
                    try:
                        if osplatform == 'linux':
                            with Timeout(10):
                                cmdresponse = changeinputall(sysinfo, inputcode)
                        else:
                            cmdresponse = changeinputall(sysinfo, inputcode)
                    except:
                        pass
                        msg = ('Error 4 in {0}'.format(gotthefunc))
                        sendlog(gotthefunc, msg, 40)
                        err = traceback.format_exc()
                        msg = ('Error 4 Detail: {0}'.format(err))
                        sendlog(gotthefunc, msg, 10)
                else:
                    try:
                        if osplatform == 'linux':
                            with Timeout(10):
                                cmdresponse = changeinput(sysinfo, inputcode)
                        else:
                            cmdresponse = changeinput(sysinfo, inputcode)
                    except:
                        pass
                        msg = ('Error 5 in {0}'.format(gotthefunc))
                        sendlog(gotthefunc, msg, 40)
                        err = traceback.format_exc()
                        msg = ('Error 5 Detail: {0}'.format(err))
                        sendlog(gotthefunc, msg, 10)
                update_dict(sysinfo, cmdresponse)
                sysinfo['SYSTEM']['BASEINPUT'] = 'dp'
            if sysinfo['SYSTEM']['COMMAND'] == 'input_hdmi' or sysinfo['SYSTEM']['COMMAND'] == 'input_hdmi1':
## Temporary until I can get this to only affect drequested screen numbers
                sysinfo['SYSTEM']['CHAINPREVIOUS']['ON'] = sysinfo['SYSTEM']['CHAIN']['ON']
                sysinfo['SYSTEM']['CHAIN']['ON'] = 1
                try:
                    if osplatform == 'linux':
                        with Timeout(10):
                            cmdresponse = changeinput(sysinfo, inputcode)
                    else:
                        cmdresponse = changeinput(sysinfo, inputcode)
                except:
                    pass
                    msg = ('Error 8 in {0}'.format(gotthefunc))
                    sendlog(gotthefunc, msg, 40)
                    err = traceback.format_exc()
                    msg = ('Error 8 Detail: {0}'.format(err))
                    sendlog(gotthefunc, msg, 10)
                update_dict(sysinfo, cmdresponse)
                sysinfo['SYSTEM']['BASEINPUT'] = 'hdmi'
            if sysinfo['SYSTEM']['COMMAND'] == 'input_hdmi2':
## Temporary until I can get this to only affect drequested screen numbers
                sysinfo['SYSTEM']['CHAINPREVIOUS']['ON'] = sysinfo['SYSTEM']['CHAIN']['ON']
                sysinfo['SYSTEM']['CHAIN']['ON'] = 1
                try:
                    if osplatform == 'linux':
                        with Timeout(10):
                            cmdresponse = changeinput(sysinfo, inputcode)
                    else:
                        cmdresponse = changeinput(sysinfo, inputcode)
                except:
                    pass
                    msg = ('Error 8 in {0}'.format(gotthefunc))
                    sendlog(gotthefunc, msg, 40)
                    err = traceback.format_exc()
                    msg = ('Error 8 Detail: {0}'.format(err))
                    sendlog(gotthefunc, msg, 10)
                update_dict(sysinfo, cmdresponse)
            if sysinfo['SYSTEM']['COMMAND'] == 'input_hdmi3':
## Temporary until I can get this to only affect drequested screen numbers
                sysinfo['SYSTEM']['CHAINPREVIOUS']['ON'] = sysinfo['SYSTEM']['CHAIN']['ON']
                sysinfo['SYSTEM']['CHAIN']['ON'] = 1
                try:
                    if osplatform == 'linux':
                        with Timeout(10):
                            cmdresponse = changeinput(sysinfo, inputcode)
                    else:
                        cmdresponse = changeinput(sysinfo, inputcode)
                except:
                    pass
                    msg = ('Error 8 in {0}'.format(gotthefunc))
                    sendlog(gotthefunc, msg, 40)
                    err = traceback.format_exc()
                    msg = ('Error 8 Detail: {0}'.format(err))
                    sendlog(gotthefunc, msg, 10)
                update_dict(sysinfo, cmdresponse)
            if sysinfo['SYSTEM']['COMMAND'] == 'input_hdmi4':
## Temporary until I can get this to only affect drequested screen numbers
                sysinfo['SYSTEM']['CHAINPREVIOUS']['ON'] = sysinfo['SYSTEM']['CHAIN']['ON']
                sysinfo['SYSTEM']['CHAIN']['ON'] = 1
                try:
                    if osplatform == 'linux':
                        with Timeout(10):
                            cmdresponse = changeinput(sysinfo, inputcode)
                    else:
                        cmdresponse = changeinput(sysinfo, inputcode)
                except:
                    pass
                    msg = ('Error 8 in {0}'.format(gotthefunc))
                    sendlog(gotthefunc, msg, 40)
                    err = traceback.format_exc()
                    msg = ('Error 8 Detail: {0}'.format(err))
                    sendlog(gotthefunc, msg, 10)
                update_dict(sysinfo, cmdresponse)
            if sysinfo['SYSTEM']['COMMAND'] == 'input_hdmi-all':
                sysinfo['SYSTEM']['CHAINPREVIOUS']['ON'] = sysinfo['SYSTEM']['CHAIN']['ON']
                sysinfo['SYSTEM']['CHAIN']['ON'] = 1
                try:
                    if osplatform == 'linux':
                        with Timeout(10):
                            cmdresponse = changeinputall(sysinfo, inputcode)
                    else:
                        cmdresponse = changeinputall(sysinfo, inputcode)
                except:
                    pass
                    msg = ('Error 9 in {0}'.format(gotthefunc))
                    sendlog(gotthefunc, msg, 40)
                    err = traceback.format_exc()
                    msg = ('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']['CHAINPREVIOUS']['ON'] = sysinfo['SYSTEM']['CHAIN']['ON']
                sysinfo['SYSTEM']['CHAIN']['ON'] = 0
                try:
                    if osplatform == 'linux':
                        with Timeout(10):
                            cmdresponse = changeinputrange(sysinfo, inputcode, 4)
                    else:
                        cmdresponse = changeinputrange(sysinfo, inputcode, 4)
                except:
                    pass
                    msg = ('Error 10 in {0}'.format(gotthefunc))
                    sendlog(gotthefunc, msg, 40)
                    err = traceback.format_exc()
                    msg = ('Error 10 Detail: {0}'.format(err))
                    sendlog(gotthefunc, msg, 10)
                update_dict(sysinfo, cmdresponse)
            if sysinfo['SYSTEM']['COMMAND'] == 'input_hdmi_splitter':
                sysinfo['SYSTEM']['CHAINPREVIOUS']['ON'] = sysinfo['SYSTEM']['CHAIN']['ON']
                sysinfo['SYSTEM']['CHAIN']['ON'] = 0
                try:
                    if osplatform == 'linux':
                        with Timeout(10):
                            cmdresponse = changeinput(sysinfo, inputcode)
                    else:
                        cmdresponse = changeinput(sysinfo, inputcode)
                except:
                    pass
                    msg = ('Error 11 in {0}'.format(gotthefunc))
                    sendlog(gotthefunc, msg, 40)
                    err = traceback.format_exc()
                    msg = ('Error 11 Detail: {0}'.format(err))
                    sendlog(gotthefunc, msg, 10)
                update_dict(sysinfo, cmdresponse)
                sysinfo['SYSTEM']['BASEINPUT'] = 'hdmi_splitter'
            if sysinfo['SYSTEM']['COMMAND'] == 'input_hdmi_1a':
                sysinfo['SYSTEM']['CHAINPREVIOUS']['ON'] = sysinfo['SYSTEM']['CHAIN']['ON']
                sysinfo['SYSTEM']['CHAIN']['ON'] = 1
                ##### routelist can be [[1,'a'],[1,'b']] for multiple route changes
                routelist = [[1,'a']]
                try:
                    if osplatform == 'linux':
                        with Timeout(10):
                            cmdresponse = setmatrixroute(sysinfo['SYSTEM']['MATRIXIP'],sysinfo['SYSTEM']['MYIP'],routelist)
                    else:
                        cmdresponse = setmatrixroute(sysinfo['SYSTEM']['MATRIXIP'],sysinfo['SYSTEM']['MYIP'],routelist)
                except:
                    pass
                    msg = ('Error 20 in {0}'.format(gotthefunc))
                    sendlog(gotthefunc, msg, 40)
                    err = traceback.format_exc()
                    msg = ('Error 20 Detail: {0}'.format(err))
                    sendlog(gotthefunc, msg, 10)
#                update_dict(sysinfo, cmdresponse)
                sysinfo['SYSTEM']['BASEINPUT'] = 'hdmi_1a'
            """ 
            Add some tiling for matrix. Need to store current matrix routes and revert when tiling is done, like inputs.
            1) Save current routes
            2) Build route list
            3) Change matrix routes
            4) Set chain to 0
            5) tile on

            When tile is off
            1) Retreive previous route list
            2) Restore routes
            3) Tile off
            """
            if sysinfo['SYSTEM']['COMMAND'] == 'input_hdmi_7a':
                sysinfo['SYSTEM']['CHAINPREVIOUS']['ON'] = sysinfo['SYSTEM']['CHAIN']['ON']
                sysinfo['SYSTEM']['CHAIN']['ON'] = 1
                routelist = [[7,'a']]
                try:
                    if osplatform == 'linux':
                        with Timeout(10):
                            cmdresponse = setmatrixroute(sysinfo['SYSTEM']['MATRIXIP'],sysinfo['SYSTEM']['MYIP'],routelist)
                    else:
                        cmdresponse = setmatrixroute(sysinfo['SYSTEM']['MATRIXIP'],sysinfo['SYSTEM']['MYIP'],routelist)
                except:
                    pass
                    msg = ('Error 20 in {0}'.format(gotthefunc))
                    sendlog(gotthefunc, msg, 40)
                    err = traceback.format_exc()
                    msg = ('Error 20 Detail: {0}'.format(err))
                    sendlog(gotthefunc, msg, 10)
#                update_dict(sysinfo, cmdresponse)
                sysinfo['SYSTEM']['BASEINPUT'] = 'hdmi_7a'
            if sysinfo['SYSTEM']['COMMAND'] == 'input_hdmi_matrix':
                sysinfo['SYSTEM']['CHAINPREVIOUS']['ON'] = sysinfo['SYSTEM']['CHAIN']['ON']
                sysinfo['SYSTEM']['CHAIN']['ON'] = 0
                sysinfo['SYSTEM']['USEMATRIX'] = 1
                try:
                    if osplatform == 'linux':
                        with Timeout(10):
                            cmdresponse = changeinput(sysinfo, inputcode)
                    else:
                        cmdresponse = changeinput(sysinfo, inputcode)
                except:
                    pass
                    msg = ('Error 20 in {0}'.format(gotthefunc))
                    sendlog(gotthefunc, msg, 40)
                    err = traceback.format_exc()
                    msg = ('Error 20 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']['CHAINPREVIOUS']['ON'] = sysinfo['SYSTEM']['CHAIN']['ON']
                sysinfo['SYSTEM']['CHAIN']['ON'] = 1
                try:
                    if osplatform == 'linux':
                        with Timeout(10):
                            cmdresponse = changeinput(sysinfo, inputcode)
                    else:
                        cmdresponse = changeinput(sysinfo, inputcode)
                except:
                    pass
                    msg = ('Error 12 in {0}'.format(gotthefunc))
                    sendlog(gotthefunc, msg, 40)
                    err = traceback.format_exc()
                    msg = ('Error 12 Detail: {0}'.format(err))
                    sendlog(gotthefunc, msg, 10)
                update_dict(sysinfo, cmdresponse)
            if sysinfo['SYSTEM']['COMMAND'] == 'tile_8_on':
                dbsuccess = 0
                while dbsuccess == 0:
                    try:
                        conn = sqlite3.connect(sysinfo['SYSTEM']['DATABASEFILE'])
                        c = conn.cursor()
                        c.execute('SELECT value FROM counters WHERE tags = ?', ('lasttileid',))
                    except sqlite3.OperationalError:
                        pass
                        msg = ('{}: Database locked (14). Trying again.',format(gotthefunc))
                        sendlog(gotthefunc, msg, 10)
                        sleep(0.5)
                    except:
                        pass
                        err = traceback.format_exc()
                        msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
                        sendlog(gotthefunc, msg, 10)
                        sleep(0.5)
                    else:
                        row = c.fetchone()
                        dbsuccess = 1
                    finally:
                        conn.close()
                lasttileid = row[0]
                tileid = lasttileid + 1
                dbsuccess = 0
                while dbsuccess == 0:
                    try:
                        conn = sqlite3.connect(sysinfo['SYSTEM']['DATABASEFILE'])
                        c = conn.cursor()
                        c.execute('UPDATE counters SET value = ? WHERE tags = ?', (tileid,'lasttileid'))
                        conn.commit()
                    except sqlite3.OperationalError:
                        pass
                        msg = ('{}: Database locked (15). Trying again.',format(gotthefunc))
                        sendlog(gotthefunc, msg, 10)
                        sleep(0.5)
                    except:
                        pass
                        err = traceback.format_exc()
                        msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
                        sendlog(gotthefunc, msg, 10)
                        sleep(0.5)
                    else:
                        dbsuccess = 1
                    finally:
                        conn.close()
                try:
                    if osplatform == 'linux':
                        with Timeout(130):
                            cmdresponse = buildtmfactory(sysinfo,4,2,tileid)
                    else:
                        cmdresponse = buildtmfactory(sysinfo,4,2,tileid)
                except:
                    pass
                    msg = ('Error 13 in {0}'.format(gotthefunc))
                    sendlog(gotthefunc, msg, 40)
                    err = traceback.format_exc()
                    msg = ('Error 13 Detail: {0}'.format(err))
                    sendlog(gotthefunc, msg, 10)
                msg = cmdresponse
                sendlog(gotthefunc, msg, 10)
                update_dict(sysinfo, cmdresponse)
                sysinfo['SYSTEM']['TILE'] = 8
            if sysinfo['SYSTEM']['COMMAND'] == 'tile_9_on':
                msg = ('Entered tile_9_on command section')
                sendlog(gotthefunc, msg, 10)
                displaylist = [1,2,3,4,5,6,7,8,9]
                if sysinfo['WALL']['NUMBERING'] == 'L2R':
                    if sysinfo['WALL']['LAYOUT'] == '4x3':
                        if sysinfo['SYSTEM']['TARGET'] == 1:
                            displaylist = [1,2,3,5,6,7,9,10,11]
                        if sysinfo['SYSTEM']['TARGET'] == 2:
                            displaylist = [2,3,4,6,7,8,10,11,12]
                sysinfo['SYSTEM']['CHAIN']['ON'] = 1
                dbsuccess = 0
                while dbsuccess == 0:
                    try:
                        conn = sqlite3.connect(sysinfo['SYSTEM']['DATABASEFILE'])
                        c = conn.cursor()
                        c.execute('SELECT value FROM counters WHERE tags = ?', ('lasttileid',))
                    except sqlite3.OperationalError:
                        pass
                        msg = ('{}: Database locked (16). Trying again.',format(gotthefunc))
                        sendlog(gotthefunc, msg, 10)
                        sleep(0.5)
                    except:
                        pass
                        err = traceback.format_exc()
                        msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
                        sendlog(gotthefunc, msg, 10)
                        sleep(0.5)
                    else:
                        row = c.fetchone()
                        dbsuccess = 1
                    finally:
                        conn.close()
                lasttileid = row[0]
                tileid = lasttileid + 1
                dbsuccess = 0
                while dbsuccess == 0:
                    try:
                        conn = sqlite3.connect(sysinfo['SYSTEM']['DATABASEFILE'])
                        c = conn.cursor()
                        c.execute('UPDATE counters SET value = ? WHERE tags = ?', (tileid,'lasttileid'))
                        conn.commit()
                    except sqlite3.OperationalError:
                        pass
                        msg = ('{}: Database locked (17). Trying again.',format(gotthefunc))
                        sendlog(gotthefunc, msg, 10)
                        sleep(0.5)
                    except:
                        pass
                        err = traceback.format_exc()
                        msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
                        sendlog(gotthefunc, msg, 10)
                        sleep(0.5)
                    else:
                        dbsuccess = 1
                    finally:
                        conn.close()
                try:
                    if osplatform == 'linux':
                        with Timeout(130):
                            cmdresponse = buildtmfactory(sysinfo,3,3,tileid,displaylist)
                    else:
                        cmdresponse = buildtmfactory(sysinfo,3,3,tileid,displaylist)
                except:
                    pass
                    msg = ('Error 13 in {0}'.format(gotthefunc))
                    sendlog(gotthefunc, msg, 40)
                    err = traceback.format_exc()
                    msg = ('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_12_on':
                msg = ('Entered tile_12_on command section')
                sendlog(gotthefunc, msg, 10)
                displaylist = []
                if sysinfo['WALL']['NUMBERING'] == 'L2R':
                    if sysinfo['WALL']['LAYOUT'] == '4x3':
                        if sysinfo['SYSTEM']['TARGET'] == 1:
                            displaylist = [1,2,3,4,5,6,7,8,9,10,11,12]
                sysinfo['SYSTEM']['CHAIN']['ON'] = 1
                dbsuccess = 0
                while dbsuccess == 0:
                    try:
                        conn = sqlite3.connect(sysinfo['SYSTEM']['DATABASEFILE'])
                        c = conn.cursor()
                        c.execute('SELECT value FROM counters WHERE tags = ?', ('lasttileid',))
                    except sqlite3.OperationalError:
                        pass
                        msg = ('{}: Database locked (18). Trying again.',format(gotthefunc))
                        sendlog(gotthefunc, msg, 10)
                        sleep(0.5)
                    except:
                        pass
                        err = traceback.format_exc()
                        msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
                        sendlog(gotthefunc, msg, 10)
                        sleep(0.5)
                    else:
                        row = c.fetchone()
                        dbsuccess = 1
                    finally:
                        conn.close()
                lasttileid = row[0]
                tileid = lasttileid + 1
                dbsuccess = 0
                while dbsuccess == 0:
                    try:
                        conn = sqlite3.connect(sysinfo['SYSTEM']['DATABASEFILE'])
                        c = conn.cursor()
                        c.execute('UPDATE counters SET value = ? WHERE tags = ?', (tileid,'lasttileid'))
                        conn.commit()
                    except sqlite3.OperationalError:
                        pass
                        msg = ('{}: Database locked (19). Trying again.',format(gotthefunc))
                        sendlog(gotthefunc, msg, 10)
                        sleep(0.5)
                    except:
                        pass
                        err = traceback.format_exc()
                        msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
                        sendlog(gotthefunc, msg, 10)
                        sleep(0.5)
                    else:
                        dbsuccess = 1
                    finally:
                        conn.close()
                try:
                    if osplatform == 'linux':
                        with Timeout(130):
                            cmdresponse = buildtmfactory(sysinfo,4,3,tileid)
                    else:
                        cmdresponse = buildtmfactory(sysinfo,4,3,tileid)
                except:
                    pass
                    msg = ('Error 13 in {0}'.format(gotthefunc))
                    sendlog(gotthefunc, msg, 40)
                    err = traceback.format_exc()
                    msg = ('Error 13 Detail: {0}'.format(err))
                    sendlog(gotthefunc, msg, 10)
                msg = cmdresponse
                sendlog(gotthefunc, msg, 10)
                update_dict(sysinfo, cmdresponse)
                sysinfo['SYSTEM']['TILE'] = 12
            if sysinfo['SYSTEM']['COMMAND'] == 'tile_16_on':
                msg = ('Entered tile_16_on command section')
                sendlog(gotthefunc, msg, 10)
                displaylist = None
                if sysinfo['WALL']['NUMBERING'] == 'L2R':
                    if sysinfo['WALL']['LAYOUT'] == '4x3':
                        if sysinfo['SYSTEM']['TARGET'] == 1:
                            displaylist = []
                            displaylist = [1,2,3,4,5,6,7,8,9,10,11,12]
                sysinfo['SYSTEM']['CHAIN']['ON'] = 1
                dbsuccess = 0
                while dbsuccess == 0:
                    try:
                        conn = sqlite3.connect(sysinfo['SYSTEM']['DATABASEFILE'])
                        c = conn.cursor()
                        c.execute('SELECT value FROM counters WHERE tags = ?', ('lasttileid',))
                    except sqlite3.OperationalError:
                        pass
                        msg = ('{}: Database locked (20). Trying again.',format(gotthefunc))
                        sendlog(gotthefunc, msg, 10)
                        sleep(0.5)
                    except:
                        pass
                        err = traceback.format_exc()
                        msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
                        sendlog(gotthefunc, msg, 10)
                        sleep(0.5)
                    else:
                        row = c.fetchone()
                        dbsuccess = 1
                    finally:
                        conn.close()
                lasttileid = row[0]
                tileid = lasttileid + 1
                dbsuccess = 0
                while dbsuccess == 0:
                    try:
                        conn = sqlite3.connect(sysinfo['SYSTEM']['DATABASEFILE'])
                        c = conn.cursor()
                        c.execute('UPDATE counters SET value = ? WHERE tags = ?', (tileid,'lasttileid'))
                        conn.commit()
                    except sqlite3.OperationalError:
                        pass
                        msg = ('{}: Database locked (21). Trying again.',format(gotthefunc))
                        sendlog(gotthefunc, msg, 10)
                        sleep(0.5)
                    except:
                        pass
                        err = traceback.format_exc()
                        msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
                        sendlog(gotthefunc, msg, 10)
                        sleep(0.5)
                    else:
                        dbsuccess = 1
                    finally:
                        conn.close()
                try:
                    if osplatform == 'linux':
                        with Timeout(130):
                            cmdresponse = buildtmfactory(sysinfo,4,4,tileid,displaylist)
                    else:
                        cmdresponse = buildtmfactory(sysinfo,4,4,tileid,displaylist)
                except:
                    pass
                    msg = ('Error 13 in {0}'.format(gotthefunc))
                    sendlog(gotthefunc, msg, 40)
                    err = traceback.format_exc()
                    msg = ('Error 13 Detail: {0}'.format(err))
                    sendlog(gotthefunc, msg, 10)
                msg = cmdresponse
                sendlog(gotthefunc, msg, 10)
                update_dict(sysinfo, cmdresponse)
                sysinfo['SYSTEM']['TILE'] = 16
            if sysinfo['SYSTEM']['COMMAND'] == 'tile_on':
                msg = ('Entered tile_on command section')
                sendlog(gotthefunc, msg, 10)
                displaylist = []
                if sysinfo['WALL']['NUMBERING'] == 'L2R':
                    if sysinfo['WALL']['LAYOUT'] == '2x2':
                        if sysinfo['SYSTEM']['TARGET'] == 1:
                            displaylist = [1,2,3,4]
                    if sysinfo['WALL']['LAYOUT'] == '3x2':
                        if sysinfo['SYSTEM']['TARGET'] == 1:
                            displaylist = [1,2,4,5]
                        if sysinfo['SYSTEM']['TARGET'] == 2:
                            displaylist = [2,3,5,6]
                    if sysinfo['WALL']['LAYOUT'] == '4x2':
                        if sysinfo['SYSTEM']['TARGET'] == 1:
                            displaylist = [1,2,5,6]
                        if sysinfo['SYSTEM']['TARGET'] == 2:
                            displaylist = [2,3,6,7]
                        if sysinfo['SYSTEM']['TARGET'] == 3:
                            displaylist = [3,4,7,8]
                    if sysinfo['WALL']['LAYOUT'] == '6x2':
                        if sysinfo['SYSTEM']['TARGET'] == 1:
                            displaylist = [1,2,7,8]
                        if sysinfo['SYSTEM']['TARGET'] == 2:
                            displaylist = [2,3,8,9]
                        if sysinfo['SYSTEM']['TARGET'] == 3:
                            displaylist = [3,4,9,10]
                        if sysinfo['SYSTEM']['TARGET'] == 4:
                            displaylist = [4,5,10,11]
                        if sysinfo['SYSTEM']['TARGET'] == 5:
                            displaylist = [5,6,11,12]
                    if sysinfo['WALL']['LAYOUT'] == '4x3':
                        if sysinfo['SYSTEM']['TARGET'] == 1:
                            displaylist = [1,2,5,6]
                        if sysinfo['SYSTEM']['TARGET'] == 2:
                            displaylist = [2,3,6,7]
                        if sysinfo['SYSTEM']['TARGET'] == 3:
                            displaylist = [3,4,7,8]
                        if sysinfo['SYSTEM']['TARGET'] == 5:
                            displaylist = [5,6,9,10]
                        if sysinfo['SYSTEM']['TARGET'] == 7:
                            displaylist = [7,8,11,12]                        
#                    if sysinfo['SYSTEM']['USEMATRIX'] == 1:
#                        for displayid in displaylist:
#                            outputletter = chr(96 + displayid)
#                            sysinfo['SYSTEM']['MATRIXCOMMANDS'].append([d,'{}'.format(outputletter)])
#                        sysinfo['SYSTEM']['CHAIN']['ON'] = 0
#                    else:
#                        sysinfo['SYSTEM']['CHAIN']['ON'] = 1
                    sysinfo['SYSTEM']['CHAIN']['ON'] = 1
                else:
                    d = sysinfo['SYSTEM']['TARGET']
                    t = d
                    while t < d+4:
                        # print(t)
                        # print(displaylist)
                        displaylist.append(t)
                        t += 1
                    if sysinfo['SYSTEM']['USEMATRIX'] == 1:
                        outputnum = d
                        sysinfo['SYSTEM']['MATRIXCOMMANDS'] = []
                        while outputnum < d+4:
                            outputletter = chr(96 + outputnum)
                            sysinfo['SYSTEM']['MATRIXCOMMANDS'].append([d,'{}'.format(outputletter)])
                            outputnum += 1
                        sysinfo['SYSTEM']['CHAIN']['ON'] = 0
                    else:
                        sysinfo['SYSTEM']['CHAIN']['ON'] = 1
                dbsuccess = 0
                while dbsuccess == 0:
                    try:
                        conn = sqlite3.connect(sysinfo['SYSTEM']['DATABASEFILE'])
                        c = conn.cursor()
                        c.execute('SELECT value FROM counters WHERE tags = ?', ('lasttileid',))
                    except sqlite3.OperationalError:
                        pass
                        msg = ('{}: Database locked (22). Trying again.',format(gotthefunc))
                        sendlog(gotthefunc, msg, 10)
                        sleep(0.5)
                    except:
                        pass
                        err = traceback.format_exc()
                        msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
                        sendlog(gotthefunc, msg, 10)
                        sleep(0.5)
                    else:
                        row = c.fetchone()
                        dbsuccess = 1
                    finally:
                        conn.close()
                lasttileid = row[0]
                tileid = lasttileid + 1
                dbsuccess = 0
                while dbsuccess == 0:
                    try:
                        conn = sqlite3.connect(sysinfo['SYSTEM']['DATABASEFILE'])
                        c = conn.cursor()
                        c.execute('UPDATE counters SET value = ? WHERE tags = ?', (tileid,'lasttileid'))
                        conn.commit()
                    except sqlite3.OperationalError:
                        pass
                        msg = ('{}: Database locked (23). Trying again.',format(gotthefunc))
                        sendlog(gotthefunc, msg, 10)
                        sleep(0.5)
                    except:
                        pass
                        err = traceback.format_exc()
                        msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
                        sendlog(gotthefunc, msg, 10)
                        sleep(0.5)
                    else:
                        dbsuccess = 1
                    finally:
                        conn.close()
                '''
                    checking for displays already tiled - need to figure out what happens when someone accidentally sends
                    a tile command for the same displays already in the same tile. We don't want to force an untile to
                    retile the same way. for now, we'll force an untile just because of early development.
                '''
                for x in displaylist:
                    dbsuccess = 0
                    while dbsuccess == 0:
                        try:
                            conn = sqlite3.connect(sysinfo['SYSTEM']['DATABASEFILE'])
                            c = conn.cursor()
                            c.execute('SELECT tileid from tiles WHERE member=?', (x,))
                        except sqlite3.OperationalError:
                            pass
                            msg = ('{}: Database locked (24). Trying again.',format(gotthefunc))
                            sendlog(gotthefunc, msg, 10)
                            sleep(0.5)
                        except:
                            pass
                            err = traceback.format_exc()
                            msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
                            sendlog(gotthefunc, msg, 10)
                            sleep(0.5)
                        else:
                            row = c.fetchone()
                            dbsuccess = 1
                        finally:
                            conn.close()
                    if row:
                        cmdresponse = disabletmfactory(sysinfo,row[0],0)
                try:
                    if osplatform == 'linux':
                        with Timeout(30):
                            if displaylist:
                                cmdresponse = buildtmfactory(sysinfo,2,2,tileid,displaylist)
                            else:
                                cmdresponse = buildtmfactory(sysinfo,2,2,tileid)
                    else:
                        if displaylist:
                            cmdresponse = buildtmfactory(sysinfo,2,2,tileid,displaylist)
                        else:
                            cmdresponse = buildtmfactory(sysinfo,2,2,tileid)
                except:
                    pass
                    msg = ('Error 14 in {0}'.format(gotthefunc))
                    sendlog(gotthefunc, msg, 40)
                    err = traceback.format_exc()
                    msg = ('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':
                dbsuccess = 0
                while dbsuccess == 0:
                    try:
                        conn = sqlite3.connect(sysinfo['SYSTEM']['DATABASEFILE'])
                        c = conn.cursor()
                        c.execute('SELECT tileid from tiles WHERE member = ?', (sysinfo['SYSTEM']['TARGET'], ))
                    except sqlite3.OperationalError:
                        pass
                        msg = ('{}: Database locked (25). Trying again.',format(gotthefunc))
                        sendlog(gotthefunc, msg, 10)
                        sleep(0.5)
                    except:
                        pass
                        err = traceback.format_exc()
                        msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
                        sendlog(gotthefunc, msg, 10)
                        sleep(0.5)
                    else:
                        row = c.fetchone()
                        dbsuccess = 1
                    finally:
                        conn.close()
                if row:
                    try:
                        if osplatform == 'linux':
                            with Timeout(30):
                                cmdresponse = disabletmfactory(sysinfo,row[0])
                        else:
                            cmdresponse = disabletmfactory(sysinfo,row[0])
                    except:
                        pass
                        msg = ('Error 14 in {0}'.format(gotthefunc))
                        sendlog(gotthefunc, msg, 40)
                        err = traceback.format_exc()
                        msg = ('Error 14 Detail: {0}'.format(err))
                        sendlog(gotthefunc, msg, 10)
                else:
                    msg = ('No tile found for target {}'.format(sysinfo['SYSTEM']['TARGET']))
                    sendlog(gotthefunc, msg, 40)     
            if sysinfo['SYSTEM']['COMMAND'].startswith('volume'):
                vol = sysinfo['SYSTEM']['COMMAND'].split("_")
                try:
                    if osplatform == 'linux':
                        with Timeout(10):
                            cmdresponse = changevolume(sysinfo)
                    else:
                        cmdresponse = changevolume(sysinfo)
                except:
                    pass
                    msg = ('Error 17 in {0}'.format(gotthefunc))
                    sendlog(gotthefunc, msg, 40)
                    err = traceback.format_exc()
                    msg = ('Error 17 Detail: {0}'.format(err))
                    sendlog(gotthefunc, msg, 10)
                update_dict(sysinfo, cmdresponse)
                msg = cmdresponse
                sendlog(gotthefunc, msg, 10)
#            if sysinfo['SYSTEM']['PLATFORM'].lower() == 'lava':
            if 'lava' in sysinfo['SYSTEM']['PLATFORM'].lower() or sysinfo['SYSTEM']['PLATFORM'].lower() == 'onprem' or sysinfo['SYSTEM']['PLATFORM'].lower() == 'on-prem':
                if 'power' in sysinfo['SYSTEM']['COMMAND']:
                    power = sysinfo['SYSTEM']['COMMAND'].split('_')
                    try:
                        if osplatform == 'linux':
                            with Timeout(10):
                                if power[1] == 'off':
                                    changepower(sysinfo, 0)
                                if power[1] == 'on':
                                    changepower(sysinfo, 1)
                        else:
                            if power[1] == 'off':
                                changepower(sysinfo, 0)
                            if power[1] == 'on':
                                changepower(sysinfo, 1)
                    except:
                        pass
                        msg = ('Error 5 in {0}'.format(gotthefunc))
                        sendlog(gotthefunc, msg, 40)
                        err = traceback.format_exc()
                        msg = ('Error 5 Detail: {0}'.format(err))
                        sendlog(gotthefunc, msg, 10)
#                    update_dict(sysinfo, cmdresponse)
            if sysinfo['SYSTEM']['PLATFORM'].lower() == 'de':
                if sysinfo['SYSTEM']['COMMAND'] == 'display_all_power_off':
                    try:
                        if osplatform == 'linux':
                            with Timeout(10):
                                changepower(sysinfo, 0)
                        else:
                            changepower(sysinfo, 0)
                    except:
                        pass
                        msg = ('Error 18 in {0}'.format(gotthefunc))
                        sendlog(gotthefunc, msg, 40)
                        err = traceback.format_exc()
                        msg = ('Error 18 Detail: {0}'.format(err))
                        sendlog(gotthefunc, msg, 10)
                if sysinfo['SYSTEM']['COMMAND'] == 'display_all_power_on':
                    try:
                        if osplatform == 'linux':
                            with Timeout(10):
                                changepower(sysinfo, 1)
                        else:
                            changepower(sysinfo, 1)
                    except:
                        pass
                        msg = ('Error 19 in {0}'.format(gotthefunc))
                        sendlog(gotthefunc, msg, 40)
                        err = traceback.format_exc()
                        msg = ('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

        # Confirm the command just dispatched (event-linked → Firing History).
        # Accept-level: the controller dispatched it to the panel(s). Best-effort,
        # never raises. reboot/empty polls leave CURRENTITEM None and are skipped.
        currentitem = sysinfo['SYSTEM'].get('CURRENTITEM')
        if currentitem:
            try:
                lccore.report_command(sysinfo, currentitem, 'applied', 'controller dispatched {}'.format(currentitem.get('command')))
            except:
                pass
            sysinfo['SYSTEM']['CURRENTITEM'] = None

        sleep(int(sysinfo['SYSTEM']['INTERVAL']))
        if csloop >= 60:
            savestate(sysinfo)
            csloop = 0
        else:
            csloop += 1
        dbsuccess = 0
        while dbsuccess == 0:
            try:
                conn = sqlite3.connect(sysinfo['SYSTEM']['DATABASEFILE'])
                c = conn.cursor()
                conn.commit()
            except sqlite3.OperationalError:
                pass
                msg = ('{}: Database locked (26). Trying again.',format(gotthefunc))
                sendlog(gotthefunc, msg, 10)
                sleep(0.5)
            except:
                pass
                err = traceback.format_exc()
                msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
                sendlog(gotthefunc, msg, 10)
                sleep(0.5)
            else:
                dbsuccess = 1
            finally:
                conn.close()
            
def iswallalive(sysinfo,iplist):
    gotthefunc = inspect.stack()[0][3]
    msg = ('Entered {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)
    socketnum = 0
    walldown = 0
    pingqueue = Queue()
    pingworkers = []
    for ipid, ipaddr in iplist.items():
        now = datetime.datetime.now()
        pingworker = Process(target=ping, args=(sysinfo, ipaddr, socketnum, pingqueue))
        pingworkers.append(pingworker)
        pingworker.start()
        socketnum += 1
    pingresults = {}
    for i in range(socketnum):
        update_dict(pingresults, pingqueue.get())
    for w in pingworkers:
        w.join()
    sortedresults = collections.OrderedDict(sorted(pingresults.items()))
    for key,val in sortedresults.items():
        msg = ('{0} = {1}'.format(key,val))
        sendlog(gotthefunc, msg, 10)
        if val['UP'] == False:
            subject = ('DISPLAY {} DOWN!'.format(key))
            msg = ('DISPLAY {} DOWN!'.format(key))
            sendemail(sysinfo,subject,msg)
            sendlog(gotthefunc, msg, 50)
            sleep(5)
            walldown = 1
    msg = ('Exited {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)
    return(walldown)

def main(application_path=None):
    gotthefunc = inspect.stack()[0][3]
    msg = ('Entered {} function'.format(gotthefunc))
    
    sysinfo = {}
    sysinfo['PLATFORM'] = {}
    sysinfo['SYSTEM'] = {}
    sysinfo['WALL'] = {}
    sysinfo['SYSTEM']['CHAIN'] = {}
    sysinfo['SYSTEM']['CHAINPREVIOUS'] = {}
    sysinfo['SYSTEM']['LASTHEADDATE'] = None
    sysinfo['SYSTEM']['SERIALPORT'] = None
    sysinfo['SYSTEM']['TOKEN'] = None
    sysinfo['SYSTEM']['MAC'] = None
    
    if osplatform == 'linux':
        sysinfo['SYSTEM']['DATABASEFILE'] = ('/var/lib/lavacontrols/lavacontrols.db')
        sysinfo['SYSTEM']['LAVACONTROLSCONFIGFILE'] = ('/etc/lavacontrols/lavacontrols.conf')
        sysinfo['SYSTEM']['LAVACONTROLSLOGFILE'] = ('/var/log/lavacontrols/lavacontrols.log')
        sysinfo['SYSTEM']['LAVACONTROLSLOCKFILE'] = ('/var/lib/lavacontrols/lavacontrols.run')
        sysinfo['SYSTEM']['LAVACONTROLSNETFILE'] = ('/var/lib/lavacontrols/lavacontrols.netinfo')
        sysinfo['SYSTEM']['LAVACONTROLSSTATEFILE'] = ('{}\\db\\lavacontrols.state'.format(application_path))
    elif osplatform == 'windows':
        if not application_path:
            application_path = os.getcwd()
        sysinfo['SYSTEM']['DATABASEFILE'] = ('{}\\db\\lavacontrols.db'.format(application_path))
        sysinfo['SYSTEM']['LAVACONTROLSCONFIGFILE'] = ('{}\\conf\\lavacontrols.conf'.format(application_path))
        sysinfo['SYSTEM']['LAVACONTROLSLOGFILE'] = ('{}\\logs\\lavacontrols.log'.format(application_path))
#        sysinfo['SYSTEM']['LAVACONTROLSLOGFILE'] = ('{}/logs/lavacontrols.log'.format(application_path))
        sysinfo['SYSTEM']['LAVACONTROLSLOCKFILE'] = ('{}\\db\\lavacontrols.run'.format(application_path))
        sysinfo['SYSTEM']['LAVACONTROLSNETFILE'] = ('{}\\db\\lavacontrols.netinfo'.format(application_path))
        sysinfo['SYSTEM']['LAVACONTROLSSTATEFILE'] = ('{}\\db\\lavacontrols.state'.format(application_path))

    parser = argparse.ArgumentParser(description='LAVA 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)
    mainworkers = []
    try:
        mainworker = Process(target=logserver, args=(sysinfo,))
        mainworkers.append(mainworker)
        mainworker.start()
    except:
        err = traceback.format_exc()
        msg = ('Error Detail: {0}'.format(err))
        sendlog(gotthefunc, msg, 10)
        sys.exit()

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

    sleep(5)
    
    logger = logging.getLogger('main')
    sock = logging.handlers.SocketHandler('localhost', 9042)
    logger.addHandler(sock)
    if cliargs.consolelogs:
        consolelog = logging.StreamHandler(sys.stdout)
        logger.addHandler(consolelog)
    logger.setLevel(logging.DEBUG)
    
    sleep(5)

    msg = 'LAVA Controls {} Startup'.format(version())
    sendlog(gotthefunc, msg, 10)
    
    try:
        conn = sqlite3.connect(sysinfo['SYSTEM']['DATABASEFILE'])
        c = conn.cursor()
    except:
        msg = ('Cannot open database {}'.format(sysinfo['SYSTEM']['DATABASEFILE']))
        sendlog(gotthefunc, msg, 10)
        err = traceback.format_exc()
        msg = ('Error Detail: {0}'.format(err))
        sendlog(gotthefunc, msg, 10)
    else:
        msg = ('Startup database open successful.'.format(sysinfo['SYSTEM']['DATABASEFILE']))
        sendlog(gotthefunc, msg, 10)

    # Read config
    config = configparser.ConfigParser()
    try:
        config.read([sysinfo['SYSTEM']['LAVACONTROLSCONFIGFILE']])
    except configparser.Error as e:
        msg = ('Unable to read configuration file {}! Halting!'.format(sysinfo['SYSTEM']['LAVACONTROLSCONFIGFILE']))
        if osplatform == 'linux':
            sendlog(gotthefunc, msg, 10)
        sys.exit()
    except:
        msg = ('Error in {0} Reading Config! Halting!'.format(gotthefunc))
        sendlog(gotthefunc, msg, 10)
        err = traceback.format_exc()
        msg = ('Error Detail: {0}'.format(err))
        sendlog(gotthefunc, msg, 10)
        sys.exit()
    else:
        msg = ('CONFIG: Loaded config file')
        sendlog(gotthefunc, msg, 10)
    #get platform type, whether new LAVA Controls API or legacy display engine
    try:
        sysinfo['SYSTEM']['PLATFORM'] = config.get('system', 'platform')
    except (configparser.NoOptionError, configparser.InterpolationMissingOptionError):
        msg = ('No platform type in configuration file! Halting!')
        sendlog(gotthefunc, msg, 10)
        sys.exit()
    except configparser.Error as e:
        msg = ('Config read entry system platform type error! Defaulting to lava!')
        sendlog(gotthefunc, msg, 10)
        sys.exit()
    except:
        msg = ('Error in {0} Reading Config! Defaulting to lava!'.format(gotthefunc))
        sendlog(gotthefunc, msg, 40)
        err = traceback.format_exc()
        sendlog(gotthefunc, msg, 10)
        sys.exit()
    else:
        if sysinfo['SYSTEM']['PLATFORM'] == 'de':
            sysinfo['PLATFORM']['API'] = 'https://cloud.ideacage.com'
        elif sysinfo['SYSTEM']['PLATFORM'] == 'lava':
            sysinfo['PLATFORM']['API'] = 'https://endpoint.lavacontrols.com'
        elif sysinfo['SYSTEM']['PLATFORM'] == 'lava-dev':
            sysinfo['PLATFORM']['API'] = 'https://api-dev.lavacontrols.com'
        elif sysinfo['SYSTEM']['PLATFORM'] == 'onprem' or sysinfo['SYSTEM']['PLATFORM'] == 'on-prem':
            try:
                sysinfo['PLATFORM']['API'] = config.get('platform', 'api')
            except:
                msg = ('Error in {0} Reading Config! Cannot read onprem api server entry. Halting.'.format(gotthefunc))
                sendlog(gotthefunc, msg, 10)
                err = traceback.format_exc()
                msg = ('Error Detail: {0}'.format(err))
                sendlog(gotthefunc, msg, 10)
                sys.exit()
        else:
            sysinfo['SYSTEM']['PLATFORM'] = 'lava'
            sysinfo['PLATFORM']['API'] = 'https://endpoint.lavacontrols.com'
        msg = ('CONFIG: Loaded platform type {} with API {}'.format(sysinfo['SYSTEM']['PLATFORM'], sysinfo['PLATFORM']['API']))
        sendlog(gotthefunc, msg, 10)
    while True:
        try:
            sysinfo['SYSTEM']['MAC'], sysinfo['SYSTEM']['MYIP'], sysinfo['SYSTEM']['IFACE'] = netinfo(sysinfo)
        except:
            msg = ('Error in {0} calling netinfo! Halting!'.format(gotthefunc))
            sendlog(gotthefunc, msg, 10)
            err = traceback.format_exc()
            msg = ('Error Detail: {0}'.format(err))
            sendlog(gotthefunc, msg, 10)
            sleep(5)
        else:
            msg = ('CONFIG: Got MAC {} and IP {} IFACE {}'.format(sysinfo['SYSTEM']['MAC'], sysinfo['SYSTEM']['MYIP'], sysinfo['SYSTEM']['IFACE']))
            sendlog(gotthefunc, msg, 10)
            break
    try:
        sysinfo['SYSTEM']['INTERVAL'] = config.get('system', 'interval')
    except (configparser.NoOptionError, configparser.InterpolationMissingOptionError):
        msg = ('No interval specified in configuration file. Defaulting to 1!')
        sendlog(gotthefunc, msg, 10)
        sysinfo['SYSTEM']['INTERVAL'] = '1'
        pass
    except configparser.Error as e:
        sysinfo['SYSTEM']['INTERVAL'] = '1'
    except:
        msg = ('Error in {0} Reading Config! Halting!'.format(gotthefunc))
        sendlog(gotthefunc, msg, 10)
        err = traceback.format_exc()
        msg = ('Error Detail: {0}'.format(err))
        sendlog(gotthefunc, msg, 10)
        sysinfo['SYSTEM']['INTERVAL'] = '1'
    else:
        msg = ('CONFIG: Got interval')
        sendlog(gotthefunc, msg, 10)
    try:
        sysinfo['SYSTEM']['LOGLEVEL'] = config.get('system', 'loglevel')
    except (configparser.NoOptionError, configparser.InterpolationMissingOptionError):
        msg = ('No logging type specified in configuration file. Defaulting to INFO!')
        sendlog(gotthefunc, msg, 10)
        sysinfo['SYSTEM']['LOGLEVEL'] = '20'
        pass
    except configparser.Error as e:
        msg = ('Config read entry system loglevel error! Halting!')
        sendlog(gotthefunc, msg, 10)
        sys.exit()
    except:
        msg = ('Error in {0} Reading Config! Halting!'.format(gotthefunc))
        sendlog(gotthefunc, msg, 10)
        err = traceback.format_exc()
        msg = ('Error Detail: {0}'.format(err))
        sendlog(gotthefunc, msg, 10)
        sys.exit()
    else:
        msg = ('CONFIG: Got logging level')
        sendlog(gotthefunc, msg, 10)
    try:
        sysinfo['SYSTEM']['EMAIL'] = [e.strip() for e in config.get('system', 'emailnotification').split(',')]
    except (configparser.NoOptionError, configparser.InterpolationMissingOptionError):
        msg = ('No email(s) for notifications specified in configuration file. Continuing with no notifications.')
        sendlog(gotthefunc, msg, 10)
        sysinfo['SYSTEM']['EMAIL'] = ['service@lavacontrols.com']
        pass
    except configparser.Error as e:
        msg = ('Config read entry system email notification error! Clearing entry.')
        sendlog(gotthefunc, msg, 10)
        sysinfo['SYSTEM']['EMAIL'] = ['service@lavacontrols.com']
        pass
    except:
        msg = ('Unknown Error in {0} Reading Config! Halting!'.format(gotthefunc))
        sendlog(gotthefunc, msg, 10)
        err = traceback.format_exc()
        msg = ('Unknown Error in {0} Detail: {1}'.format(gotthefunc,err))
        print(msg)
        sendlog(gotthefunc, msg, 10)
        sys.exit()
    try:
        sysinfo['SYSTEM']['SMTPHOST'] = config.get('system', 'smtphost')
    except (configparser.NoOptionError, configparser.InterpolationMissingOptionError):
        msg = ('No smtp hosts for notifications specified in configuration file. Default to localhost.')
        sendlog(gotthefunc, msg, 10)
        sysinfo['SYSTEM']['SMTPHOST'] = 'localhost'
        pass
    except configparser.Error as e:
        msg = ('Config read entry system email notification error! Clearing entry.')
        sendlog(gotthefunc, msg, 10)
        sysinfo['SYSTEM']['SMTPHOST'] = 'localhost'
        pass
    except:
        msg = ('Unknown Error in {0} Reading Config! Halting!'.format(gotthefunc))
        sendlog(gotthefunc, msg, 10)
        err = traceback.format_exc()
        msg = ('Unknown Error in {0} Detail: {1}'.format(gotthefunc,err))
        print(msg)
        sendlog(gotthefunc, msg, 10)
        sys.exit()
    try:
        sysinfo['SYSTEM']['HTTPPROXY'] = config.get('system', 'proxy')
    except (configparser.NoOptionError, configparser.InterpolationMissingOptionError):
        msg = ('No proxy specified in configuration file. Defaulting to none')
        sendlog(gotthefunc, msg, 10)
        pass
    except configparser.Error as e:
        msg = ('Config read entry system proxy error. Moving on.')
        sendlog(gotthefunc, msg, 10)
        pass
    except:
        msg = ('Error in {0} Reading Config! Moving on.'.format(gotthefunc))
        sendlog(gotthefunc, msg, 10)
        err = traceback.format_exc()
        msg = ('Error Detail: {0}'.format(err))
        sendlog(gotthefunc, msg, 10)
        pass
    else:
        msg = ('CONFIG: Got proxy {}'.format(sysinfo['SYSTEM']['HTTPPROXY']))
        sendlog(gotthefunc, msg, 10)
    try:
        sysinfo['SYSTEM']['HTTPTIMEOUT'] = int(config.get('system', 'httptimeout'))
    except (configparser.NoOptionError, configparser.InterpolationMissingOptionError):
        msg = ('No HTTPTIMEOUT specified in config. Defaulting to 60 seconds.')
        sendlog(gotthefunc, msg, 10)
        sysinfo['SYSTEM']['HTTPTIMEOUT'] = 60
        pass
    except configparser.Error as e:
        msg = ('No HTTPTIMEOUT specified in config. Defaulting to 60 seconds.')
        sendlog(gotthefunc, msg, 10)
        sysinfo['SYSTEM']['HTTPTIMEOUT'] = 60
        pass
    except:
        msg = ('Error in {0} Reading Config! Halting!'.format(gotthefunc))
        sendlog(gotthefunc, msg, 10)
        err = traceback.format_exc()
        msg = ('Error Detail: {0}'.format(err))
        sendlog(gotthefunc, msg, 10)
        sysinfo['SYSTEM']['HTTPTIMEOUT'] = 60
        msg = ('No HTTPTIMEOUT specified in config. Defaulting to 60 seconds.')
        sendlog(gotthefunc, msg, 10)
        pass
    else:
        msg = ('CONFIG: Got http timeout of {}'.format(sysinfo['SYSTEM']['HTTPTIMEOUT']))
        sendlog(gotthefunc, msg, 10)
    try:
        sysinfo['SYSTEM']['CONNECTION'] = config.get('system', 'connection')
    except (configparser.NoOptionError, configparser.InterpolationMissingOptionError):
        msg = ('No connection type (ip or serial) specified in configuration file. Defaulting to IP!')
        sendlog(gotthefunc, msg, 10)
        sysinfo['SYSTEM']['CONNECTION'] = 'ip'
        pass
    except configparser.Error as e:
        msg = ('Config read entry system connection error! Halting!')
        sendlog(gotthefunc, msg, 10)
        sys.exit()
    except:
        msg = ('Error in {0} Reading Config! Halting!'.format(gotthefunc))
        sendlog(gotthefunc, msg, 10)
        err = traceback.format_exc()
        msg = ('Error Detail: {0}'.format(err))
        sendlog(gotthefunc, msg, 10)
        sys.exit()
    else:
        msg = ('CONFIG: Got connection type {}'.format(sysinfo['SYSTEM']['CONNECTION']))
        sendlog(gotthefunc, msg, 10)
    if sysinfo['SYSTEM']['CONNECTION'] == 'ip':
        # 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
    elif sysinfo['SYSTEM']['CONNECTION'] == 'serial':
        try:
            sysinfo['SYSTEM']['SERIALPORT'] = config.get('serial', 'port')
        except (configparser.NoOptionError, configparser.InterpolationMissingOptionError):
            msg = ('No serial port specified in configuration file. Defaulting to /dev/ttyUSB0')
            sendlog(gotthefunc, msg, 10)
            sysinfo['SYSTEM']['SERIALPORT'] = '/dev/ttyUSB0'
            pass
        except configparser.Error as e:
            msg = ('Config read entry system serial port error! Halting!')
            sendlog(gotthefunc, msg, 10)

            sys.exit()
        except:
            msg = ('Error in {0} Reading Config! Halting!'.format(gotthefunc))
            sendlog(gotthefunc, msg, 10)
            err = traceback.format_exc()
            msg = ('Error Detail: {0}'.format(err))
            sendlog(gotthefunc, msg, 10)
            sys.exit()
        else:
            msg = ('CONFIG: Got serial port {}'.format(sysinfo['SYSTEM']['SERIALPORT']))
            sendlog(gotthefunc, msg, 10)
    else:
        msg = ('Invalid connection type specified in configuration file. Halting!')
        sendlog(gotthefunc, msg, 10)
        sys.exit()
    try:
        sysinfo['SYSTEM']['MODE'] = config.get('system', 'mode')
    except (configparser.NoOptionError, configparser.InterpolationMissingOptionError):
        msg = ('No mode specified in configuration file. Defaulting to wall.')
        sendlog(gotthefunc, msg, 10)
        sysinfo['SYSTEM']['MODE'] = 'wall'
        pass
    except configparser.Error as e:
        msg = ('Config read entry system mode error. Moving on.')
        sendlog(gotthefunc, msg, 10)
        sysinfo['SYSTEM']['MODE'] = 'wall'
        pass
    except:
        msg = ('Error in {0} Reading Config! Moving on.'.format(gotthefunc))
        sendlog(gotthefunc, msg, 10)
        err = traceback.format_exc()
        msg = ('Error Detail: {0}'.format(err))
        sendlog(gotthefunc, msg, 10)
        sysinfo['SYSTEM']['MODE'] = 'wall'
        pass
    else:
        msg = ('CONFIG: Got mode {}'.format(sysinfo['SYSTEM']['MODE']))
        sendlog(gotthefunc, msg, 10)
    if sysinfo['SYSTEM']['MODE'] == 'wall':
        try:
            sysinfo['WALL']['NUMBERING'] = config.get('wall', 'numbering')
        except (configparser.NoOptionError, configparser.InterpolationMissingOptionError):
            msg = ('No numbering type specified in configuration file. Defaulting to zig-zag!')
            sendlog(gotthefunc, msg, 10)
            sysinfo['WALL']['NUMBERING'] = 'zig-zag'
            pass
        except configparser.Error as e:
            msg = ('Config read entry wall numbering error! Halting!')
            sendlog(gotthefunc, msg, 10)
            sys.exit()
        except:
            msg = ('Error in {0} Reading Config! Halting!'.format(gotthefunc))
            sendlog(gotthefunc, msg, 10)
            err = traceback.format_exc()
            msg = ('Error Detail: {0}'.format(err))
            sendlog(gotthefunc, msg, 10)
            sys.exit()
        try:
            sysinfo['WALL']['LAYOUT'] = config.get('wall', 'layout')
        except (configparser.NoOptionError, configparser.InterpolationMissingOptionError):
            msg = ('No video wall layout specified in configuration file. Halting!')
            sendlog(gotthefunc, msg, 10)
            sys.exit()
        except configparser.Error as e:
            msg = ('Config read entry wall layout error! Halting!')
            sendlog(gotthefunc, msg, 10)
            sys.exit()
        except:
            msg = ('Error in {0} Reading Config! Halting!'.format(gotthefunc))
            sendlog(gotthefunc, msg, 10)
            err = traceback.format_exc()
            msg = ('Error Detail: {0}'.format(err))
            sendlog(gotthefunc, msg, 10)
            sys.exit()
        try:
            sysinfo['SYSTEM']['DEFAULTINPUT'] = config.get('wall', 'defaultinput')
        except (configparser.NoOptionError, configparser.InterpolationMissingOptionError):
            msg = ('No default input specified in configuration file. Defaulting to DVI!')
            sendlog(gotthefunc, msg, 10)
            sysinfo['SYSTEM']['DEFAULTINPUT'] = 'dvi'
            pass
        except configparser.Error as e:
            msg = ('Config read entry wall defaultinput error! Halting!')
            sendlog(gotthefunc, msg, 10)
            sysinfo['SYSTEM']['DEFAULTINPUT'] = 'dvi'
            pass
        except:
            msg = ('Error in {0} Reading Config! Halting!'.format(gotthefunc))
            sendlog(gotthefunc, msg, 10)
            err = traceback.format_exc()
            msg = ('Error Detail: {0}'.format(err))
            sendlog(gotthefunc, msg, 10)
            sysinfo['SYSTEM']['DEFAULTINPUT'] = 'dvi'
            pass
        try:
            sysinfo['SYSTEM']['MATRIX'] = config.get('wall', 'matrix')
        except (configparser.NoOptionError, configparser.InterpolationMissingOptionError):
            msg = ('No matrix type specified in configuration file. None will be applied to system.')
            sendlog(gotthefunc, msg, 10)
            sysinfo['SYSTEM']['MATRIX'] = None
            sysinfo['SYSTEM']['USEMATRIX'] = 0
            pass
        except configparser.Error as e:
            msg = ('Config read entry wall matrix ip error! Halting!')
            sendlog(gotthefunc, msg, 10)
            sys.exit()
        except:
            msg = ('Error in {0} Reading Config! Halting!'.format(gotthefunc))
            sendlog(gotthefunc, msg, 10)
            err = traceback.format_exc()
            msg = ('Error Detail: {0}'.format(err))
            sendlog(gotthefunc, msg, 10)
            sys.exit()
        else:
            msg = ('CONFIG: Retrieved matrix type: {}'.format(sysinfo['SYSTEM']['MATRIX']))
            sendlog(gotthefunc, msg, 10)
            sysinfo['SYSTEM']['USEMATRIX'] = 1
            if sysinfo['SYSTEM']['MATRIX'] == 'apantac':
                sysinfo['SYSTEM']['APANTAC'] = {}
                try:
                    for (x, y) in config.items('apantac'):
                        sysinfo['SYSTEM']['APANTAC']['{}'.format(x)] = y
                except (configparser.NoOptionError, configparser.InterpolationMissingOptionError):
                    msg = ('No apantac receivers or transmitters specified in configuration file. None will be applied to system.')
                    sendlog(gotthefunc, msg, 10)
                    sysinfo['SYSTEM']['USEMATRIX'] = 0
                    pass
                except configparser.Error as e:
                    msg = ('Config read entry apantac matrix data error! Halting!')
                    sendlog(gotthefunc, msg, 10)
                    sys.exit()
                except:
                    msg = ('Error in {0} Reading Config! Halting!'.format(gotthefunc))
                    sendlog(gotthefunc, msg, 10)
                    err = traceback.format_exc()
                    msg = ('Error Detail: {0}'.format(err))
                    sendlog(gotthefunc, msg, 10)
                    sys.exit()
                else:
                    msg = ('CONFIG: Retrieved apantac matrix data {}'.format(sysinfo['SYSTEM']['APANTAC']))
                    sendlog(gotthefunc, msg, 10)
                    sysinfo['SYSTEM']['USEMATRIX'] = 1
            elif sysinfo['SYSTEM']['MATRIX'] == 'gefen':
                try:
                    sysinfo['SYSTEM']['MATRIXIP'] = config.get('gefen', 'matrixip')
                except (configparser.NoOptionError, configparser.InterpolationMissingOptionError):
                    msg = ('No gefen matrix switch ip address specified in configuration file. None will be applied to system.')
                    sendlog(gotthefunc, msg, 10)
                    sysinfo['SYSTEM']['MATRIXIP'] = None
                    sysinfo['SYSTEM']['USEMATRIX'] = 0
                    pass
                except configparser.Error as e:
                    msg = ('Config read entry wall matrix ip error! Halting!')
                    sendlog(gotthefunc, msg, 10)
                    sys.exit()
                except:
                    msg = ('Error in {0} Reading Config! Halting!'.format(gotthefunc))
                    sendlog(gotthefunc, msg, 10)
                    err = traceback.format_exc()
                    msg = ('Error Detail: {0}'.format(err))
                    sendlog(gotthefunc, msg, 10)
                    sys.exit()
                else:
                    msg = ('CONFIG: Retrieved matrix ip: {}'.format(sysinfo['SYSTEM']['MATRIXIP']))
                    sendlog(gotthefunc, msg, 10)
                    sysinfo['SYSTEM']['USEMATRIX'] = 1
            else:
                msg = ('CONFIG: Invalid matrix type {}. Valid types are apantac and gefen. None will be applied to the system.'.format(sysinfo['SYSTEM']['MATRIX']))
                sendlog(gotthefunc, msg, 10)
                sysinfo['SYSTEM']['USEMATRIX'] = 0
    
    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)

    tsmain = time()
    
    if 'HTTPPROXY' in sysinfo['SYSTEM']:
        if len(sysinfo['SYSTEM']['HTTPPROXY']) > 0:
            os.environ["http_proxy"] = sysinfo['SYSTEM']['HTTPPROXY']
            os.environ["https_proxy"] = sysinfo['SYSTEM']['HTTPPROXY']
            
    msg = 'Gathering network information'
    sendlog(gotthefunc, msg, 10)
    gateways = {}
    msg = "Waiting for network to come up"

    sendlog(gotthefunc, msg, 10)
    while 'default' not in gateways or not defaultgateway:
        try:
            gateways = netifaces.gateways()
        except:
            pass
            msg = ("Exception in {}!:".format(gotthefunc))
            sendlog(gotthefunc, msg, 10)
            err = traceback.format_exc()
            msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
            sendlog(gotthefunc, msg, 10)
        try:
            defaultgateway = gateways['default'][netifaces.AF_INET][0]
        except:
            pass
            defaultgateway = None
            msg = ('Network not ready in {0}.'.format(gotthefunc))
            sendlog(gotthefunc, msg, 10)
            sleep(1)
        else:
            msg = 'Obtained default gateway ip address'
            sendlog(gotthefunc, msg, 20)
    msg = 'Attempting to ping default gateway'
    sendlog(gotthefunc, msg, 10)
    while True:
        isup = ping(sysinfo, defaultgateway)
        if isup:
            msg = 'Default gateway {} is alive.'.format(defaultgateway)
            sendlog(gotthefunc, msg, 10)
            break
        else:
            msg = 'Default gateway {} is down. Looping.'.format(defaultgateway)
            sendlog(gotthefunc, msg, 10)
    if sysinfo['SYSTEM']['PLATFORM'].lower() == 'de':
            # Load license key
            try:
                sysinfo['SYSTEM']['LICENSEKEY'] = config.get('system', 'licensekey')
            except (configparser.NoOptionError, configparser.InterpolationMissingOptionError):
                msg = ('No license key in configuration file! Halting!')
                sendlog(gotthefunc, msg, 10)
                sys.exit()
            except configparser.Error as e:
                msg = ('Config read entry system license key error! Halting!')
                sendlog(gotthefunc, msg, 10)
                sys.exit()
            except:
                msg = ('Error in {0} Reading Config! Halting!'.format(gotthefunc))
                sendlog(gotthefunc, msg, 10)
                err = traceback.format_exc()
                msg = ('Error Detail: {0}'.format(err))
                sendlog(gotthefunc, msg, 10)
                sys.exit()
            else:
                msg = ('CONFIG: Loaded license key')
                sendlog(gotthefunc, msg, 10)            
    else:
        # Load token key
        try:
            sysinfo['SYSTEM']['TOKEN'] = config.get('system', 'token')
        except (configparser.NoOptionError, configparser.InterpolationMissingOptionError):
            err = traceback.format_exc()
            msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
            sendlog(gotthefunc, msg, 10)
        except configparser.Error as e:
            err = traceback.format_exc()
            msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
            sendlog(gotthefunc, msg, 10)        
        except:
            err = traceback.format_exc()
            msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
            sendlog(gotthefunc, msg, 10)            
            sys.exit()
        if not sysinfo['SYSTEM']['TOKEN'] and sysinfo['SYSTEM']['PLATFORM'].lower() != 'de':
            looper = 0
            while looper < 5:
#                try:
#                    popup('LAVA Box Self-Provisioning','Please stand by.',3000)
#                except:
#                    err = traceback.format_exc()
#                    msg = ('Error in {} Detail: {}'.format(gotthefunc,err))
#                    sendlog(gotthefunc, msg, 10)
#                    pass
                try:
                    sysinfo['SYSTEM']['TOKEN'] = gettoken(sysinfo)
                except:
                    err = traceback.format_exc()
                    msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
                    sendlog(gotthefunc, msg, 10)
                    subject = 'LAVA Box Failed Self-Provisioning'
                    message = 'LAVA Box Failed Self-Provisioning \n {}'.format(msg)
                    #sendemail(sysinfo,subject,message)
                    sleep(5)
                else:
                    break
                looper += 1
            if looper >= 5:
                msg = ('LAVA Box Failed Self-Provisioning Please contact support@lavacontrols.com!')
                sendlog(gotthefunc, msg, 10)  
#                try:
#                    popup('LAVA Box Failed Self-Provisioning','Please contact support@lavacontrols.com!',3000000)
#                except:
#                    err = traceback.format_exc()
#                    msg = ('Error in {} Detail: {}'.format(gotthefunc,err))
#                    sendlog(gotthefunc, msg, 10)
#                    pass
#                else:
#                    sleep(300)
#                    sys.exit()
            else:
                configbuilder(sysinfo['SYSTEM']['LAVACONTROLSCONFIGFILE'], token=sysinfo['SYSTEM']['TOKEN'])

    # Test network connection
    
    
    
    
        
    if sysinfo['SYSTEM']['CONNECTION'] == 'ip':
        try:
            walldown = iswallalive(sysinfo, IP)
        except:
            msg = ('Error in {0}!'.format(gotthefunc))
            sendlog(gotthefunc, msg, 10)  
            err = traceback.format_exc()
            msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
            sendlog(gotthefunc, msg, 10)
            sys.exit()
        if walldown == 1:
            msg = ('One or more wall displays are unreachable. Halting system. Add self-healing stuff here.'.format(gotthefunc,err))
            sendlog(gotthefunc, msg, 10)  

##### Borrowed from declientlib. Added as placeholder. Function copied to lavacontrolslib but needs to be converted to lavacontrols.            
#            tempjobsdata['SYSTEM']['EMAIL'] = ['sloeckle@lucidnetworks.net']
#            sendemail(tempjobsdata,subject,message)


        # Begin startupdisplaystatus to query all displays for current configuration and status.
        # Additionally, ID to IP learning and mapping takes place.

        socketnum = 0
        startupqueue = Queue()
        startupworkers = []
        for ipid, ipaddr in IP.items():
            now = datetime.datetime.now()
            startupworker = Process(target=startupdisplaystatus, args=(sysinfo, ipaddr, socketnum, startupqueue))
            startupworkers.append(startupworker)
            startupworker.start()
            socketnum += 1
        sysinfo['SYSTEM']['DISPLAYS'] = socketnum
        startupresults = {}
        for i in range(socketnum):
            startupqueuedata = startupqueue.get()
            sendlog(gotthefunc, startupqueuedata, 10)
            update_dict(startupresults, startupqueuedata)
        for w in startupworkers:
            w.join()
        sortedresults = collections.OrderedDict(sorted(startupresults.items()))
        
    if sysinfo['SYSTEM']['CONNECTION'] == 'serial':
        
        if sysinfo['SYSTEM']['MODE'] == 'single':
            startupresults = startupdisplaystatus(sysinfo)
        elif sysinfo['SYSTEM']['MODE'] == 'wall':
            layout = sysinfo['WALL']['LAYOUT'].split('x')
            displaycount = int(int(layout[0])*int(layout[1]))+1
            x = 1
            y = 1
            startupresults = {}
            while x < displaycount:
                startupresult = startupdisplaystatus(sysinfo, socketid=y, id=x)
                update_dict(startupresults, startupresult)
                x += 1
                y += 1
        sortedresults = collections.OrderedDict(sorted(startupresults.items()))
        msg = sortedresults
        sendlog(gotthefunc, msg, 10)
        
        
    for key,val in sortedresults.items():
        val['LAVA_ID'] = 'UNKNOWN'
        try:
            if not val['F1']:
                val['F1'] = ''
        except:
            pass
            val['F1'] = ''
        try:
            if not val['F2']:
                val['F2'] = ''
        except:
            pass
            val['F2'] = ''
        try:
            if not val['F3']:
                val['F3'] = ''
        except:
            pass
            val['F3'] = ''
        try:
            if not val['F4']:
                val['F4'] = ''
        except:
            pass
            val['F4'] = ''
        try:
            if not val['LAVA_ID']:
                val['LAVA_ID'] = ''
        except:
            pass
            val['LAVA_ID'] = ''
        try:
            if not val['IP']:
                val['IP'] = ''
        except:
            pass
            val['IP'] = ''
        try:
            if not val['MODEL']:
                val['MODEL'] = ''
        except:
            pass
            val['MODEL'] = ''
        try:
            if not val['VOL']:
                val['VOL'] = 0
        except:
            pass
            val['VOL'] = 0
        try:
            if not val['PWR']:
                val['PWR'] = 0
        except:
            pass
            val['PWR'] = 0
        try:
            if not val['INPUT']:
                val['INPUT'] = 0
        except:
            pass
            val['INPUT'] = 0
        try:
            if not val['TMMODE']:
                val['TMMODE'] = 0
        except:
            pass
            val['TMMODE'] = 0
        try:
            if not val['TMH']:
                val['TMH'] = 0
        except:
            pass
            val['TMH'] = 0
        try:
            if not val['TMV']:
                val['TMV'] = 0
        except:
            pass
            val['TMV'] = 0
        try:
            if not val['TMP']:
                val['TMP'] = 0
        except:
            pass
            val['TMP'] = 0
        try:
            if not val['SERIAL']:
                val['SERIAL'] = ''
        except:
            pass
            val['SERIAL'] = ''
            
        dbsuccess = 0
        
        while dbsuccess == 0:
            try:
                conn = sqlite3.connect(sysinfo['SYSTEM']['DATABASEFILE'])
                c = conn.cursor()
                c.execute('SELECT id, lava_id FROM displays WHERE serial = ?', (val['SERIAL'],))
            except sqlite3.OperationalError:
                pass
                msg = ('{}: Database locked (27). Trying again.',format(gotthefunc))
                sendlog(gotthefunc, msg, 10)
                sleep(0.5)
            except:
                pass
                err = traceback.format_exc()
                msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
                sendlog(gotthefunc, msg, 10)
                sleep(0.5)
            else:
                row = c.fetchone()
                dbsuccess = 1
            finally:
                conn.close()
        if sysinfo['SYSTEM']['PLATFORM'].lower() != 'de':
            try:
                apiqueryconn = QueryAPI(sysinfo)
            except:
                pass
                err = traceback.format_exc()
                msg = ('Error in {} Detail: {}'.format(gotthefunc,err))
                sendlog(gotthefunc, msg, 10)      
            while True:        
                try:
                    displayinfo = apiqueryconn.display(val['ID'])
                except:
                    pass
                    err = traceback.format_exc()
                    msg = ('Error in {} Detail: {}'.format(gotthefunc,err))
                    sendlog(gotthefunc, msg, 10)     
                    msg = ("Data retreived from apiqueryconn.display(val['ID']): {}".format(displayinfo))
                    sendlog(gotthefunc, msg, 10)     
                else:
                    break
            try:
                val['LAVA_ID'] = displayinfo['id']
            except:
                msg = ('No displays configured in LAVA API. Halting.')
                sendlog(gotthefunc, msg, 10)     
                err = traceback.format_exc()
                msg = ('Error in {} Detail: {}'.format(gotthefunc,err))
                sendlog(gotthefunc, msg, 10)        
                sys.exit()
        print(val)
        if row is None and val['PWR'] != 0:
            try:
                if sysinfo['SYSTEM']['MODE'] == 'wall':
                    dbsuccess = 0
                    while dbsuccess == 0:
                        try:
                            conn = sqlite3.connect(sysinfo['SYSTEM']['DATABASEFILE'])
                            c = conn.cursor()
                            c.execute('INSERT into displays (model, serial, ip, make, f1, f2, f3, f4, id, lava_id, power_state, input, volume, tile_state, tile_h, tile_v, tile_p) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', (val['MODEL'], val['SERIAL'], val['IP'], val['MAKE'], val['F1'], val['F2'], val['F3'], val['F4'], val['ID'], val['LAVA_ID'], val['PWR'], val['INPUT'], val['VOL'], val['TMMODE'], val['TMH'], val['TMV'], val['TMP']))
                            conn.commit()
                        except sqlite3.OperationalError:
                            pass
                            msg = ('{}: Database locked (28). Trying again.',format(gotthefunc))
                            sendlog(gotthefunc, msg, 10)
                            sleep(0.5)
                        except:
                            pass
                            err = traceback.format_exc()
                            msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
                            sendlog(gotthefunc, msg, 10)
                            sleep(0.5)
                        else:
                            dbsuccess = 1
                        finally:
                            conn.close()
                else:
                    dbsuccess = 0
                    while dbsuccess == 0:
                        try:
                            conn = sqlite3.connect(sysinfo['SYSTEM']['DATABASEFILE'])
                            c = conn.cursor()
                            c.execute('INSERT into displays (model, serial, ip, make, f1, f2, f3, f4, id, lava_id, power_state, input, volume) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', (val['MODEL'], val['SERIAL'], val['IP'], val['MAKE'], val['F1'], val['F2'], val['F3'], val['F4'], val['ID'], val['LAVA_ID'], val['PWR'], val['INPUT'], val['VOL']))
                            conn.commit()
                        except sqlite3.OperationalError:
                            pass
                            msg = ('{}: Database locked (29). Trying again.',format(gotthefunc))
                            sendlog(gotthefunc, msg, 10)
                            sleep(0.5)
                        except:
                            pass
                            err = traceback.format_exc()
                            msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
                            sendlog(gotthefunc, msg, 10)
                            sleep(0.5)
                        else:
                            dbsuccess = 1
                        finally:
                            conn.close()
            except:
                msg = ('Startup display entry insert for {} failed. Pass.'.format(key))
                sendlog(gotthefunc, msg, 10)     
                err = traceback.format_exc()
                msg = ('Error Detail: {0}'.format(err))
                sendlog(gotthefunc, msg, 10)       
                pass
                sleep(0.5)
            else:
                msg = ('Startup display entry insert for {} successful.'.format(key))
                sendlog(gotthefunc, msg, 10)     
        else:
            try:
                if sysinfo['SYSTEM']['MODE'] == 'wall':          
                    dbsuccess = 0
                    while dbsuccess == 0:
                        try:
                            conn = sqlite3.connect(sysinfo['SYSTEM']['DATABASEFILE'])
                            c = conn.cursor()     
                            c.execute('UPDATE displays SET ip = ?, make = ?, f1 = ?, f2 = ?, f3 = ?, f4 = ?, id = ?, lava_id = ?, power_state = ?, input = ?, volume = ?, tile_state = ?, tile_h = ?, tile_v = ?, tile_p = ? WHERE serial = ?', (val['IP'], val['MAKE'], val['F1'], val['F2'], val['F3'], val['F4'], val['ID'], val['LAVA_ID'], val['PWR'], val['INPUT'], val['VOL'], val['TMMODE'], val['TMH'], val['TMV'], val['TMP'], val['SERIAL']))
                            conn.commit()
                        except sqlite3.OperationalError:
                            pass
                            msg = ('{}: Database locked (30). Trying again.',format(gotthefunc))
                            sendlog(gotthefunc, msg, 10)
                            sleep(0.5)
                        except:
                            pass
                            err = traceback.format_exc()
                            msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
                            sendlog(gotthefunc, msg, 10)
                            sleep(0.5)
                        else:
                            dbsuccess = 1
                        finally:
                            conn.close()
                else:
                    dbsuccess = 0
                    while dbsuccess == 0:
                        try:
                            conn = sqlite3.connect(sysinfo['SYSTEM']['DATABASEFILE'])
                            c = conn.cursor()     
                            c.execute('UPDATE displays SET ip = ?, make = ?, f1 = ?, f2 = ?, f3 = ?, f4 = ?, id = ?, lava_id = ?, power_state = ?, input = ?, volume = ? WHERE serial = ?', (val['IP'], val['MAKE'], val['F1'], val['F2'], val['F3'], val['F4'], val['ID'], val['LAVA_ID'], val['PWR'], val['INPUT'], val['VOL'], val['SERIAL']))
                            conn.commit()
                        except sqlite3.OperationalError:
                            pass
                            msg = ('{}: Database locked (31). Trying again.',format(gotthefunc))
                            sendlog(gotthefunc, msg, 10)
                            sleep(0.5)
                        except:
                            pass
                            err = traceback.format_exc()
                            msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
                            sendlog(gotthefunc, msg, 10)
                            sleep(0.5)
                        else:
                            dbsuccess = 1
                        finally:
                            conn.close()
            except:
                err = traceback.format_exc()
                sendlog(gotthefunc, msg, 10)     
                pass
                sleep(0.5)
            else:
                msg = ('Startup display entry update for {} successful.'.format(key))
                sendlog(gotthefunc, msg, 10)     
            startupresults['D{0}'.format(row[0])]['lava_id'] = row[1]
    update_dict(sysinfo, startupresults)

    msg = ('Took {}'.format(time() - tsmain))
    sendlog(gotthefunc, msg, 10)     
    # Merge startup results into master sysinfo dictionary
    
    # Let's set some display defaults, like OSD Off

    if sysinfo['SYSTEM']['CONNECTION'] == 'ip':
        tsmain = time()
        socketnum = 0
        setdefaultqueue = Queue()
        setdefaultworkers = []
        for key, value in sysinfo.items():
            if key.startswith('D') and key != 'DNone':
                setdefaultworker = Process(target=setdefaults, args=(sysinfo, sysinfo['{}'.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()
    elif sysinfo['SYSTEM']['CONNECTION'] == 'serial':
        if sysinfo['SYSTEM']['MODE'] == 'single':
            setdefaultresults = {}
            for key, value in sysinfo.items():
                if key.startswith('D') and key != 'DNone':
                    setdefaultresult = setdefaults(sysinfo, sysinfo['{}'.format(key)]['ID'])
                    update_dict(setdefaultresults, setdefaultresult)
        elif sysinfo['SYSTEM']['MODE'] == 'wall':
            setdefaultresults = {}
            for key, value in sysinfo.items():
                if key.startswith('D') and key != 'DNone':
                    setdefaultresult = setdefaults(sysinfo, sysinfo['{}'.format(key)]['ID'])
                    update_dict(setdefaultresults, setdefaultresult)
    sortedresults = collections.OrderedDict(sorted(setdefaultresults.items()))
    for key,val in sortedresults.items():
        msg = ("{0} = {1}".format(key,val))
        sendlog(gotthefunc, msg, 10)     
        dbsuccess = 0
        while dbsuccess == 0:
            try:
                conn = sqlite3.connect(sysinfo['SYSTEM']['DATABASEFILE'])
                c = conn.cursor()
                c.execute('SELECT * FROM displays WHERE serial = ?', (val['SERIAL'],))
            except sqlite3.OperationalError:
                pass
                msg = ('{}: Database locked (32). Trying again.',format(gotthefunc))
                sendlog(gotthefunc, msg, 10)
                sleep(0.5)
            except:
                pass
                err = traceback.format_exc()
                msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
                sendlog(gotthefunc, msg, 10)
                sleep(0.5)
            else:
                row = c.fetchone()
                dbsuccess = 1
            finally:
                conn.close()
        if row is None:
            msg = ('{} Display with serial {} does not exist in database. Something is wrong.'.format(key,val['SERIAL']))
            sendlog(gotthefunc, msg, 10)     
        else:
            try:
                if not val['OSD']:
                    val['OSD'] = 0
            except:
                pass
                val['OSD'] = 0
            msg = ('{} Display with serial {} found in database. Updating.'.format(key,val['SERIAL']))
            sendlog(gotthefunc, msg, 10)     
            dbsuccess = 0
            while dbsuccess == 0:
                try:
                    conn = sqlite3.connect(sysinfo['SYSTEM']['DATABASEFILE'])
                    c = conn.cursor()
                    c.execute('UPDATE displays SET osd = ? WHERE serial = ?', (val['OSD'], val['SERIAL']))
                    conn.commit()
                except sqlite3.OperationalError:
                    pass
                    msg = ('{}: Database locked (33). Trying again.',format(gotthefunc))
                    sendlog(gotthefunc, msg, 10)
                    sleep(0.5)
                except:
                    pass
                    err = traceback.format_exc()
                    msg = ('Error in {0} Detail: {1}'.format(gotthefunc,err))
                    sendlog(gotthefunc, msg, 10)
                    sleep(0.5)
                else:
                    dbsuccess = 1
                finally:
                    conn.close()
    msg = ('Took {}'.format(time() - tsmain))
    sendlog(gotthefunc, msg, 10)            
    update_dict(sysinfo, setdefaultresults)

    # If it exists, load old state file in case there was a power issue
    defaultsystem = 0
    laststate = {}
    sysinfo['SYSTEM']['LASTUPDATED'] = 0
    sysinfo['SYSTEM']['COMMAND'] = None
    sysinfo['SYSTEM']['COMMANDQUEUE'] = []
    sysinfo['SYSTEM']['TILE'] = 0
    sysinfo['SYSTEM']['BASEINPUT'] = None
    sysinfo['SYSTEM']['CHAIN']['ON'] = 0
    
        # Set LASTINPUT to default input from config if LASTINPUT has never been set

    # Begin cloud server query for data
    test = 0
    mainworker = Process(target=api, args=(sysinfo,))
    mainworkers.append(mainworker)
    mainworker.start()
    mainworker = Process(target=hardware, args=(sysinfo,test))
    mainworkers.append(mainworker)
    mainworker.start()
    mainworker = Process(target=jobs, args=(sysinfo,test))
    mainworkers.append(mainworker)
    mainworker.start()
    for w in mainworkers:
        w.join()
        
    msg = ('Exited {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)
#    logworker.join()


if __name__ == '__main__':
    freeze_support()
    if osplatform == 'windows':
        application_path = os.getcwd()
        lockfile = ('{}\\db\\lavacontrols.run'.format(application_path))
        try:
            lock = zc.lockfile.LockFile(lockfile, content_template='{pid};{hostname}')
        except:
            sys.exit()
    main()