def set_resistance(r): """ Sets the resistance of this resistor to be the string |r|, after appropriately modifying it. """ if not r: self.board.display_message('No resistance entered', ERROR) return try: old_r = self.get_resistance() new_r = resistance_to_string(resistance_from_string(r)) do = lambda: _set_resistance(new_r) undo = lambda: _set_resistance(old_r) do() self.board.set_changed(True, Action(do, undo, 'set_resistance')) except Exception as e: self.board.display_message(e.message, ERROR)
def draw_on(self, canvas, offset=(0, 0)): ox, oy = offset w, h = self.width, self.height self._resistor_zig_zags = draw_resistor_zig_zags(canvas, ox, oy, w, h) self.parts |= self._resistor_zig_zags text = resistance_to_string(resistance_from_string(self.init_resistance)) if w > h: # horizontal self.resistor_text = canvas.create_text(ox + w / 2, oy - RESISTOR_TEXT_PADDING, text=text, font=FONT) else: # vertical self.resistor_text = canvas.create_text(ox + w + RESISTOR_TEXT_PADDING + 8, oy + h / 2, text=text, font=FONT) self.parts.add(self.resistor_text) def get_resistance(): """ Returns the string representing this resistor's resistance. """ return canvas.itemcget(self.resistor_text, 'text') self.get_resistance = get_resistance def _set_resistance(r): canvas.itemconfig(self.resistor_text, text=r) self.init_resistance = r def set_resistance(r): """ Sets the resistance of this resistor to be the string |r|, after appropriately modifying it. """ if not r: self.board.display_message('No resistance entered', ERROR) return try: old_r = self.get_resistance() new_r = resistance_to_string(resistance_from_string(r)) do = lambda: _set_resistance(new_r) undo = lambda: _set_resistance(old_r) do() self.board.set_changed(True, Action(do, undo, 'set_resistance')) except Exception as e: self.board.display_message(e.message, ERROR) self.set_resistance = set_resistance
def run_analysis(board, analyze, solve_circuit=False, ensure_pwr_gnd_nodes=False, no_shorts=False, ensure_signal_files=False): """ Extracts a Circuit object from what is drawn on the given |board| and calls the given function |analyze| on it. The funtion |analyze| should take as arguments the circuit, as well as the plotters that are collected. """ # remove current message on board, if any board.remove_message() # components in the circuit circuit_components = [] # analysis plotters plotters = [] # probe labels probe_plus, probe_minus = None, None # constants for motors, motor_pots, and photosensors # we use this state to be able to identify robot head groups head_connector_group_ids = set() n_motor_plus = defaultdict(str) n_motor_minus = defaultdict(str) i_motor = defaultdict(str) motor_label = defaultdict(str) n_motor_pot_top = defaultdict(str) n_motor_pot_middle = defaultdict(str) n_motor_pot_bottom = defaultdict(str) i_motor_pot_top_middle = defaultdict(str) i_motor_pot_middle_bottom = defaultdict(str) motor_pot_label = defaultdict(str) n_photo_left = defaultdict(str) n_photo_common = defaultdict(str) n_photo_right = defaultdict(str) i_photo_left_common = defaultdict(str) i_photo_common_right = defaultdict(str) photo_lamp_angle_signal = defaultdict(str) photo_lamp_distance_signal = defaultdict(str) photo_label = defaultdict(str) # constants for robot power and robot analog inputs and outputs robot_connector_group_ids = set() robot_pwr = defaultdict(str) robot_gnd = defaultdict(str) robot_power_label = defaultdict(str) robot_ai1 = defaultdict(lambda: (None, None)) robot_ai2 = defaultdict(lambda: (None, None)) robot_ai3 = defaultdict(lambda: (None, None)) robot_ai4 = defaultdict(lambda: (None, None)) robot_ao = defaultdict(lambda: (None, None)) # first identify all power and ground nodes and use the same name for all # power nodes, as well as the same name for all ground nodes power_nodes, ground_nodes = set(), set() for drawable in board.get_drawables(): # wires attached to this component nodes = [wire.label for wire in drawable.wires()] # power component if isinstance(drawable, Power_Drawable): for node in nodes: power_nodes.add(node) # ground component elif isinstance(drawable, Ground_Drawable): for node in nodes: ground_nodes.add(node) # robot connector component elif isinstance(drawable, Robot_Power_Drawable): for node in [wire.label for wire in drawable.pwr.wires()]: power_nodes.add(node) for node in [wire.label for wire in drawable.gnd.wires()]: ground_nodes.add(node) robot_connector_group_ids.add(drawable.group_id) robot_pwr[drawable.group_id] = POWER robot_gnd[drawable.group_id] = GROUND robot_power_label[drawable.group_id] = drawable.label # ensure that there is at least one power component if ensure_pwr_gnd_nodes and not power_nodes: board.display_message('No power nodes', ERROR) return # ensure that there is at least one ground component if ensure_pwr_gnd_nodes and not ground_nodes: board.display_message('No ground nodes', ERROR) return # ensure that power nodes and ground nodes are disjoint (no short circuit) if no_shorts and power_nodes.intersection(ground_nodes): board.display_message('Short circuit', ERROR) return # add voltage source to circuit circuit_components.append(Voltage_Source(POWER, GROUND, current_name( board, POWER, GROUND), POWER_VOLTS)) def maybe_rename_node(node): """ If this |node| is a power node or a ground node, this method returns the appropriate unique name, otherwise the original name is returned. """ if node in power_nodes: return POWER elif node in ground_nodes: return GROUND return node for drawable in board.get_drawables(): # wires attached to this component nodes = [wire.label for wire in drawable.wires()] # probe plus component if isinstance(drawable, Probe_Plus_Drawable): if nodes: probe_plus = maybe_rename_node(nodes[0]) circuit_components.append(Probe('+', probe_plus)) # probe minus component elif isinstance(drawable, Probe_Minus_Drawable): if nodes: probe_minus = maybe_rename_node(nodes[0]) circuit_components.append(Probe('-', probe_minus)) # resistor component elif isinstance(drawable, Resistor_Drawable): connector_it = iter(drawable.connectors) pin_1_nodes = [wire.label for wire in connector_it.next().wires()] pin_2_nodes = [wire.label for wire in connector_it.next().wires()] # get its resistance try: c1, c2, e = resistance_from_string(drawable.get_resistance()) r = (c1 * 10 + c2) * 10 ** e except: board.display_message('Could not obtain resistance constant', ERROR) return n1 = maybe_rename_node(pin_1_nodes[0]) if pin_1_nodes else None n2 = maybe_rename_node(pin_2_nodes[0]) if pin_2_nodes else None resistor = Resistor(n1, n2, current_name(drawable, n1, n2), r) resistor.label = drawable.label circuit_components.append(resistor) # op amp component elif isinstance(drawable, Op_Amp_Drawable): plus_nodes = [wire.label for wire in drawable.plus_port.wires()] minus_nodes = [wire.label for wire in drawable.minus_port.wires()] out_nodes = [wire.label for wire in drawable.out_port.wires()] na1 = maybe_rename_node(plus_nodes[0]) if plus_nodes else None na2 = maybe_rename_node(minus_nodes[0]) if minus_nodes else None nb1 = maybe_rename_node(out_nodes[0]) if out_nodes else None nb2 = GROUND op_amp = Op_Amp(na1, na2, current_name(drawable, na1, na2), nb1, nb2, current_name(drawable, nb1, nb2), jfet=drawable.jfet) op_amp.label = drawable.label circuit_components.append(op_amp) # pot component elif isinstance(drawable, Pot_Drawable): if ensure_signal_files and not drawable.signal_file: board.display_message('No signal file loaded for Pot', ERROR) return pot_variables = {'pot_r': None, 'pot_signal': None} if drawable.signal_file: execfile(drawable.signal_file, pot_variables) if ensure_signal_files and not (pot_variables['pot_r'] and pot_variables['pot_signal']): board.display_message('Invalid Pot signal file', ERROR) return top_nodes = [wire.label for wire in drawable.top_connector.wires()] middle_nodes = [wire.label for wire in drawable.middle_connector.wires()] bottom_nodes = [wire.label for wire in drawable.bottom_connector.wires()] n_top = maybe_rename_node(top_nodes[0]) if top_nodes else None n_middle = maybe_rename_node(middle_nodes[0]) if middle_nodes else None n_bottom = maybe_rename_node(bottom_nodes[0]) if bottom_nodes else None pot = Signalled_Pot(n_top, n_middle, n_bottom, current_name(drawable, n_top, n_middle), current_name(drawable, n_middle, n_bottom), pot_variables['pot_r'], pot_variables['pot_signal']) pot.label = drawable.label circuit_components.append(pot) plotters.append(Signalled_Pot_Plotter(pot)) # motor component elif isinstance(drawable, Motor_Drawable): plus_nodes = [wire.label for wire in drawable.plus.wires()] minus_nodes = [wire.label for wire in drawable.minus.wires()] plus_node = maybe_rename_node(plus_nodes[0]) if plus_nodes else None minus_node = maybe_rename_node(minus_nodes[0]) if minus_nodes else None i = current_name(drawable, n_motor_plus, n_motor_minus) if not drawable.group_id: motor = Motor(plus_node, minus_node, i) motor.label = drawable.label circuit_components.append(motor) plotters.append(Motor_Plotter(motor)) else: head_connector_group_ids.add(drawable.group_id) n_motor_plus[drawable.group_id] = plus_node n_motor_minus[drawable.group_id] = minus_node i_motor[drawable.group_id] = i motor_label[drawable.group_id] = drawable.label # motor pot component elif isinstance(drawable, Motor_Pot_Drawable): pot_top_nodes = [wire.label for wire in drawable.top.wires()] pot_middle_nodes = [wire.label for wire in drawable.middle.wires()] pot_bottom_nodes = [wire.label for wire in drawable.bottom.wires()] head_connector_group_ids.add(drawable.group_id) n_motor_pot_top[drawable.group_id] = (maybe_rename_node(pot_top_nodes[0]) if pot_top_nodes else None) n_motor_pot_middle[drawable.group_id] = maybe_rename_node( pot_middle_nodes[0]) if pot_middle_nodes else None n_motor_pot_bottom[drawable.group_id] = maybe_rename_node( pot_bottom_nodes[0]) if pot_bottom_nodes else None i_motor_pot_top_middle[drawable.group_id] = current_name(drawable, n_motor_pot_top, n_motor_pot_middle) i_motor_pot_middle_bottom[drawable.group_id] = current_name(drawable, n_motor_pot_middle, n_motor_pot_bottom) motor_pot_label[drawable.group_id] = drawable.label # photosensor component elif isinstance(drawable, Photosensors_Drawable): if ensure_signal_files and not drawable.signal_file: board.display_message('No signal file loaded for Photosensors', ERROR) return lamp_signals = {'lamp_angle_signal': None, 'lamp_distance_signal': None} if drawable.signal_file: execfile(drawable.signal_file, lamp_signals) photo_lamp_angle_signal[drawable.group_id] = lamp_signals[ 'lamp_angle_signal'] photo_lamp_distance_signal[drawable.group_id] = lamp_signals[ 'lamp_distance_signal'] photo_left_nodes = [wire.label for wire in drawable.left.wires()] photo_common_nodes = [wire.label for wire in drawable.common.wires()] photo_right_nodes = [wire.label for wire in drawable.right.wires()] head_connector_group_ids.add(drawable.group_id) n_photo_left[drawable.group_id] = (maybe_rename_node(photo_left_nodes[0]) if photo_left_nodes else None) n_photo_common[drawable.group_id] = maybe_rename_node( photo_common_nodes[0]) if photo_common_nodes else None n_photo_right[drawable.group_id] = maybe_rename_node( photo_right_nodes[0]) if photo_right_nodes else None i_photo_left_common[drawable.group_id] = current_name(drawable, n_photo_left, n_photo_common) i_photo_common_right[drawable.group_id] = current_name(drawable, n_photo_common, n_photo_right) photo_label[drawable.group_id] = drawable.label # motor analog i/o component elif isinstance(drawable, Robot_IO_Drawable): node = maybe_rename_node(nodes[0]) if nodes else None robot_connector_group_ids.add(drawable.group_id) if drawable.name == 'Ai1': robot_ai1[drawable.group_id] = (node, drawable.label) elif drawable.name == 'Ai2': robot_ai2[drawable.group_id] = (node, drawable.label) elif drawable.name == 'Ai3': robot_ai3[drawable.group_id] = (node, drawable.label) elif drawable.name == 'Ai4': robot_ai4[drawable.group_id] = (node, drawable.label) elif drawable.name == 'Ao': robot_ao[drawable.group_id] = (node, drawable.label) # collect robot head pieces together for group_id in head_connector_group_ids: head_connector = Head_Connector(n_motor_pot_top[group_id], n_motor_pot_middle[group_id], n_motor_pot_bottom[group_id], i_motor_pot_top_middle[group_id], i_motor_pot_middle_bottom[group_id], n_photo_left[group_id], n_photo_common[group_id], n_photo_right[ group_id], i_photo_left_common[group_id], i_photo_common_right[ group_id], n_motor_plus[group_id], n_motor_minus[group_id], i_motor[ group_id], photo_lamp_angle_signal[group_id], photo_lamp_distance_signal[group_id]) head_connector.motor_label = motor_label[group_id] head_connector.motor_pot_label = motor_pot_label[group_id] head_connector.photo_label = photo_label[group_id] circuit_components.append(head_connector) plotters.append(Head_Plotter(head_connector)) # collect robot connector pieces together for group_id in robot_connector_group_ids: ai1_node, ai1_label = robot_ai1[group_id] ai2_node, ai2_label = robot_ai2[group_id] ai3_node, ai3_label = robot_ai3[group_id] ai4_node, ai4_label = robot_ai4[group_id] ao_node, ao_label = robot_ao[group_id] robot_connector = Robot_Connector(robot_pwr[group_id], robot_gnd[group_id], ai1_node, ai2_node, ai3_node, ai4_node, ao_node) robot_connector.label = ','.join(filter(bool, [robot_power_label[group_id], ai1_label, ai2_label, ai3_label, ai4_label, ao_label])) circuit_components.append(robot_connector) # if both probes are given, display probe voltage difference graph if probe_plus and probe_minus: plotters.append(Probe_Plotter(probe_plus, probe_minus)) # create and analyze circuit circuit = Circuit(circuit_components, GROUND, solve_circuit) board.relabel_wires(maybe_rename_node) return analyze(circuit, plotters)