# pir2A.py A470
global g_pir2AVer
g_pir2AVer="A72"
#By D@CC
#on 2024AJan22

# this is the code module containing most of the
#python3 code for the PiR2 system
#pi
# NB 1. the ioDeviceArray is defined in pir2Data.py
#    2. the first program is pir2Main.py
#
# pg LIST OF MODULES
#
#  1 List of Modules
#  2 list of globals
#  2 identifyTask()
#  3 initializeTask()
#  7 createLogCopy()
#  7 createNewEmptyLogTask()
#  8 queuehelloTask()
#  8 helloTask()
#  8 manageTasks()
#  9 appendToLog()
# 11 acquire_procTemp()
# 12 osCommand()
# 13 getControl_fromOwner()
# 14 control_fromOwner()
# 16 pir2_performAorC()
# 18 control_audioTV()
# 18 control_piArea
# 18 acquire_piSN
# 18 acquire_piIP
# 19 displayHeader()
# 19 displayLog()
# 20 unixTime()
# 21 askVerbose()
# 21 openLog2()
# 21 informUserWait()
# 22 isLogAvailable()
# 25 piR2_findStrInArray()
# 25 piR2_ioDeviceAttribute()
# 25 piR2_attrFromStr()
# 27 sortableDateTime()
# 29 pir2_convertMIC_ControlTask()
# 30 acquire_pushButton()
# 31 acquire_digIn0()
# 31 control_redLED()
# 31 control_digOut0()
# 31 pir2_initGPIO()
# 33 init_I2C_Bus()
# 34 acquire_tempId00()
# 34 control_imageTV()
# 30 pir2versions()

import pir2Data

# NB pir2Data defines contents of (and array sizes)
#   pir2A.g_numioDevices
#   pir2A.g_ioDeviceArray[i]
#   pir2A.g_fileArray[i]

import time
import os
import subprocess
global g_RPiGPIO
g_RPiGPIO="RPi7GPIO"   # change this for gpiozero

#Do NOT change the next statement
if g_RPiGPIO=="RPi.GPIO":           #72
    import RPi.GPIO as GPIO         #72
else:                               #72
    import RPi7GPIO as GPIO         #72    
#if end                             #72

        
def unity(func):                          #72
    #by D@CC on 2024AJan18
    #Purpose: return "constants" such as GPIO.BCM, GPIO.HIGH etc
    #strReturn=func()
    try:
        strReturn=func()
    except:
        strReturn=True
    finally:
        pass
    #try end
    if strReturn=="GPIO.PUD_UP":
        return strReturn
    #if end
    if strReturn=="GPIO.HIGH": strReturn=func(parm=True)
    if strReturn=="GPIO.LOW" : strReturn=func(parm=False)
    return strReturn
#/unity.py
def testGPIO(g_ledR_pin):
    #ledR_pin=27
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(g_ledR_pin,GPIO.OUT)

    GPIO.output(g_ledR_pin,GPIO.HIGH)
    time.sleep(1)
    GPIO.output(g_ledR_pin,GPIO.LOW)
    time.sleep(1)
#end def

import smbus

# NB in Python,
#    A global variable (eg g_AA) needs to be declared as global
#           in every module (ie in every function that references it)
#           in this library.
#    Other modules not in this library (eg PiR2main.py) must
#           reference these global variables with a prefix eg PiR2.g_AA
#           to show where the variable can. These references can be
#           to "read" or "write" the variable.
#   Arrays must have all their elements defined before being referenced.

# list of global variables

#   global g_firstRecId
#   global g_helloTaskNumber
#   global g_isPythonVersion3Str
#   global g_ioDeviceArray
#   global g_isAddingNewLine
#   global g_lastRecId
#   global g_piIPStr
#   global g_piSN_Str
#   globat g_piOS_Str
#   global g_piAreaStr
#   global g_startTime[]
#   global g_startTimeArray[]
#   global g_taskArray[]

#   global g_pir2AVer
#   global g_pir2mainVer
#   global g_pir2DataVer
#   global g_pir2VersionsVer
#   global g_validateLogVer
#   global g_pir1AVer
#   global g_globalTestVer
#   global g_sDateTimeVer


def identifyTask():
    print("***************************")
    print("* PiR2 controller by D@CC *")
    print("*    pir2A.py v"+g_pir2AVer+"     *")
    print("*(c) MIC180RR Software    *")
    print("*  from MehInControl.com  *")
    print("* Version 0.2 2019GJul19  *")
    print("***************************")
    # Purpose
    # This python3 software is the main module of the PiR2 controller.  It
    # normally runs on a Raspberry Pi equipped with a PiR2 controller board.
    # although it will run without the PiR2 controller board.  This
    # function identifies the PiR2 software.  The functions defined and
    # used by the PiR2 software are all defined in the pir2A.py library
    # which must be imported into the piR2main.py module.
    #
    # PiR2 libraries used by the PiR2 controller software:
    #  pir2Data.py
    #
    # Non-PiR2 libraries used by the PiR2 controller software:
    #     (imported in piR2_main.py)
    # from os import system, name
    # from time import sleep
    # from subprocess import call ???
    # import os
    #
    return
#end def identifyTask()

