Example #1
0
    def arrowkeys_to_dir(up_arrow: bool, down_arrow: bool, left_arrow: bool,
                         right_arrow: bool) -> int:
        """
        Function that converts arrowkey presses (at most 2 simultaneously) to a direction in range 0..7 including
        """
        if not up_arrow and not down_arrow and not left_arrow and not right_arrow:
            logger.error(
                "Direction not clear --> No arrowkeys pressed. Try again.")
            return Direction.NULL.value

        if up_arrow:
            if left_arrow:
                return Direction.upleft.value
            if right_arrow:
                return Direction.upright.value
            return Direction.up.value
        if down_arrow:
            if left_arrow:
                return Direction.downleft.value
            if right_arrow:
                return Direction.downright.value
            return Direction.down.value
        if left_arrow:
            return Direction.left.value
        return Direction.right.value
    def __init__(self, images_per_second: int, top_camera: str,
                 front_camera: str, no_cam_feed: bool) -> None:
        if top_camera == "" and front_camera == "":
            logger.error(
                "No URL or Path to camera's were given --> Not able to provide visual feedback"
            )

        self.top_vid_link = "http://192.168.43.1:8080/video"

        self.images_per_second = images_per_second
        self.no_cam_feed = no_cam_feed

        self.is_running = True  # Bool to be modified from main loop, that ends loop
Example #3
0
def async_event_handler(sig: int, frame: object) -> None:
    """
    A Handler function for asynchronous KeyboardInterrupt events (SIGINT).
    From the Python docs:
    The handler is called with two arguments:
    :param sig:   int          The signal number
    :param frame: frame object The current stack frame

    :return: None
    """
    logger.error("\nInterrupted program execution...")
    for thread in threading.enumerate():
        print("Thread running on shutdown: ", thread.name)
    sys.exit(sig)
Example #4
0
	def traverse(self):
		"""traversing the folder structure"""
		ctx = click.get_current_context().obj
		if ctx.verbose:
			logger.info(f'traversing \"{self.root_path}\"...')

		for root, directories, filenames in os.walk(self.root_path, topdown=True):
			for filename in sorted(filenames):
				filepath = os.path.join(root, filename)
				if filepath.endswith(".ctl"):
					ctl = CTL()
					ctl_file = open(filepath, 'r')
					ctl_string = ctl_file.read()
					transform_id = self.extract_tag(ctl_string, "ACEStransformID")

					if transform_id is not None:
						ctl.transform_id = transform_id
						ctl.short_transform_id = re.sub(r'^urn:ampas:aces:transformId:v[0-9].[0-9]:', '', ctl.transform_id)

						ctl.description = self.extract_tag(ctl_string, "ACESuserName")

						relative_path = os.path.relpath(filepath, start=self.root_path)
						ctl.relative_path = relative_path

						if relative_path == "idt/vendorSupplied/pomfort/IDT.RED.log3G10.ctl":
							logger.info("")

						# FIXME: this is still under discussion
						spec_prefixes = ("ODT", "IDT", "RRT", "LMT", "RRTODT", "ACEScsc",
										 "InvODT", "InvIDT", "InvRRT", "InvLMT", "InvRRTODT")
						# spec_prefixes = ("ODT", "IDT", "RRT", "LMT", "RRTODT", "ACEScsc")

						if not ctl.short_transform_id.startswith(spec_prefixes):
							ignore_prefixes = ("ACESlib", "ACESutil", "utilities")
							if not ctl.short_transform_id.startswith(ignore_prefixes):
								logger.error("SKIPPING: wrong prefix \"{0}\"in {1}".format(ctl.short_transform_id, filepath))
						else:
							self.transforms.ctls.append(ctl)
					else:
						if ctx.verbose is True:
							logger.error("ERROR: no <ACEStransformID> found in {0}".format(filepath))
Example #5
0
    def resolve_relative_paths(self, ctl_transforms):
        for transform in self.input_transforms:
            relative_path = ctl_transforms.relative_path_for_transform_id(
                transform.short_transform_id())
            if relative_path is None and transform.applied is False:
                logger.error(
                    "WARNING: transformId \"{0}\" not found in CTLs".format(
                        transform.short_transform_id()))
                relative_path = "???"
            transform.relative_path = relative_path

        for transform in self.look_transforms:
            relative_path = ctl_transforms.relative_path_for_transform_id(
                transform.short_transform_id())
            if relative_path is None:
                logger.error(
                    "Cannot find a transform for lookTransform {0}!".format(
                        transform.short_transform_id()))
                exit(amfutil_error_cannot_find_transform)
            else:
                transform.relative_path = relative_path

        for transform in self.output_transforms:
            relative_path = ctl_transforms.relative_path_for_transform_id(
                transform.short_transform_id())
            if relative_path is None:
                logger.error(
                    "Cannot find a transform for outputTransform {0}!".format(
                        transform.short_transform_id()))
                exit(amfutil_error_cannot_find_transform)
            else:
                transform.relative_path = relative_path
