コード例 #1
0
ファイル: configurer.py プロジェクト: cwshugg/wrivcam
class Configurer:

    # Configurer Properties:
    #   filer       The Filer object used to write logs/package output
    #   lights      The LightManager used for toggling LEDs
    #   buttons     The ButtonManager used for user input
    #   dumper      The Dumper object used to dump files to a flash drive

    # Configurer Constants:
    #   TICK_RATE   The time interval (in seconds) at which the configurer
    #               ticks to check for/make updates
    #   WAIT_TIME   The time the Configurer waits before automatically going
    #               into dash-cam mode (this is in seconds)

    # Constructor
    def __init__(self):
        # create the filer, light manager, button manager, and dumper
        self.filer = Filer()
        self.lights = LightManager()
        self.buttons = ButtonManager()
        self.dumper = Dumper("dashdrive")

        # create constants
        self.TICK_RATE = 0.125
        self.WAIT_TIME = 5.0

        # try to log a new config session
        try:
            self.filer.log(
                "---------- New Config Session: " +
                str(datetime.datetime.now()) + " ----------\n", True)
        except:
            # assume the device's storage is full. Wipe it and try again
            self.wipeFiles(False)
            self.__init__()

    # Main function
    def main(self):
        # set up loop variables
        ticks = 0.0
        tickSeconds = 0.0

        # the terminate codes are as follows:
        #   -1      Don't terminate
        #    0      Terminate and launch dash cam
        #    1      Terminate and shut down
        terminateCode = -1

        self.filer.log("Configuration mode...\n")

        # main loop
        while (terminateCode < 0):
            # slowly flash the yellow light (twice every second)
            self.lights.setLED([0],
                               tickSeconds.is_integer()
                               or (tickSeconds + 0.5).is_integer())

            tickString = "[Ticks: {t1:9.2f}]  [Running Time: {t2:9.2f}]"
            tickString = tickString.format(t1=ticks, t2=int(tickSeconds))
            tickString = "[config]  " + tickString

            # if the WAIT_TIME has been exceeded, terminate with code 0
            if (tickSeconds == self.WAIT_TIME):
                tickString += "  (Wait time exceeded: terminating configuration and launching dash cam...)"
                terminateCode = 0

            # check for user input (red/yellow hold: shut down)
            if (self.buttons.isPowerPressed()
                    and self.buttons.durations[0] * self.TICK_RATE >= 2.0
                    and self.buttons.isCapturePressed()
                    and self.buttons.durations[1] * self.TICK_RATE >= 2.0):
                self.filer.log("Red/Yellow buttons held. Shutting down...")
                # create a controller and use its shutdown sequence
                shutdown_pi(self.lights, [self.buttons])
            # check for user input (output config)
            elif (self.buttons.isCapturePressed()
                  and self.buttons.durations[1] * self.TICK_RATE < 1.0
                  and self.buttons.durations[1] * self.TICK_RATE >= 0.25
                  and not self.buttons.isPowerPressed()):
                self.filer.log("Entering output config...\n")
                # disable yellow LED
                self.lights.setLED([0], False)
                self.mainOutput()

                # reset button durations (so shutdown doesn't trigger)
                self.buttons.durations = [0, 0]
                # reset the ticks/tickSeconds
                ticks = 0.0
                tickSeconds = 0.0
                # flash yellow LED to indicate mode switch
                self.lights.flashLED([0], 2)
            # check for user input (connect config)
            elif (self.buttons.isPowerPressed()
                  and self.buttons.durations[0] * self.TICK_RATE < 1.0
                  and self.buttons.durations[0] * self.TICK_RATE >= 0.25
                  and not self.buttons.isCapturePressed()):
                self.filer.log("Entering connect config...\n")
                # disable yellow LED
                self.lights.setLED([0], False)
                self.mainConnect()

                # reset button durations (so shutdown doesn't trigger)
                self.buttons.durations = [0, 0]
                # reset the ticks/tickSeconds
                ticks = 0.0
                tickSeconds = 0.0
                # flash yellow LED to indicate mode switch
                self.lights.flashLED([0], 2)

            # only log the tickString if the ticks are currently on a second
            if (tickSeconds.is_integer()):
                self.filer.log(tickString + "\n")

            # update the ticks
            ticks += 1
            tickSeconds += self.TICK_RATE
            # sleep for one TICK_RATE
            sleep(self.TICK_RATE)

        # the loop was terminated: determine why
        if (terminateCode == 0):
            # force GPIO-using classes to clean up
            self.lights.__del__()
            self.buttons.__del__()

            self.filer.log(
                "--------- Config Session Ended: " +
                str(datetime.datetime.now()) + " ---------\n\n", True)
            # create a controller to launch the dash cam
            cont = Controller()
            cont.main()

    # Output Mode main function
    def mainOutput(self):
        # set up loop variables
        ticks = 0.0
        tickSeconds = 0.0
        # terminate codes are as follows:
        #   -1      Don't terminate
        #    0      Terminate and return to config
        terminateCode = -1

        # main loop
        while (terminateCode < 0):
            # slowly flash the red/blue lights (twice every second)
            self.lights.setLED([1, 2],
                               tickSeconds.is_integer()
                               or (tickSeconds + 0.5).is_integer())

            # create a tick string
            tickString = "[Ticks: {t1:9.2f}]  [Running Time: {t2:9.2f}]"
            tickString = tickString.format(t1=ticks, t2=int(tickSeconds))
            tickString = "[config-output]  " + tickString

            # get button durations before updating them
            captureDuration = self.buttons.durations[1]
            powerDuration = self.buttons.durations[0]
            # call the button methods to update the button durations
            self.buttons.isCapturePressed()
            self.buttons.isPowerPressed()

            # check for red AND yellow button duration
            if (captureDuration > 0.0 and captureDuration < ticks
                    and powerDuration > 0.0 and powerDuration < ticks):
                # if the buttons are released, go back
                if (not self.buttons.isCapturePressed()
                        and not self.buttons.isPowerPressed()):
                    tickString += "  (Capture/Power buttons were held)"
                    terminateCode = 0
            # check for red button duration
            elif (captureDuration > 0.0 and captureDuration < ticks
                  and powerDuration == 0.0):
                # flash at 1.5 seconds (and still being held down) to indicate
                # that files will be sent to the flash drive upon button release
                if (captureDuration * self.TICK_RATE >= 1.5 and
                    (captureDuration * self.TICK_RATE) - 1.5 <= self.TICK_RATE
                        and self.buttons.isCapturePressed()):
                    self.lights.setLED([1, 2], False)
                    self.lights.flashLED([0], 1)

                # if the button is released...
                if (not self.buttons.isCapturePressed()):
                    # if released under 1.5 seconds, package output
                    if (captureDuration * self.TICK_RATE < 1.5):
                        # disable all lights
                        self.lights.setLED([0, 1, 2], False)
                        # package the output
                        self.filer.packageOutput("output.zip", self.lights)
                    # otherwise, dump to flash drive
                    elif (captureDuration * self.TICK_RATE >= 1.5):
                        # disable all lights
                        self.lights.setLED([0, 1, 2], False)
                        # dump output to flash drive, if it's plugged in
                        if (self.dumper.driveExists()):
                            self.filer.log("Drive found. Dumping files...\n")
                            self.lights.setLED([2], True)
                            # dump files
                            self.dumper.dumpToDrive(self.filer)
                            # flash the blue/red lights to show success
                            self.lights.flashLED([1, 2], 3)
                        # otherwise, flash red light to show the drive wasn't found
                        else:
                            self.filer.log(
                                "Drive not found. Cannot dump files.\n")
                            self.lights.flashLED([1], 3)
            # check for yellow button (convert videos)
            elif (powerDuration > 0.0 and powerDuration < ticks
                  and captureDuration == 0.0):
                # flash at 1.5 seconds (if the button is still held) to indicate
                # that files will be deleted upon button release
                if (powerDuration * self.TICK_RATE >= 1.5 and
                    (powerDuration * self.TICK_RATE) - 1.5 <= self.TICK_RATE
                        and self.buttons.isPowerPressed()):
                    self.lights.setLED([1, 2], False)
                    self.lights.flashLED([0], 1)

                # if the button is released
                if (not self.buttons.isPowerPressed()):
                    # if released under 1.5 seconds, convert the videos
                    if (powerDuration * self.TICK_RATE < 1.5):
                        self.lights.setLED([0, 1, 2], False)
                        # convert videos to mp4
                        self.filer.convertVideos(self.lights)
                    # otherwise, delete the output
                    elif (powerDuration * self.TICK_RATE >= 1.5):
                        self.wipeFiles()

            # log tick string if the tick is on a second
            if (tickSeconds.is_integer()):
                self.filer.log(tickString + "\n")

            # update ticks
            ticks += 1
            tickSeconds += self.TICK_RATE
            # sleep for one TICK_RATE
            sleep(self.TICK_RATE)

        # print termination message
        if (terminateCode == 0):
            self.filer.log("Returning to config...\n")
            # disable blue/red LEDs
            self.lights.setLED([1, 2], False)

    # Helper function for mainOutput() that wipes all media files from the device.
    # Takes in an optional argument of whether or not to toggle the lights when
    # wiping the files
    def wipeFiles(self, toggleLights=True):
        if (toggleLights):
            # set the red LED to ON while files are deleted
            self.lights.setLED([0, 1, 2], False)
            self.lights.setLED([1], True)

        # invoke system commands to wipe the media/log files
        os.system("sudo rm -rf ../logs")
        os.system("sudo rm -rf ../media")
        sleep(7)
        # sleep for a short time before attempting anything else
        # since the current log file was destroyed, write to
        # a new one stating what happened
        self.filer.checkDirectories()
        self.filer.log("[config-output]  Wiping all output files...\n")

        if (toggleLights):
            # flash red/blue alternating to indicate the files were
            # permanently deleted
            self.lights.flashLED([1, 2], 4)
            self.lights.setLED([1, 2], False)

    # Connect Mode main function
    def mainConnect(self):
        # set up loop variables
        ticks = 0.0
        tickSeconds = 0.0
        # terminate codes are as follows:
        #   -1      Don't terminate
        #    0      Terminate and return to config
        terminateCode = -1

        # main loop
        while (terminateCode < 0):
            # slowly flash the blue/yellow lights (twice every second)
            self.lights.setLED([0, 2],
                               tickSeconds.is_integer()
                               or (tickSeconds + 0.5).is_integer())

            # create tick string
            tickString = "[Ticks: {t1:9.2f}]  [Running Time: {t2:9.2f}]"
            tickString = tickString.format(t1=ticks, t2=int(tickSeconds))
            tickString = "[config-connect]  " + tickString

            # check for red/yellow button hold (back to config)
            if (self.buttons.isCapturePressed()
                    and self.buttons.durations[1] * self.TICK_RATE >= 1.0
                    and self.buttons.isPowerPressed()
                    and self.buttons.durations[0] * self.TICK_RATE >= 1.0):
                tickString += "  (Capture/Power buttons were held)"
                terminateCode = 0

            # log the tick string if the tickSeconds is on a second
            if (tickSeconds.is_integer()):
                self.filer.log(tickString + "\n")

            # update ticks
            ticks += 1
            tickSeconds += self.TICK_RATE
            # sleep for one TICK_RATE
            sleep(self.TICK_RATE)

        # print termination message
        if (terminateCode == 0):
            self.filer.log("Returning to config...\n")
            # disable blue/yellow LEDs
            self.lights.setLED([0, 2], False)