def initializeTask(isVerbose, fileArray):
    # @ purpose To initialize everything necessary
    #
    # @param0   array   fileArray  which is g_fileArray
    # @returns  nothing
    #
    # LEXICAL CONVENTIONS
    # conditional variables begin with is...
    # string variables end with ...Str
    # list variables end with ...Array
    # count variables begin with num...
    # task functions end with ...Task
    # global variables begin with g_.....
    # #end etc follows each if, while or for compound statement
    # doh=0 is a null statement eg after an else: null

    global g_startTimeArray
    global g_helloTaskNumber
    global g_taskArray

    #print('fileArray[0]:',fileArray[0],":")
    #print('fileArray[1]:',fileArray[1],":")
    #exit()

    #define g_ioDeviceArray array as global but no elements
    #  just  out  global g_ioDeviceArray

    print("**************************************************")    
    print("begin PiR2 initialization")
    # The number of tasks defined is 1
    # create the taskQueue
    g_startTime=[0,1,2]
    
    #define each task in the PiR2 system
    #define the first task
    g_helloTaskNumber=0
    g_taskArray=[0,1,2]
    g_taskArray[g_helloTaskNumber]="helloTask"

    #define other globals
    global g_lastRecId
    g_lastRecId=0

    #define fileArray array as global but no elements
    
    # passed in: g_fileArray as fileArray

    # defines global g_fileArray only because it uses it
    
    #g_fileArray=[0,1,2,3,4]
    #g_fileArray[0]="/home/pi/Desktop/PiR2/files/PiR2_Log"
    #g_fileArray[1]="/home/pi/Desktop/PiR2/files/PiR2_Log.txt"
    #g_fileArray[2]="/home/pi/Desktop/PiR2/files/commandResult.txt"
    #g_fileArray[3]="/home/pi/Desktop/PiR2/Audio/"
    #g_fileArray[4]="/home/pi/Downloads/PiR2_Control_"
    #                                +piSN+"_txt.html"

    # defines logical GPIO #s for GPIO pins
    # (not the actual pin numbers themselves)
    global g_btn_pin
    g_btn_pin=22               #was 27 in A46
    global g_ledR_pin
    g_ledR_pin=27              #was 18 in A46
    global g_diC_pin
    g_diC_pin=18               #was 17 in A46
    global g_di0_pin
    g_di0_pin=22               #was 23 in A46
    global g_do0_pin
    g_do0_pin=17


    #initialize the GPIO pins
    isPiR2=pir2_initGPIO(isVerbose, g_btn_pin, g_ledR_pin, g_di0_pin, \
    g_do0_pin, g_diC_pin)
    if (isPiR2==False) :
        print("Error- program should have stopped earlier")
    # end if


    # defines I2C Bus & device address #s

    global g_tempId00bus
    g_tempId00bus=1
    global g_tempId00addr
    g_tempId00addr=0x0048

    # initialize the I2C devices
    BusA=init_I2C_Bus(g_tempId00bus)
    
    import os
    
    #if there is no log file, create it

    logFileNameWoTXT=fileArray[0]
    #logFileNameWoTXT="/home/pi/Desktop/PiR2/files/PiR2_Log"
    logFileName=logFileNameWoTXT+".txt"
    if os.path.exists(logFileName) and os.path.getsize(logFileName) >0:
        #non-empty Log file exists
        print("Log file exists")
        #print("HIT ENTER TO CONTINUE")
        #aStr=input()
        
        #get id of last log record
        logF=open(logFileName,"r")
        i=0

        #new improved file read algorithm

        #in initializeTask
        recStr="na"
        lastRecStr="na"
        prevRecStr="na"
        
        numNulls=0

        #get piSN
        global g_piSN
        g_piSN=acquire_piSN()

        #remove trailing \n if it exists
        #g_piSN.rstrip()
        print("new piSN:"+g_piSN+":")


        #get g_piOS_Str
        global g_piOS_Str
        g_piOS_Str = acquire_piOS()


        isVerbose=True      #where should this be defined????
        
        while True: # ends when BREAK is encountered
            #print("Read next record.")

            #prevRec=recStr
            recStr=logF.readline()
            lenStr=len(recStr)
            if (not recStr):  #test if EOF
                break           #if EOF then BREAK out of WHILE LOOP
            #end if
            if lenStr==1 :       #indicates a Null record (only \n)
                numNulls+=1
                if numNulls==1 :
                    if isVerbose==True: print("WARNING-First Null record is #:",i)
                #end if
            else:
                i+=1
                if(i==1) :
                    firstRecStr=recStr
                #end if
                prevRecStr=lastRecStr
                lastRecStr=recStr
            #end if
        #end while
        logF.close()

        #get id of first record
        # split records into parts by commas
        result=firstRecStr.split(",")
        global g_firstRecId
        g_firstRecId=int(result[0])
        #print ("First RecordID:"+str(g_firstRecId)+":")

        # get id of last record
        #print ("Last Record:",lastRecStr)
        #remove any white space (before or after)
        lastRecStr=lastRecStr.rstrip()
        lastRecStr=lastRecStr.lstrip()
        numCol=lastRecStr.find(",")
        print("numCol:",numCol)
        if numCol==-1 :
            lastRecStr=prevRecStr
            lastRecStr=lastRecStr.rstrip()
            lastRecStr=lastRecStr.lstrip()
            numCol=lastRecStr.find(",")
        #end if
        #print(ord(lastRecStr[0:1]))
        #print(ord(lastRecStr[1:2]))
        #print(ord(lastRecStr[2:3]))
        #print(ord(lastRecStr[3:4]))
        print("numCol:",numCol)
        print ("Last Record:",lastRecStr)
        print ("lenLastRecord",len(lastRecStr))
        #if lastRecStr=="" :
        #    lastRecStr=secondLastRecStr
        #end if
        result=lastRecStr.split(",")
        print("result:",result)
        #initialize the last record id
        
        #global g_lastRecId
        g_lastRecId=int(result[0])
        
        print ("Last RecordID:"+str(g_lastRecId)+":")
        print("HIT ENTER TO CONTINUE:")

        #print("Creating PiR2_Log_id-id:")
        createLogCopy(g_firstRecId,g_lastRecId,logFileName,logFileNameWoTXT)

        #now replace the old log file

        logFileNameNew=logFileName   
        #print("Creating 2 rec. log:"+logFileNameNew+":")
        logF=open(logFileNameNew,"w")
        lastRecStrWnewline=lastRecStr
        if g_isAddingNewLine==True :
            lastRecStrWnewline=lastRecStrWnewline+"\n"
        #end if
        logF.write(lastRecStrWnewline)
        logF.close()
        #begin with a last record of old log file
        #******************************************
        unitsStr=" "
        #Control... parameters: pir2A.g_ioDeviceArray,pir2A.g_prevValueArray, \
        #pir2A.g_isAddingNewLine

        #then a record defining the piSN (even if record begins with "SN" instead
        
        print("Before call appendToLog from InitializeTask")
        appendToLog(g_piSN_Str,"A",unixTime(),"piSN",g_piSN,unitsStr, \
        g_ioDeviceArray,g_prevValueArray,g_isAddingNewLine,fileArray)



        print("Created a 2 rec. log with g_piSN:"+g_piSN+":")
        
        #print("HIT ENTER TO CONTINUE:")
        
        #aStr=input()
    else:
        #no log exists
        #initialize a new log file with a recordID of 0 etc
        recStr="need to create it"
        createNewEmptyLogTask(logFileName,recStr,"SN",-1,unixTime(), \
        g_isAddingNewLine,fileArray)
    #end if        
    

    # the helloTask is not yet queued to start
    g_startTime[g_helloTaskNumber]=0
    
    #return
#end def initializeTask()

def createLogCopy(idRecFirst,idRecLast,logFileName,logFileNameWoTXT):
    #print("Creating PiR2_Log_id-id:")

    print('idRecFirst:',idRecFirst,":")
    print('idRecLast:',idRecLast,":")    

    logFileNameCopy=logFileNameWoTXT
    logFileNameCopy=logFileNameCopy+"_"+str(idRecFirst)
    lastIdToWriteStr=str(idRecLast-1)
    if (idRecLast<=idRecFirst):
        print("NOT writing an empty Log append file")
        exit()
    else:
        logFileNameCopy=logFileNameCopy+"-"+lastIdToWriteStr+".txt"
        logF=open(logFileName,"r")
        logCopyF=open(logFileNameCopy,"w+")
        lastIdToBeWrittenStr=str(idRecLast-1)
        writtenIdStr=str(idRecFirst)
        iCnt=0
        while writtenIdStr<lastIdToBeWrittenStr:
            iCnt=iCnt+1
            #print("Now read next record.")
            recStr=logF.readline()
            logCopyF.write(recStr)
            result=recStr.split(",")
            writtenIdStr=result[0]
        #end while
        logCopyF.close()
        logF.close()
        print("wrote :"+str(iCnt)+": records.")        
        print("Created Append log:"+logFileNameCopy+":")
    #end if
#end def createLogCopy()

def createNewEmptyLogTask(logFileName,recStrIn,piSN_Str,lastRecId,timeStr, \
        isAddingNewLine,fileArray):
        # if lastRecId=-1, create first log file for this Pi

        # should remove logFileName from the parameter list
        
        #print("in createNewEmptyLogTask logF:"+logFileName+":")
        #exit()

        global g_lastRecId

        logFileNameFromArray=fileArray[1]
        print("in createNewEmptyLogTask logFfromArray:"+logFileNameFromArray+":")
        logF=open(logFileName,"w+")
    
        #unitsStr=""
        #look up the unitsUsed
        #print("ioDeviceStr:"+ioDeviceStr)
        #unitsStr=piR2_ioDeviceAttribute(pir2.g_ioDeviceArray, \
        # pir2.g_ioDeviceStr,"unitsUsed")
        unitsUsed=" "
        #print("unitsStr:"+unitsStr)
    
        #id,serNo,time,
        #recStr=piSN_Str+","+acStr+","+timeStr+","+ioDeviceStr+ \
        # ","+valueStr+","+unitsStr
        #use id=0,piSerNo=0,time=0

 
        if lastRecId==-1:
            print("WARNING-This is the first log ever on this Raspberry.")
            #exit()
            iStr=input("What is this area's max id in MIC Sync View?")

            # if the user enters the max id from the MIC Sync View
            # this process will result in the creation of a new log
            # file which skips one id value.  This will not cause
            # any errors and has the advantage of leaving an indication
            # of this new log being created.  To avoid skipping this
            # id value, the user can manually create the first log file
            # which contains 2 log records using the desired ids.
            # They can simply be records of dummy logA values.
            
            iInt=int(iStr)
            iInt=iInt+1
            idLastRec=str(iInt)
            recStr=str(idLastRec)+","+piSN_Str+",A,"+timeStr
            # insert the correct time below      TO FIX
            recStr=recStr+",toOwner,startLog,"+unitsUsed+"\n"
            logF.write(recStr)
            iInt=iInt+1
            idLastRec=str(iInt)
            recStr=str(idLastRec)+","+piSN_Str+",A,"+timeStr           
            recStr=recStr+",startLog,"+unitsUsed+"\n"
            logF.write(recStr)

            g_lastRecId = iInt

            # append the piSN
            #appendToLog(piSN_Str,acStr,timeInStr,ioDeviceStr,valueStr,unitsStr, \
            #array,prevValueArray,isAddingNewLine):
           
            appendToLog(piSN_Str,"A",timeStr,"piSN",piSN_Str,unitsUsed, \
            g_ioDeviceArray,g_prevValueArray,isAddingNewLine,fileArray)
            #idLastRec=1
            #recStr=str(idLastRec)+","+piSN_Str+",A,"+timeStr
            #recStr=recStr+",piSN,"+piSN_Str+","+unitsUsed+"\n"
        else:
            #else leave the idLastRec unchanged in the recStr and write it out
            recStr=recStrIn+"NewEmpty"
            logF.write(recStr)
            #now write out the piSN
            print("WARNING-id & time are wrong on the piSN record.")
            exit()
            #timeStr is a parameter in
            appendToLog(piSN_Str,"A",timeStr,"piSN",piSN_Str,unitsUsed, \
            g_ioDeviceArray,g_prevValueArray,isAddingNewLine,fileArray)
            #idLastRec=99
            #timeStr=unixTime()
            #recStr=str(idLastRec)+","+piSN_Str+",A,"+timeStr
            #recStr=recStr+",piSN,"+piSN_Str+","+unitsUsed+"\n"
        #end if
        #logF.write(recStr)
        logF.close()
