view m209/key_wheel.py @ 0:39b2db64fcf2

KeyWheel class and tests, first cut.
author Brian Neal <bgneal@gmail.com>
date Wed, 29 May 2013 15:58:30 -0500
parents
children c292c6b5e7ae
line wrap: on
line source
# Copyright (C) 2013 by Brian Neal.
# This file is part of m209, the M-209 simulation.
# m209 is released under the MIT License (see LICENSE.txt).

"""This module contains the key wheel related classes for the M209
simulation.

"""
from . import M209Error

class KeyWheelError(M209Error):
    """Exception class for all key wheel errors"""
    pass


class KeyWheel:
    """Simulates a key wheel in a M209 converter"""

    def __init__(self, letters, guide_letter, effective_pins=None):
        """Initialize a KeyWheel instance:

        letters - an iterable of letters which appear on the wheel face

        guide_letter - must be a letter that appears within the letters
        parameter. It indicates which letter affects the guide arm when the
        first letter in letters is displayed to the operator.

        effective_pins - see the description of set_pins(), below.

        """
        self.letters = list(letters)
        self.num_pins = len(self.letters)

        self.letter_offsets = {letter : n for n, letter in enumerate(self.letters)}

        if self.num_pins < 1:
            raise KeyWheelError("Too few key wheel letters")

        # pin effectivity list:
        if effective_pins:
            self.set_pins(effective_pins)
        else:
            self.reset_pins()

        try:
            self.guide_offset = self.letter_offsets[guide_letter]
        except KeyError:
            raise KeyWheelError("Invalid guide_letter")

        # rotational position; 0 means first letter shown to operator
        self.pos = 0

    def reset_pins(self):
        """Reset all pins to the ineffective state."""
        self.pins = [False] * self.num_pins

    def set_pins(self, effective_pins):
        """Sets which pins are effective.

        effective_pins - must be an iterable of letters whose pins are slide to
        the "effective" position (to the right). Letters not appearing in this
        sequence are considered to be in the "ineffective" position (to the
        left). If None or empty, all pins are set to be ineffective.

        """
        self.reset_pins()
        for letter in effective_pins:
            try:
                n = self.letter_offsets[letter]
            except KeyError:
                raise KeyWheelError("Invalid pin: {}".format(letter))
            self.pins[n] = True

    def rotate(self, steps=1):
        """Rotate the key wheel the given number of steps."""
        self.pos = (self.pos + steps) % self.num_pins

    def display(self):
        """Returns the letter shown to the operator based on the current key
        wheel position.

        """
        return self.letters[self.pos]

    def guide_letter(self):
        """Returns the letter of the pin that is in position to effect the guide
        arm. To check to see if this pin will effect the guide arm, call
        is_effective().

        """
        n = (self.pos + self.guide_offset) % self.num_pins
        return self.letters[n]

    def is_effective(self):
        """Returns True if the key wheel, in the current position, has a pin in
        the effective position, and False otherwise.

        """
        n = (self.pos + self.guide_offset) % self.num_pins
        return self.pins[n]

    def set_pos(self, c):
        """Sets the position of the key wheel to the letter c."""
        try:
            self.pos = self.letter_offsets[c]
        except KeyError:
            raise KeyWheelError("Invalid position {}".format(c))