1 /***************************************************************************
2 * Copyright (C) 2003-2005 by David Saxton *
3 * david@bluehaze.org *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 ***************************************************************************/
10
11 #include "canvasmanipulator.h"
12 #include "circuitdocument.h"
13 #include "circuiticndocument.h"
14 #include "circuitview.h"
15 #include "component.h"
16 #include "connector.h"
17 #include "cnitemgroup.h"
18 #include "documentiface.h"
19 #include "drawparts/drawpart.h"
20 #include "ecnode.h"
21 #include "itemdocumentdata.h"
22 #include "ktechlab.h"
23 #include "pin.h"
24 #include "simulator.h"
25 #include "subcircuits.h"
26 #include "switch.h"
27
28 #include <KLocalizedString>
29 #include <KMessageBox>
30 #include <KActionMenu>
31 #include <KToggleAction>
32
33 #include <QDebug>
34 #include <QInputDialog>
35 #include <QRegExp>
36 #include <QTimer>
37
38 #include <ktlconfig.h>
39
40
CircuitDocument(const QString & caption,const char * name)41 CircuitDocument::CircuitDocument( const QString & caption, const char *name )
42 : CircuitICNDocument( caption, name )
43 {
44 m_pOrientationAction = new KActionMenu( QIcon::fromTheme("transform-rotate"), i18n("Orientation"), this );
45
46 m_type = Document::dt_circuit;
47 m_pDocumentIface = new CircuitDocumentIface(this);
48 m_fileExtensionInfo = QString("*.circuit|%1(*.circuit)\n*|%2").arg( i18n("Circuit") ).arg( i18n("All Files") );
49
50 m_cmManager->addManipulatorInfo( CMSelect::manipulatorInfo() );
51 m_cmManager->addManipulatorInfo( CMDraw::manipulatorInfo() );
52 m_cmManager->addManipulatorInfo( CMRightClick::manipulatorInfo() );
53 m_cmManager->addManipulatorInfo( CMRepeatedItemAdd::manipulatorInfo() );
54 m_cmManager->addManipulatorInfo( CMItemResize::manipulatorInfo() );
55 m_cmManager->addManipulatorInfo( CMItemDrag::manipulatorInfo() );
56
57 connect( this, SIGNAL(connectorAdded(Connector*)), this, SLOT(requestAssignCircuits()) );
58 connect( this, SIGNAL(connectorAdded(Connector*)), this, SLOT(connectorAdded(Connector*)) );
59
60 m_updateCircuitsTmr = new QTimer();
61 connect( m_updateCircuitsTmr, SIGNAL(timeout()), this, SLOT(assignCircuits()) );
62
63 requestStateSave();
64 }
65
~CircuitDocument()66 CircuitDocument::~CircuitDocument()
67 {
68 m_bDeleted = true;
69
70 disconnect( m_updateCircuitsTmr, SIGNAL(timeout()), this, SLOT(assignCircuits()) );
71 disconnect( this, SIGNAL(connectorAdded(Connector*)), this, SLOT(connectorAdded(Connector*)) );
72 disconnect( this, SIGNAL(connectorAdded(Connector*)), this, SLOT(requestAssignCircuits()) );
73
74 for (ConnectorList::Iterator itConn = m_connectorList.begin(); itConn != m_connectorList.end(); ++itConn) {
75 Connector *connector = itConn->data();
76 disconnect( connector, SIGNAL(removed(Connector*)), this, SLOT(requestAssignCircuits()) );
77 }
78 for (ItemMap::Iterator itItem = m_itemList.begin(); itItem != m_itemList.end(); ++itItem) {
79 Item *item = itItem.value();
80 disconnect( item, SIGNAL(removed(Item*)), this, SLOT(componentRemoved(Item*)) );
81 Component *comp = dynamic_cast<Component*>( item );
82 if ( comp ) {
83 disconnect( comp, SIGNAL(elementDestroyed(Element*)), this, SLOT(requestAssignCircuits()) );
84 }
85 }
86
87 deleteCircuits();
88
89 delete m_updateCircuitsTmr;
90 delete m_pDocumentIface;
91 }
92
93
slotInitItemActions()94 void CircuitDocument::slotInitItemActions( )
95 {
96 CircuitICNDocument::slotInitItemActions();
97
98 CircuitView *activeCircuitView = dynamic_cast<CircuitView*>(activeView());
99
100 if ( !KTechlab::self() || !activeCircuitView ) return;
101
102 Component *item = dynamic_cast<Component*>( m_selectList->activeItem() );
103
104 if ( (!item && m_selectList->count() > 0) || !m_selectList->itemsAreSameType() )
105 return;
106
107 QAction * orientation_actions[] = {
108 activeCircuitView->actionByName("edit_orientation_0"),
109 activeCircuitView->actionByName("edit_orientation_90"),
110 activeCircuitView->actionByName("edit_orientation_180"),
111 activeCircuitView->actionByName("edit_orientation_270") };
112
113 if ( !item )
114 {
115 for ( unsigned i = 0; i < 4; ++i )
116 orientation_actions[i]->setEnabled(false);
117 return;
118 }
119
120 for ( unsigned i = 0; i < 4; ++ i)
121 {
122 orientation_actions[i]->setEnabled(true);
123 m_pOrientationAction->removeAction( orientation_actions[i] );
124 m_pOrientationAction->addAction( orientation_actions[i] );
125 }
126
127 if ( item->angleDegrees() == 0 )
128 (static_cast<KToggleAction*>( orientation_actions[0] ))->setChecked(true);
129 else if ( item->angleDegrees() == 90 )
130 (static_cast<KToggleAction*>( orientation_actions[1] ))->setChecked(true);
131 else if ( item->angleDegrees() == 180 )
132 (static_cast<KToggleAction*>( orientation_actions[2] ))->setChecked(true);
133 else if ( item->angleDegrees() == 270 )
134 (static_cast<KToggleAction*>( orientation_actions[3] ))->setChecked(true);
135 }
136
137
rotateCounterClockwise()138 void CircuitDocument::rotateCounterClockwise()
139 {
140 m_selectList->slotRotateCCW();
141 requestRerouteInvalidatedConnectors();
142 }
143
rotateClockwise()144 void CircuitDocument::rotateClockwise()
145 {
146 m_selectList->slotRotateCW();
147 requestRerouteInvalidatedConnectors();
148 }
149
flipHorizontally()150 void CircuitDocument::flipHorizontally()
151 {
152 m_selectList->flipHorizontally();
153 requestRerouteInvalidatedConnectors();
154 }
155
flipVertically()156 void CircuitDocument::flipVertically()
157 {
158 m_selectList->flipVertically();
159 requestRerouteInvalidatedConnectors();
160 }
161
setOrientation0()162 void CircuitDocument::setOrientation0()
163 {
164 m_selectList->slotSetOrientation0();
165 requestRerouteInvalidatedConnectors();
166 }
167
setOrientation90()168 void CircuitDocument::setOrientation90()
169 {
170 m_selectList->slotSetOrientation90();
171 requestRerouteInvalidatedConnectors();
172 }
173
setOrientation180()174 void CircuitDocument::setOrientation180()
175 {
176 m_selectList->slotSetOrientation180();
177 requestRerouteInvalidatedConnectors();
178 }
179
setOrientation270()180 void CircuitDocument::setOrientation270()
181 {
182 m_selectList->slotSetOrientation270();
183 requestRerouteInvalidatedConnectors();
184 }
185
186
createView(ViewContainer * viewContainer,uint viewAreaId,const char * name)187 View *CircuitDocument::createView( ViewContainer *viewContainer, uint viewAreaId, const char *name )
188 {
189 View *view = new CircuitView( this, viewContainer, viewAreaId, name );
190 handleNewView(view);
191 return view;
192 }
193
194
slotUpdateConfiguration()195 void CircuitDocument::slotUpdateConfiguration()
196 {
197 CircuitICNDocument::slotUpdateConfiguration();
198
199 ECNodeMap::iterator nodeEnd = m_ecNodeList.end();
200 for ( ECNodeMap::iterator it = m_ecNodeList.begin(); it != nodeEnd; ++it )
201 {
202 ECNode * n = *it; // static_cast<ECNode*>(*it);
203 n->setShowVoltageBars( KTLConfig::showVoltageBars() );
204 n->setShowVoltageColor( KTLConfig::showVoltageColor() );
205 }
206
207 ConnectorList::iterator connectorsEnd = m_connectorList.end();
208 for ( ConnectorList::iterator it = m_connectorList.begin(); it != connectorsEnd; ++it )
209 (*it)->updateConnectorLines();
210
211 ComponentList::iterator componentsEnd = m_componentList.end();
212 for ( ComponentList::iterator it = m_componentList.begin(); it != componentsEnd; ++it )
213 (*it)->slotUpdateConfiguration();
214 }
215
216
update()217 void CircuitDocument::update()
218 {
219 CircuitICNDocument::update();
220
221 bool animWires = KTLConfig::animateWires();
222
223 if ( KTLConfig::showVoltageColor() || animWires )
224 {
225 if ( animWires )
226 {
227 // Wire animation is for showing currents, so we need to recalculate the currents
228 // in the wires.
229 calculateConnectorCurrents();
230 }
231
232 ConnectorList::iterator end = m_connectorList.end();
233 for ( ConnectorList::iterator it = m_connectorList.begin(); it != end; ++it )
234 {
235 (*it)->incrementCurrentAnimation( 1.0 / double(KTLConfig::refreshRate()) );
236 (*it)->updateConnectorLines( animWires );
237 }
238 }
239
240 if ( KTLConfig::showVoltageColor() || KTLConfig::showVoltageBars() )
241 {
242 ECNodeMap::iterator end = m_ecNodeList.end();
243 for ( ECNodeMap::iterator it = m_ecNodeList.begin(); it != end; ++it )
244 {
245 // static_cast<ECNode*>(*it)->setNodeChanged();
246 (*it)->setNodeChanged();
247 }
248 }
249 }
250
251
fillContextMenu(const QPoint & pos)252 void CircuitDocument::fillContextMenu( const QPoint &pos )
253 {
254 CircuitICNDocument::fillContextMenu(pos);
255
256 CircuitView *activeCircuitView = dynamic_cast<CircuitView*>(activeView());
257
258 if ( !activeCircuitView ) return;
259
260 bool canCreateSubcircuit = (m_selectList->count() > 1 && countExtCon(m_selectList->items()) > 0);
261 QAction * subcircuitAction = activeCircuitView->actionByName("circuit_create_subcircuit");
262 subcircuitAction->setEnabled( canCreateSubcircuit );
263
264 if ( m_selectList->count() < 1 ) return;
265
266 Component *item = dynamic_cast<Component*>( selectList()->activeItem() );
267
268 // NOTE: I negated this whole condition because I couldn't make out quite what the
269 //logic was --electronerd
270 if (!( (!item && m_selectList->count() > 0) || !m_selectList->itemsAreSameType() ))
271 {
272 QAction * orientation_actions[] = {
273 activeCircuitView->actionByName("edit_orientation_0"),
274 activeCircuitView->actionByName("edit_orientation_90"),
275 activeCircuitView->actionByName("edit_orientation_180"),
276 activeCircuitView->actionByName("edit_orientation_270") };
277
278 if ( !item ) return;
279
280 for ( unsigned i = 0; i < 4; ++ i)
281 {
282 m_pOrientationAction->removeAction( orientation_actions[i] );
283 m_pOrientationAction->addAction( orientation_actions[i] );
284 }
285
286 QList<QAction*> orientation_actionlist;
287 // orientation_actionlist.prepend( new KActionSeparator() );
288 orientation_actionlist.append( m_pOrientationAction );
289 KTechlab::self()->plugActionList( "orientation_actionlist", orientation_actionlist );
290 }
291 }
292
293
deleteCircuits()294 void CircuitDocument::deleteCircuits()
295 {
296 const CircuitList::iterator end = m_circuitList.end();
297 for ( CircuitList::iterator it = m_circuitList.begin(); it != end; ++it )
298 {
299 if (!Simulator::isDestroyedSim()) {
300 Simulator::self()->detachCircuit(*it);
301 }
302 delete *it;
303 }
304 m_circuitList.clear();
305 m_pinList.clear();
306 m_wireList.clear();
307 }
308
309
requestAssignCircuits()310 void CircuitDocument::requestAssignCircuits()
311 {
312 // qDebug() << Q_FUNC_INFO << endl;
313 if (m_bDeleted) {
314 return;
315 }
316 deleteCircuits();
317 m_updateCircuitsTmr->stop();
318 m_updateCircuitsTmr->setSingleShot( true );
319 m_updateCircuitsTmr->start( 0 /*, true */ );
320 }
321
322
connectorAdded(Connector * connector)323 void CircuitDocument::connectorAdded( Connector * connector )
324 {
325 if (connector) {
326 connect( connector, SIGNAL(numWiresChanged(unsigned )), this, SLOT(requestAssignCircuits()) );
327 connect( connector, SIGNAL(removed(Connector*)), this, SLOT(requestAssignCircuits()) );
328 }
329 }
330
331
itemAdded(Item * item)332 void CircuitDocument::itemAdded( Item * item)
333 {
334 CircuitICNDocument::itemAdded( item );
335 componentAdded( item );
336 }
337
338
componentAdded(Item * item)339 void CircuitDocument::componentAdded( Item * item )
340 {
341 Component *component = dynamic_cast<Component*>(item);
342
343 if (!component) return;
344
345 requestAssignCircuits();
346
347 connect( component, SIGNAL(elementCreated(Element*)), this, SLOT(requestAssignCircuits()) );
348 connect( component, SIGNAL(elementDestroyed(Element*)), this, SLOT(requestAssignCircuits()) );
349 connect( component, SIGNAL(removed(Item*)), this, SLOT(componentRemoved(Item*)) );
350
351 // We don't attach the component to the Simulator just yet, as the
352 // Component's vtable is not yet fully constructed, and so Simulator can't
353 // tell whether or not it is a logic component
354 if ( !m_toSimulateList.contains(component) )
355 m_toSimulateList << component;
356 }
357
358
componentRemoved(Item * item)359 void CircuitDocument::componentRemoved( Item * item )
360 {
361 Component *component = dynamic_cast<Component*>(item);
362
363 if (!component) return;
364
365 m_componentList.removeAll( component );
366 m_toSimulateList.removeAll( component );
367
368 requestAssignCircuits();
369
370 if (!Simulator::isDestroyedSim()) {
371 Simulator::self()->detachComponent(component);
372 }
373 }
374
375 // I think this is where the inf0z from cnodes/branches is moved into the midle-layer
376 // pins/wires.
377
calculateConnectorCurrents()378 void CircuitDocument::calculateConnectorCurrents()
379 {
380 const CircuitList::iterator circuitEnd = m_circuitList.end();
381 for ( CircuitList::iterator it = m_circuitList.begin(); it != circuitEnd; ++it )
382 (*it)->updateCurrents();
383
384 PinList groundPins;
385
386 // Tell the Pins to reset their calculated currents to zero
387 m_pinList.removeAll((Pin*)nullptr);
388
389 const PinList::iterator pinEnd = m_pinList.end();
390 for ( PinList::iterator it = m_pinList.begin(); it != pinEnd; ++it )
391 {
392 if ( Pin *n = dynamic_cast<Pin*>((Pin*)*it) ) {
393 n->resetCurrent();
394 n->setSwitchCurrentsUnknown();
395
396 if ( !n->parentECNode()->isChildNode() ) {
397 n->setCurrentKnown( true );
398 // (and it has a current of 0 amps)
399 } else if ( n->groundType() == Pin::gt_always ) {
400 groundPins << n;
401 n->setCurrentKnown( false );
402 } else {
403 // Child node that is non ground
404 n->setCurrentKnown( n->parentECNode()->numPins() < 2 );
405 }
406 }
407 }
408
409
410 // Tell the components to update their ECNode's currents' from the elements
411 // currents are merged into PINS.
412 const ComponentList::iterator componentEnd = m_componentList.end();
413 for ( ComponentList::iterator it = m_componentList.begin(); it != componentEnd; ++it )
414 (*it)->setNodalCurrents();
415
416
417 // And now for the wires and switches...
418 m_wireList.removeAll((Wire*)nullptr);
419 const WireList::iterator clEnd = m_wireList.end();
420 for ( WireList::iterator it = m_wireList.begin(); it != clEnd; ++it )
421 (*it)->setCurrentKnown(false);
422
423 SwitchList switches = m_switchList;
424 WireList wires = m_wireList;
425 bool found = true;
426 while ( (!wires.isEmpty() || !switches.isEmpty() || !groundPins.isEmpty()) && found )
427 {
428 found = false;
429
430 for ( WireList::iterator itW = wires.begin(); itW != wires.end(); )
431 {
432 if ( (*itW)->calculateCurrent() )
433 {
434 found = true;
435 itW = wires.erase(itW);
436 // note: assigning a temporary interator, incrementing and erasing, seems to crash
437 } else {
438 ++itW;
439 }
440 }
441
442 SwitchList::iterator switchesEnd = switches.end();
443 for ( SwitchList::iterator it = switches.begin(); it != switchesEnd; )
444 {
445 if ( (*it)->calculateCurrent() )
446 {
447 found = true;
448 // note: assigning a temporary interator, incrementing and erasing, seems to crash
449 // it = container.erase( it ) seems to crash other times
450 SwitchList::iterator oldIt = it;
451 ++it;
452 switches.erase(oldIt);
453 } else ++it;
454 }
455
456 /*
457 make the ground pins work. Current engine doesn't treat ground explicitly.
458 */
459
460 PinList::iterator groundPinsEnd = groundPins.end();
461 for ( PinList::iterator it = groundPins.begin(); it != groundPinsEnd; ) {
462 if ( (*it)->calculateCurrentFromWires() ) {
463 found = true;
464 // note: assigning a temporary interator, incrementing and erasing, seems to crash sometimes;
465 // it = container.erase( it ) seems to crash other times
466 PinList::iterator oldIt = it;
467 ++it;
468 groundPins.erase(oldIt);
469 } else ++it;
470 }
471 }
472 }
473
474
assignCircuits()475 void CircuitDocument::assignCircuits()
476 {
477 // Now we can finally add the unadded components to the Simulator
478 const ComponentList::iterator toSimulateEnd = m_toSimulateList.end();
479 for ( ComponentList::iterator it = m_toSimulateList.begin(); it != toSimulateEnd; ++it )
480 Simulator::self()->attachComponent(*it);
481
482 m_toSimulateList.clear();
483
484 // Stage 0: Build up pin and wire lists
485 m_pinList.clear();
486
487 const ECNodeMap::const_iterator nodeListEnd = m_ecNodeList.end();
488 for ( ECNodeMap::const_iterator it = m_ecNodeList.begin(); it != nodeListEnd; ++it )
489 {
490 // if ( ECNode * ecnode = dynamic_cast<ECNode*>(*it) )
491 ECNode* ecnode = *it;
492
493 for ( unsigned i = 0; i < ecnode->numPins(); i++ )
494 m_pinList << ecnode->pin(i);
495
496 }
497
498 m_wireList.clear();
499
500 const ConnectorList::const_iterator connectorListEnd = m_connectorList.end();
501 for ( ConnectorList::const_iterator it = m_connectorList.begin(); it != connectorListEnd; ++it )
502 {
503 for ( unsigned i = 0; i < (*it)->numWires(); i++ )
504 m_wireList << (*it)->wire(i);
505 }
506
507 typedef QList<PinList> PinListList;
508
509 // Stage 1: Partition the circuit up into dependent areas (bar splitting
510 // at ground pins)
511 PinList unassignedPins = m_pinList;
512 PinListList pinListList;
513
514 while( !unassignedPins.isEmpty() ) {
515 PinList pinList;
516 getPartition( *unassignedPins.begin(), & pinList, & unassignedPins );
517 pinListList.append(pinList);
518 }
519
520 // qDebug () << "pinListList.size()="<<pinListList.size()<<endl;
521
522 // Stage 2: Split up each partition into circuits by ground pins
523 const PinListList::iterator nllEnd = pinListList.end();
524 for ( PinListList::iterator it = pinListList.begin(); it != nllEnd; ++it )
525 splitIntoCircuits(&*it);
526
527 // Stage 3: Initialize the circuits
528 m_circuitList.removeAll(nullptr);
529 CircuitList::iterator circuitListEnd = m_circuitList.end();
530 for ( CircuitList::iterator it = m_circuitList.begin(); it != circuitListEnd; ++it )
531 (*it)->init();
532
533 m_switchList.clear();
534 m_componentList.clear();
535 const ItemMap::const_iterator cilEnd = m_itemList.end();
536 for ( ItemMap::const_iterator it = m_itemList.begin(); it != cilEnd; ++it )
537 {
538 Component *component = dynamic_cast<Component*>(*it);
539
540 if ( !component ) continue;
541
542 m_componentList << component;
543 component->initElements(0);
544 m_switchList += component->switchList();
545 }
546
547 circuitListEnd = m_circuitList.end();
548 for ( CircuitList::iterator it = m_circuitList.begin(); it != circuitListEnd; ++it )
549 (*it)->createMatrixMap();
550
551 for ( ItemMap::const_iterator it = m_itemList.begin(); it != cilEnd; ++it )
552 {
553 Component *component = dynamic_cast<Component*>(*it);
554
555 if (component) component->initElements(1);
556 }
557
558 circuitListEnd = m_circuitList.end();
559 for ( CircuitList::iterator it = m_circuitList.begin(); it != circuitListEnd; ++it )
560 {
561 (*it)->initCache();
562 Simulator::self()->attachCircuit(*it);
563 }
564 }
565
566
getPartition(Pin * pin,PinList * pinList,PinList * unassignedPins,bool onlyGroundDependent)567 void CircuitDocument::getPartition( Pin *pin, PinList *pinList, PinList *unassignedPins, bool onlyGroundDependent )
568 {
569 if (!pin) return;
570
571 unassignedPins->removeAll(pin);
572
573 if ( pinList->contains(pin) ) return;
574
575 pinList->append(pin);
576
577 const PinList localConnectedPins = pin->localConnectedPins();
578 const PinList::const_iterator end = localConnectedPins.end();
579 for ( PinList::const_iterator it = localConnectedPins.begin(); it != end; ++it )
580 getPartition( *it, pinList, unassignedPins, onlyGroundDependent );
581
582 const PinList groundDependentPins = pin->groundDependentPins();
583 const PinList::const_iterator dEnd = groundDependentPins.end();
584 for ( PinList::const_iterator it = groundDependentPins.begin(); it != dEnd; ++it )
585 getPartition( *it, pinList, unassignedPins, onlyGroundDependent );
586
587 if (!onlyGroundDependent) {
588 PinList circuitDependentPins = pin->circuitDependentPins();
589 const PinList::const_iterator dEnd = circuitDependentPins.end();
590 for ( PinList::const_iterator it = circuitDependentPins.begin(); it != dEnd; ++it )
591 getPartition( *it, pinList, unassignedPins, onlyGroundDependent );
592 }
593 }
594
595
splitIntoCircuits(PinList * pinList)596 void CircuitDocument::splitIntoCircuits( PinList *pinList )
597 {
598 // First: identify ground
599 PinList unassignedPins = *pinList;
600 typedef QList<PinList> PinListList;
601 PinListList pinListList;
602
603 while ( !unassignedPins.isEmpty() ) {
604 PinList tempPinList;
605 getPartition( *unassignedPins.begin(), & tempPinList, & unassignedPins, true );
606 pinListList.append(tempPinList);
607 }
608
609 const PinListList::iterator nllEnd = pinListList.end();
610 for ( PinListList::iterator it = pinListList.begin(); it != nllEnd; ++it )
611 Circuit::identifyGround(*it);
612
613 while ( !pinList->isEmpty() ) {
614 PinList::iterator end = pinList->end();
615 PinList::iterator it = pinList->begin();
616
617 while ( it != end && (*it)->eqId() == -1 )
618 ++it;
619
620 if ( it == end ) break;
621 else {
622 Circuitoid *circuitoid = new Circuitoid;
623 recursivePinAdd( *it, circuitoid, pinList );
624
625 if ( !tryAsLogicCircuit(circuitoid) )
626 m_circuitList += createCircuit(circuitoid);
627
628 delete circuitoid;
629 }
630 }
631
632
633 // Remaining pins are ground; tell them about it
634 // TODO This is a bit hacky....
635 const PinList::iterator end = pinList->end();
636 for ( PinList::iterator it = pinList->begin(); it != end; ++it ) {
637 (*it)->setVoltage(0.0);
638 ElementList elements = (*it)->elements();
639 const ElementList::iterator eEnd = elements.end();
640 for ( ElementList::iterator it = elements.begin(); it != eEnd; ++it )
641 {
642 if ( LogicIn * logicIn = dynamic_cast<LogicIn*>(*it) )
643 {
644 logicIn->setLastState(false);
645 logicIn->callCallback();
646 }
647 }
648 }
649 }
650
651
recursivePinAdd(Pin * pin,Circuitoid * circuitoid,PinList * unassignedPins)652 void CircuitDocument::recursivePinAdd( Pin *pin, Circuitoid *circuitoid, PinList *unassignedPins )
653 {
654 if(!pin) return;
655
656 if(pin->eqId() != -1 )
657 unassignedPins->removeAll(pin);
658
659 if(circuitoid->contains(pin) ) return;
660
661 circuitoid->addPin(pin);
662
663 if(pin->eqId() == -1 ) return;
664
665 const PinList localConnectedPins = pin->localConnectedPins();
666 const PinList::const_iterator end = localConnectedPins.end();
667 for ( PinList::const_iterator it = localConnectedPins.begin(); it != end; ++it )
668 recursivePinAdd( *it, circuitoid, unassignedPins );
669
670 const PinList groundDependentPins = pin->groundDependentPins();
671 const PinList::const_iterator gdEnd = groundDependentPins.end();
672 for ( PinList::const_iterator it = groundDependentPins.begin(); it != gdEnd; ++it )
673 recursivePinAdd( *it, circuitoid, unassignedPins );
674
675 const PinList circuitDependentPins = pin->circuitDependentPins();
676 const PinList::const_iterator cdEnd = circuitDependentPins.end();
677 for ( PinList::const_iterator it = circuitDependentPins.begin(); it != cdEnd; ++it )
678 recursivePinAdd( *it, circuitoid, unassignedPins );
679
680 const ElementList elements = pin->elements();
681 const ElementList::const_iterator eEnd = elements.end();
682 for ( ElementList::const_iterator it = elements.begin(); it != eEnd; ++it )
683 circuitoid->addElement(*it);
684 }
685
686
tryAsLogicCircuit(Circuitoid * circuitoid)687 bool CircuitDocument::tryAsLogicCircuit( Circuitoid *circuitoid )
688 {
689 if (!circuitoid) return false;
690
691 if ( circuitoid->elementList.size() == 0 )
692 {
693 // This doesn't quite belong here...but whatever. Initialize all
694 // pins to voltage zero as they won't get set to zero otherwise
695 const PinList::const_iterator pinListEnd = circuitoid->pinList.constEnd();
696 for ( PinList::const_iterator it = circuitoid->pinList.constBegin(); it != pinListEnd; ++it )
697 (*it)->setVoltage(0.0);
698
699 // A logic circuit only requires there to be no non-logic components,
700 // and at most one LogicOut - so this qualifies
701 return true;
702 }
703
704 LogicInList logicInList;
705 LogicOut *out = nullptr;
706
707 uint logicInCount = 0;
708 uint logicOutCount = 0;
709 ElementList::const_iterator end = circuitoid->elementList.end();
710 for ( ElementList::const_iterator it = circuitoid->elementList.begin(); it != end; ++it )
711 {
712 if ( (*it)->type() == Element::Element_LogicOut )
713 {
714 logicOutCount++;
715 out = static_cast<LogicOut*>(*it);
716 } else if ( (*it)->type() == Element::Element_LogicIn )
717 {
718 logicInCount++;
719 logicInList += static_cast<LogicIn*>(*it);
720 } else return false;
721 }
722
723 if ( logicOutCount > 1 ) return false;
724 else if ( logicOutCount == 1 )
725 Simulator::self()->createLogicChain( out, logicInList, circuitoid->pinList );
726 else {
727 // We have ourselves stranded LogicIns...so lets set them all to low
728
729 const PinList::const_iterator pinListEnd = circuitoid->pinList.constEnd();
730 for ( PinList::const_iterator it = circuitoid->pinList.constBegin(); it != pinListEnd; ++it )
731 (*it)->setVoltage(0.0);
732
733 for ( ElementList::const_iterator it = circuitoid->elementList.begin(); it != end; ++it )
734 {
735 LogicIn * logicIn = static_cast<LogicIn*>(*it);
736 logicIn->setNextLogic(nullptr);
737 logicIn->setElementSet(nullptr);
738 if ( logicIn->isHigh() )
739 {
740 logicIn->setLastState(false);
741 logicIn->callCallback();
742 }
743 }
744 }
745
746 return true;
747 }
748
749
createCircuit(Circuitoid * circuitoid)750 Circuit *CircuitDocument::createCircuit( Circuitoid *circuitoid )
751 {
752 if (!circuitoid) return nullptr;
753
754 Circuit *circuit = new Circuit();
755
756 const PinList::const_iterator nEnd = circuitoid->pinList.end();
757 for ( PinList::const_iterator it = circuitoid->pinList.begin(); it != nEnd; ++it )
758 circuit->addPin(*it);
759
760 const ElementList::const_iterator eEnd = circuitoid->elementList.end();
761 for ( ElementList::const_iterator it = circuitoid->elementList.begin(); it != eEnd; ++it )
762 circuit->addElement(*it);
763
764 return circuit;
765 }
766
767
createSubcircuit()768 void CircuitDocument::createSubcircuit()
769 {
770 ItemList itemList = m_selectList->items();
771 const ItemList::iterator itemListEnd = itemList.end();
772 for ( ItemList::iterator it = itemList.begin(); it != itemListEnd; ++it )
773 {
774 if ( !dynamic_cast<Component*>((Item*)*it) )
775 *it = nullptr;
776 }
777
778 itemList.removeAll((Item*)nullptr);
779
780 if ( itemList.isEmpty() ) {
781 KMessageBox::sorry( activeView(), i18n("No components were found in the selection.") );
782 return;
783 }
784
785 // Number of external connections
786 const int extConCount = countExtCon(itemList);
787 if ( extConCount == 0 ) {
788 KMessageBox::sorry( activeView(), i18n("No External Connection components were found in the selection.") );
789 return;
790 }
791
792 bool ok;
793 const QString name = QInputDialog::getText(activeView(), "Subcircuit", "Name", QLineEdit::Normal, QString(), &ok);
794 if (!ok) return;
795
796 SubcircuitData subcircuit;
797 subcircuit.addItems(itemList);
798 subcircuit.addNodes( getCommonNodes(itemList) );
799 subcircuit.addConnectors( getCommonConnectors(itemList) );
800
801 Subcircuits::addSubcircuit( name, subcircuit.toXML() );
802 }
803
804
countExtCon(const ItemList & itemList) const805 int CircuitDocument::countExtCon( const ItemList &itemList ) const
806 {
807 int count = 0;
808 const ItemList::const_iterator end = itemList.end();
809 for ( ItemList::const_iterator it = itemList.begin(); it != end; ++it )
810 {
811 Item * item = *it;
812
813 if ( item && item->type() == "ec/external_connection" )
814 count++;
815 }
816 return count;
817 }
818
819
isValidItem(const QString & itemId)820 bool CircuitDocument::isValidItem( const QString &itemId )
821 {
822 return itemId.startsWith("ec/") || itemId.startsWith("dp/") || itemId.startsWith("sc/");
823 }
824
825
isValidItem(Item * item)826 bool CircuitDocument::isValidItem( Item *item )
827 {
828 return (dynamic_cast<Component*>(item) || dynamic_cast<DrawPart*>(item));
829 }
830
831
displayEquations()832 void CircuitDocument::displayEquations()
833 {
834 qDebug() << "######################################################" << endl;
835 const CircuitList::iterator end = m_circuitList.end();
836 int i = 1;
837 for ( CircuitList::iterator it = m_circuitList.begin(); it != end; ++it )
838 {
839 qDebug() << "Equation set "<<i<<":\n";
840 (*it)->displayEquations();
841 i++;
842 }
843 qDebug() << "######################################################" << endl;
844 }
845