#end def createNewEmptyLogTask()

def queuehelloTask():
    global g_startTimeArray
    global g_helloTaskNumber
    
    g_startTimeArray[g_helloTaskNumber]=unixTime()
    #return
#end def queuehelloTask()

def helloTask():
    print("helloTask says 'Hello World'")
    #return
#end def helloTask()

def manageTasks():
    global g_startTimeArray
    
    print("manageTasks begins here")
    print("g_startTimeArray[0]:0")
    #return
#end def manageTasks()

def appendToLog(piSN_Str,acStr,timeInStr,ioDeviceStr,valueStr,unitsStr, \
array,prevValueArray,isAddingNewLine,fileArray):

    # purpose: This appends a full record to the log file
    #           and puts the value field in the
    #           prevValueArray[id]
    # param0 str piSN_Str
    # param1 str acStr
    # param2 str timeInStr
    # param3 str ioDeviceStr
    # param4 str valueStr
    # param5 str unitsStr
    # param6 str array              ioDeviceArray
    # param7 str prevValueArray
    # param8 str isAddingNewLine
    # param9 str fileArray          g_fileArray eg Log
    #
    # returns ?

    global g_lastRecId
    
    #print("In Append A, ioDeviceStr:"+ioDeviceStr+":")
    #exit()
    timeStr=str(timeInStr)
    # get the units from the g_ioDeviceArray passed as array

    # array is ....
    # the g_ioDeviceArray that is defined in the  (Is this True?)
    #    pir2A.py

    # requires pir2Data  is done at the beginning
    #from pir2Data import piR2_findStrInArray
    #from pir2Data import piR2_ioDeviceAttribute
    
    # g_ioDeviceArray is defined in the (Xmainprogram) pir2Data.py
    ioDeviceRec=piR2_findStrInArray(array,"|nameDevice|procTemp|")
    #print("ioDeviceRec:"+ioDeviceRec)

    #unitsStr=""
    #look up the unitsUsed
    #print("ioDeviceStr:"+ioDeviceStr)
    unitsStr=piR2_ioDeviceAttribute(array,ioDeviceStr,"unitsUsed")
    #unitsStr="nu"
    #print("unitsStr:"+unitsStr)

    # create the log record NOT yet ending with a newline char
    #print("timeStr:",timeStr)
    recStr=""
    #print("recStrB4:"+recStr+"?")
    g_lastRecId=g_lastRecId+1
    
    #print("In Append B, last rec id:"+str(g_lastRecId)+":")
    recIdStr=str(g_lastRecId)
    #exit()
    recStr=recIdStr+","+piSN_Str+","+acStr+","+timeStr+","+ioDeviceStr+"," \
    +valueStr+","+unitsStr
    #print("recStr  :"+recStr+":")
    if isAddingNewLine ==True:
        #print("recStr  :"+recStr+":")
        #print("adding \ n (NewLine code:LF)")
        recStr=recStr+"\n"
    #print("recStr  :"+recStr+":before writing log rec")
    #  is Log already open?
    
    #global g_fileArray

    log=open(fileArray[1],"a")
    #log=open("/home/pi/Desktop/PiR2/files/PiR2_Log.txt","a")
    #print("in Append, log rec2:"+recStr+":")
    #exit()

    log.write(recStr)
    #print("In Append C close")
    log.close()
    
    nStr=piR2_ioDeviceAttribute(array,ioDeviceStr,"id")
    # n is the array index number for this ioDevice
    if nStr.isnumeric() :
        n=int(nStr)
        #print("n:",n)

        prevValueArray[n]=valueStr
        #print("array:",prevValueArray)
    else:
        print("WARNING: ioDevice:"+ioDeviceStr+" is not yet defined")
    #end if
#end def appendToLog()

#************************************************
#  an acquire_device routine is needed for      *
#   each device that acquires data.             *
#  Each is called from piR2_main.py             *
#************************************************
def acquire_procTemp(isSimulating):
    # no longer
    # passing condition as a string because maybe Python does pass
    # a True or False conditional correctly.
    resultStr="-49.9'C"
    #print("in pir2A isNotSimulateStr:"+isNotSimulateStr)
    if (isSimulating == False):
        resultStr=osCommand("vcgencmd measure_temp >files/resultStr.txt")
        # strip off any trailing new line (ie line feed)
        resultStr=resultStr.rstrip()
        resultStr=resultStr[5:9]
    else:
        resultStr="49.8"  # same format as after editing from vcgencmd
    #end if
    #print("resultStr: "+resultStr)
    return resultStr
    #end if
#end def acquire_procTemp()  

def osCommand(commandStr):
    # @purpose: to issue a system command and return the result
    # @param1:  str commandStr       eg: "vcgencmd measure_temp"
    # @returns: str commandResultStr eg: "temp=36.9'C"
    
    from subprocess import call
    #import os
    from os import system, name, remove
    from time import sleep


    #print("osCommand received commandStr:"+commandStr)
    commandResultStr="none"

    global g_fileArray

    #commandRFileName=g_fileArray[2]
    commandRFileName="/home/pi/Desktop/PiR2/files/commandResult.txt"
    # name is not a function, but a special variable
    if name == 'nt':
        _ =system('cls')
    else:
        if (commandStr=="clear"):
            doh=0   #null statement
            print("before clear")
            _= system("clear")
            print("after clear")
        else:
            if (False):
                # in case no file exists, appending something will create it
                # for sure
                fh=open(commandRFileName, "a")
                fh.write("any")
                fh.close()
                # now remove it
                remove(commandRFileName)
            #endif

            # save result in txt file
            saveOutputStr=" >"+commandRFileName
            revisedCommandStr=commandStr+saveOutputStr
            #print("revisedCommandStr:"+revisedCommandStr)

            #invoke system command
            _= system(revisedCommandStr)
            fh=open(commandRFileName, "r")
            # read the result of the command
            commandResultStr=fh.readline()
            fh.close()
        #end if
    return commandResultStr
    #end if
#end def osCommand()

