コード例 #1
0
ファイル: Actuator.py プロジェクト: kronecker08/Tapomatic
class Actuator:

	# Class attributes that can be accessed using ActuatorControl.X (not actuatorcontrol.X)
	MAX_NUM_OF_SERVOS = 0		# Circular servos
	MAX_NUM_OF_MOTORS = 12		# Circular stepper or brushless DC motors
	MAX_NUM_OF_LINEAR_ACT = 5 	# Linear actuators
	N_A = 0						# Not Applicable

	# Circular & linear actuator direction CONSTANTS
	CCW = -1  			# Counter-Clockwise
	CW = 1    			# Clockwise
	LINEAR_IN = CCW		# Towardsbase of linear actuator
	LINEAR_OUT = CW		# Away from base of linear
	SERVO_SLACK = 0.2	# Positional accuaracy slack for servo so that control system does not go crazy
	FORWARD = 1
	BACKWARD = -1

	# Pin value CONSTANTS
	LOW =  0
	HIGH = 1

	# Wire value CONTSTANTS
	# Raspberry Pi 4 Pin Layout https://pinout.xyz/pinout/pin1_3v3_power
	NO_PIN = -1  						#TODO This constant may not be needed :)
	NO_WIRE = 0
	VCC_3_3V = 1
	VCC_3_3V_NAME = "BOARD1"     		# 3.3 Volts @ upto 0.050 Amps = 0.165 Watts https://pinout.xyz/pinout/pin1_3v3_power
	VCC_5V = 2
	VCC_5V_NAME = "BOARD2"        		# 5 Volts @ upto ~1.5 Amps (Power Adapter - Pi usgae) = 7.5 Watts https://pinout.xyz/pinout/pin2_5v_power
	GND = "BOARD6&9&14&20&25&30&34&39"	# Digital Ground (0 Volts) https://pinout.xyz/pinout/ground

	# Negative to NOT confuse it with Pi BOARD 12 https://pinout.xyz/pinout/pin12_gpio18
	HIGH_PWR_5V = 5					# 5.00 Volts @ upto 5.0 Amps = 25.0 Watts to power Pi, force / load cell sensor and servos
	HIGH_PWR_12V = 12					# 12.0 Volts @ upto 5.0 Amps = 70.0 Watts to power linear actuators
	HIGH_PWR_36V = 36					# TODO (30 or 36) Volts @ upto 5 Amps = 150 Watts to power Stepper Motors

	# wires are on the actuator side of hardwrae schematic. While pins are on the CPU side, but often have similar names
	wires = [NO_WIRE, NO_WIRE, NO_WIRE, NO_WIRE, NO_WIRE, NO_WIRE, NO_WIRE]


	def __init__(self, aType, actuatorID, pins, partNumber, direction):
		"""
		Constructor to initialize an Actutator object, which can be an AngularServo(), Motor(), or Relay()

		Key arguments:
		self - Newly created object
		aType - Single String character to select type of actuator to create (S=Servo, M=Motor, R=Relay)
		actuatorID - Interger CONSTANT defined in Driver.py to enable quick array searches
		pins - Array to document wires / pins being used by Raspberry Pi to control an actuator
		partNumber - Vendor part number string variable (e.g. Seamuing MG996R)
		direction - Set counter-clockwise (CCW) / Linear IN or clockwise (CW) / Linear OUT as the forward direction

		Return value:
		Newly created Actuator() object
	    """

		currentProgramFilename = os.path.basename(__file__)
		self.DebugObject = Debug(True, currentProgramFilename)

		self.actuatorID = actuatorID
		self.actuatorType = aType

		numOfWires = len(pins)
		wires = np.empty(numOfWires, dtype=object)   # TODO wires = ndarray((len(pins),),int) OR wires = [None] * len(pins) 				# Create an array on same length as pins[?, ?, ?]
		for i in range(numOfWires):
			#TODO REMOVE print("PIN: "  + repr(i))
			self.wires[i] = pins[i]

		self.partNumber = partNumber
		self.forwardDirection = direction

		# The last wire in array is the PWM control pin
		tempServoObject = Servo(pins[0]) #TODO REMOVE BECAUSE TO SIMPLE AN OBJECT
		#tempServoObject = gpiozero.Servo(pins[0]) #TODO REMOVE BECAUSE TO SIMPLE AN OBJECT
		tempAngularServoObject = AngularSevo(wires[len(wires)-1])

    	# The last two wires in array are the INPUT control pins
		tempMotorObject = Motor(wires[len(wires)-2], wires[len(wires)-1])

	    # The last wire in array is the relay control pin
		tempRelayObject = OutputDevice(wires[len(wires)-1])

	    # https://gist.github.com/johnwargo/ea5edc8516b24e0658784ae116628277
	    # https://gpiozero.readthedocs.io/en/stable/api_output.html
	    # https://stackoverflow.com/questions/14301967/bare-asterisk-in-function-arguments/14302007#14302007
		if(aType == "S"):
			self.actuatorObject = tempAngularServoObject
			#TODO If above DOES NOT WORK: self.actuatorType = Servo(wires[0], initial_value=0, min_pulse_width=1/1000, max_pulse_width=2/1000, frame_width=20/1000, pin_factory=None)
		elif(aType == "M"):
			self.actuatorObject = tempMotorObject
			#TODO If above DOES NOT WORK: self.actuatorType = Motor(wires[0], wires[1], pwm=true, pin_factory=None)
		elif(aType == "R"):
			self.actuatorObject = tempRelayObject
			#TODO If above DOES NOT WORK: self.actuatorObject = gpiozero.OutputDevice(wired[0], active_high=False, initial_value=False)
		else:
			self.DebugObject.Dprint("INVALID Actutator Type in __init__ method, please use S, M, or R string as first parameter to Actuator() Object")


	def Run(self, duration, newPosition, speed, direction):
		#TODO https://www.google.com/search?q=pass+object+to+python+function&rlz=1C1GCEA_enUS892US892&oq=pass+object+to+python+function&aqs=chrome..69i57.5686j0j7&sourceid=chrome&ie=UTF-8
		#TODO https://stackoverflow.com/questions/20725699/how-do-i-pass-instance-of-an-object-as-an-argument-in-a-function-in-python
		"""
		Run an actuator for a given number of milliseconds to a given position at percentage of max speed in FORWARD or BACKWARDS direction

		Key arguments:
		duration - Time actuator is in motion, for Servo() objects this can be used to control speed of movement
		newPosition - New position between -1 and 1 that  actuator should move to
		speed - Speed at which actuator moves at, for Servo() objects this parameter is NOT used
		direction - Set counter-clockwise (CCW or LINEAR_IN) or clockwise (CW or LINEAR_OUT) as the forward direction

		Return Value:
		NOTHING
		"""

		print("Actuator.py Run() function started!")

		if(type == "S"):
			currentPosition = self.actuatorObject.value()
			if(currentPosition < (newPosition - Actuator.SERVO_SLACK)):
				self.actuatorObject.max() #TODO THIS MAY NOT STOP AND GO ALL THE WAY TO MAX POS
			elif(currentPosition > (newPosition - Actuator.SERVO_SLACK)):
				self.actuatorObject.min() #TODO THIS MAY NOT STOP AND GO ALL THE WAY TO MIN POS
			else:
				# NEAR to new position DO NOTHING
				self.actuatotObject.dettach()
		elif(type == "M"):
			#TODO Write motor control code

			Motor.enable() #TODO CHANGE TO self.acutatorObject
			currentPosition = actuatorObject.value

			while(currentPosition != newPosition):
				if(actuatorObject.forwardDirection == Actuator.CW):
					Motor.forward(speed)
				else:
					Motor.reverse(speed)

				currentPosition = self.actuatorObject.value

			sleep(duration)    #TODO signal.pause(duration)
			Motor.disable()

		elif(type == "R"):
			relay.on()
			sleep(duration) 	#TODO signal.pause(duration)
			relay.off()
		else:
			self.DebugObect.Dprint("INVALID Actutator Type sent to Run method, please use S, M, R as first parameter to Actuator() Object")

		self.DebugObject.Dprint("Run function completed!")


	def setAngularPosition(self, newAngle):
		"""
		Set the rotational position of a AngularServo() or Motor() object

		Key arguments:
        newAngle - Rotational angle to set actuator to, more exact for Servo() objects then Motor() object

        Return value:
        NOTHING
        """

		if(self.actuatorType == "S"):
			self.angle = newAngle
		elif(self.actuatorType == "M"):
			self.DebugObject.Dprint("THIS CODE IS GOING TO BE HARD")
			#TODO Possible global variable with dead recoking needed
		elif(self.actuatorType == "R"):
			self.Debug.Dprint("Relays do not have rotational positions. Are you sure you called the correct object?")
		else:
			self.DebugObject.Dprint("INVALID Actutator Type sent to SetAngularPosition method, please use S, M, R as first parameter to Actuator() Object")


	def getPosition(self):
		"""
		Read the linear or rotational positon on an actuator

		Return value:
		The position of actuator, with value between -1.0 and 1.0 inclusively
		"""

		if(self.actuatorType == "S"):
			print("TODO")
			#TODO return self.value


	def isActive(self):
		"""
		Determine if actuator is moving

		Return value:
		TRUE if actuator is powered on and moving, FALSE otherwise
		"""

		return self.actuatorOjbect.isActive


	def setAngle(self, angle):
		print("TODO")