コード例 #2
0
class Controller:

    # Controller properties:
    #   camera       The Camera object used to record videos/take pictures
    #   filer        The Filer object responsible for managing system files
    #   lights       The LightManager object used for toggling LEDs
    #   buttons      The ButtonManager used to sense button presses

    # Controller constants:
    #   TICK_RATE    The time interval (in seconds) at which the system ticks
    #                to check for/make updates
    #   PASSIVE_LEN  The length (in seconds) of the dash cam's passive videos

    # Constructor
    def __init__(self):
        # create a camera
        self.camera = DashCam()
        # create a Filer
        self.filer = Filer()
        # create a light manager and turn on the power LED
        self.lights = LightManager()
        self.lights.setLED([0], True)
        # create a button manager
        self.buttons = ButtonManager()

        # create constants
        self.TICK_RATE = 0.125
        self.PASSIVE_LEN = 10.0 * 60

        # log that a new session has begun
        self.filer.log(
            "---------- New Session: " + str(datetime.datetime.now()) +
            " ----------\n", True)

    # Destructor
    def __del__(self):
        # log that the session has ended
        self.filer.log(
            "--------- Session Ended: " + str(datetime.datetime.now()) +
            " ---------\n\n", True)

    # Main process function. Loops indefinitely until the program is terminated
    def main(self):
        # start passively recording
        self.passiveRecording(1)

        # termination code: used to help determine why the main loop
        # was broken (could be the power button, could be too hot
        # of a cpu, etc.)
        #   -1  =  not terminated
        #    0  =  CPU is too hot
        #    1  =  power button was pressed
        #    2  =  debug terminate (exit program but keep pi powered on)
        terminateCode = -1

        # --------------- main loop --------------- #
        ticks = 0.0
        tickSeconds = 0.0
        tickPrintRate = 1
        # logs the tickString every 'tickPrintRate' seconds
        terminate = False
        while (not terminate):
            tickOnSecond = tickSeconds.is_integer()
            # write a tick string to be printed to the log
            tickString = "Tick: {t1:9.2f}  |  Running Time: {t2:9.2f}"
            tickString = tickString.format(t1=ticks, t2=int(tickSeconds))
            tickHeader = "[dashcam]  "
            tickHeader += "[LED: " + (str(self.lights.states[0]) + str(
                self.lights.states[1]) + str(self.lights.states[2])) + "]  "
            tickHeader += "[Button: " + (
                str(int(self.buttons.durations[0] * self.TICK_RATE)) + "|" +
                str(int(self.buttons.durations[1] * self.TICK_RATE)) + "]  ")
            tickString = tickHeader + tickString

            # check the CPU temperature (terminate if needed)
            if (tickOnSecond):
                cpuTemp = self.getCPUTemp()
                terminate = cpuTemp > 80.0
                # add to the tick string
                tickString += "  [CPU Temp: " + str(cpuTemp) + "]"
                # if the temperature exceeds the threshold, stop the program
                terminate = cpuTemp > 80
                if (terminate):
                    self.filer.log("CPU running too hot! Shutting down...")
                    terminateCode = 0

            # perform any mode actions needed
            tickString += self.update(ticks, tickSeconds)

            # grab the initial button durations
            powerDuration = self.buttons.durations[0]
            captureDuration = self.buttons.durations[1]
            # call the button-detection methods to update their durations
            self.buttons.isPowerPressed()
            self.buttons.isCapturePressed()

            # check for both buttons being pressed
            if (self.buttons.isPowerPressed()
                    and powerDuration * self.TICK_RATE >= 2.0
                    and self.buttons.isCapturePressed()
                    and captureDuration * self.TICK_RATE >= 2.0):
                # terminate and shut down
                terminate = True
                terminateCode = 1
            # check for power button press
            elif (self.buttons.isPowerPressed()
                  and powerDuration * self.TICK_RATE >= 2.0
                  and not self.buttons.isCapturePressed()):
                # terminate but don't shut down
                terminate = True
                terminateCode = 2
            # check for capture button press (TAKE PICTURE)
            elif (captureDuration * self.TICK_RATE <= 2.0
                  and captureDuration * self.TICK_RATE > 0.0
                  and not self.buttons.isCapturePressed()):
                self.filer.log("Capturing image...")
                # flash LED and take picture
                self.lights.flashLED([1], 2)
                self.camera.takePicture(Filer.makeFileName(1),
                                        self.filer.imagePath)

            # update the camera's overlay text
            if (self.camera.currVideo != None):
                self.camera.updateOverlays(datetime.datetime.now())
                self.camera.currVideo.duration += self.TICK_RATE

            # sleep for one tick rate
            self.camera.picam.wait_recording(self.TICK_RATE)
            # log the tick string
            if (tickSeconds % tickPrintRate == 0):
                self.filer.log(tickString + "\n")
            # increment ticks
            ticks += 1
            tickSeconds += self.TICK_RATE
        # ----------------------------------------- #

        self.filer.log("Terminate Code: " + str(terminateCode) + "\n")
        # check terminate code: shutdown if needed
        if (terminateCode == 0 or terminateCode == 1):
            self.filer.log("Shutting down...\n")
            shutdown_pi(self.lights, [self.buttons, self])

        if (terminateCode == 2):
            # flash LED to show debug terminate
            self.lights.setLED([0, 1], False)
            self.lights.flashLED([0, 1], 5)
            self.filer.log(
                "Terminating dash cam, but keeping Pi powered on...\n")

    # ------------------------ Mode Updates ------------------------ #
    # Main update method for whatever mode the dash cam is in. Takes in
    # the main loop's tick and tick-second count. Returns a string to
    # be added to the "tickString" in the main loop to reflect any
    # changes that were made in this function
    def update(self, ticks, tickSeconds):
        stringAddon = ""
        tickOnSecond = tickSeconds.is_integer()

        # if the tick is on a second, toggle the "rolling" LED
        if (tickOnSecond):
            self.lights.setLED([1], not self.lights.getLED(1))

        # if it's time to save the video, split the recording
        if (tickSeconds % self.PASSIVE_LEN == 0 and ticks > 0):
            self.passiveRecording(0)
            # add to the tick string
            stringAddon += "  (Starting next passive video)"
            # flash LED
            self.lights.flashLED([1], 4)

        # return the string to be added to the tickString
        return stringAddon

    # -------------------- File Saving/Deleting -------------------- #
    # Deletes the oldest passive recording and begins a new one (or splits one
    # that's already running)
    def passiveRecording(self, isNew):
        # first, delete the oldest passive recording
        self.filer.deleteOldestPassive()

        # depending on the given input, either START a new video, or STOP the
        # current one and create a new one
        if (isNew):
            self.filer.log("Beginning passive recording...\n")
            self.camera.startVideo(Filer.makeFileName(0),
                                   self.filer.passivePath)
        else:
            self.filer.log("Splitting passive recording...\n")
            self.camera.stopVideo()
            self.camera.startVideo(Filer.makeFileName(0),
                                   self.filer.passivePath)

    # ---------------------- Helper Functions ---------------------- #
    # Function that finds the cpu's current temperature and returns the value
    # as a double
    def getCPUTemp(self):
        # run the temp check in the command line and parse the output
        tempOutput = os.popen(
            "vcgencmd measure_temp | egrep -o '[0-9]*\.[0-9]*'").readlines()
        return float(tempOutput[0].replace("\n", ""))