def getControl_fromOwner(array,prevValueArray,g_isAddingNewLine,fileArray):
    # @purpose: accept and log a fromOwner command (containing a ":")
    #           to display the log (comments have no ":")
    # @param0   array   g_ioDeviceArray     as defined in pir2Data.py
    # @param1   str     fromOwnerValueStr   eg "acquire:piSN"
    # @param2   array   prevValueArray      eg 
    #                                       eg2 "piArea:GreatRoom"
    # @param3   array   fileArray           eg g_fileArray
    # @returns  nothing

    ################################################################
    # maybe move these to the initialize routine ????????    
    global g_piSN_Str
    g_piSN_Str=acquire_piSN()   #this works
    #print("piSN:"+g_piSN_Str+":")
    
    # but don't display the piSN while testing
    ################################################################
    
    g_piSN_Str="SN"
    #should get the unitsStr from the ioDevice array
    unitStr=""              ########################################
    
    #a=input("after acquire_piSN(): Hit Enter")
    ################################################################

    global g_piIPStr
    g_piIPStr=acquire_piIP()   #this works, 
    #print("piIP:"+g_piIPStr+":")
    #a=input("after acquire_piIP(): Hit Enter")

    global g_isPythonVersion3Str
    g_isPythonVersion3Str="True"
    
    # display pin #s
    sp6="      "
    print(" GPIO pins              deviceNames")

    print("g_di0_pin ",g_di0_pin,   "piSN")
    print("g_do0_pin ",g_do0_pin,   "procTemp")
    print("g_diC_pin ",g_diC_pin,   "pushButton")
    print("g_ledR_pin",g_ledR_pin,  "redLED")
    print(sp6,sp6,sp6,              "piOS")
    print(sp6,sp6,sp6,              "piArea")
    print(sp6,sp6,sp6,              "piIP")
    print(sp6,sp6,sp6,              "digOut0")
    print(sp6,sp6,sp6,              "digIn0")
    print(sp6,sp6,sp6,              "tempId00")
    print(sp6,sp6,sp6,              "toOwner")
    print(sp6,sp6,sp6,              "ambTemp")
    print(sp6,sp6,sp6,              "ambTemp1")
    print(sp6,sp6,sp6,              "ambTemp2")
    print(sp6,sp6,sp6,              "audioTV:alarm.wav")
    print(sp6,sp6,sp6,              "imageTV:PiR2_Clock.jpg")
    print(sp6,sp6,sp6,              "logA:155 kg")
    print(sp6,sp6,sp6,              "piButton(fut)")
    print(sp6,sp6,sp6,              "ambLight(fut)")
    print(sp6,sp6,sp6,              "ambHum(fut)")
    print(sp6,sp6,sp6,              "ambAirpr(fut)") 
    print(sp6,sp6,sp6,              "digLight(fut)")
    print(sp6,sp6,sp6,              "piModel(fut)")
    print(sp6,sp6,sp6,              "piRam(fut)")
    print(sp6,sp6,sp6,              "usbAmps0(fut)")
    print(sp6,sp6,sp6,              "emailOut0(fut)")
    print(sp6,sp6,sp6,              "emailOut1(fut)")
    print(sp6,sp6,sp6,              "digMains(fut)")
    
    posColon=0
    colonStr=":"
    fromOwnerValueStr=colonStr
    while (fromOwnerValueStr.find(colonStr) != -1):

        global g_lastRecId
        #print("g_lastRecId:"+str(g_lastRecId)+":")
        print("Enter a fromOwner command eg acquire:pushButton ")
        print("                          or audioTV:alarm.wav ")
        print("                          or logA:1 ")        
        fromOwnerValueStr=input(" or Return(<-) to monitor the Log:")
        #print("fromOwnerValueStr:"+fromOwnerValueStr)
        #log1Str=g_piSN_Str+","+"C"
        #log2Str="fromOwner"+","+fromOwnerValueStr+","+unitsStr

        if fromOwnerValueStr =="" :
            doh=0   #null statement
            #do not append it
        else:
            #unitsStr=piR2_ioDeviceAttribute(array,"fromOwner","unitsUsed")
            #print("unitsStr:"+unitsStr)

            #####################################################################
            #from line 555 of piR2A
            unitsStr=""
            # append to log the "fromOwner" value
            #print("about to append to Log fromOwner")

            appendToLog(g_piSN_Str,"C",unixTime(),"fromOwner",fromOwnerValueStr, \
            unitsStr,array,prevValueArray,g_isAddingNewLine,fileArray)
        # end if
        if (fromOwnerValueStr.find(colonStr) != -1):
                #print("found a colon")
                #if it has a ":" in the reply
                #process the control_fromOwner command

                control_fromOwner(array,fromOwnerValueStr,prevValueArray)
                #####################################################################
        else:
            doh=0   #null statement
            #fall through to test the while again
        #end if
    #end while
    return
#end def getControl_fromOwner()

def control_fromOwner(array,fromOwnerValueStr,prevValueArray):           
    # @purpose: to process the control commands received
    #               via the MIC Control file (wrapped in html tags)
    # @param0   array   g_ioDeviceArray     as defined in pir2Data.py
    # @param1   str     fromOwnerValueStr   eg "acquire:piSN"
    # @param2   array   prevValueArray      eg 
    #                                       eg2 "piArea:BedRoom"
    # @returns  nothing
    #print("in control_fromOwner")
    colonStr=":"
    parts=fromOwnerValueStr.split(colonStr)
    #print("parts:",parts)
    part0=parts[0]
    if part0=="acquire":
        ioDeviceFromOwner=parts[1]
        ioValueFromOwner=""  #no value from Owner when acquiring data
        isAcquire=True
    else:
        # eg "acquire:piSN"
        ioDeviceFromOwner=parts[0]
        ioValueFromOwner=parts[1]
        #print("ioValueFromOwner:"+ioValueFromOwner+":")
        ioValue=parts[1]
        #print("ioValue:"+ioValue)
        isAcquire=False
    #end if    
    # now perform the requested action and then
    #    append to log the info received fromOwner

    # but only if it is a valid ioDevice name    
    # g_ioDeviceArray is defined in the mainprogram
    # eg ioDeviceFromOwner is "piArea"
    # if ioDevice is not defined, the line below will return ""
    ioDeviceRec=piR2_findStrInArray(array, \
    "|nameDevice|"+ioDeviceFromOwner+"|")
    if ioDeviceRec == "" :
        print("WARNING: ioDevice:"+ioDeviceFromOwner+" is not yet Defined")
        #so don't process it
    else:
        # it exists, so process it now

        acStr="C"
        if isAcquire==True: acStr="A"
        #actually perform the Acquire or Control action
        ioValueToLog=pir2_performAorC(array,ioDeviceRec,ioDeviceFromOwner, \
        ioValueFromOwner,acStr)

        # get the units
        unitsStr=piR2_ioDeviceAttribute(array,ioDeviceFromOwner,"unitsUsed")
        #print("unitsStr:"+unitsStr)

        print("ioValueToLog:",ioValueToLog,":")
        # now write it to the log
        appendToLog(g_piSN_Str,acStr,unixTime(),ioDeviceFromOwner,ioValueToLog, \
        unitsStr,array,prevValueArray,g_isAddingNewLine,g_fileArray)
        # clear this "fromOwner reply"
        fromOwnerValueStr=colonStr
        #print("clear the fromOwner reply")
    #end if
    return
#end def control_fromOwner()

def pir2_performAorC(array,ioDeviceRec,ioDeviceFromOwner,ioValueFromOwner,acStr):
    # @Purpose      to perform the "acquire" or "control" function that
    #               was requested by the Owner
    # @param0   array   g_ioDeviceArray     as defined in pir2Data.py
    # @param1   record  ioDeviceRec         row of above arrayfor ioDevice
    # @param2   str     ioDeviceFromOwner   eg piSN the exact ioDevice name
    # @param3   str     ioValueFromOwner    eg alarm.wav
    # @param4   str     acStr               eg "A"  A:Acquire or C:Control
    # @param5   obj     busA                eg 1 for tempId00 bus (TMP102)
    #
    # returns   str     ioValueToLog        eg 192.168.0.10
    
    #print("ioDeviceRec:"+ioDeviceRec+" is Defined")

    #print("in performAorC")
    if acStr=="A":
        #print("isAcquire:True")
        #print("in pir2_performAorC todo A")
        #acStr="A"
        ioValueToLog="unassigned"
        if ioDeviceFromOwner=="piArea":
            ioValueToLog=g_piAreaStr
        #end if
        if ioDeviceFromOwner=="piSN":
            #get the actual piSN not the g_piSN_Str
            #    which contains "SN" during testing
            ioValueToLog=acquire_piSN()
        #end if
        if ioDeviceFromOwner=="piIP":
            ioValueToLog=g_piIPStr
        #end if
        if ioDeviceFromOwner=="piOS":
            ioValueToLog=g_piOS_Str
        #end if
        if ioDeviceFromOwner=="lastRecId":
            ioValueToLog=str(g_lastRecId)
        #end if
        if ioDeviceFromOwner=="logA":
            ioValueToLog=g_logAStr
        #end if
        if ioDeviceFromOwner=="pushButton":
            #print("g_btn_pin:",g_btn_pin)
            ioValueToLog=acquire_pushButton(g_btn_pin)
        #end if
        if ioDeviceFromOwner=="digIn0":
            #print("g_di0_pin:",g_di0_pin)
            ioValueToLog=acquire_digIn0(g_di0_pin)
        #end if
        if ioDeviceFromOwner=="tempId00":
            print("g_tempId00addr:",hex(g_tempId00addr))
            busA=init_I2C_Bus(g_tempId00bus)
            ioValueToLog=acquire_tempId00(busA,g_tempId00addr," C")
        #end if
    else:

        #print("in pir2_performAorC todo C")
        ioValueToLog=ioValueFromOwner
        #acStr="C"
        #unitsStr=""
        #print("ioDeviceRec:"+ioDeviceRec)
        
        #look up the unitsUsed
        unitsStr=piR2_ioDeviceAttribute(array,ioDeviceFromOwner,"unitsUsed")
        #print("unitsStr:"+unitsStr)

        #print("invoking the Control action:"+ioDeviceFromOwner)
        #now invoke the Control (before logging it)
        # do this only for physical (not data) devices
        if ioDeviceFromOwner=="audioTV":
            control_audioTV(ioValueFromOwner)
        #end if

        if ioDeviceFromOwner=="redLED":
            #print("before redLED, pin:",g_ledR_pin)
            #a=input('pause')
            control_redLED(g_ledR_pin,ioValueFromOwner)
        #end if
            
        if ioDeviceFromOwner=="digOut0":
            #print("before digOut0, do0_pin:",g_do0_pin)
            #a=input('pause')
            control_digOut0(g_do0_pin,ioValueFromOwner,HIGH,LOW,g_RPIGPIO)
        #end if

        if ioDeviceFromOwner=="imageTV":
            #print("before imageTV, image:",ioValueFromOwner)
            #a=input('pause')
            control_imageTV(ioValueFromOwner, "", 5)
        #end if
                        
        #use the ioDevice defined by the Owner
        if ioDeviceFromOwner=="piArea":
            control_piArea(ioValueFromOwner)
        #end if
    #end if
    return ioValueToLog