コード例 #2
0
ファイル: Actuator.py プロジェクト: kronecker08/Tapomatic
	from gpiozero import PingServer, pi_info

	# Useful pin status tools and math tools
	from gpiozero.tools import all_values, negated, sin_values

	# Useful for controlling devices based on date and time
	from gpiozero import TimeOfDay

	# Allow control of output devices such as Motors, Servos, LEDs, and Relays
	from gpiozero import Motor, Servo, LED, Energenie, OutputDevice

except ImportError:
	#TODO DO LOW LEVEL PIN CONTROL THAT WORKS EVER WHERE? http://wiringpi.com/the-gpio-utility/
	currentProgramFilename = os.path.basename(__file__)
	ImportDebugObject = Debug(True, currentProgramFilename)
	ImportDebugObject.Dprint("WARNING: You are running code on Mac or PC (NOT a Raspberry Pi 4), thus hardware control is not possible.")


class Actuator:

	# Class attributes that can be accessed using ActuatorControl.X (not actuatorcontrol.X)
	MAX_NUM_OF_SERVOS = 0		# Circular servos
	MAX_NUM_OF_MOTORS = 12		# Circular stepper or brushless DC motors
	MAX_NUM_OF_LINEAR_ACT = 5 	# Linear actuators
	N_A = 0						# Not Applicable

	# Circular & linear actuator direction CONSTANTS
	CCW = -1  			# Counter-Clockwise
	CW = 1    			# Clockwise
	LINEAR_IN = CCW		# Towardsbase of linear actuator
	LINEAR_OUT = CW		# Away from base of linear