Example #6
0
def ctls(ctx, **kwargs):
    """Parse a folder with CTL files and print mapping between transformId and filepath."""
    ctx.load_args(**kwargs)

    traverser = TransformsTraverser(ctx.ctl_root_path)

    if ctx.relativectlpath is None:
        # logger.info("Mappings:")
        ctx.verbose = True  # show errors while traversing
        traverser.log_ctl_mappings()
    else:
        # logger.info("... looking for {0}".format(ctx.relativectlpath))
        # traverser.log_ctl_mappings()

        transformId = traverser.transforms.transform_id_for_relative_path(
            ctx.relativectlpath)
        if ctx.description:
            if transformId is None:
                if ctx.verbose is True:
                    logger.error(
                        "Couldn't find description for relative path {0}".
                        format(ctx.relativectlpath))
                exit(203)
            else:
                description = traverser.transforms.description_for_relative_path(
                    ctx.relativectlpath)
                logger.info(description)
        else:
            if transformId is None:
                if ctx.verbose is True:
                    logger.error(
                        "Couldn't find transformId for relative path {0}".
                        format(ctx.relativectlpath))
                exit(203)
            else:
                logger.info(transformId)
    def retrieve_current_image(self,
                               needle_pos_feed: multiprocessing.Queue) -> None:
        """ Retrieves and processes video feed, then sends needle pos/ori back to main Process.

        Parameters
        ----------
        needle_pos_feed : multiprocessing.Queue
            Queue to pass needle position and orientation to main Process.

        Raises
        ------
        OpenCV Error
            Could not connect to video stream from file

        Returns
        -------
        None
        """

        # Create Queue of size 10 that is used for image acq/proc
        video_feed = Queue(maxsize=10)

        top_vidcap = cv2.VideoCapture(self.top_vid_link)

        process_frame = self.fps_to_images_per_second(
            top_vidcap.get(cv2.CAP_PROP_FPS))

        while self.is_running:
            # Image Acquisition
            frame_nr = top_vidcap.get(1)  # Current frame of the video feed
            success, frame = top_vidcap.read(
            )  # Retrieve frame from video feed

            if success:
                # If camera feed is shown (nofeed == false)
                if not self.no_cam_feed:

                    cv2.imshow("Top Camera Feed", frame)

                    if cv2.waitKey(1) == 27:  # Escape Key exits loop
                        logger.error("Exiting Video Acquisition Process.")
                        top_vidcap.release()
                        cv2.destroyAllWindows()
                        # Sentinel Value to end main program loop
                        needle_pos_feed.put((None, None))
                        break

                # Only send a certain few frames per second to processing
                if frame_nr % process_frame == 0:
                    video_feed.put(frame)

            else:
                logger.error(
                    "Not able to retrieve image from VideoCapture Object. Exiting Process."
                )
                top_vidcap.release()
                cv2.destroyAllWindows()
                needle_pos_feed.put(
                    (None, None)
                )  # Can't open video feed, send Sentinel value to Main Process
                break

            # Image Processing
            if not video_feed.empty(
            ):  # Check queue if an image can be processed

                current_frame = video_feed.get()

                # Retrieve needle tip and orientation and measure processing time.
                start = time.time()
                tip_position, tip_ori = position_from_image(current_frame,
                                                            "config.ini",
                                                            flip='yes',
                                                            filtering='yes',
                                                            show='yes')
                end = time.time()

                logger.success("processing took {}".format((end - start)))

                if tip_position is None or tip_ori is None:  # No lines detected, so don't get pos and ori
                    continue

                # Send Needle position and orientation back to main process.
                needle_pos_feed.put((tip_position, tip_ori))