#end def pir2_performAorC()

def control_audioTV(soundByteMp3OrWav):

    global g_fileArray
    
    audioFolder=g_fileArray[3]
    #audioFolder="/home/pi/Desktop/PiR2/Audio/"
    commandStr="aplay "+audioFolder+soundByteMp3OrWav #72
    #print("commandStr:"+commandStr+":")
    osCommand(commandStr)
    #print("after commandStr")
    return
#end def control_audioTV()

def control_piArea(roomNameStr):

    global g_piAreaStr
    #print("Before g_piAreaStr definition")
    g_piAreaStr=roomNameStr
    #print("g_piAreaStr:"+g_piAreaStr+":")
    return
#end def control_piArea()

def acquire_piSN():
    commandStr="grep Serial /proc/cpuinfo"
    piSN_Str=osCommand(commandStr)
    #print("piSN_Str:"+piSN_Str+":")
    #piSN_Str=piSN_Str.rstrip()
    #print("piSN_Str:"+piSN_Str+":")
    piSN_Str=piSN_Str[12:34]
    #remove any trailing \n, if it exists
    piSN_Str=piSN_Str.rstrip()
    #print("piSN_Str:"+piSN_Str+":")
    return piSN_Str
#end def acquire_piSN()

def acquire_piIP():
    commandStr="ifconfig | grep inet"
    piIP_Str=osCommand(commandStr)
    #print("piIP_Str:"+piIP_Str+":")
    ipCol=piIP_Str.find("inet")+5
    #print("ipCol:",ipCol,":")
    ipEnd=ipCol+12
    #print("ipEnd:",ipEnd,":")
    #print("piIP_Str:"+piIP_Str+":")
    piIP_Str=piIP_Str[ipCol:ipEnd]
    #print("piIP_Str:"+piIP_Str+":")
    piIP_Str=piIP_Str.rstrip()
    #print("piIP_Str:"+piIP_Str+":")
    piIP_Str=piIP_Str.rstrip(" Bcast:")
    #print("piIP_Str:"+piIP_Str+":")
    return piIP_Str
#end def acquire_piIP()

def acquire_piOS():
    commandStr="uname -a"
    piOS_Str=osCommand(commandStr)
    print("piOS_Str:"+piOS_Str+":")

    ipCol=piOS_Str.find("#")
    print("ipCol:",ipCol,":")
    
    piOS_Str=piOS_Str[0:ipCol]
    #remove any trailing \n, if it exists
    piOS_Str=piOS_Str.rstrip()
    print("piOS_Str:"+piOS_Str+":")
    #a=input("pause")
    return piOS_Str
#end def acquire_piOS()

def displayHeader(dateInStr):
    # @Purpose   To display the refreshing log header
    # @Param0   str dateInStr   "2019GJul23"
    # @returns  nothing

    global g_pir2AVer
    global g_piAreaStr
    global g_piIPStr
    global g_piSN

    g_piSN = acquire_piSN()

    # display the log header

    
    #print("g_piAreaStr:"+g_piAreaStr+":")
    # g_piArea Str="This_Room"
    
    recStr="piArea:"+g_piAreaStr+": piOS:"+g_piOS_Str+":"
    print(recStr)

    #g_piIPStr = acquire_piSN()
    
    #print("g_piIPStr:"+g_piIPStr+":")
    # g_piIPStr="192.192.192.192"
    
    recStr="piIP:"+g_piIPStr+" piSN:"+g_piSN+"."
    print(recStr,)
   
    recStr="Log date: ["+dateInStr+"] v:"+g_pir2AVer
    print(recStr)
    
    print("  id# SN ac hh:mm:ss.xxx ioDevice Value Units")
    #end
#end def displayHeader()

def displayLog():
    # @purpose  display the log file
    # @param0   none
    # @returns  nothing
    
    global g_fileArray
    
    log=open(g_fileArray[1],"r")
    #log=open("/home/pi/Desktop/PiR2/files/PiR2_Log.txt","r")
    #count the records
    numRecs=0
    for x in log:
        numRecs += 1
        #end for
                       
    log.close()
    logArray=[0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5]
    

    numActualRecords = numRecs    #*****************************
    #print("logCount:",str(numActualRecords))

    # read the Log file again
    # saving the displayable records in the logArray

    logFileName=g_fileArray[1]
    #logFileName="/home/pi/Desktop/PiR2/files/PiR2_Log.txt"
    log=open(logFileName,"r")
    numToDisplay=24
    for x in range(numActualRecords):
        recStr = log.readline()
        if (x> (numActualRecords-numToDisplay-1)):
            y=numActualRecords-x-1
            #print("y:",y,":",recStr.rstrip())
            logArray[y]=recStr.rstrip()
        #end if
    #end for
    log.close()

    y=1
    #print(y, logArray[y])

    # now display the saved displayable records
    # range(24) actually means 0,1,2,.... 23
    for y in range(numToDisplay):
        #print(y,":",logArray[y],)
        print(logArray[y],)
    #end for
    print("***")
#end def displayLog()

def unixTime():
    # @Purpose  to return the current time as unix time string
    # @param0   nothing
    # @returns str    tStr  eg 1234567890.123
    
    import time
    
    rFloat=time.time()
    #print("rFloat:",rFloat)
    x=str(rFloat).find(".")
    #print("x:",x)
    tStr=str(rFloat)
    tStr=tStr[0:14]
    #print("tStr:",tStr)
    return tStr
#end def unixTime()

def askVerbose(isQuerying):
    # @Purpose  To ask the user if verbose interactions should be used
    # @param    cond isQuerying         eg True (will stop & ask user)
    # @returns  cond isVerboseResult    eg True
    # if called with False, it will not ask anything
    # and will stay in NOT verbose mode
    isVerboseResult=False
    if isQuerying==True:
        isVerboseResult=True
        a=input("For NOT verbose,hit Enter: ")
        if a=="":
            isVerboseResult=False
        #end if
    #end if
    return isVerboseResult
#end def askVerbose():

def openLog2(isVerbose,logFileName,errorMessageStr):
    # @Purpose   Tries to open the specified file
    #            NB Program stops if this fails
    # @param0 cond isVerbose        eg True
    # @param1 str  logFileName      eg "/home/pi/Desktop/PiR2/files/PiR2_Log.txt"  
    # @param2 str  errorMessageStr  eg eg "ERRORxxx-Bad File Name:"
    # @returns file handle          eg log
    if not(os.path.exists(logFileName) and (os.path.getsize(logFileName)>0)):
        # NB File is not available
        print(errorMessageStr)
        exit()
    else:
        log= open(logFileName, "r")
        if isVerbose==True:        print("Opened file name:",logFileName)
    #end if
    return log
#end def openLog2()

def informUserWait(isVerbose,secondsDt,countBy5):
    # @param1 cond isVerbose    eg True
    # @param2 int  secondsDt    eg 1
    # @param3 int  countBy5     eg 20
    #
    # @returns nothing
    # Now wait 20 seconds and check to see if Log File length has changed
    if isVerbose==True:
        print("monitoring Log use for 20 seconds (is it growing?)")
    #end if
    i=0
    for j in range(int(countBy5/5)):
        for k in range(int(countBy5/5)):
            i+=1
            if g_isPythonVersion3Str=="True" :
                endStr='end=" "'
            else:
                endStr="2."
            #end if
            if isVerbose==True:        print(".", endStr)
            time.sleep(secondsDt)
        #end for
        i+=1
        print (".")
        time.sleep(secondsDt)
    #end for
    return
#end def informUserWait()