コード例 #3
0
class CocoDrink:

    # Drink Name CONSTANTS
    NONE = "NONE"
    NO_DRINK = 0
    COCONUT = 1
    MAX_DRINK_NAME = COCONUT

    # Coconut sizing CONSTANTS
    SIZE_102MM = 102
    SIZE_88MM = 88

    # Addon Name CONSTANTS                  #TODO CONVERT INTERGERS TO STRINGS???
    IMMUNITY_BOOST = 1
    DAILY_VITAMINS = 2
    ENERGY_BOOST = 3
    PINA_COLADA = 4
    PINEAPPLE_FLAVOR = 5
    ORANGE_FLAVOR = 6
    MAX_ADD_ON_NAME = ORANGE_FLAVOR

    # Extra add-ons for v2021.1
    CBD = 7
    ENERGY_BOOST = 8
    ORGINAL_RED_BULL = 9
    RUM = 10

    # Add-On level CONSTANTS
    MAX_ADD_ON_LEVEL = 5
    MIN_ADD_ON_LEVEL = 0

    # User GUI.py program flow selection CONSTANTS
    TAPPED = 0
    TOPPED_OFF = 1  #TODO REMOVE - LIKELY NOT GOING TO BE SUPPORTED IN v2020.1
    FLAVOR = 2
    HEALTH_ADDITIVE = 3

    # LASER branding PNG filename CONSTANTS
    RESORT_WORLD_LOGO = "ResortWorldLogoV0.png"
    COCOTAPS_LOGO = "CocoTapsLogoV0.png"
    WYNN_HOTEL_LOGO = "WynnHotelLogoV0.png"
    RED_BULL_LOGO = "RedBullLogoV0.png"
    BACARDI_LOGO = "BacardiLogoV0.png"
    ROYAL_CARRIBBEAN_LOGO = "RoyalCarribbeanLogoV0.png"

    def __init__(self, drinkNameID, addOnFlavor, addOnFlavorLevel,
                 addOnHealthAdditive, addOnHealthAdditiveLevel, tapOrCutFlow,
                 brandingArt):
        """
		Constructor to initialize an CocoDrink() object, which stores add-ons and artwork

		Key arguments:
		self -- Newly created CocoDrink object
		drinkNameID -- CONSTANT product name of drink
		addOnFlavor -- CONSTANT product name of flavoring being added to base drink (e.g. IMMUNITY_BOOST, PINA_COLADA, etc)
		addOnFlavorLevel -- The unit step amount of flavor to be added to a drink from 0 to MAX_ADD_ON_LEVEL
							Each unit is a different but constant volume (in milliLiters) for each flavor
		addOnHealthAdditive -- CONSTANT product name of  health additive being added to base liquid
		addOnHealthAdditiveLevel -- The unit step amount of additive to be added to a drink from 0 to MAX_ADD_ON_LEVEL
									Each discrete unit is a differnt but constant volume (in milliLiters) for each additive
		tapOrCutFlow -- Variable used as conditional in program flow to determin if coconut is topped off or tapped 
		brandingArt -- PNG image to LASER brand onto the side of the coconut (max size is 200 MB or ? x ? pixels / ? x ? inches)

		Return value:
		New CocoDrink() object
		"""

        currentProgramFilename = os.path.basename(__file__)
        self.DebugObject = Debug(True, currentProgramFilename)

        self.drinkNameID = drinkNameID
        self.addOnFlavor = addOnFlavor
        self.addOnFlavorLevel = addOnFlavorLevel
        self.addOnHealthAdditive = addOnHealthAdditive
        self.addOnHealthAdditiveLevel = addOnHealthAdditiveLevel
        self.tapOrCutFlow = tapOrCutFlow  #TODO REMOVE - LIKELY NOT GOING TO BE SUPPORTED IN v2020.1
        self.brandingArt = brandingArt

        if (addOnFlavorLevel > CocoDrink.MAX_ADD_ON_LEVEL
                or addOnHealthAdditiveLevel > CocoDrink.MAX_ADD_ON_LEVEL):
            self.DebugObject.Dprint(
                "OBJECT CREATION ERROR: You created a CocoDrink() object with add-on level greater then "
                + MAX_ADD_ON_LEVEL)
            __exit__()  # Destructor / memory clean up

        if (addOnFlavorLevel < 0 or addOnHealthAdditiveLevel < 0):
            self.DebugObject.Dprint(
                "OBJECT CREATION ERROR: You created a CocoDrink() object with add-on level less then "
                + MIN_ADD_ON_LEVEL)
            __exit__()  # Destructor / memory clean up

        if (CocoDrink.NO_DRINK > drinkNameID
                or drinkNameID > CocoDrink.MAX_DRINK_NAME):
            self.DebugObject.Dprint(
                "OBJECT CREATION ERROR: You created a CocoDrink() object with a drink name that doesn't exist. Why did you do that genius?"
            )
            __exit__()  # Destructor / memory clean up

    def __enter__(self):
        """
		The 'with' statement clarifies code that previously would use try...finally blocks to ensure that clean-up code is executed. 
		https://docs.python.org/2.5/whatsnew/pep-343.html
		https://stackoverflow.com/questions/1984325/explaining-pythons-enter-and-exit

		Key arguments:
        NONE
        
		Return value:
		NOTHING
		"""

        print("in __enter__")

        return self

    def __exit__(self, exception_type, exception_value, traceback):
        """
		Memory cleanup

		Key arguments:
		exception_type --
		exception_value --
		traceback --

		Return value:
		NOTHING
		"""

        self.DebugObject.Dprint("Cleaning up CocoDrink() object in __exit__ ")

    def GetAddOn(self, lType):
        """
		Determine the type of add-on the user selected from GUI.py and branch code floW

		Key arguments:
		lType -- Type of liquid add-on to inject into a coconut

		Return value:
		addOn -- CocoDrink CONSTANT based on user selection
		"""

        if (lType == CocoDrink.FLAVOR):
            addOn = self.GetFlavorType()
        elif (lType == CocoDrink.HEALTH_ADDITIVE):
            addOn = self.GetHealthAdditiveType()  #TODO OR (self)

        return addOn

    def GetFlavorType(self):
        """
	    Get product name of flavoring
	    
	    Key arguments:
	    NONE
	    
	    Return value:
	    Integer GLOBAL CONSTANT from CocoDrink.py
	    """

        return self.addOnFlavor

    def GetHealthAdditiveType(self):
        """
		Get product name of additive fluid

    	Key arguments:
        NONE
        
    	Return value:
    	return Integer GLOBAL CONSTANT from CocoDrink.py
		"""

        return self.addOnHealthAdditive

    def GetLaserArtType(self):
        """
		Get filename of logo LASER branding into coconut

    	Key arguments:
    	NONE

    	Return value:
    	return String GLOBAL CONSTANT from CocoDrink.py
		"""

        return self.brandingArt

    def GetFlavorLevel(self):
        """
		Get amount / level of flavor fluid in a coco drink in User Interface units

    	Key arguments:
        NONE
        
    	Return value:
    	return Integer value between 0 and  MAX_ADD_ON_LEVEL inclusively
		"""

        return self.addOnFlavorLevel

    def GetHealthAdditiveLevel(self):
        """
		Get amount / level of health additive fluid in a Cocodrink in User Interface units

    	Key arguments:
        NONE
        
    	Return value:
    	return Integer value between 0 and  MAX_ADD_ON_LEVEL inclusively
		"""

        return self.addOnHealthAdditiveLevel

    def ConvertLevelToVolume(self, addOnUnits):
        """
		Since each fluid has a different flavor or health effect strength per mL this function defined what the User Interface units represent

		Key arguments:
		addOnUnits -- Integer variable, between and 0 and MAX_ADD_ON_LEVEL (inclusive)

		Reture value:
		volume -- Interger variable, in units of mL for pumps to dispense
		"""

        self.addOnFlavor = addOnFlavor
        self.addOnFlavorLevel = addOnFlavorLevel
        self.addOnHealthAdditive = addOnHealthAdditive
        self.addOnHealthAdditiveLevel = addOnHealthAdditiveLevel

        if (self.addOnHealthAdditive == IMMUNITY_BOOST):
            oneUnitToMilliLiterFactor = 15
        elif (self.addOnHealthAdditive == DAILY_VITAMINS):
            oneUnitToMilliLiterFactor = 13.3
        elif (self.addOnHealthAdditive == ENERGY_BOOST):
            oneUnitToMilliLiterFactor = 13.3

        elif (self.addOnFlavor == PINA_COLADA):
            oneUnitToMilloiiterFactor = 12
        elif (self.addOnFlavor == PINEAPPLE_FLAVOR):
            oneUnitToMilliLiterFactor = 19
        elif (self.addOnFlavor == ORANGE_FLAVOR):
            oneUnitToMilliLiterFactor = 3

        else:
            self.DebugObject.Dprint("ERROR CODE (0x" +
                                    GLOBAL_CONSTANT_USAGE_ERROR +
                                    ") Verfiy that this Tapomatic unit has " +
                                    self.addOnHealthAdditice + " & " +
                                    self.addOneFlavor + " stocked.")

        volume = addOnUnits * oneUnitToMilliLeterFactor

        return volume

    def ConvertVolumeToPumpRunTime(volume, cocoPartNumber):
        """
		Since motor models can vary in dispense volume / sec this function adjusts run time 
        
        Key argument:
        volume -- Interger variable, of desired volume to be dispensed
        cocoPartNumber -- String variable, which is internal CocoTaps data in the format (XXX-YYYY-ZZ)) 
        
        Return value:
        seconds - Float variable, that pump should run in order to dispense the correct volume of liquid
        """

        thiccBoiFactor = 1  # Set to default value

        if (pumpPartNumber == "202-0006-A"):
            oneMilloLiterToSecondFactor = 2.2
        else:
            this.DebugObject.Dprint(
                "ERROR CODE (0x" + PUMP_CONFIGURATION_ERROR +
                ") Verify that you are using the correct pump part nuumber, as defined in Actuator.py and CocoDrink.py"
            )

        if (self.drinkName == PINA_COLADA):
            thiccBoiFactor = 1.75  # This flavoring is extra thick, pump longer to get correct volume

        seconds = volume * oneMilloLiterToSecondFactor * thiccBoiFactor

        return seconds

    def UnitTest():
        print("START CocoDrink.py UnitTest()")

        TestCocoDrinkObject = CocoDrink(CocoDrink.COCONUT, CocoDrink.NONE, 0,
                                        CocoDrink.DAILY_VITAMINS, 5,
                                        CocoDrink.TAPPED,
                                        CocoDrink.COCOTAPS_LOGO)
        print("AddOn flavor ID # is: ",
              TestCocoDrinkObject.GetAddOn(CocoDrink.FLAVOR))
        print("Flavor LEVEL (in User Interface units) selected was: ",
              TestCocoDrinkObject.GetFlavorLevel())

        print("AddOn health additive ID # is: ",
              TestCocoDrinkObject.GetAddOn(CocoDrink.HEALTH_ADDITIVE))
        print("Health additive LEVEL (in Use Interface units) selected was: ",
              TestCocoDrinkObject.GetHealthAdditiveLevel())

        print("END CocoDrink.py UnitTest()")
