#!/usr/bin/python3
# LAVA Controls - Copyright 2018 Lucid Networks - Stephen Loeckle

import socket
import serial
import binascii
import logging
import socketserver
import struct
import os
import sys
import time
import json
import inspect
from time import time, sleep
import datetime
import collections
import configparser
import argparse
import pickle
import urllib.request
import traceback
import netifaces
import subprocess
import sqlite3
from multiprocessing import Process, Queue
from lavacontrolslib import buildtm, CoreDisplaySettings, DisplayInfo, fetchcmdDE, fetchcmdLAVA, getmymac, getpowermode, Input, jobs, ping, sendlog, saveconfig, savestate, sendemail, sethdmimodes, setmatrixroute, setpower, sockclose, Timeout, Tiling, update_dict, UpdateDatabase, Volume

PORT = 7142
database = ('/var/lib/lavacontrols/lavacontrols.db')

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

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

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

class LogRecordSocketReceiver(socketserver.ThreadingTCPServer):
    allow_reuse_address = True

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

    def serve_until_stopped(self):
        import select
        abort = 0
        while not abort:
            rd, wr, ex = select.select([self.socket.fileno()],
                                       [], [],
                                       self.timeout)
            if rd:
                self.handle_request()
            abort = self.abort
        
def startupdisplaystatus(connection, ip=None, port=None, socketid=0, q=None, serial=None):
    gotthefunc = inspect.stack()[0][3]
    msg = ('Entered {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)
    ts = time()
    s = {}
    if connection == 'ip':
        s[socketid] = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s[socketid].settimeout(10)
        s[socketid].connect((ip, port))
        cmd = '01 30 2A 30 43 30 36 02 30 32 33 45 03 1A 0D'
        cmd = bytes.fromhex(cmd)
        s[socketid].send(cmd)
        response = s[socketid].recv(27)
    elif connection == 'serial':
        s[socketid] = serial
        s[socketid].write(b"\x01\x30\x2A\x30\x43\x30\x36\x02\x30\x32\x33\x45\x03\x1A\x0D")
        s[socketid].flushInput()
        response = s[socketid].read(27)
    hexresponse = binascii.b2a_hex(response)
    if hexresponse == b'01303041443132023030303233453030303036343030303103700d':
        displayid = 1
    if hexresponse == b'01303042443132023030303233453030303036343030303203700d':
        displayid = 2
    if hexresponse == b'01303043443132023030303233453030303036343030303303700d':
        displayid = 3
    if hexresponse == b'01303044443132023030303233453030303036343030303403700d':
        displayid = 4
    if hexresponse == b'01303045443132023030303233453030303036343030303503700d':
        displayid = 5
    if hexresponse == b'01303046443132023030303233453030303036343030303603700d':
        displayid = 6
    if hexresponse == b'01303047443132023030303233453030303036343030303703700d':
        displayid = 7
    if hexresponse == b'01303048443132023030303233453030303036343030303803700d':
        displayid = 8
    if hexresponse == b'01303049443132023030303233453030303036343030303903700d':
        displayid = 9
    if hexresponse == b'0130304a4431320230303032334530303030363430303041030b0d':
        displayid = 10
    if hexresponse == b'0130304b443132023030303233453030303036343030304203090d':
        displayid = 11
    if hexresponse == b'0130304c4431320230303032334530303030363430303043030f0d':
        displayid = 12

    output = {}
    output['D{0}'.format(displayid)] = {}
    output['D{0}'.format(displayid)]['IP'] = ip
    output['D{0}'.format(displayid)]['ID'] = displayid
    dispconn = DisplayInfo(displayid,s[socketid])
    output['D{0}'.format(displayid)]['MODEL'] = dispconn.getmodel()
    output['D{0}'.format(displayid)]['SERIAL'] = dispconn.getserial()
    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'],output['D{0}'.format(displayid)]['TEMP4'] = dispconn.gettemperature()
    inputconn = Input(displayid,s[socketid])
    output['D{0}'.format(displayid)]['INPUT'] = inputconn.getinput()
    tmconn = Tiling(displayid,s[socketid])
    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()
    output['D{0}'.format(displayid)]['PWR'] = getpowermode(displayid,s[socketid])
    volconn = Volume(displayid,s[socketid])
    output['D{0}'.format(displayid)]['VOL'] = volconn.getvolume()
    sockclose(s[socketid],gotthefunc)
    output['D{0}'.format(displayid)]['ST'] = 'Last process time: {0}'.format(time() - ts)
    q.put(output)
    msg = ('Exited {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)

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

def buildtmworker(sysinfo, port, tileid, displayid, hnum, vnum, pos, socketid, q):
    gotthefunc = inspect.stack()[0][3]
    msg = ('Entered {} function with options {} {} {} {}'.format(gotthefunc,displayid, hnum, vnum, pos))
    sendlog(gotthefunc, msg, 10)
    conn = {}
    c = {}
    conn[socketid] = sqlite3.connect(database)
    c[socketid] = conn[socketid].cursor()
    ts = time()
    output = {} 
    output['D{0}'.format(displayid)] = {}
    target = sysinfo['SYSTEM']['TARGET']
    s = {}
    s[socketid] = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s[socketid].settimeout(10)
    s[socketid].connect((sysinfo['D{}'.format(displayid)]['IP'], port))
    returninput = Input(displayid,s[socketid])
#    returnbcmode, returnhnum, returnvnum, returnpos = buildtm(target,displayid,hnum,vnum,pos,sysinfo['WALL']['NUMBERING'],s[socketid])
    if displayid != sysinfo['SYSTEM']['TARGET']:
        if sysinfo['SYSTEM']['DPCHAIN'] == 1:
            if sysinfo['D{0}'.format(displayid)]['TMMODE'] == 0:
               output['D{0}'.format(displayid)]['LASTINPUT'] = returninput.getinput()
            output['D{0}'.format(displayid)]['INPUT'] = returninput.setinput('dp')
            msg = output['D{0}'.format(displayid)]['INPUT']
            sendlog(gotthefunc, msg, 10)
        else:
            msg = 'D{0}: DP Chain Disabled'.format(displayid)
            sendlog(gotthefunc, msg, 10)
    else:
        msg = ('displayid from builttmworker = {}'.format(displayid))
        sendlog(gotthefunc, msg, 10)
        
# MOVED setmode above other tiling functions to diagnose some tiling issues

    sleep(.75)
    tmconn = Tiling(displayid,s[socketid])
    returntmmode = tmconn.setmode(2)
    sleep(.75)
    returnbcmode, returnhnum, returnvnum, returnpos = buildtm(target,displayid,hnum,vnum,pos,sysinfo['WALL']['NUMBERING'],sysinfo['WALL']['LAYOUT'],s[socketid])

#     returntmmode = settm(displayid, 1, s[socketid])
    sleep(.75)
    output['D{0}'.format(displayid)]['SAVED']=0
    returnsaved = saveconfig(displayid, s[socketid])
    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
    sockclose(s[socketid],gotthefunc)
    output['D{0}'.format(displayid)]['TMTIME'] = 'Last process time: {0}'.format(time() - ts)
    q.put(output)
    c[socketid].execute('INSERT into tiles (tileid, member, size, pos) values (?, ?, ?, ?)', (tileid, displayid, str(hnum)+'x'+str(vnum), pos))
    conn[socketid].commit()
    conn[socketid].close()
    msg = ('Exited {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)

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
    target = sysinfo['SYSTEM']['TARGET']
    position = 1
    buildtmq = Queue()
    buildtmqresults = {}
    buildtmprocs = []
    if displaylist:
        for displayid in displaylist:
            buildtmproc = Process(target=buildtmworker, args=(sysinfo, PORT, tileid, displayid, h, w, position, socketnum, buildtmq))
            buildtmprocs.append(buildtmproc)
            socketnum += 1
            position += 1
    else:
        displayid = sysinfo['SYSTEM']['TARGET']
        end = displayid + (h*w)
        while (displayid < end):
            buildtmproc = Process(target=buildtmworker, args=(sysinfo, PORT, tileid, displayid, h, w, position, socketnum, buildtmq))
            buildtmprocs.append(buildtmproc)
            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()
    msg = ('Took {}'.format(time() - tsmain))
    sendlog(gotthefunc, msg, 10)
    msg = buildtmqresults
    sendlog(gotthefunc, msg, 10)
    msg = ('Exited {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)
    print('*********************************************************************************************')
    print(buildtmqresults)
    print('*********************************************************************************************')
    return(buildtmqresults)

def changeinput(sysinfo, newinput, displayid=999):
    gotthefunc = inspect.stack()[0][3]
    ip = sysinfo['D{0}'.format(sysinfo['SYSTEM']['TARGET'])]['IP']
    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:
        ip = sysinfo['D{0}'.format(displayid)]['IP']
        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']
    s = {}
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.settimeout(10)
    s.connect((ip, PORT))
    returninputstatus = Input(displayid,s)
    output['D{0}'.format(displayid)]['INPUT']=returninputstatus.setinput(newinput)
##  Need to store hdmi mode (or any other input) states
    if input == 'hdmi':
        returninputsettings = sethdmimodes(displayid, s)
    output['D{0}'.format(displayid)]['SAVED']=0
    returnsaved = saveconfig(displayid, s)
    output['D{0}'.format(displayid)]['SAVED']=returnsaved
    
    sockclose(s,gotthefunc)
    output['D{0}'.format(displayid)]['CHANGEINPUTTIME'] = 'Last process time: {0}'.format(time() - ts)
    msg = ('Exited {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)
    UpdateDatabase(output)
    return(output)
    
def changeinputprocess(sysinfo, ip, port, displayid, newinput, socketid, q):
    gotthefunc = inspect.stack()[0][3]
    msg = ('Entered {} function with options {} {}'.format(gotthefunc, displayid, newinput))
    sendlog(gotthefunc, msg, 10)
    ts = time()
    output = {}
    output['D{0}'.format(displayid)] = {}
    s = {}
    s[socketid] = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s[socketid].settimeout(10)
    s[socketid].connect((ip, port))
    output['D{0}'.format(displayid)]['SERIAL'] = sysinfo['D{0}'.format(displayid)]['SERIAL']
    lastinput = Input(displayid,s[socketid])
    output['D{0}'.format(displayid)]['LASTINPUT'] = lastinput.getinput()
    returninputstatus = Input(displayid,s[socketid])
    output['D{0}'.format(displayid)]['INPUT']=returninputstatus.setinput(newinput)
    if input == 'hdmi':
        returninputsettings = sethdmimodes(displayid, s[socketid])
    output['D{0}'.format(displayid)]['SAVED']=0
    returnsaved = saveconfig(displayid, s[socketid])
    output['D{0}'.format(displayid)]['SAVED']=returnsaved
    
    sockclose(s[socketid],gotthefunc)
    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'):
            """
                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']['DPCHAIN'] == 1:
#                   if sysinfo['SYSTEM']['TARGET'] == 1:
#                       dplist = [2,3,4]
#                   if sysinfo['SYSTEM']['TARGET'] == 7:
#                       dplist = [8,9,10]
#                   if any(x in key for x in dplist):
#                       dpchain = 1
#                   else:
#                       dpchain = 0
#            if sysinfo['SYSTEM']['TILE'] == 9:
#                msg = 'Currently in 3x3 tile. Checking for DP chain status'
#                sendlog(gotthefunc, msg, 10)
#                if sysinfo['SYSTEM']['DPCHAIN'] == 1:
#                    dplist = [2,3,4,5,6,7,8,9]
#                    if any(x in key for x in dplist):
#                        dpchain = 1
#                    else:
#                        dpchain = 0
            dpchain = 0
            if dpchain == 0:
                changeinputallworker = Process(target=changeinputprocess, args=(sysinfo, sysinfo['{0}'.format(key)]['IP'], PORT, 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'], PORT, 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(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']['DPCHAIN'] == 1:
            if sysinfo['SYSTEM']['TARGET'] == 1:
                dplist = [2,3,4]
            if sysinfo['SYSTEM']['TARGET'] == 7:
                dplist = [8,9,10]
    if sysinfo['SYSTEM']['TILE'] == 9:
        msg = 'Currently in 3x3 tile. Checking for DP chain status'
        sendlog(gotthefunc, msg, 10)
        if sysinfo['SYSTEM']['DPCHAIN'] == 1:
            dplist = [2,3,4,5,6,7,8,9]
    while (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'], PORT, 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'], PORT, 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
    powerqueue = Queue()
    powerworkers = []
    for key, value in sysinfo.items():
        if key.startswith('D'):
            powerworker = Process(target=changepowerworker, args=(sysinfo['{0}'.format(key)]['IP'], PORT, sysinfo['{0}'.format(key)]['ID'], power, socketnum, powerqueue))
            powerworkers.append(powerworker)
            powerworker.start()
            socketnum += 1
    powerresults = {}
    for i in range(socketnum):
        update_dict(powerresults, powerqueue.get())
    msg = ('Took {}'.format(time() - tsmain))
    sendlog(gotthefunc, msg, 10)
    # Merge power results into master sysinfo dictionary
    update_dict(sysinfo, powerresults)
    msg = ('Exited {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)

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

def changevolume(sysinfo,resetvolume=0):
    gotthefunc = inspect.stack()[0][3]
    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)] = {}
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.settimeout(10)
    s.connect((ip, PORT))
    try:
        with Timeout(3):
            returnvolumestatus = Volume(displayid, s)
            output['D{0}'.format(sysinfo['SYSTEM']['TARGET'])]['VOL'] = returnvolumestatus.setvolume(int(vol[1]))
    except Timeout.Timeout as e:
        pass
        msg = ('Change Volume Timeout')
        sendlog(gotthefunc, msg, 40)
        output['D{0}'.format(sysinfo['SYSTEM']['TARGET'])]['VOL'] = sysinfo['D{0}'.format(sysinfo['SYSTEM']['TARGET'])]['VOL']
    except:
        pass
        msg = ('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']
    returnsaved = saveconfig(displayid, s)
    sockclose(s,gotthefunc)
    msg = ('Exited {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)
    return(output)
 
def disabletmworker(sysinfo, port, tileid, displayid, save, socketid, q):
    gotthefunc = inspect.stack()[0][3]
    msg = ('Entered {} function with options {} {}'.format(gotthefunc, displayid, sysinfo['D{0}'.format(displayid)]['LASTINPUT']))
    sendlog(gotthefunc, msg, 10)
    conn = {}
    c = {}
    conn[socketid] = sqlite3.connect(database)
    c[socketid] = conn[socketid].cursor()
    ts = time()
    output = {}
    output['D{0}'.format(displayid)] = {}
    target = sysinfo['SYSTEM']['TARGET']
    s = {}
    s[socketid] = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s[socketid].settimeout(10)
    s[socketid].connect((sysinfo['D{}'.format(displayid)]['IP'], port))
    if displayid != sysinfo['SYSTEM']['TARGET']:
        returninput = Input(displayid,s[socketid])
        output['D{0}'.format(displayid)]['INPUT'] = returninput.setinput(sysinfo['D{0}'.format(displayid)]['LASTINPUT'])
        msg = output['D{0}'.format(displayid)]['INPUT']
        sendlog(gotthefunc, msg, 10)
    sleep(.75)
    returnbcmode, returnhnum, returnvnum, returnpos = buildtm(target,displayid,1,1,1,sysinfo['WALL']['NUMBERING'],sysinfo['WALL']['LAYOUT'],s[socketid])
    sleep(.75)
    tmconn = Tiling(displayid,s[socketid])
    returntmmode = tmconn.setmode(1)
#    returntmmode = settm(displayid, 0, s[socketid])
    output['D{0}'.format(displayid)]['SAVED'] = 0
    if save == 1:
        sleep(.75)
        returnsaved = saveconfig(displayid, s[socketid])
        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)]['TMMODE'] = returntmmode
    sockclose(s[socketid],gotthefunc)
    output['D{0}'.format(displayid)]['DISABLETMTIME'] = 'Last process time: {0}'.format(time() - ts)
    q.put(output)
    c[socketid].execute('DELETE from tiles WHERE ( tileid=? and member=? )', (tileid, displayid))
    conn[socketid].commit()
    conn[socketid].close()
    msg = ('Exited {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)

def disabletmfactory(sysinfo,tileid,save=1):
    gotthefunc = inspect.stack()[0][3]
    msg = ('Entered {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)
    displaylist = []
    conn = sqlite3.connect(database)
    conn.row_factory = lambda cursor, row: row[0]
    c = conn.cursor()
    c.execute('select member from tiles where tileid=?', (tileid, ))
    rows = c.fetchall()
    print(rows)
    for row in rows:
        displaylist.append(row)
    tsmain = time()
    socketnum = 0
    disabletmq = Queue()
    disabletmqresults = {}
    disabletmprocs = []
    for displayid in displaylist:
        disabletmproc = Process(target=disabletmworker, args=(sysinfo, PORT, tileid, displayid, save, socketnum, disabletmq))
        disabletmprocs.append(disabletmproc)
        socketnum += 1
    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)
    conn.close()
    msg = ('Exited {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)
    print('*********************************************************************************************')
    print(disabletmqresults)
    print('*********************************************************************************************')
    return(disabletmqresults)
    
def logserver():
    gotthefunc = inspect.stack()[0][3]
    msg = ('Entered {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)
#    logging.basicConfig(format='%(asctime)s %(processName)-10s %(name)s %(levelname)-8s %(message)s',handlers=[logging.handlers.RotatingFileHandler('/var/log/lavacontrols/lavacontrols.log', mode='a', maxBytes=10*1024*1024, backupCount=0, encoding=None, delay=False)])
    logging.basicConfig(format='%(asctime)s %(processName)-10s %(name)s %(levelname)-8s %(message)s',handlers=[logging.handlers.RotatingFileHandler('/var/log/lavacontrols/lavacontrols.log', mode='a', maxBytes=10000*1024, backupCount=30, encoding=None, delay=False)])
#    logging.basicConfig(format='%(asctime)s %(processName)-10s %(name)s %(levelname)-8s %(message)s',filename='/var/log/lavacontrols/lavacontrols.log')
    tcpserver = LogRecordSocketReceiver()
    tcpserver.serve_until_stopped()
    msg = ('Exited {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)
    
def hardware(sysinfo,test):
    gotthefunc = inspect.stack()[0][3]
    msg = ('Entered {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)
    csloop = 0
    while True:
        conn = sqlite3.connect(database)
        c = conn.cursor()
        displaylist = None
        try:
            with Timeout(3):
                olddict = sysinfo.copy()
        except Timeout.Timeout as e:
            pass
            msg = ('CSLoop Start Class Timeout')
            sendlog(gotthefunc, msg, 40)
        except:
            pass
            msg = ('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:
            with Timeout(3):
                if sysinfo['SYSTEM']['PLATFORM'].lower() == 'lava':
                    cmd = fetchcmdLAVA(sysinfo)
                elif sysinfo['SYSTEM']['PLATFORM'].lower() == 'de':
                    cmd = fetchcmdDE(sysinfo)
        except Timeout.Timeout as e:
            pass
            msg = ('CSLoop cmd = fetchcmd Class Timeout')
            sendlog(gotthefunc, msg, 40)
        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:
            with Timeout(3):
                update_dict(sysinfo, cmd)
        except Timeout.Timeout as e:
            pass
            msg = ('CSLoop update_dict Class Timeout')
            sendlog(gotthefunc, msg, 40)
        except:
            pass
            msg = ('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)
        cmdresponse = {}
        if sysinfo['SYSTEM']['COMMAND'] != 'null' or sysinfo['SYSTEM']['COMMAND'] is not None:
            if sysinfo['SYSTEM']['COMMAND'] == 'input_dvi':
                sysinfo['SYSTEM']['DPCHAINPREV'] = sysinfo['SYSTEM']['DPCHAIN']
                sysinfo['SYSTEM']['DPCHAIN'] = 1
                if sysinfo['SYSTEM']['DPCHAINPREV'] == 0:
                    try:
                        with Timeout(10):
                            cmdresponse = changeinputall(sysinfo, 'dvi')
                    except Timeout.Timeout as e:
                        pass
                        msg = ('CSLoop changeinputall dvi Class Timeout')
                        sendlog(gotthefunc, msg, 40)
                    except:
                        pass
                        msg = ('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, 'dvi')
                    except Timeout.Timeout as e:
                        pass
                        msg = ('CSLoop changeinput dvi Class Timeout')
                        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'] = 'dvi'
            if sysinfo['SYSTEM']['COMMAND'] == 'input_dvi_4':
                sysinfo['SYSTEM']['DPCHAINPREV'] = sysinfo['SYSTEM']['DPCHAIN']
                sysinfo['SYSTEM']['DPCHAIN'] = 1
                try:
                    with Timeout(10):
                        cmdresponse = changeinputrange(sysinfo, 'dvi', 4)
                except Timeout.Timeout as e:
                    pass
                    msg = ('CSLoop changeinputrange dvi Class Timeout')
                    sendlog(gotthefunc, msg, 40)
                except:
                    pass
                    msg = ('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']['DPCHAINPREV'] = sysinfo['SYSTEM']['DPCHAIN']
                sysinfo['SYSTEM']['DPCHAIN'] = 1
                try:
                    with Timeout(10):
                        cmdresponse = changeinputall(sysinfo, 'dvi')
                except Timeout.Timeout as e:
                    pass
                    msg = ('CSLoop changeinputall dvi 2 Class Timeout')
                    sendlog(gotthefunc, msg, 40)
                except:
                    pass
                    msg = ('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']['DPCHAINPREV'] = sysinfo['SYSTEM']['DPCHAIN']
                sysinfo['SYSTEM']['DPCHAIN'] = 1
                if sysinfo['SYSTEM']['DPCHAINPREV'] == 0:
                    try:
                        with Timeout(10):
                            cmdresponse = changeinputall(sysinfo, 'dp')
                    except Timeout.Timeout as e:
                        pass
                        msg = ('CSLoop changeinputall dp Class Timeout')
                        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, 'dp')
                    except Timeout.Timeout as e:
                        pass
                        msg = ('CSLoop changeinput dp Class Timeout')
                        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'] = 'dp'
            if sysinfo['SYSTEM']['COMMAND'] == 'input_hdmi':
## Temporary until I can get this to only affect drequested screen numbers
                sysinfo['SYSTEM']['DPCHAINPREV'] = sysinfo['SYSTEM']['DPCHAIN']
                sysinfo['SYSTEM']['DPCHAIN'] = 1
                try:
                    with Timeout(10):
                        cmdresponse = changeinput(sysinfo, 'hdmi')
                except Timeout.Timeout as e:
                    pass
                    msg = ('CSLoop changeinput hdmi Class Timeout')
                    sendlog(gotthefunc, msg, 40)
                except:
                    pass
                    msg = ('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_hdmi_all':
                sysinfo['SYSTEM']['DPCHAINPREV'] = sysinfo['SYSTEM']['DPCHAIN']
                sysinfo['SYSTEM']['DPCHAIN'] = 1
                try:
                    with Timeout(10):
                        cmdresponse = changeinputall(sysinfo, 'hdmi')
                except Timeout.Timeout as e:
                    pass
                    msg = ('CSLoop changeinputall hdmi Class Timeout')
                    sendlog(gotthefunc, msg, 40)
                except:
                    pass
                    msg = ('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']['DPCHAINPREV'] = sysinfo['SYSTEM']['DPCHAIN']
                sysinfo['SYSTEM']['DPCHAIN'] = 0
                try:
                    with Timeout(10):
                        cmdresponse = changeinputrange(sysinfo, 'hdmi', 4)
                except Timeout.Timeout as e:
                    pass
                    msg = ('CSLoop changeinputrange hdmi Class Timeout')
                    sendlog(gotthefunc, msg, 40)
                except:
                    pass
                    msg = ('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']['DPCHAINPREV'] = sysinfo['SYSTEM']['DPCHAIN']
                sysinfo['SYSTEM']['DPCHAIN'] = 0
                try:
                    with Timeout(10):
                        cmdresponse = changeinput(sysinfo, 'hdmi')
                except Timeout.Timeout as e:
                    pass
                    msg = ('CSLoop changeinputall 2 hdmi Class Timeout')
                    sendlog(gotthefunc, msg, 40)
                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']['DPCHAINPREV'] = sysinfo['SYSTEM']['DPCHAIN']
                sysinfo['SYSTEM']['DPCHAIN'] = 1
                ##### routelist can be [[1,'a'],[1,'b']] for multiple route changes
                routelist = [[1,'a']]
                try:
                    with Timeout(10):
                        cmdresponse = setmatrixroute(sysinfo['SYSTEM']['MATRIXIP'],sysinfo['SYSTEM']['MYIP'],routelist)
                except Timeout.Timeout as e:
                    pass
                    msg = ('CSLoop setmatrixroute 1a hdmi Class Timeout')
                    sendlog(gotthefunc, msg, 40)
                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']['DPCHAINPREV'] = sysinfo['SYSTEM']['DPCHAIN']
                sysinfo['SYSTEM']['DPCHAIN'] = 1
                routelist = [[7,'a']]
                try:
                    with Timeout(10):
                        cmdresponse = setmatrixroute(sysinfo['SYSTEM']['MATRIXIP'],sysinfo['SYSTEM']['MYIP'],routelist)
                except Timeout.Timeout as e:
                    pass
                    msg = ('CSLoop setmatrixroute 7a hdmi Class Timeout')
                    sendlog(gotthefunc, msg, 40)
                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']['DPCHAINPREV'] = sysinfo['SYSTEM']['DPCHAIN']
                sysinfo['SYSTEM']['DPCHAIN'] = 0
                sysinfo['SYSTEM']['USEMATRIX'] = 1
                try:
                    with Timeout(10):
                        cmdresponse = changeinput(sysinfo, 'hdmi')
                except Timeout.Timeout as e:
                    pass
                    msg = ('CSLoop changeinputall 2 hdmi Class Timeout')
                    sendlog(gotthefunc, msg, 40)
                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']['DPCHAINPREV'] = sysinfo['SYSTEM']['DPCHAIN']
                sysinfo['SYSTEM']['DPCHAIN'] = 1
                try:
                    with Timeout(10):
                        cmdresponse = changeinput(sysinfo, 'option')
                except Timeout.Timeout as e:
                    pass
                    msg = ('CSLoop changeinput optioni Class Timeout')
                    sendlog(gotthefunc, msg, 40)
                except:
                    pass
                    msg = ('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':
                c.execute('SELECT value FROM counters WHERE tags = ?', ('lasttileid',))
                row = c.fetchone()
                lasttileid = row[0]
                tileid = lasttileid + 1
                c.execute('UPDATE counters SET value = ? WHERE tags = ?', (tileid,'lasttileid'))
                conn.commit()
                try:
                    with Timeout(130):
                        cmdresponse = buildtmfactory(sysinfo,4,2,tileid)
                except Timeout.Timeout as e:
                    pass
                    msg = ('CSLoop buildtmfactory 4x2 Class Timeout')
                    sendlog(gotthefunc, msg, 40)
                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':
                c.execute('SELECT value FROM counters WHERE tags = ?', ('lasttileid',))
                row = c.fetchone()
                lasttileid = row[0]
                tileid = lasttileid + 1
                c.execute('UPDATE counters SET value = ? WHERE tags = ?', (tileid,'lasttileid'))
                conn.commit()
                try:
                    with Timeout(130):
                        cmdresponse = buildtmfactory(sysinfo,3,3,tileid)
                except Timeout.Timeout as e:
                    pass
                    msg = ('CSLoop buildtmfactory 3x3 Class Timeout')
                    sendlog(gotthefunc, msg, 40)
                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_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['SYSTEM']['USEMATRIX'] == 1:
                        for displayid in displaylist:
                            outputletter = chr(96 + displayid)
                            sysinfo['SYSTEM']['MATRIXCOMMANDS'].append([d,'{}'.format(outputletter)])
                        sysinfo['SYSTEM']['DPCHAIN'] = 0
                    else:
                        sysinfo['SYSTEM']['DPCHAIN'] = 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']['DPCHAIN'] = 0
                    else:
                        sysinfo['SYSTEM']['DPCHAIN'] = 1
                c.execute('SELECT value FROM counters WHERE tags = ?', ('lasttileid',))
                row = c.fetchone()
                lasttileid = row[0]
                tileid = lasttileid + 1
                c.execute('UPDATE counters SET value = ? WHERE tags = ?', (tileid,'lasttileid'))
                conn.commit()
                '''
                    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:
                    c.execute('SELECT tileid from tiles WHERE member=?', (x,))
                    row = c.fetchone()
                    if row:
                        cmdresponse = disabletmfactory(sysinfo,row[0],0)
                try:
                    with Timeout(30):
                        if displaylist:
                            cmdresponse = buildtmfactory(sysinfo,2,2,tileid,displaylist)
                        else:
                            cmdresponse = buildtmfactory(sysinfo,2,2,tileid)
                except Timeout.Timeout as e:
                    pass
                    msg = ('CSLoop buildtmfactory 2x2 Class Timeout')
                    sendlog(gotthefunc, msg, 40)
                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':
                c.execute('SELECT tileid from tiles WHERE member = ?', (sysinfo['SYSTEM']['TARGET'], ))
                row = c.fetchone()
                if row:
                    try:
                        with Timeout(30):
                            cmdresponse = disabletmfactory(sysinfo,row[0])
                    except Timeout.Timeout as e:
                        pass
                        msg = ('CSLoop disabletmfactory from hardware Class Timeout')
                        sendlog(gotthefunc, msg, 40)
                    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:
                    with Timeout(10):
                        cmdresponse = changevolume(sysinfo)
                except Timeout.Timeout as e:
                    pass
                    msg = ('CSLoop changevolume Class Timeout')
                    sendlog(gotthefunc, msg, 40)
                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']['COMMAND'] == 'display_all_power_off':
                try:
                    with Timeout(10):
                        changepower(sysinfo, 0)
                except Timeout.Timeout as e:
                    pass
                    msg = ('CSLoop changepower off Class Timeout')
                    sendlog(gotthefunc, msg, 40)
                except:
                    pass
                    msg = ('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:
                    with Timeout(10):
                        changepower(sysinfo, 1)
                except Timeout.Timeout as e:
                    pass
                    msg = ('CSLoop changepower on Class Timeout')
                    sendlog(gotthefunc, msg, 40)
                except:
                    pass
                    msg = ('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
            
        sleep(1)
        if csloop >= 60:
            savestate(sysinfo)
            csloop = 0
        else:
            csloop += 1
        conn.commit()
        conn.close()
            
def iswallalive(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=(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))
        print(msg)
        sendlog(gotthefunc, msg, 10)
        if val['UP'] == False:
            subject = ('DISPLAY {} DOWN!'.format(key))
            msg = ('DISPLAY {} DOWN!'.format(key))
            emailaddresses = ['sloeckle@lucidnetworks.net']
            smtphost = 'localhost'
            sendemail(emailaddresses,smtphost,subject,msg)
            sendlog(gotthefunc, msg, 50)
            sleep(5)
            walldown = 1
    msg = ('Exited {} function'.format(gotthefunc))
    sendlog(gotthefunc, msg, 10)
    return(walldown)

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

    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 = []
    
    mainworker = Process(target=logserver)
    mainworkers.append(mainworker)
    mainworker.start()

#    logworker = Process(target=logserver)
#    logworker.start()
    
    msg = ('Logworker started. Waiting 10 seconds for network to come alive.')
    sleep(10)

    conn = sqlite3.connect(database)
    c = conn.cursor()

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

    # Read config
    config = configparser.ConfigParser()
    try:
        config.read(['/etc/lavacontrols/lavacontrols.conf'])
    except configparser.Error as e:
        msg = ('Unable to read configuration file /etc/lavacontrols/lavacontrols.conf! Halting!')
        sendlog(gotthefunc, msg, 40)
        sys.exit()
    except:
        msg = ('Error in {0} Reading Config! Halting!'.format(gotthefunc))
        sendlog(gotthefunc, msg, 40)
        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, 40)
        sys.exit()
    except configparser.Error as e:
        msg = ('Config read entry system platform type error! Halting!')
        sendlog(gotthefunc, msg, 40)
        sys.exit()
    except:
        msg = ('Error in {0} Reading Config! Halting!'.format(gotthefunc))
        sendlog(gotthefunc, msg, 40)
        err = traceback.format_exc()
        msg = ('Error Detail: {0}'.format(err))
        sendlog(gotthefunc, msg, 10)
        sys.exit()
    else:
        msg = ('CONFIG: Loaded platform type')
        sendlog(gotthefunc, msg, 10)
    if sysinfo['SYSTEM']['PLATFORM'].lower() == 'lava':
        # Load token key
        try:
            sysinfo['SYSTEM']['TOKEN'] = config.get('system', 'token')
        except (configparser.NoOptionError, configparser.InterpolationMissingOptionError):
            msg = ('No token in configuration file! Halting!')
            sendlog(gotthefunc, msg, 40)
            sys.exit()
        except configparser.Error as e:
            msg = ('Config read entry system token error! Halting!')
            sendlog(gotthefunc, msg, 40)
            sys.exit()
        except:
            msg = ('Error in {0} Reading Config! Halting!'.format(gotthefunc))
            sendlog(gotthefunc, msg, 40)
            err = traceback.format_exc()
            msg = ('Error Detail: {0}'.format(err))
            sendlog(gotthefunc, msg, 10)
            sys.exit()
        else:
            msg = ('CONFIG: Loaded authorization token')
            sendlog(gotthefunc, msg, 10)
    elif 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, 40)
            sys.exit()
        except configparser.Error as e:
            msg = ('Config read entry system license key error! Halting!')
            sendlog(gotthefunc, msg, 40)
            sys.exit()
        except:
            msg = ('Error in {0} Reading Config! Halting!'.format(gotthefunc))
            sendlog(gotthefunc, msg, 40)
            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:
        msg = ('Unknown platform type! Halting!')
        sendlog(gotthefunc, msg, 40)
        sys.exit()
    # Get machine mac address
    try:
        sysinfo['SYSTEM']['MAC'] = getmymac("eth0")
    except:
        msg = ('Error in {0} Getting MAC Address! Halting!'.format(gotthefunc))
        sendlog(gotthefunc, msg, 40)
        err = traceback.format_exc()
        msg = ('Error Detail: {0}'.format(err))
        sendlog(gotthefunc, msg, 10)
        sys.exit()
    else:
        msg = ('CONFIG: Got MAC address')
        sendlog(gotthefunc, msg, 10)
    gotmyip = 0
    while not gotmyip:
        try:
            netifaces.interfaces()
            sysinfo['SYSTEM']['MYIP'] = netifaces.ifaddresses('eth0')[netifaces.AF_INET][0]['addr']
        except KeyError:
            msg = ('Interface eth0 does not have an IP address. Trying again.')
            sendlog(gotthefunc, msg, 10)
            sleep(1)
            pass
        except:
            msg = ('Error in {0} Getting Local IP Address! Halting!'.format(gotthefunc))
            sendlog(gotthefunc, msg, 40)
            err = traceback.format_exc()
            msg = ('Error Detail: {0}'.format(err))
            print(msg)
            sendlog(gotthefunc, msg, 10)
            sys.exit()
        else:
            msg = ('CONFIG: Retrieved my ip: {}'.format(sysinfo['SYSTEM']['MYIP']))
            print(msg)
            sendlog(gotthefunc, msg, 10)
            gotmyip = 1
    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, 40)
        sysinfo['SYSTEM']['LOGLEVEL'] = '20'
        pass
    except configparser.Error as e:
        msg = ('Config read entry system loglevel error! Halting!')
        sendlog(gotthefunc, msg, 40)
        sys.exit()
    except:
        msg = ('Error in {0} Reading Config! Halting!'.format(gotthefunc))
        sendlog(gotthefunc, msg, 40)
        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']['HTTPPROXY'] = config.get('system', 'proxy')
    except (configparser.NoOptionError, configparser.InterpolationMissingOptionError):
        msg = ('No proxy specified in configuration file. Defaulting to none')
        sendlog(gotthefunc, msg, 40)
        pass
    except configparser.Error as e:
        msg = ('Config read entry system proxy error. Moving on.')
        sendlog(gotthefunc, msg, 40)
        pass
    except:
        msg = ('Error in {0} Reading Config! Moving on.'.format(gotthefunc))
        sendlog(gotthefunc, msg, 40)
        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']['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, 40)
        sysinfo['SYSTEM']['CONNECTION'] = 'ip'
        pass
    except configparser.Error as e:
        msg = ('Config read entry system connection error! Halting!')
        sendlog(gotthefunc, msg, 40)
        sys.exit()
    except:
        msg = ('Error in {0} Reading Config! Halting!'.format(gotthefunc))
        sendlog(gotthefunc, msg, 40)
        err = traceback.format_exc()
        msg = ('Error Detail: {0}'.format(err))
        sendlog(gotthefunc, msg, 10)
        sys.exit()
    else:
        msg = ('CONFIG: Got connection type')
        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('system', 'serialport')
        except (configparser.NoOptionError, configparser.InterpolationMissingOptionError):
            msg = ('No serial port specified in configuration file. Defaulting to /dev/ttyUSB0')
            sendlog(gotthefunc, msg, 40)
            sysinfo['SYSTEM']['serialport'] = '/dev/ttyUSB0'
            pass
        except configparser.Error as e:
            msg = ('Config read entry system serial port error! Halting!')
            sendlog(gotthefunc, msg, 40)
            sys.exit()
        except:
            msg = ('Error in {0} Reading Config! Halting!'.format(gotthefunc))
            sendlog(gotthefunc, msg, 40)
            err = traceback.format_exc()
            msg = ('Error Detail: {0}'.format(err))
            sendlog(gotthefunc, msg, 10)
            sys.exit()
    else:
        msg = ('Invalid connection type specified in configuration file. Halting!')
        sendlog(gotthefunc, msg, 40)
        sys.exit()
    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, 40)
        sysinfo['WALL']['NUMBERING'] = 'zig-zag'
        pass
    except configparser.Error as e:
        msg = ('Config read entry wall numbering error! Halting!')
        sendlog(gotthefunc, msg, 40)
        sys.exit()
    except:
        msg = ('Error in {0} Reading Config! Halting!'.format(gotthefunc))
        sendlog(gotthefunc, msg, 40)
        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. Defaulting to 4x2!')
        sendlog(gotthefunc, msg, 40)
        sysinfo['WALL']['LAYOUT'] = '4x2'
        pass
    except configparser.Error as e:
        msg = ('Config read entry wall layout error! Halting!')
        sendlog(gotthefunc, msg, 40)
        sys.exit()
    except:
        msg = ('Error in {0} Reading Config! Halting!'.format(gotthefunc))
        sendlog(gotthefunc, msg, 40)
        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, 40)
        sysinfo['SYSTEM']['DEFAULTINPUT'] = 'dvi'
        pass
    except configparser.Error as e:
        msg = ('Config read entry wall defaultinput error! Halting!')
        sendlog(gotthefunc, msg, 40)
        sys.exit()
    except:
        msg = ('Error in {0} Reading Config! Halting!'.format(gotthefunc))
        sendlog(gotthefunc, msg, 40)
        err = traceback.format_exc()
        msg = ('Error Detail: {0}'.format(err))
        sendlog(gotthefunc, msg, 10)
        sys.exit()
    try:
        sysinfo['SYSTEM']['MATRIXIP'] = config.get('wall', 'matrixip')
    except (configparser.NoOptionError, configparser.InterpolationMissingOptionError):
        msg = ('No matrix switch ip address specified in configuration file. None will be applied to system.')
        sendlog(gotthefunc, msg, 40)
        sysinfo['SYSTEM']['MATRIXIP'] = None
        pass
    except configparser.Error as e:
        msg = ('Config read entry wall matrix ip error! Halting!')
        sendlog(gotthefunc, msg, 40)
        sys.exit()
    except:
        msg = ('Error in {0} Reading Config! Halting!'.format(gotthefunc))
        sendlog(gotthefunc, msg, 40)
        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']))
        print(msg)
        sendlog(gotthefunc, msg, 10)
    finally:
        sysinfo['SYSTEM']['USEMATRIX'] = 0
    logger = logging.getLogger('main')
    if sysinfo['SYSTEM']['LOGLEVEL'] == '10':
        logger.setLevel(logging.DEBUG)
    if sysinfo['SYSTEM']['LOGLEVEL'] == '20':
        logger.setLevel(logging.INFO)
    if sysinfo['SYSTEM']['LOGLEVEL'] == '30':
        logger.setLevel(logging.WARNING)
    if sysinfo['SYSTEM']['LOGLEVEL'] == '40':
        logger.setLevel(logging.ERROR)
    if sysinfo['SYSTEM']['LOGLEVEL'] == '50':
        logger.setLevel(logging.CRITICAL)
    sock = logging.handlers.SocketHandler('localhost', logging.handlers.DEFAULT_TCP_LOGGING_PORT)
    logger.addHandler(sock)
    if cliargs.consolelogs:
        consolelog = logging.StreamHandler(sys.stdout)
        logger.addHandler(consolelog)
    sleep(2)

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

    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']

    # Test network connection
    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, 40)
            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(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']['CONNECTION'] == 'ip':
        try:
            walldown = iswallalive(IP)
        except:
            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)
            print(msg)
            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)
            sys.exit()            

##### Borrowed from declientlib. Added as placeholder. Function copied to lavacontrolslib but needs to be converted to lavacontrols.            
#            tempjobsdata['SYSTEM']['CONFIG']['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['SYSTEM']['CONNECTION'], ipaddr, PORT, 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()))
        for key,val in sortedresults.items():
            msg = ("{0} = {1}".format(key,val))
            sendlog(gotthefunc, msg, 10)
            c.execute('SELECT * FROM displays WHERE serial = ?', (val['SERIAL'],))
            row = c.fetchone()
            if row is None:
                try:
                    c.execute('INSERT into displays (model, serial, ip, f1, f2, f3, f4, id, power_state, input, volume, tile_state, tile_h, tile_v, tile_p) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', (val['MODEL'], val['SERIAL'], val['IP'], val['F1'], val['F2'], val['F3'], val['F4'], val['ID'], val['PWR'], val['INPUT'], val['VOL'], val['TMMODE'], val['TMH'], val['TMV'], val['TMP']))
                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)   
                else:
                    msg = ('Startup display entry insert for {} successful.'.format(key))
                    sendlog(gotthefunc, msg, 10)
            else:
                try:
                    c.execute('UPDATE displays SET ip = ?, f1 = ?, f2 = ?, f3 = ?, f4 = ?, id = ?, power_state = ?, input = ?, volume = ?, tile_state = ?, tile_h = ?, tile_v = ?, tile_p = ? WHERE serial = ?', (val['IP'], val['F1'], val['F2'], val['F3'], val['F4'], val['ID'], val['PWR'], val['INPUT'], val['VOL'], val['TMMODE'], val['TMH'], val['TMV'], val['TMP'], val['SERIAL']))
                except:
                    msg = ('Startup display entry update for {} failed. Pass.'.format(key))
                    sendlog(gotthefunc, msg, 10)
                    err = traceback.format_exc()
                    msg = ('Error Detail: {0}'.format(err))
                    sendlog(gotthefunc, msg, 10)   
                else:
                    msg = ('Startup display entry update for {} successful.'.format(key))
                    sendlog(gotthefunc, msg, 10)
        conn.commit()
        update_dict(sysinfo, startupresults)
        
    if sysinfo['SYSTEM']['CONNECTION'] == 'serial':

        sendlog(gotthefunc, msg, 10)
        socketnum = 0
        serialport = serial.Serial('/dev/ttyUSB0', baudrate=9600, bytesize=8, parity=serial.PARITY_NONE, stopbits=1, timeout=5)
        for i in range(sysinfo['SYSTEM']['DISPLAYS']):
            startupresults = startupdisplaystatus(connection=sysinfo['SYSTEM']['CONNECTION'], serial=serialport)
            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

    tsmain = time()
    socketnum = 0
    setdefaultqueue = Queue()
    setdefaultworkers = []
    for key, value in sysinfo.items():
        if key.startswith('D'):
            setdefaultworker = Process(target=setdefaults, args=(sysinfo, PORT, sysinfo['{0}'.format(key)]['ID'], socketnum, setdefaultqueue))
            setdefaultworkers.append(setdefaultworker)
            setdefaultworker.start()
            socketnum += 1
    setdefaultresults = {}
    for i in range(socketnum):
        update_dict(setdefaultresults, setdefaultqueue.get())
    for w in setdefaultworkers:
        w.join()
    sortedresults = collections.OrderedDict(sorted(setdefaultresults.items()))
    for key,val in sortedresults.items():
        msg = ("{0} = {1}".format(key,val))
        sendlog(gotthefunc, msg, 10)
        c.execute('SELECT * FROM displays WHERE serial = ?', (val['SERIAL'],))
        row = c.fetchone()
        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:
            msg = ('{} Display with serial {} found in database. Updating.'.format(key,val['SERIAL']))
            sendlog(gotthefunc, msg, 10)
            try:
                c.execute('UPDATE displays SET osd = ? WHERE serial = ?', (val['OSD'], val['SERIAL']))
            except:
                msg = ('Set display defaults entries update for {} failed. Pass.'.format(key))
                sendlog(gotthefunc, msg, 10)
                err = traceback.format_exc()
                msg = ('Error Detail: {0}'.format(err))
                sendlog(gotthefunc, msg, 10)   
            else:
                msg = ('Set display defaults entries update for {} successful.'.format(key))
                sendlog(gotthefunc, msg, 10)
    conn.commit()
    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
    if os.path.exists("/var/lib/lavacontrols/lavacontrols.state"):
        try:
            with open('/var/lib/lavacontrols/lavacontrols.state', 'r') as f:
                laststate = json.load(f)
            f.close()
        except:
            pass
            os.remove('/var/lib/lavacontrols/lavacontrols.state')
            defaultsystem = 1
        else:
            sysinfo['SYSTEM']['LASTUPDATED'] = laststate['SYSTEM']['LASTUPDATED']
            sysinfo['SYSTEM']['COMMAND'] = laststate['SYSTEM']['COMMAND']
            sysinfo['SYSTEM']['TILE'] = laststate['SYSTEM']['TILE']
            sysinfo['SYSTEM']['BASEINPUT'] = laststate['SYSTEM']['BASEINPUT']
            sysinfo['SYSTEM']['DPCHAIN'] = laststate['SYSTEM']['DPCHAIN']
            for key, value in sysinfo.items():
                if key.startswith('D'):
                    sysinfo['{0}'.format(key)]['LASTINPUT'] = laststate['{0}'.format(key)]['LASTINPUT']
    else:
        defaultsystem = 1
    if defaultsystem == 1:
        laststate = {}
        sysinfo['SYSTEM']['LASTUPDATED'] = 0
        sysinfo['SYSTEM']['COMMAND'] = None
        sysinfo['SYSTEM']['TILE'] = 0
        sysinfo['SYSTEM']['BASEINPUT'] = None
        sysinfo['SYSTEM']['DPCHAIN'] = 0
        # Set LASTINPUT to default input from config if LASTINPUT has never been set
        for key, value in sysinfo.items():
            if key.startswith('D'):
                sysinfo['{0}'.format(key)]['LASTINPUT'] = config.get('wall', 'defaultinput')

    print(sysinfo)
    # Begin cloud server query for data
    test = 0
    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__':
   main()