def isLogAvailable(isVerbose,logFileName):
    # @Purpose  to check if the log file is available
    #           ie not already being used by another running process
    # @param0   cond    isVerbose   False for minimum dialog
    # @param1   str logFileName     eg "....PiR2_Log.txt"
    # @returns  cond    isQuerying  eg True  if Log file is available
    #                               ie not active (ie not in use)
    # @usedBy   pir2main.py
    # @param none
    # 
    #logFileName="/home/pi/Desktop/PiR2/files/PiR2_Log.txt"

    ################################################################
    # isQuerying=True means "ask the user if verbose info is wanted"
    #    This can be set False to Never ask the user
    ################################################################
    #moved to pir2main.py
    #isQuerying=True
    # ask user who will
    #     hit return for non-verbose
    #isVerbose =askVerbose(isQuerying)
    print("Watching (20 sec) to see if log file is available. . . :")

    import os

    numNulls=0    
    # return True if Log  doesn't exist yet
    if os.path.exists(logFileName) and os.path.getsize(logFileName) >0:
        #non-empty Log file exists
        log=openLog2(isVerbose,logFileName,"ERROR-Bad File Name:")
        
        #NB size is the total size of the file (in bytes)
        size=os.path.getsize(logFileName)
        i=0

        # in isLogAvailable
        recStr="na"
        #prevRec="na"
        while (True):

            prevRec=recStr          #recStr has no trailing \n (New Line)
            recStr=log.readline()   #recStr as read always has trailing \n
            lenStr=len(recStr)

            if (not recStr ): #test if eof
                break #out of the while  NB BREAK OUT OF THE WHILE LOOP
            #end if
            #if i==0 :
            #   print("i:"+str(i))
            #end if
            i=i+1                   #count the records
            if lenStr==1 :          #if it only has the \n
                numNulls+=1
                if numNulls==1:      #mention only the first one
                    if isVerbose==True:        print("WARNING: first null record is #:",i)
                #end if
            else:
                #recStr=recStr[0:lenStr-1]   #strip trailing \n (New Line)
                recStr=recStr.rstrip()       #strip trailing \n (New Line)
                #                           and any spaces (unfortunately)
                lenStr=len(recStr)          # revise lenStr
                #print(i,",",lenStr,",",recStr)
            #end if
        #end while ie until isEof=True (accomplished by the "break")
                
        lenStr=len(recStr)
        if isVerbose==True:     print("last Log record:",prevRec)

        log.close()
        lenLogFile=i                #as counted in the while loop
        if isVerbose==True:     print("log file had ",lenLogFile," records.")
        avgRecLen=int(size/lenLogFile)
        if isVerbose==True:         print("avgRecLen=",avgRecLen)
        if numNulls>0:
            if isVerbose==True:     print("WARNING-Total # of Null records:", \
                                    numNulls)
        #end if    
        informUserWait(isVerbose,1,20) #20 seconds in 1 second intervals
        log=openLog2(isVerbose,logFileName,"ERROR011-File disappeared...doh:")
        newSize=os.path.getsize(logFileName)
        log.close()
        if newSize>size :
            numRecs=int((newSize/avgRecLen)+0.5)
            if isVerbose==True:        print("Log now has ",numRecs,"records.")
            if isVerbose==True:        print("Log file is IN USE")
            returnTruthValue= False
        else:
            # log file is not in use, ie available
            returnTruthValue= True
        #end if
    else:
        # log file is empty or doesn't exist yet
        #######################################################
        returnTruthValue= True        ####MUST INVESTIGATE ####
        #######################################################
        
    return returnTruthValue
#end def isLogAvailable()

def piR2_findStrInArray(array,typeandtextStr):
    #This function returns the first matching list
    # in the passed array
    # or a null string
    # @purpose  to provide the list of attributes for a specified ioNameDevice
    #           from an array of all the PiR2 ioDevices.
    #           The ioNameDevice is unique for each row of the array
    #           eg find and return the full list of attributes for the
    #           ioDevice whose "nameDevice" is "procTemp"
    # @param1   array   array           eg g_ioDeviceArray
    # @param2   str     ioNameDevice    eg "|nameDevice|procTemp|"
    # @returns  list    array[n]        eg |id|0001| |ioCode|A002| ...|KER|0|
    #
    #array is passed in
    #number of rows in the Array is the height (ie len(array) )
    
    global g_ioDeviceArray

    returnList=""
    if typeandtextStr !="":
        height=len(array)
        #print("height: ",height)
        foundThisRecordNumber=-1
        for x in range(height):
            #strRec=array[x])
            if type(array[x])==str:
                found=array[x].find(typeandtextStr)
                if found !=-1:
                    foundThisRecordNumber=x
                #end if
            #end if
        #end for
        if foundThisRecordNumber != -1:
            returnList=array[foundThisRecordNumber]
        #endif
    return returnList
#end def piR2_findStrInArray()



def piR2_ioDeviceAttribute(array,ioNameDevice,attributeName):
    # @purpose  to provide the attribute value for a specified atributeName
    #           for a specified ioNameDevice
    #           from an array of all the PiR2 ioDevices.
    #           The ioNameDevice is unique for each row of the array
    #           eg find and return "d'C" as unitsUsed for procTemp
    # @param0   array   array           eg g_ioDeviceArray
    # @param1   str     ioNameDevice    eg procTemp
    # @param2   str     attributeName   eg unitsUsed
    #
    # @returns  str     result          eg d'C (for procTemp)
    #
    #print("DEBUG: In piR2_ioDeviceAttribute")
    # array is g_ioDeviceArray
    #print("ioNameDevice:"+ioNameDevice)
    #print("attributeName:"+attributeName)
    searchForStr="|nameDevice|"+ioNameDevice+"|"
    #print("searchForStr:"+searchForStr)

    # ioAttribues is a python "list" of all the attributes
    ioAttributes=piR2_findStrInArray(array,searchForStr)
    data=ioAttributes.split()
    #print("data:",data)
    yesStr="|yesStr|empty|"
    for attribute in data:
        #print(attribute)
        aPos=attribute.find("|"+attributeName+"|")
        if aPos>-1:
            yesStr=attribute
        #endif
    #end for
    #print(yesStr)
    parts=yesStr.split("|")
    result=parts[2]
    #print("result:",result)
    #print(result)
    #return the result
    return result
#end def piR2_ioDeviceAttribute()

def piR2_attrFromStr(anyStr,attrNameStr):
    # @purpose  to provide the value of a specified attribute for a
    #           PiR2 ioDevice in a string which contains a list of
    #           all the attributes for one specific PiR2 ioDevice
    #           eg |unitsUsed|d'C| where unitsUsed is the attribute
    #               and d'C is the value of this attribute of the
    #               procTemp ioDevice
    # @param0   str     anyStr      eg |anAttr|anyValue| .. |nameDevice|procTemp|  
    # @param1   str     attrNameStr     eg nameDevice
    #
    # @returns  str     result          eg procTemp
    #
    workStr=anyStr+" "
    beg=workStr.find("|"+attrNameStr+"|")
    #print("beg:",beg)
    end=workStr.find(" ",beg)
    #print("end:",end)
    attrStr=workStr[beg:end]
    #print("attrStr:",attrStr)
    parts=attrStr.split("|")
    #print("parts:",parts)
    rStr=parts[2]
    #print("rStr:",rStr)
    return rStr
#end def piR2_attrFromStr()

def sortableDateTime(timeIn):

    # @purpose: to return dkcc sortable date & time w msec & Time Zone
    #           Also: the columns will remain unchanged
    #           eg time is 02:04   not 2:4 
    # @param1  str  timeIn eg "1561500618.3098228"
    # @returns str  resultStr   eg "2019FJun03 21:46:11.740 EDT"
    
    #print("timeIn:"+timeIn+":")
    import time;
    timeG=timeIn
    timeGStr=timeIn
    parts=timeGStr.split(".")
    #print("parts[0]:"+parts[0])
    #print("parts[1]:"+parts[1])
    timeInt=int(parts[0])
    tfrStr="."+parts[1]
    #keep only 3 digits of decimal (msec)
    #print("tfrStr1:"+tfrStr+":")
    tfrStr=tfrStr+"000"

    #print("tfrStr2:"+tfrStr+":")
    tfrStr=tfrStr[0:4]
    ###################################################################
    # must prefix single digit seconds with a "0" to be sortable
    ###################################################################

    #print("tfrStr3:"+tfrStr+":")

    tGStr=time.strftime("%a, %d %b %Y %H:%M:%S %Z")
    #print("strftime,timeG:"+tGStr+":")
    monthIntStr=time.strftime("%m")
    monthInt=int(monthIntStr)
    alphabetStr="ABCDEFGHIJKL"   #first 12 letters
    monthLetter=alphabetStr[monthInt-1:monthInt]
    #print("monthLetter:"+monthLetter+":")
    ####################################################################
    tFStr=time.strftime("%Y"+monthLetter+"%b%d %H:%M:%S"+tfrStr+" %Z")
    # must prefix single digit numbers with a "0" to be sortable       #
    ####################################################################
    resultStr=tFStr
    return resultStr

#end def sortableDateTime()