コード例 #4
0
import os

try:
    # Robotic Beverage Technologies code for controlling many different types of motors, servos, and relays
    from Actuator import *

    # Robotic Beverage Technologies code for controlling the physical movement and power output of a LASER
    from LASER import *

except ImportError:
    #TODO DO LOW LEVEL PIN CONTROL THAT WORKS EVER WHERE? http://wiringpi.com/the-gpio-utility/
    currentProgramFilename = os.path.basename(__file__)
    TempDebugObject = Debug(
        True, "Try/Catch ImportError in " + currentProgramFilename)
    TempDebugObject.Dprint(
        "WARNING - You are running code on Mac or PC (NOT a Raspberry Pi 4), thus hardware control is not possible."
    )


class CocoDrink:

    # Drink Name CONSTANTS
    NONE = "NONE"
    NO_DRINK = 0
    COCONUT = 1
    MAX_DRINK_NAME = COCONUT

    # Coconut sizing CONSTANTS
    SIZE_102MM = 102
    SIZE_88MM = 88
コード例 #5
0
class LASER:

    # Preset LASER power level CONSTANTS (units are Watts)
    HIGH_POWER = 10.00
    STANDARD_POWER = 5.00
    LOW_POWER = 2.50
    MAX_POWER_LEVEL = HIGH_POWER
    DEFAULT_LASER_CONSTANT = 0.05264472  #TODO Adjust this until LASER branding looks good

    # Global class variable
    laserConstant = -1
    laserConfig = -1

    def __init__(self, gpioFirePin, supplierPartNumber, cocoPartNumber,
                 powerLevel, maxPowerLevel, brandingArt):
        """
	    Create a LASER object with power settings, part numbers, and image data to used when fired via GPIO pin

	    Key arguments:
        gpioFirePin -- 5V GPIO pin used to control a LASER or a 12V relay connected to a LASER
	    supplierPartNumber -- External supplier part number (i.e. PA-07-12-5V)
	    cocoPartNumber -- Internal part number (i.e XXX-YYYYY-Z)) linked to one supplier part number
	    powerLevel -- Power in Wats to intialize a LASER module first fire to
	    maxPowerLevel -- Max power in Watts that LASER can support in continous operation (> 30 seconds)
	    brandingArt -- Black & White PNG image to brand / burn into an object

	    Return value:
	    New LASER() object
	    """

        currentProgramFilename = os.path.basename(__file__)
        self.DebugObject = Debug(True, currentProgramFilename)

        #self.gpioFirePin = gpiozero.OutputDevice(gpioFirePin)

        self.supplierPartNumber = supplierPartNumber

        if (len(cocoPartNumber) != 10):
            self.DebugObject.Dprint(
                "Invalid part number format, please verify part number looks like XXX-YYYYY-Z"
            )

        # List of current valid internal LASER part numbers
        if (cocoPartNumber == "205-0003-A" or cocoPartNumber == "???-????-?"):
            self.cocoPartNumber = cocoPartNumber
        else:
            self.DebugObject.Dprint("Invalid part number format")

        if (0 > powerLevel or powerLevel > LASER.MAX_POWER_LEVEL):
            # Check for valid power level and default to 10 Watts if invalid
            Debug.Dprint(
                DebugOject, "Invalid power. I'm  setting the LASER power to " +
                repr(self.maxPowerLevel / 2) + " Watts")
            self.powerLevel = self.MAX_POWER_LEVEL / 2
        else:
            self.powerLevel = powerLevel

        self.brandingArt = LASER.__WarpImage(
            CocoDrink.COCOTAPS_LOGO,
            CocoDrink.SIZE_102MM)  # Initialize to standard CocoTaps logo

        LASER.__ConfigureLaserForNewImage(self.brandingArt)

    def LoadImage(fileName):
        """

		Key arguments:
		filename -- PNG file to load into memory

		Return value:
		bwImg -- Black & White PNG image
		"""

        return bwImg

    def __WarpImage(fileName, coconutSize):
        """
		Load a PNG image on the local harddrive into RAM
		Wrap a straight / square image so that after LASER branding on coconut its straight again

		Key arguments:
		fileName -- PNG file to load into memory (TODO max size in ? x ? pixels / ?? MB)
		coconutSize -- Horizontal diameter of coconut in millimeters

		Return value:
		newImage -- A new image that has been warpped to to display correctly after LASER branding 
		"""

        # https://docs.opencv.org/2.4/doc/tutorials/core/mat_the_basic_image_container/mat_the_basic_image_container.html
        # https://pythonprogramming.net/loading-images-python-opencv-tutorial/

        fileNameLength = len(fileName)
        periodIndex = fileNameLength - 3
        fileExtension = fileName[periodIndex:]

        if (fileExtension.lower() == "png"):
            path = "static/images/" + fileName
            originalImage = cv2.imread(path)

            # Convert to gray scale first to apply better thresholding which will create a better black and white image
            grayImg = cv2.cvtColor(originalImage, cv2.COLOR_BGR2GRAY)

            # Anything above 127 on a scale from 0 to 255 is WHITE
            (thresh, bwImg) = cv2.threshold(grayImg, 127, 255,
                                            cv2.THRESH_BINARY)
        else:
            self.DebugObject.Dprint(
                "Please pass a .png file to the LoadImage() function in LASER.py code"
            )

        imgWidth = originalImage.size
        imgHeight = originalImage.size

        for xPixel in range(imgWidth):
            print("xPixel = ", xPixel)
            for yPixel in range(imgHeight):
                print("yPixel = ", yPixel)
                print(" of ", imgHeight)
                #TODO rgbColor = originalImage.at<Vec3b>(xPixel,yPixel)
                # Split image into three part vertically and horizonatlly
                ### TODO SyntaxError: can't assign to comparison
                # warppedImg.at<Vec3b>(xPixel,yPixel) = rgbColor

                if (xPixel < (imgWidth / 5)):
                    xPixel = xPixel + 8  # Skip EIGHT pixels since ends warps more at ends
                elif ((imgWidth / 5) <= xPixel and xPixel <
                      (imgWidth * 2 / 5)):
                    xPixel = xPixel + 4  # Skip EIGHT pixels since ends wraps more at ends
                elif ((imgWidth * 2 / 5) <= xPixel and xPixel <
                      (imgWidth * 3 / 5)):
                    xPixel = xPixel + 0  # Skip EIGHT pixels since ends wraps more at ends
                elif ((imgWidth * 3 / 5) <= xPixel and xPixel <
                      (imgWidth * 4 / 5)):
                    xPixel = xPixel + 4  # Skip EIGHT pixels since ends wraps more at ends
                elif ((imgWidth * 4 / 5) <= xPixel and xPixel < (imgWidth)):
                    xPixel = xPixel + 8  # Skip EIGHT pixels since ends wraps more at ends

    def __ConfigureLaserForNewImage(img):
        """
		PRIVATE FUNCATION (See __)

		Calculate pixel dwell duration based on LASER power level and image size

        Key arguments:
        img -- PNG file to load into memory

        Return value:
        pixelBurnDuration -- Time in seconds that LASER should dwell on coconut pixel
		"""

        numOfPixels = LASER.__GetNumOfPixels(img)
        moistureLevel = GetCoconutMoistureLevel()

        if (0 < self.powerLevel or self.powerLevel <= LOW_POWER):
            laserConstant = DEFAULT_LASER_CONSTANT * 0.5
        elif (LOW < self.powerLevel or self.powerLevel < STANDARD_POWER):
            laserConstant = DEFAULT_LASER_CONSTANT * 1.0
        elif (self.powerLevel >= STANDARD_POWER):
            laserConstant = DEFAULT_LASER_CONSTANT * 1.5
        else:
            self.DebugObject.Lprint(
                "ERROR: Invalid power level choosen in ConfigureLaserForNewImage() function"
            )

        pixelBurnDuration = laserConstant * moistureLevel / 100.0 * numOfPixels / 1000000

        return pixelBurnDuration

    def StopLaser(self):
        """
	    Toogle GPIO pin connected to high power relay LOW to turn OFF a LASER

	    Key arguments:
	    NONE

	    Return value:
	    NOTHING
	    """
        self.gpioFirePin.off()
        #gpiozero.off(self.gpioFirePin)

    def BurnImage(self, laserConfig):
        """
		Toogle GPIO pin possibly connected to a high power relay HIGH to turn ON a LASER
		Puts CPU to sleep so NOT a threadable function yet

		Key arguments:
		laserConfig -- TODO REMOVE?

		Return value:
		NOTHING
		"""

        pixelBurnDuration = self.__ConfigureLaserForNewImage

        dutyCycle = self.powerLevel / self.maxPowerLevel
        imageBurnComplete = False
        frequency = 100  # Desired LASER pulse in Hz
        while (not imageBurnComplete):
            # laserConstant is a class variable
            highTime = 1 / frequency * dutyCycle * laserConstant
            sleep(highTime)  # Sleep upto 10 ms and keep LASER ON
            self.gpioFirePin.on()
            sleep(0.010 - highTime)  # Sleep 10 ms minus time is HIGH
            self.gpioFirePin.off()

        imageBurnComplete = MoveLaserStepperMotor(pixelDwellDuration,
                                                  frequency)

    def MoveLaserStepperMotor(self, frequency, motorID):
        """

		Return value:
		NOTHING
		"""

        for pixelNum in range(0, GetNumOfPixels(filename) - 1):
            sleep(pixelDwellDuration + 1 / frequency)
            #TODO if(pixelNum = )

    def SetPowerLevel(self, watts, cocoPartNumber):
        """
		Set the power level based on LASER part number being used

		Key arguments:
		watts -- Power in Watts to set LASER output to
		cocoPartNumber -- Internal XXX-YYYYY-Z part number linked to a vendor part number
		"""

        if (cocoPartNumber == "205-00003-A"):
            if (0 > watts or watts > 10):
                self.DebugObject.Dprint(
                    self.DebugObject,
                    "The 400067260113 LASER must have power level between or equal to 0.1 and 10 Watts"
                )
            else:
                self.powerLevel = watts
        else:
            self.DebugObject.Dprint(
                "This LASER supplier part number is not supported in LASER.py code base"
            )

    def __GetNumOfPixels(inputImage):
        """
		Calculate the total number of (pixels / 1,000,000) that is in an image file

		Key argument:
        inputImage

		Return value:
		totalNumOfPixels -- Total number of megapixels (million pixels) in an image
		"""

        #img = LoadLImage(self.brandingArt #TODO DOES LoadImage RETURN a img variable)
        img = cv2.imread(inputImage)
        imgWidth = img.width
        imgHeight = img.height
        totalNumOfPixels = imgWidth * imgHeight

        return totalNumOfPixels

    def GetCoconutMoistureLevel(self):
        """
		Moisture level from 0 to 100 corresponing to % humidity

    	Key arguments:
    	NONE

    	Return value:
    	moisturePercentage -- An float from 0.0 to 100.0
		"""

        #TODO Moisture sensor in fridge
        print("TODO I2C sensor")
        moisturePercentage = 100
        return moisturePercentage

    def CompileImageAndStoreToEMMC():
        """
	    
	    Key arguments:
	    NONE
	    
	    Return value:
	    NOTHING
	    """

        LASER.__WarpImage()