Example #8
0
    def move_to_dir_syncv2(self, gdo, report=0):
        """
        Receive direction (gdo) --> Drive needle with motors synchronously
        1) Find A and B such that gdo.stepsout = (stepsx, stepsy) = A(motor_u) + B(motor_Z)
            (linear combination of 2 motor vectors to reach gdo.stepsout vector)
        2a) view the push option (corresponds with run_forward() method) ELSE do 2b)
        2b) view the pull option (corresponds with run_backward() method ELSE do *)
        *) return error in case both options do not work

        gdo = get direction output (an object of the class Output(direction, stepsout) )
        """

        # 1) if push available then find the steps for motorpush0 and motorpush1
        motor_matrix_push = []
        for n in self.dirpush[gdo.direction]:
            motor_matrix_push.append(self.motorvec[n])
        a = np.array(motor_matrix_push, dtype=int)
        b = np.array([
            int(gdo.stepsout[0] * self.sensitivity),
            int(gdo.stepsout[1] * self.sensitivity)
        ],
                     dtype=int)
        x = np.linalg.solve(a.transpose(), b)
        big_a = int(abs(x[0]))
        big_b = int(abs(x[1]))
        if report == 1:
            print("NEEDLE->move_syncV2: PUSH system of equations: \n"
                  "|     a = " + str(a.transpose()[0]) + "\n"
                  "|         " + str(a.transpose()[1]) + "\n"
                  "|     b = " + str(b) + "\n"
                  "|     [big_a, big_b] = " + str([big_a, big_b]) + "\n")

        motorpush0 = self.dirpush[gdo.direction][0]
        motorpush1 = self.dirpush[gdo.direction][1]
        if report == 1:
            print("|     motorpush0 = " + str(motorpush0) + " motorpush1 = " +
                  str(motorpush1))

        # can we push? IF yes ==> then push ELSE continue to pull
        if self.motors[motorpush0].get_count() + abs(
                big_a) <= 400 and self.motors[motorpush1].get_count() + abs(
                    big_b) <= 400:
            print("NEEDLE->move_syncV2: PUSH available, starting...")
            if big_b > big_a:
                if big_a == 0:  # removing division by zero error
                    big_a = 1
                ratio = int(big_b / big_a)
                #print(" mod ratio = ", ratio)
                for i in range(big_a + big_b):
                    if i % (ratio + 1) == 0 and i != 0:
                        #print("big_a at i = ", i)
                        self.motors[motorpush0].run_forward(1)
                    else:
                        self.motors[motorpush1].run_forward(1)
            else:
                if big_b == 0:
                    big_b = 1
                ratio = int(big_a / big_b)
                #print(" mod ratio = ", ratio)
                for i in range(big_a + big_b):
                    if i % (ratio + 1) == 0 and i != 0:
                        #print("big_b at i = ", i)
                        self.motors[motorpush1].run_forward(1)
                    else:
                        self.motors[motorpush0].run_forward(1)
            # report final motor positions to user
            for motor_i in range(len(self.motors)):
                self.motors[motor_i].get_count(report=1)
            return 0

        print("\nNEEDLE->move_syncV2: PUSH not available, trying PULL")
        # 2) if pull available then find the steps for motorpull0 and motorpull1
        motor_matrix_pull = []
        for n in self.dirpull[gdo.direction]:
            motor_matrix_pull.append(self.motorvec[n])
        a = np.array(motor_matrix_pull, dtype=int)
        b = np.array([
            int(gdo.stepsout[0] * self.sensitivity),
            int(gdo.stepsout[1] * self.sensitivity)
        ],
                     dtype=int)
        x = np.linalg.solve(a.transpose(), b)
        big_a = int(abs(x[0]))
        big_b = int(abs(x[1]))
        if report == 1:
            print("NEEDLE->move_syncV2: PULL system of equations: \n"
                  "|     a = " + str(a.transpose()[0]) + "\n"
                  "|         " + str(a.transpose()[1]) + "\n"
                  "|     b = " + str(b) + "\n"
                  "|     [big_a, big_b] = " + str([big_a, big_b]) + "\n")

        motorpull0 = self.dirpull[gdo.direction][0]
        motorpull1 = self.dirpull[gdo.direction][1]
        if report == 1:
            print("|     motorpull0 = " + str(motorpull0) + " motorpull1 = " +
                  str(motorpull1))

        # can we pull? IF yes ==> then pull ELSE report the movement requested is not possible
        if self.motors[motorpull0].get_count() - abs(
                big_a) >= 0 and self.motors[motorpull1].get_count() - abs(
                    big_b) >= 0:
            print("NEEDLE->move_syncV2: PULL available, starting...")
            if big_b > big_a:
                if big_a == 0:  # removing division by zero error
                    big_a = 1
                ratio = int(big_b / big_a)
                #print(" mod ratio = ", ratio)
                for i in range(big_a + big_b):
                    if i % (ratio + 1) == 0 and i != 0:
                        #print("big_a at i = ", i)
                        self.motors[motorpull0].run_backward(1)
                    else:
                        self.motors[motorpull1].run_backward(1)
            else:
                if big_b == 0:
                    big_b = 1
                ratio = int(big_a / big_b)
                #print(" mod ratio = ", ratio)
                for i in range(big_a + big_b):
                    if i % (ratio + 1) == 0 and i != 0:
                        #print("big_b at i = ", i)
                        self.motors[motorpull1].run_backward(1)
                    else:
                        self.motors[motorpull0].run_backward(1)
            # report final motor positions to user
            for motor_i in range(len(self.motors)):
                self.motors[motor_i].get_count(report=1)
            return 0
        else:
            logger.error(
                "\n NEEDLE->move_syncV2: neither PUSH nor PULL available; no movement of needle occurred"
            )