def pir2_convertMIC_ControlTask(array,prevValueArray,
g_isAddingNewLine,fileArray):
    #@ Purpose: 1. to look for (very often, ie every minute)
    #               the presence of an incoming MIC control file
    #           2. to convert the control file coming from MIC (in
    #               its html wrapper) into a proper text file format
    #               including the piSN and unixTime
    #           3. to actually invoke each control command
    #               eg1 "audioTV:alarm.wav"
    #               eg2 "acquire:All"
    # @param0   array   array               pir2A.g_ioDeviceArray
    # @param1   array   prevValueArray      pir2A.g_prevValueArray
    # @param2   bool    g_isAddingNewLine   pir2A.g_isAddingNewLine
    #
    # @Returns: int resultInt   eg2   # count of control command lines
    #                                   (which is usually 0)
    #
    # NB this task should be run more often than once per minute

    #global g_fileArray

    fileNameMIC_Control=fileArray[4]+acquire_piSN()+"_txt.html"
    #print('C file name:',fileNameMIC_Control)
    fileNameMIC_test="/home/pi/Downloads/PiR2_Control_00000045db9ca6_txt.html"
    #if fileNameMIC_Control != fileNameMIC_test :
    #       print('ERROR991:C file name:',fileNameMIC_Control)
    #end if
    #
    import os

    resultInt=0
    numNulls=0
    i=0
    if os.path.exists(fileNameMIC_Control) and os.path.getsize(fileNameMIC_Control) >0:
        # process the incoming MIC Control file
        print("NOTICE: An incoming MIC Control file exists.")
        fc=open(fileNameMIC_Control)
        while True: # ends when BREAK is encountered
            recStr=fc.readline()
            lenStr=len(recStr)
            if (not recStr): #test if EOF
                break        #if EOF then BREAK out of the WHILE LOOP
            #end if
            #print("recStr:",recStr)

            if lenStr==1 :  #indicates a Null record (only \n)
                numNulls +=1
            else:
                #print("after NullCheck, recStr:",recStr)
                i+=1
                # strip white space from beginning and end
                controlStr=recStr.strip()
                isControlCmd=True
                if len(controlStr)==0: isControlCmd=False
                firstChar=recStr[0:1]
                if firstChar.find("<")>-1: isControlCmd=False
                if isControlCmd :
                    #does this string begin with an html tag ("<")



                    #here
                    #print("cmd recStr:",recStr)
                    #process the command
                    resultInt+=1
                    #print("Control Command is:"+controlStr)
                    piSN_Str=g_piSN_Str
                    #piSN_Str=acquire_piSN()
                    #create the outgoing controlRecord ending with a New Line
                    #controlRecord=piSN_Str+","+unixTime()+","+controlStr+"\n"
                    #print("PiR2 Control Record:"+controlRecord+":")

                    #copied similar code from line 555 of piR2A
                    # to fix       ********************UnitsStr******
                    unitsStr=""
                    # append to log the "fromOwner" value
                    print("about to append to Log fromOwner")
                    fromOwnerValueStr="a:"
                    appendToLog(piSN_Str,"C",unixTime(),"fromOwner",controlStr, \
                    unitsStr,array,prevValueArray,g_isAddingNewLine,g_fileArray)
                    colonStr=":"

                    #array=array(1,2,3)
                    #prevValueArray=array(1,2,3)
                    if (controlStr.find(colonStr) != -1):
                        print("found a colon")
                        #if it has a ":" in the reply
                        #process the control_fromOwner command
                        control_fromOwner(array,controlStr,prevValueArray)
                    #end if
                #end if
            #end if  #process the next record
        #end while
        #print("encountered EOF on MIC Control file")
        fc.close()
        
        import os

        # delete the Control file that came from MIC
        os.remove(fileNameMIC_Control)
        #print("deleted:",fileNameMIC_Control)
    else:
        print("NOTICE: no incoming MIC Control file was found.")
    #end if
    print("ctrl-Z to exit!")
    
    #print("returning:",resultInt)
    # return the number of commands found
    return resultInt
#end def pir2_convertMIC_ControlTask

# it might be necessary to install the rpi.gpio software
# sudo apt-get update
# pip install rpi.gpio

def acquire_pushButton(btn_pin):
    # @purpose:  to sense if the pushButton is down
    # @param0:      unused
    # returns   str 1 if button is down (else 0)
    
    #GPIO.setmode(GPIO.BCM)
    #print("@2.1596 in acquire_PB, btn_pin:",btn_pin)
    #GPIO.setup(btn_pin,unity(GPIO.IN))  #72 by calling program
    #print("@2.1598 after GPIO.setup")

    if GPIO.input(btn_pin) :
       downInt=0
    else:
       downInt=1
    # end if
    print("acquire_PB, returning downInt:",downInt)
    #a=input()
    return str(downInt)
# end def

def acquire_digIn0(di0_pin):
    # Digital Input line 0
    # @purpose:  to sense if the di0 is down
    # @param0:      unused
    # returns   str 1 if button is down (else 0)
    
    if GPIO.input(di0_pin) :
       downInt=1
    else:
       downInt=0
    # end if
    return str(downInt)
# end def

def control_redLED(ledR_pin,onOne):
    # @purpose:  to turn the Red Led on or off
    # @param0:  int onOne 1 to turn it ON (0 to turn it OFF)
    # @returns:  nothing

    #print("in control_redLED, pin:",ledR_pin)
    #print("onOne:",onOne)
    #a=input("pause in redLED")
    if (onOne=="1"):
        #a=input("before GPIO 1")

        #import RPi.GPIO as GPIO
        GPIO.setmode(GPIO.BCM)
        GPIO.setup(ledR_pin,GPIO.OUT)

        GPIO.output(ledR_pin,GPIO.HIGH)
        #a=input("after GPIO redLED HIGH")
    # end if
    if (onOne=="0"):    
        #a=input("before GPIO 0")

        #import RPi.GPIO as GPIO
        GPIO.setmode(GPIO.BCM)
        GPIO.setup(ledR_pin,GPIO.OUT)
        
        GPIO.output(ledR_pin,GPIO.LOW)
        #a=input("after GPIO redLED LOW")
    # end if
    return
# end def

def control_digOut0(do0_pin,onOne,HIGH,LOW,RPiXGPIO):   #72
    # Digital Output line 0
    # @purpose:  to turn the do0 on or off (on = 3.3v)
    # @param0:   pin (# for RPi.GPIO, object for RPi7GPIO)
    # @param1:   int onOne 1 to turn it ON (0 to turn it OFF)
    # @param2:   HIGH:
    # @param3:   LOW
    # @param4:   RPiGPIO (aka global)
    # returns   nothing
    #print("@2.1668 in control_digOut0, do0_pin,onOne:",do0_pin,",",onOne)
    #GPIO.setup(do0_pin,GPIO.OUT)   #72 must do in calling routine
    if (onOne==True):
        STATE=HIGH
    else:
        STATE=LOW
    #if end
    #print("@2.1680,RPiXGPIO:",RPiXGPIO)     #72 display which RPi?GPIO is being used
    #print("@2.1681,do0_pin:",do0_pin)
    GPIO.output(do0_pin,STATE)              #72
    return
# end def