コード例 #6
0
#import numpy as np     #TODO REMOVE SINCE IM NOT USE ARRAY ANY MORE
#import cv2 as cv       #TODO REMOVE SINCE MAKING SIMPLER IS DUMB!
import cv2

try:
    # Allow control of output devices such as LEDs and Relays
    from gpiozero import LED, OutputDevice
    #from gpiozero import Energenize #TODO REMOVE SINCE TOO SPECIFIC???

except ImportError:
    #TODO DO LOW LEVEL PIN CONTROL THAT WORKS EVER WHERE? http://wiringpi.com/the-gpio-utility/
    currentProgramFilename = os.path.basename(__file__)
    TempDebugObject = Debug(
        True, "Try/Catch ImportError in " + currentProgramFilename)
    TempDebugObject.Dprint(
        "WARNING - You are running code on Mac or PC (NOT a Raspberry Pi 4), thus hardware control is not possible."
    )


class LASER:

    # Preset LASER power level CONSTANTS (units are Watts)
    HIGH_POWER = 10.00
    STANDARD_POWER = 5.00
    LOW_POWER = 2.50
    MAX_POWER_LEVEL = HIGH_POWER
    DEFAULT_LASER_CONSTANT = 0.05264472  #TODO Adjust this until LASER branding looks good

    # Global class variable
    laserConstant = -1
    laserConfig = -1