# TODO: module in development, not ready for any use yet
#
-import os.path, pygame, engine
+import os.path, pygame, engine, library
from library import enum
+
class TraceSegment:
""" Models a straight line segment of a complete Trace """
- # C-style pseudo-enum representing the directions a segment might run
- directions = enum('N', 'NE', 'E', 'SE', 'S' , 'SW', 'W' , 'NW')
- angles = list( 0 , 45 , 90, 135, 180, 225, 270, 315)
+ # N NE E SE S SW W NW
+ angles = ( 0 , 45 , 90, 135, 180, 225, 270, 315)
# references to an attached Via and its position
via = None
- via_pos = enum('START', 'MID', 'END')
via_position = None
- def __init__(self, length=1, direction=TraceSegment.directions.E, via=None, position=TraceSegment.via_pos.START):
+ def __init__(self, length=1, direction=library.directions.E, via=None, via_position=library.positions.NONE):
""" default segment runs for one game tile length left-to-right
- length: number of game 'tiles' this segment covers
- direction: direction
+ length: number of game 'tiles' this segment covers
+ direction: enum directions
+ via: Via
+ via_position: enum positions
"""
# ensure all values are legal before doing any assignment
- if not direction in TraceSegment.directions.reverse_mapping:
+ if not direction in library.directions.reverse_mapping:
# 'direction' is an index enum, so ensure it exists in the enum before using it to select the angle
raise IndexError('direction invalid')
- elif not position in TraceSegment.via_pos:
+ elif not via_position in library.positions:
# 'position' is an enum so ensure it is valid
raise IndexError('position invalid')
elif via and not isinstance(via, Via):
self.length = length
self.angle = angles[direction]
self.via = via
- self.via_position = position
+ self.via_position = via_position
- def via_set(self, via, position):
+ def set_via(self, via, position):
""" link to the related Via
via: Via object
position: enum via_pos
"""
- if via and isinstance(via, Via):
- if position in via_pos:
- # FIXME: ensure the line above works for testing position's membership of via_pos
- self.via = via
- self.via_position = position
+ if not isinstance(via, Via):
+ raise TypeError('expecting a Via')
+ elif not position in library.positions:
+ # FIXME: ensure the line above works for testing position's membership
+ raise IndexError('position invalid')
+
+ self.via = via
+ self.via_position = position
+
+ def set_direction(self, direction):
+ """ set the direction the segment is headed
+ direction: enum directions
+ """
+ if not direction in library.directions.reverse_mapping:
+ raise IndexError('direction invalid')
+ self.angle = self.angles(direction)
class Trace:
""" Models a single circuit trace made up of multiple segments """
segments = list() # ordered list of one or more TraceSegments
colours = dict()
- colours['metal'] = (192, 192, 192) # colours of the trace
- colours['shadow'] = (64, 64, 64)
- colours['reflection'] = (255, 255, 255)
+ # colours['metal'] = (192, 192, 192) # colours of the trace
+ # colours['shadow'] = (64, 64, 64)
+ # colours['reflection'] = (255, 255, 255)
def check_range(self, colour):
""" Ensure a colour value is legal """
- return colour >= 0 and colour <= 255
+ if not (type(colour) == tuple or type(colour) == int):
+ raise TypeError('colour expeted to be tuple or int')
+ elif not len(colour) == 3:
+ raise ValueError('colour tuples require exactly 3 items (RGB)')
+
+ if type(colour) == int:
+ return colour >= 0 and colour <= 255
+ else:
+ return self.check_range(colour[0]) and self.check_range(colour[1]) and self.check_range(colour[2])
- def __init__(self, metal=None, shadow=None, reflection=None, segments=TraceSegment()):
+ def __init__(self, metal=(192, 192, 192), shadow=(64, 64, 64), reflection=(255, 255, 255), segments=None):
# set the colours of the trace to give a 3D raised appearance
if metal and check_range(metal): self.colours['metal'] = metal
if shadow and check_range(shadow): self.colours['shadow'] = shadow
if reflection and check_range(reflection): self.colours['reflection'] = reflection
- # initialise the via dictionary
+ if segments == None:
+ self.segments = TraceSegment()
+ else:
+ self.segments = segments
+
with self.segments[0]:
via = None
- via_position = via_pos.START
+ via_position = library.positions.NONE
def draw(self, surface):
""" Draw the Trace onto pygame.Surface """
+ return
+
class Via:
""" Models a through-hole otherwise known as a Via """
class IntegratedCircuit:
""" semiconductor component """
- signals = None # key = signal name, value = pin number
-
- def __init__(self):
- """ populate pseudo-enum with common signal names """
- if not IntegratedCircuit.signals:
- signals = ('NC', 'GND', 'Vcc', 'Vdd')
- # logic gate signal names (covers multi-gate components up to quad-gate)
- for l in ('A', 'B', 'Y'):
- for n in range(1, 5):
- signals.append("%s%s" % (n, l))
-
- IntegratedCircuit.signals = enum(signals)
+ pins = list() # index = pin number, value = signal name
class DualInLine(IntegratedCircuit):
""" Dual In-Line Integrated Circuit """
sides = 2
qty = 0
- pins = list()
- def __init__(self, pins):
- if pins > 0 and pins % 2 == 0:
- for p in range(1, pins+1):
- pins[p] = IntegratedCircuit.signals.NC
+ def __init__(self, pin_qty):
+ if not (pin_qty > 0 and pin_qty % 2 == 0):
+ raise ValueError('DIL ICs have even number of pins')
+
+ for p in range(1, pin_qty+1):
+ self.pins.insert(p, library.signals.NC)
class Gate:
types = None
inputs = list()
output = False
- type = None
+ gate_type = None
- def __init__(self, type, inputs=None):
- # populate dictionary with common logic gate types
- if not Gate.types:
- Gate.types = enum('AND', 'OR', 'XOR', 'NOT', 'NAND', 'NOR')
+ def __init__(self, gate_type, inputs=None):
+ if not gate_type in library.gates:
+ raise IndexError()
- if type in Gate.types:
- self.type = type
+ self.gate_type = gate_type
- if inputs and type(inputs) is list and isinstance(inputs[0], bool):
- self.inputs = inputs
- else:
+ if inputs == None:
# default to 2 inputs at logic 0
self.inputs = (False, False)
-
+ elif not (type(inputs) is list and isinstance(inputs[0], bool)):
+ raise TypeError()
+
+ self.inputs = inputs
self.input_max = len(self.inputs)
self.update()
return
def set_input(self, input, value):
- if input >= 0 and input < self.input_max:
- if isinstance(value, bool):
- self.inputs[input] = value
- self.update()
+ if not(input >= 0 and input < self.input_max):
+ raise ValueError("input expected between 0 and" % self.input_max)
+
+ if not isinstance(value, bool):
+ raise TypeError('Boolean expected')
+
+ self.inputs[input] = value
+ self.update()
def get_output(self):
return self.output
def min_inputs(self, inputs, minimum):
- """ enforce a minimum number of logic inputs """
+ """ enforce a minimum number of logic inputs in the list """
return inputs if len(inputs) >= minimum else (inputs, (False for i in range(0, minimum - len(inputs))))
# Derive each of the common logic gate types
class GateAND(Gate):
def __init__(self, inputs=None):
- Gate.__init__(Gate.types.AND, self.min_inputs(inputs, 2))
+ Gate.__init__(library.gates.AND, self.min_inputs(inputs, 2))
def update(self):
self.output = self.inputs[0]
class GateOR(Gate):
def __init__(self, inputs=None):
- Gate.__init__(Gate.types.OR, self.min_inputs(inputs, 2))
+ Gate.__init__(library.gates.OR, self.min_inputs(inputs, 2))
def update(self):
self.output = self.inputs[0]
class GateXOR(Gate):
def __init__(self, inputs=None):
- Gate.__init__(Gate.types.XOR, self.min_inputs(inputs, 2))
+ Gate.__init__(library.gates.XOR, self.min_inputs(inputs, 2))
def update(self):
self.output = self.inputs[0]
class GateNOT(Gate):
def __init__(self, inputs=(False, )):
- if len(inputs) > 1:
- # inverters can only have 1 input
- inputs = (inputs[0], )
- Gate.__init__(Gate.types.NOT, inputs)
+ if len(inputs) != 1:
+ raise ValueError('NOT must have only 1 input')
+ Gate.__init__(library.gates.NOT, inputs)
def update(self):
self.output = ~self.inputs[0] # invert
class GateNAND(Gate):
- def __init__(self, inputs=None):
- Gate.__init__(Gate.types.NAND, self.min_inputs(inputs, 2))
+ def __init__(self, inputs=None):
+ Gate.__init__(library.gates.NAND, self.min_inputs(inputs, 2))
def update(self):
self.output = self.inputs[0]
class GateNOR(Gate):
def __init__(self, inputs=None):
- Gate.__init__(Gate.types.NOR, self.min_inputs(inputs, 2))
+ Gate.__init__(library.gates.NOR, self.min_inputs(inputs, 2))
def update(self):
self.output = self.inputs[0]
class ICLogic:
""" Models an IC that contains one or more digital logic gates """
gates = list() # list of gates
- package = None
- def __init__(self, gates=(), package=DualInLine(14)):
- if package: self.package = package
- if type(gates) is list and type gates[0] is Gate: self.gates = gates
+ def __init__(self, gates=()):
+ if not type(gates) is list:
+ raise TypeError('expected list')
+ elif not type(gates[0]) is Gate:
+ raise TypeError('expected Gate')
+
+ self.gates = gates
def add(self, gate):
- if gate and type(gate) is Gate: self.gates.append(gate)
+ if not type(gate) is Gate:
+ raise TypeError('expected Gate')
+
+ self.gates.append(gate)
+class ICLogicDIL(ICLogic, DualInLine):
+ """ Model of a Logic IC ina DIL package """
+ def __init__(self, gates=(), pins_qty=14):
+ ICLogic.__init(gates)
+ DualInLine.__init__(pins_qty)
-class SN7400(ICLogic, DualInLine):
+
+class SN7400(ICLogicDIL):
""" Model of a Texas Instruments SN7400 quad 2-input NAND gate DIP14 package """
def __init__(self):
gates = list()
for i in range(0, 4):
gates.append(GateNAND())
- # TODO: may need to move this functionality into an intermediate super class that combines package and Logic chip
- ICLogic.__init__(gates)
- DualInLine.__init(14)
+ ICLogicDIL.__init__(gates, 14)
-class SN741G00(ICLogic, ...):
+class SN741G00(ICLogicDIL):
""" Model of a Texas Instruments SN741G00 single 2-input NAND gate """
pins = list()
def __init__(self, pins):
-
+ return
class PCB:
def __init__(self):
""" """
+ return
def trace_add(self, trace_list):
- """ Add a """
+ """ Add a Trace """
+ return
def component_insert(self, component):
- """ Adds a component to """
+ """ Adds a component to the circuit """
+ return