Example #9
0
    def __init__(self, comport_arduino, startsteps, sensitivity, invertx: bool,
                 run_test: str):
        # Handle input parameters
        self.port = comport_arduino
        self.startcount = startsteps
        self.init_pos = 0
        self.sensitivity = float(sensitivity)
        if self.sensitivity > 1:
            self.sensitivity = 0.5
            logger.info("Invalid sensitivity entered: new value = {}".format(
                self.sensitivity))
        self.invert_x_axis = invertx
        self.test = run_test

        # Setup Arduino and Stepper Motors
        self.board = pyfirmata.Arduino(self.port)
        time.sleep(1)
        self.motors = []
        self.default_motor_setup()
        self.dirpull = {
            0: [0, 1],
            1: [0],
            2: [0, 3],
            3: [3],
            4: [2, 3],
            5: [2],
            6: [1, 2],
            7: [1],
        }

        self.dirpush = {
            0: [2, 3],
            1: [2],
            2: [1, 2],
            3: [1],
            4: [0, 1],
            5: [0],
            6: [0, 3],
            7: [3],
        }

        # motor vectors that code for the directions in the x-y space of the needle
        self.motorvec = {
            0: np.array([1, 1]),
            1: np.array([-1, 1]),
            2: np.array([-1, -1]),
            3: np.array([1, -1]),
        }
        """
        FESTO section
        !!! to use FESTO:    first upload "FESTO_controlv3.lua" to the T7 with Kipling 3 software
                            then close the connection with Kipling 3 software
        """
        # config
        self.config_object = ConfigParser()
        self.config_object.read('config.ini')
        festo = self.config_object["FESTO"]

        self.init_FESTO_pos = int(festo["initial_pos"])
        self.init_FESTO_speed = float(festo["initial_speed"])
        self.FESTO_stepsize = int(festo["step_size"])

        self.AIN0addr = 0  # position (0-10V)
        self.DAC0addr = 1000  # speed ref.signal (2.5V)
        self.DAC1addr = 1002  # speed out signal (-2.5 - 2.5V)
        self.initialpos_addr = 46000
        self.targetpos_addr = 46002
        self.speed_addr = 46004
        self.enable_addr = 46008
        self.f_datatype = ljm.constants.FLOAT32
        self.i_datatype = ljm.constants.UINT16

        self.offsetV = 2.5  # (offsetV+2.5V on DAC1 = 25 mm/s)
        self.offV = 0.0299544557929039  # low voltage that T7 can certainly output
        self.maxpos = 50  # mm
        self.minpos = 3  # mm
        self.currentpos = self.init_FESTO_pos

        try:
            FESTO_handle = ljm.openS("ANY", "USB", "ANY")
        except ljm.LJMError as error:
            FESTO_handle = None
            logger.error(
                "No FESTO_handle: thus not able to use the FESTO functions \n Error presented: "
                + str(error))

        if FESTO_handle is not None:
            self.FESTO_handle = FESTO_handle
            # Set initial positions (keep target pos at init_FESTO_pos at the start)
            ljm.eWriteAddress(self.FESTO_handle, self.initialpos_addr,
                              self.f_datatype, self.init_FESTO_pos)
            ljm.eWriteAddress(self.FESTO_handle, self.targetpos_addr,
                              self.f_datatype, self.init_FESTO_pos)
            # Set speed
            ljm.eWriteAddress(self.FESTO_handle, self.speed_addr,
                              self.f_datatype, self.init_FESTO_speed)
            logger.success(
                "FESTO connected, handle is available, init is set, current position ="
                + str(
                    ljm.eReadAddress(self.FESTO_handle, self.AIN0addr,
                                     self.f_datatype)))
            time.sleep(0.3)

            # Enable init LUA program
            ljm.eWriteAddress(self.FESTO_handle, self.enable_addr,
                              self.f_datatype, 1)
            logger.success("FESTO moving to initial position")
        else:
            logger.error(
                "Something went wrong when creating a FESTO Handle. Check if all adresses are correct in needle.py"
            )
            self.FESTO_handle = None