annotate 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
rev   line source
bgneal@0 1 # Copyright (C) 2013 by Brian Neal.
bgneal@0 2 # This file is part of m209, the M-209 simulation.
bgneal@0 3 # m209 is released under the MIT License (see LICENSE.txt).
bgneal@0 4
bgneal@0 5 """This module contains the key wheel related classes for the M209
bgneal@0 6 simulation.
bgneal@0 7
bgneal@0 8 """
bgneal@0 9 from . import M209Error
bgneal@0 10
bgneal@0 11 class KeyWheelError(M209Error):
bgneal@0 12 """Exception class for all key wheel errors"""
bgneal@0 13 pass
bgneal@0 14
bgneal@0 15
bgneal@0 16 class KeyWheel:
bgneal@0 17 """Simulates a key wheel in a M209 converter"""
bgneal@0 18
bgneal@0 19 def __init__(self, letters, guide_letter, effective_pins=None):
bgneal@0 20 """Initialize a KeyWheel instance:
bgneal@0 21
bgneal@0 22 letters - an iterable of letters which appear on the wheel face
bgneal@0 23
bgneal@0 24 guide_letter - must be a letter that appears within the letters
bgneal@0 25 parameter. It indicates which letter affects the guide arm when the
bgneal@0 26 first letter in letters is displayed to the operator.
bgneal@0 27
bgneal@0 28 effective_pins - see the description of set_pins(), below.
bgneal@0 29
bgneal@0 30 """
bgneal@0 31 self.letters = list(letters)
bgneal@0 32 self.num_pins = len(self.letters)
bgneal@0 33
bgneal@0 34 self.letter_offsets = {letter : n for n, letter in enumerate(self.letters)}
bgneal@0 35
bgneal@0 36 if self.num_pins < 1:
bgneal@0 37 raise KeyWheelError("Too few key wheel letters")
bgneal@0 38
bgneal@0 39 # pin effectivity list:
bgneal@0 40 if effective_pins:
bgneal@0 41 self.set_pins(effective_pins)
bgneal@0 42 else:
bgneal@0 43 self.reset_pins()
bgneal@0 44
bgneal@0 45 try:
bgneal@0 46 self.guide_offset = self.letter_offsets[guide_letter]
bgneal@0 47 except KeyError:
bgneal@0 48 raise KeyWheelError("Invalid guide_letter")
bgneal@0 49
bgneal@0 50 # rotational position; 0 means first letter shown to operator
bgneal@0 51 self.pos = 0
bgneal@0 52
bgneal@0 53 def reset_pins(self):
bgneal@0 54 """Reset all pins to the ineffective state."""
bgneal@0 55 self.pins = [False] * self.num_pins
bgneal@0 56
bgneal@0 57 def set_pins(self, effective_pins):
bgneal@0 58 """Sets which pins are effective.
bgneal@0 59
bgneal@0 60 effective_pins - must be an iterable of letters whose pins are slide to
bgneal@0 61 the "effective" position (to the right). Letters not appearing in this
bgneal@0 62 sequence are considered to be in the "ineffective" position (to the
bgneal@0 63 left). If None or empty, all pins are set to be ineffective.
bgneal@0 64
bgneal@0 65 """
bgneal@0 66 self.reset_pins()
bgneal@0 67 for letter in effective_pins:
bgneal@0 68 try:
bgneal@0 69 n = self.letter_offsets[letter]
bgneal@0 70 except KeyError:
bgneal@0 71 raise KeyWheelError("Invalid pin: {}".format(letter))
bgneal@0 72 self.pins[n] = True
bgneal@0 73
bgneal@0 74 def rotate(self, steps=1):
bgneal@0 75 """Rotate the key wheel the given number of steps."""
bgneal@0 76 self.pos = (self.pos + steps) % self.num_pins
bgneal@0 77
bgneal@0 78 def display(self):
bgneal@0 79 """Returns the letter shown to the operator based on the current key
bgneal@0 80 wheel position.
bgneal@0 81
bgneal@0 82 """
bgneal@0 83 return self.letters[self.pos]
bgneal@0 84
bgneal@0 85 def guide_letter(self):
bgneal@0 86 """Returns the letter of the pin that is in position to effect the guide
bgneal@0 87 arm. To check to see if this pin will effect the guide arm, call
bgneal@0 88 is_effective().
bgneal@0 89
bgneal@0 90 """
bgneal@0 91 n = (self.pos + self.guide_offset) % self.num_pins
bgneal@0 92 return self.letters[n]
bgneal@0 93
bgneal@0 94 def is_effective(self):
bgneal@0 95 """Returns True if the key wheel, in the current position, has a pin in
bgneal@0 96 the effective position, and False otherwise.
bgneal@0 97
bgneal@0 98 """
bgneal@0 99 n = (self.pos + self.guide_offset) % self.num_pins
bgneal@0 100 return self.pins[n]
bgneal@0 101
bgneal@0 102 def set_pos(self, c):
bgneal@0 103 """Sets the position of the key wheel to the letter c."""
bgneal@0 104 try:
bgneal@0 105 self.pos = self.letter_offsets[c]
bgneal@0 106 except KeyError:
bgneal@0 107 raise KeyWheelError("Invalid position {}".format(c))