def pir2_initGPIO(isVerbose,btn_pin, ledR_pin, di0_pin, do0_pin,diC_pin):
    # @purpose  to initialize the GPIO pins
    # @param0   bool isVerbose  False to minimize user dialog
    # @param1   int  btn_pin    GPIO pin of pushButton
    # @param2   int  ledR_pin   GPIO pin of redLED
    # @param3   int  di0_pin    GPIO pin of digIn0
    # @param4   int  do0_pin    GPIO pin of digOut0
    # @param5   int  diC_pin    GPIO pin of digInVC (not controlable)
    
    # returns   bool isPiR2     False if PiR2 does not exist

    import RPi.GPIO as GPIO
    import pir2A

    # GPIO setup
    GPIO.setmode(GPIO.BCM)

    #print("diC_pin:",diC_pin)
    
    # setup and enable the internal pull-up resistors
    # (and the internal resistors (300ohm) on the Output pins
    GPIO.setup(btn_pin,GPIO.IN,GPIO.PUD_UP)
    GPIO.setup(ledR_pin,GPIO.OUT)
    GPIO.setup(do0_pin,GPIO.OUT)
    GPIO.setup(di0_pin,GPIO.IN,GPIO.PUD_UP)
    GPIO.setup(diC_pin,GPIO.OUT)
    
    # set diC_pin as a source of 3v3 volts for di0_pin
    # This offers the protection of a internal 300 ohm
    #   resistor so that the 3v3 Voltage source is not
    #   used directly as a voltage source for the di0_pin

    # Note that di0 is on the pi's hardware pin 16.
    # For testing, it can be connected to either
    #    hardware pin 14 which is ground
    # or hardware pin 12 which outputs 3.3v through
    #    the internal 300 ohm resistor.

    # if a simple reed switch (ie a set of external
    #    contacts) is used to sense an open door, the
    #   2 contacts should be connected to pins 12 and 16.

    #  The following statement provides 3v3 on the diC_pin
    #   through the internal 300 ohm resistor.

    #global g_diC_pin #done in the calling program
    #g_diC_pin=17
    
    #a=input("before diC")
    GPIO.output(diC_pin,GPIO.HIGH)
    #a=input("after diC")
    

    
    if (isVerbose):
        #test to see if the PiR2 controller is connected
        if GPIO.input(btn_pin) :
           downInt=0
        else:
           downInt=1
        # end if
        #print("downInt:",downInt)
        print("To continue, press the PiR2 pushButton within 5 sec")
        isPiR2=False
        downIntNew="0"
        timeStart=time.time()
        #print("Start:',timeStart)
        while downIntNew=="0" :
            if time.time()>timeStart+5.0 :
                # time is up
                if isPiR2==False :
                    print("pushButton was not pressed . . .")
                    print("but saw ctrl-c, Conclusion: . . .")
                    print("ERROR031:No PiR2 controller is connected.")
                    print("This test showed the push button always Up")
                    print("It looks like no PiR2 controller is connected.")
                    #####################################################
                    #GPIO.cleanup()
                    #####################################################
                    return
                #end if
            else:
                # time is not yet up
                #check the button again and again
                
                #acquire always returns a string
                downIntNew=acquire_pushButton(btn_pin)
                #print("downIntNew:",downIntNew)

                if downIntNew=="1" :
                    #button was pressed within 5 sec.
                    print("saw pushButton down")
                    isPiR2=True
                    break #out of the while
                # end if
            # end if
        # end while
        if isPiR2==True:
            # now test the PiR2
            print("pushButton is initially Up")
            print("red LED will be on for 4 seconds")
            print("")
            control_redLED(ledR_pin,"1") #on when GPIO.HIGH
            time.sleep(4)
            control_redLED(ledR_pin,"0") #off when GPIO.LOW
        #end if
    else:
        #if not verbose . . . presume PiR2 is connected
        isPiR2=True
    #end if
    return isPiR2
# end def pir2_initGPIO


def init_I2C_Bus(I2C_BUS_Number) :
    # @purpose  to initialize a specific I2C_BUS
    # @param0   int I2C_BUS_Number  eg 1
    #           bus # 1 uses GPIO2 and GPIO3 on the Raspberry Pi
    # @returns  obj busA        eg the object assigned to this I2C bus
    if I2C_BUS_Number !=1 :
        print("ERROR041-invalid bus number for I2C TMP102 (tempIdxx) iodevice")
        exit()
    else:
        busA=smbus.SMBus(I2C_BUS_Number)
    # end if
    return busA
# end def

def acquire_tempId00(busA,DEVICE_ADDRESS,unitsStr) :
    # @purpose to read the temp of this device on this bus in these units
    # @param0   obj busA created in the init_tempBus(I2C_BUS) method
    # @param1   int DEVICE_ADDRESS  eg 0x48 for the first TMP102 iodevice
    #                       TMP102 is factory hardwired to 0x48
    # @param2   str unitsStr     eg " C" for Centigrade (or " F" for Fahr)
    # @returns  str returnValue      eg 20.0 for Centigrade
    #
    # this routine also converts the temp into the requested units
    #
    temp_registerInt=0
    #print("@2.1818, DEVICE_ADDRESS:",DEVICE_ADDRESS)
    temp_reg_12bit=busA.read_word_data(DEVICE_ADDRESS,temp_registerInt)
    #eg temp_reg_12bit of 61459 is returned
    temp_loByte=(temp_reg_12bit & 0xff00) >>8
    temp_hiByte=(temp_reg_12bit & 0x00ff)
    # convert to temp from page 6 of datasheet
    tempInt=((( temp_hiByte * 256) + temp_loByte) >>4 )
    # handle negative temperatures
    if tempInt > 0x7ff :
        tempInt=tempInt - 4096
    # end if

    #convert to Centigrade first
    temp_C=float(tempInt) * 0.0625
    returnValueFloat=temp_C
    if unitsStr ==" F" or unitsStr==" f":
        temp_F=temp_C * 9/5 + 32
        returnValueFloat=temp_F
    # end if
    
    if unitsStr !=" C" and unitsStr!=" F" and unitsStr!=" c" and unitsStr!=" f" :
        print("WARNING: bad units requested:",unitsStr,": returning C units")
    # end if

    #must return a string not a float
    return str(returnValueFloat)
# end def

def control_imageTV(imageNameStr, folderStr, displaySecsCnt) :
    # @purpose To display an image on the TV HDMI for a number of seconds
    #           this uses the python fbi method and the pir2.osCommand()
    # @param0   str imageNameStr    name of image (jpg or png or tif etc)
    #                               including the .jpg etc
    # @param1   str folderStr       usually "" indicating that the
    #                               image is in PiR2/images folder
    #                               if not null the folder must be fully
    #                               specified eg "/home/pi/Desktop/PiR2/images/"
    # @param2   int displaySecCnt      the time duration to display the image
    #                               (after this time, screen returns to python)
    # @returns  bool returnValueBool    True if displayed ok, else False
    
    #displaySecsCnt=timeSecInt #seconds
    #print("Now display an image for ", displaySecsCnt," secs.")
    #print("@1852 into control_imageTV")   #72
    if folderStr == "" :
        folderStr="/home/pi/Desktop/PiR2/images/"
    else :
        if folderStr[0:1] != "/" :
            prefixStr="/"
        else :
            prefixStr=""
        # end if
        lenStr=len(folderStr)
        if folderStr[lenStr-1:lenStr] != "/" :
            suffixStr="/"
        else :
            suffixStr=""
        # end if
        folderStr=prefixStr+folderStr+suffixStr
    # end if
    imgStr=imageNameStr
    #imgStr="t.jpg"

    extraWait=2
    time.sleep(displaySecsCnt+extraWait)
    if False :                                 #72
        # to install fbi use terminal to:
        # >>> sudo apt-get update
        # >>> sudo apt-get -y install fbi
        cmdStr="sudo fbi -T 1 -d /dev/fb0 -a -1 -t "+str(displaySecsCnt)+" "
        # without sudo, permission is denied
        # -T 1 is tty1
        # -d "dev/fb0" is the ???
        # -a -1 is ??
        # -t is the time(sec) to display a photo (usually of a slide show)
        # followed by the fullPath/fileName
        #
        # python stripped off the trailing space above. . . doh
        # so I added it in below (2 spaces would also work)

        
        cmdwoImgStr=" "+cmdStr+folderStr
        cmdStr=cmdwoImgStr+imgStr
    else:                                      #72
        # try sxiv
        cmdStr=" nsxiv "+folderStr+imgStr
    #if end
    #testing
    #print("cmdStr:",cmdStr,":")
    #a=input("next")
    #print("@1878, cmdStr:",cmdStr)             #72
    osCommand(cmdStr)
    # if extraWait<=1 Raspian hangs, needs a reboot
    # if extraWait=2 it works fine

    # no known error checks/tests have been included
    returnValue=True
    return returnValue
#end control_imageTV()

def pir2versions():
    #global  g_pir2AVer
    #   g_pir2AVer="v46"
    global  g_pir2mainVer
    g_pir2mainVer="v46"
    global  g_pir2DataVer
    g_pir2DataVer="v45"
    global  g_pir2VersionsVer
    g_pir2VersionsVer="A03"
    global  g_validateLogVer
    g_validateLogVer="A02"
    global  g_pir1AVer
    g_pir1AVer="A01"
    global  g_globalTestVer
    g_globalTestVer="A01"
    global  g_sDateTimeVer
    g_sDateTimeVer="A01"
    global  g_test_TMP102Ver
    g_test_TMP102Ver="A03"

    print('piR2 modules, as of 2019GJul19 :')
    print(' g_pir2AVer:',g_pir2AVer,":")
    print(' g_pir2mainVer:',g_pir2mainVer,":")
    print(' g_pir2DataVer:',g_pir2DataVer,":")
    print(' g_pir2VersionsVer:',g_pir2VersionsVer,":")
    print(' g_validateLogVer:',g_validateLogVer,":")
    print(' g_pir1AVerVer:',g_g_pir1AVer,":")
    print(' g_globalTestVer:',g_globalTestVer,":")
    print(' g_sDateTimeVer:',g_sDateTimeVer,":")
    print(' g_test_TMP102Ver:',g_test_TMP102Ver,":")
    print("end")
    return
#end def pir2versions.py A03
                      
# /FLA42/PiR2/python/pir2A.py _A46
# /r/PiR2/python/pir2A.py
#end piR2A.py
