File indexing completed on 2025-02-16 08:27:01
0001 /*************************************************************************** 0002 * Copyright (C) 2003-2005 by David Saxton * 0003 * david@bluehaze.org * 0004 * * 0005 * This program is free software; you can redistribute it and/or modify * 0006 * it under the terms of the GNU General Public License as published by * 0007 * the Free Software Foundation; either version 2 of the License, or * 0008 * (at your option) any later version. * 0009 ***************************************************************************/ 0010 0011 #include "circuitdocument.h" 0012 #include "canvasmanipulator.h" 0013 #include "circuiticndocument.h" 0014 #include "circuitview.h" 0015 #include "cnitemgroup.h" 0016 #include "component.h" 0017 #include "connector.h" 0018 #include "documentiface.h" 0019 #include "drawparts/drawpart.h" 0020 #include "ecnode.h" 0021 #include "itemdocumentdata.h" 0022 #include "ktechlab.h" 0023 #include "pin.h" 0024 #include "simulator.h" 0025 #include "subcircuits.h" 0026 #include "switch.h" 0027 0028 #include <KActionMenu> 0029 #include <KLocalizedString> 0030 #include <KMessageBox> 0031 #include <KToggleAction> 0032 0033 #include <QInputDialog> 0034 #include <QRegExp> 0035 #include <QTimer> 0036 0037 #include <ktlconfig.h> 0038 #include <ktechlab_debug.h> 0039 0040 CircuitDocument::CircuitDocument(const QString &caption) 0041 : CircuitICNDocument(caption) 0042 { 0043 m_pOrientationAction = new KActionMenu(QIcon::fromTheme("transform-rotate"), i18n("Orientation"), this); 0044 0045 m_type = Document::dt_circuit; 0046 m_pDocumentIface = new CircuitDocumentIface(this); 0047 m_fileExtensionInfo = QString("*.circuit|%1(*.circuit)\n*|%2").arg(i18n("Circuit")).arg(i18n("All Files")); 0048 m_fileExtensionValue = QString(".circuit"); 0049 0050 m_cmManager->addManipulatorInfo(CMSelect::manipulatorInfo()); 0051 m_cmManager->addManipulatorInfo(CMDraw::manipulatorInfo()); 0052 m_cmManager->addManipulatorInfo(CMRightClick::manipulatorInfo()); 0053 m_cmManager->addManipulatorInfo(CMRepeatedItemAdd::manipulatorInfo()); 0054 m_cmManager->addManipulatorInfo(CMItemResize::manipulatorInfo()); 0055 m_cmManager->addManipulatorInfo(CMItemDrag::manipulatorInfo()); 0056 0057 connect(this, &CircuitDocument::connectorAdded, this, &CircuitDocument::requestAssignCircuits); 0058 connect(this, &CircuitDocument::connectorAdded, this, &CircuitDocument::connectorAddedSlot); 0059 0060 m_updateCircuitsTmr = new QTimer(); 0061 connect(m_updateCircuitsTmr, &QTimer::timeout, this, &CircuitDocument::assignCircuits); 0062 0063 requestStateSave(); 0064 } 0065 0066 CircuitDocument::~CircuitDocument() 0067 { 0068 m_bDeleted = true; 0069 0070 disconnect(m_updateCircuitsTmr, &QTimer::timeout, this, &CircuitDocument::assignCircuits); 0071 disconnect(this, &CircuitDocument::connectorAdded, this, &CircuitDocument::connectorAddedSlot); 0072 disconnect(this, &CircuitDocument::connectorAdded, this, &CircuitDocument::requestAssignCircuits); 0073 0074 for (ConnectorList::Iterator itConn = m_connectorList.begin(); itConn != m_connectorList.end(); ++itConn) { 0075 Connector *connector = itConn->data(); 0076 disconnect(connector, &Connector::removed, this, &CircuitDocument::requestAssignCircuits); 0077 } 0078 for (ItemMap::Iterator itItem = m_itemList.begin(); itItem != m_itemList.end(); ++itItem) { 0079 Item *item = itItem.value(); 0080 disconnect(item, &Item::removed, this, &CircuitDocument::componentRemoved); 0081 Component *comp = dynamic_cast<Component *>(item); 0082 if (comp) { 0083 disconnect(comp, &Component::elementDestroyed, this, &CircuitDocument::requestAssignCircuits); 0084 } 0085 } 0086 0087 deleteCircuits(); 0088 0089 delete m_updateCircuitsTmr; 0090 delete m_pDocumentIface; 0091 } 0092 0093 void CircuitDocument::slotInitItemActions() 0094 { 0095 CircuitICNDocument::slotInitItemActions(); 0096 0097 CircuitView *activeCircuitView = dynamic_cast<CircuitView *>(activeView()); 0098 0099 if (!KTechlab::self() || !activeCircuitView) 0100 return; 0101 0102 Component *item = dynamic_cast<Component *>(m_selectList->activeItem()); 0103 0104 if ((!item && m_selectList->count() > 0) || !m_selectList->itemsAreSameType()) 0105 return; 0106 0107 QAction *orientation_actions[] = { 0108 activeCircuitView->actionByName("edit_orientation_0"), activeCircuitView->actionByName("edit_orientation_90"), activeCircuitView->actionByName("edit_orientation_180"), activeCircuitView->actionByName("edit_orientation_270")}; 0109 0110 if (!item) { 0111 for (unsigned i = 0; i < 4; ++i) 0112 orientation_actions[i]->setEnabled(false); 0113 return; 0114 } 0115 0116 for (unsigned i = 0; i < 4; ++i) { 0117 orientation_actions[i]->setEnabled(true); 0118 m_pOrientationAction->removeAction(orientation_actions[i]); 0119 m_pOrientationAction->addAction(orientation_actions[i]); 0120 } 0121 0122 if (item->angleDegrees() == 0) 0123 (static_cast<KToggleAction *>(orientation_actions[0]))->setChecked(true); 0124 else if (item->angleDegrees() == 90) 0125 (static_cast<KToggleAction *>(orientation_actions[1]))->setChecked(true); 0126 else if (item->angleDegrees() == 180) 0127 (static_cast<KToggleAction *>(orientation_actions[2]))->setChecked(true); 0128 else if (item->angleDegrees() == 270) 0129 (static_cast<KToggleAction *>(orientation_actions[3]))->setChecked(true); 0130 } 0131 0132 void CircuitDocument::rotateCounterClockwise() 0133 { 0134 m_selectList->slotRotateCCW(); 0135 requestRerouteInvalidatedConnectors(); 0136 } 0137 0138 void CircuitDocument::rotateClockwise() 0139 { 0140 m_selectList->slotRotateCW(); 0141 requestRerouteInvalidatedConnectors(); 0142 } 0143 0144 void CircuitDocument::flipHorizontally() 0145 { 0146 m_selectList->flipHorizontally(); 0147 requestRerouteInvalidatedConnectors(); 0148 } 0149 0150 void CircuitDocument::flipVertically() 0151 { 0152 m_selectList->flipVertically(); 0153 requestRerouteInvalidatedConnectors(); 0154 } 0155 0156 void CircuitDocument::setOrientation0() 0157 { 0158 m_selectList->slotSetOrientation0(); 0159 requestRerouteInvalidatedConnectors(); 0160 } 0161 0162 void CircuitDocument::setOrientation90() 0163 { 0164 m_selectList->slotSetOrientation90(); 0165 requestRerouteInvalidatedConnectors(); 0166 } 0167 0168 void CircuitDocument::setOrientation180() 0169 { 0170 m_selectList->slotSetOrientation180(); 0171 requestRerouteInvalidatedConnectors(); 0172 } 0173 0174 void CircuitDocument::setOrientation270() 0175 { 0176 m_selectList->slotSetOrientation270(); 0177 requestRerouteInvalidatedConnectors(); 0178 } 0179 0180 View *CircuitDocument::createView(ViewContainer *viewContainer, uint viewAreaId) 0181 { 0182 View *view = new CircuitView(this, viewContainer, viewAreaId); 0183 handleNewView(view); 0184 return view; 0185 } 0186 0187 void CircuitDocument::slotUpdateConfiguration() 0188 { 0189 CircuitICNDocument::slotUpdateConfiguration(); 0190 0191 ECNodeMap::iterator nodeEnd = m_ecNodeList.end(); 0192 for (ECNodeMap::iterator it = m_ecNodeList.begin(); it != nodeEnd; ++it) { 0193 ECNode *n = *it; // static_cast<ECNode*>(*it); 0194 n->setShowVoltageBars(KTLConfig::showVoltageBars()); 0195 n->setShowVoltageColor(KTLConfig::showVoltageColor()); 0196 } 0197 0198 ConnectorList::iterator connectorsEnd = m_connectorList.end(); 0199 for (ConnectorList::iterator it = m_connectorList.begin(); it != connectorsEnd; ++it) 0200 (*it)->updateConnectorLines(); 0201 0202 ComponentList::iterator componentsEnd = m_componentList.end(); 0203 for (ComponentList::iterator it = m_componentList.begin(); it != componentsEnd; ++it) 0204 (*it)->slotUpdateConfiguration(); 0205 } 0206 0207 void CircuitDocument::update() 0208 { 0209 CircuitICNDocument::update(); 0210 0211 bool animWires = KTLConfig::animateWires(); 0212 0213 if (KTLConfig::showVoltageColor() || animWires) { 0214 if (animWires) { 0215 // Wire animation is for showing currents, so we need to recalculate the currents 0216 // in the wires. 0217 calculateConnectorCurrents(); 0218 } 0219 0220 ConnectorList::iterator end = m_connectorList.end(); 0221 for (ConnectorList::iterator it = m_connectorList.begin(); it != end; ++it) { 0222 (*it)->incrementCurrentAnimation(1.0 / double(KTLConfig::refreshRate())); 0223 (*it)->updateConnectorLines(animWires); 0224 } 0225 } 0226 0227 if (KTLConfig::showVoltageColor() || KTLConfig::showVoltageBars()) { 0228 ECNodeMap::iterator end = m_ecNodeList.end(); 0229 for (ECNodeMap::iterator it = m_ecNodeList.begin(); it != end; ++it) { 0230 // static_cast<ECNode*>(*it)->setNodeChanged(); 0231 (*it)->setNodeChanged(); 0232 } 0233 } 0234 } 0235 0236 void CircuitDocument::fillContextMenu(const QPoint &pos) 0237 { 0238 CircuitICNDocument::fillContextMenu(pos); 0239 0240 CircuitView *activeCircuitView = dynamic_cast<CircuitView *>(activeView()); 0241 0242 if (!activeCircuitView) 0243 return; 0244 0245 bool canCreateSubcircuit = (m_selectList->count() > 1 && countExtCon(m_selectList->items()) > 0); 0246 QAction *subcircuitAction = activeCircuitView->actionByName("circuit_create_subcircuit"); 0247 subcircuitAction->setEnabled(canCreateSubcircuit); 0248 0249 if (m_selectList->count() < 1) 0250 return; 0251 0252 Component *item = dynamic_cast<Component *>(selectList()->activeItem()); 0253 0254 // NOTE: I negated this whole condition because I couldn't make out quite what the 0255 // logic was --electronerd 0256 if (!((!item && m_selectList->count() > 0) || !m_selectList->itemsAreSameType())) { 0257 QAction *orientation_actions[] = { 0258 activeCircuitView->actionByName("edit_orientation_0"), activeCircuitView->actionByName("edit_orientation_90"), activeCircuitView->actionByName("edit_orientation_180"), activeCircuitView->actionByName("edit_orientation_270")}; 0259 0260 if (!item) 0261 return; 0262 0263 for (unsigned i = 0; i < 4; ++i) { 0264 m_pOrientationAction->removeAction(orientation_actions[i]); 0265 m_pOrientationAction->addAction(orientation_actions[i]); 0266 } 0267 0268 QList<QAction *> orientation_actionlist; 0269 // orientation_actionlist.prepend( new KActionSeparator() ); 0270 orientation_actionlist.append(m_pOrientationAction); 0271 KTechlab::self()->plugActionList("orientation_actionlist", orientation_actionlist); 0272 } 0273 } 0274 0275 void CircuitDocument::deleteCircuits() 0276 { 0277 const CircuitList::iterator end = m_circuitList.end(); 0278 for (CircuitList::iterator it = m_circuitList.begin(); it != end; ++it) { 0279 if (!Simulator::isDestroyedSim()) { 0280 Simulator::self()->detachCircuit(*it); 0281 } 0282 delete *it; 0283 } 0284 m_circuitList.clear(); 0285 m_pinList.clear(); 0286 m_wireList.clear(); 0287 } 0288 0289 void CircuitDocument::requestAssignCircuits() 0290 { 0291 // qCDebug(KTL_LOG); 0292 if (m_bDeleted) { 0293 return; 0294 } 0295 deleteCircuits(); 0296 m_updateCircuitsTmr->stop(); 0297 m_updateCircuitsTmr->setSingleShot(true); 0298 m_updateCircuitsTmr->start(0 /*, true */); 0299 } 0300 0301 void CircuitDocument::connectorAddedSlot(Connector *connector) 0302 { 0303 if (connector) { 0304 connect(connector, &Connector::numWiresChanged, this, &CircuitDocument::requestAssignCircuits); 0305 connect(connector, &Connector::removed, this, &CircuitDocument::requestAssignCircuits); 0306 } 0307 } 0308 0309 void CircuitDocument::itemAdded(Item *item) 0310 { 0311 CircuitICNDocument::itemAdded(item); 0312 componentAdded(item); 0313 } 0314 0315 void CircuitDocument::componentAdded(Item *item) 0316 { 0317 Component *component = dynamic_cast<Component *>(item); 0318 0319 if (!component) 0320 return; 0321 0322 requestAssignCircuits(); 0323 0324 connect(component, &Component::elementCreated, this, &CircuitDocument::requestAssignCircuits); 0325 connect(component, &Component::elementDestroyed, this, &CircuitDocument::requestAssignCircuits); 0326 connect(component, &Component::removed, this, &CircuitDocument::componentRemoved); 0327 0328 // We don't attach the component to the Simulator just yet, as the 0329 // Component's vtable is not yet fully constructed, and so Simulator can't 0330 // tell whether or not it is a logic component 0331 if (!m_toSimulateList.contains(component)) 0332 m_toSimulateList << component; 0333 } 0334 0335 void CircuitDocument::componentRemoved(Item *item) 0336 { 0337 Component *component = dynamic_cast<Component *>(item); 0338 0339 if (!component) 0340 return; 0341 0342 m_componentList.removeAll(component); 0343 m_toSimulateList.removeAll(component); 0344 0345 requestAssignCircuits(); 0346 0347 if (!Simulator::isDestroyedSim()) { 0348 Simulator::self()->detachComponent(component); 0349 } 0350 } 0351 0352 // I think this is where the inf0z from cnodes/branches is moved into the midle-layer 0353 // pins/wires. 0354 0355 void CircuitDocument::calculateConnectorCurrents() 0356 { 0357 const CircuitList::iterator circuitEnd = m_circuitList.end(); 0358 for (CircuitList::iterator it = m_circuitList.begin(); it != circuitEnd; ++it) 0359 (*it)->updateCurrents(); 0360 0361 PinList groundPins; 0362 0363 // Tell the Pins to reset their calculated currents to zero 0364 m_pinList.removeAll(static_cast<Pin *>(nullptr)); 0365 0366 const PinList::iterator pinEnd = m_pinList.end(); 0367 for (PinList::iterator it = m_pinList.begin(); it != pinEnd; ++it) { 0368 if (Pin *n = dynamic_cast<Pin *>(static_cast<Pin *>(*it))) { 0369 n->resetCurrent(); 0370 n->setSwitchCurrentsUnknown(); 0371 0372 if (!n->parentECNode()->isChildNode()) { 0373 n->setCurrentKnown(true); 0374 // (and it has a current of 0 amps) 0375 } else if (n->groundType() == Pin::gt_always) { 0376 groundPins << n; 0377 n->setCurrentKnown(false); 0378 } else { 0379 // Child node that is non ground 0380 n->setCurrentKnown(n->parentECNode()->numPins() < 2); 0381 } 0382 } 0383 } 0384 0385 // Tell the components to update their ECNode's currents' from the elements 0386 // currents are merged into PINS. 0387 const ComponentList::iterator componentEnd = m_componentList.end(); 0388 for (ComponentList::iterator it = m_componentList.begin(); it != componentEnd; ++it) 0389 (*it)->setNodalCurrents(); 0390 0391 // And now for the wires and switches... 0392 m_wireList.removeAll(static_cast<Wire *>(nullptr)); 0393 const WireList::iterator clEnd = m_wireList.end(); 0394 for (WireList::iterator it = m_wireList.begin(); it != clEnd; ++it) 0395 (*it)->setCurrentKnown(false); 0396 0397 SwitchList switches = m_switchList; 0398 WireList wires = m_wireList; 0399 bool found = true; 0400 while ((!wires.isEmpty() || !switches.isEmpty() || !groundPins.isEmpty()) && found) { 0401 found = false; 0402 0403 for (WireList::iterator itW = wires.begin(); itW != wires.end();) { 0404 if ((*itW)->calculateCurrent()) { 0405 found = true; 0406 itW = wires.erase(itW); 0407 // note: assigning a temporary interator, incrementing and erasing, seems to crash 0408 } else { 0409 ++itW; 0410 } 0411 } 0412 0413 SwitchList::iterator switchesEnd = switches.end(); 0414 for (SwitchList::iterator it = switches.begin(); it != switchesEnd;) { 0415 if ((*it)->calculateCurrent()) { 0416 found = true; 0417 // note: assigning a temporary interator, incrementing and erasing, seems to crash 0418 // it = container.erase( it ) seems to crash other times 0419 SwitchList::iterator oldIt = it; 0420 ++it; 0421 switches.erase(oldIt); 0422 } else 0423 ++it; 0424 } 0425 0426 /* 0427 make the ground pins work. Current engine doesn't treat ground explicitly. 0428 */ 0429 0430 PinList::iterator groundPinsEnd = groundPins.end(); 0431 for (PinList::iterator it = groundPins.begin(); it != groundPinsEnd;) { 0432 if ((*it)->calculateCurrentFromWires()) { 0433 found = true; 0434 // note: assigning a temporary interator, incrementing and erasing, seems to crash sometimes; 0435 // it = container.erase( it ) seems to crash other times 0436 PinList::iterator oldIt = it; 0437 ++it; 0438 groundPins.erase(oldIt); 0439 } else 0440 ++it; 0441 } 0442 } 0443 } 0444 0445 void CircuitDocument::assignCircuits() 0446 { 0447 // Now we can finally add the unadded components to the Simulator 0448 const ComponentList::iterator toSimulateEnd = m_toSimulateList.end(); 0449 for (ComponentList::iterator it = m_toSimulateList.begin(); it != toSimulateEnd; ++it) 0450 Simulator::self()->attachComponent(*it); 0451 0452 m_toSimulateList.clear(); 0453 0454 // Stage 0: Build up pin and wire lists 0455 m_pinList.clear(); 0456 0457 const ECNodeMap::const_iterator nodeListEnd = m_ecNodeList.end(); 0458 for (ECNodeMap::const_iterator it = m_ecNodeList.begin(); it != nodeListEnd; ++it) { 0459 // if ( ECNode * ecnode = dynamic_cast<ECNode*>(*it) ) 0460 ECNode *ecnode = *it; 0461 0462 for (unsigned i = 0; i < ecnode->numPins(); i++) 0463 m_pinList << ecnode->pin(i); 0464 } 0465 0466 m_wireList.clear(); 0467 0468 const ConnectorList::const_iterator connectorListEnd = m_connectorList.end(); 0469 for (ConnectorList::const_iterator it = m_connectorList.begin(); it != connectorListEnd; ++it) { 0470 for (unsigned i = 0; i < (*it)->numWires(); i++) 0471 m_wireList << (*it)->wire(i); 0472 } 0473 0474 typedef QList<PinList> PinListList; 0475 0476 // Stage 1: Partition the circuit up into dependent areas (bar splitting 0477 // at ground pins) 0478 PinList unassignedPins = m_pinList; 0479 PinListList pinListList; 0480 0481 while (!unassignedPins.isEmpty()) { 0482 PinList pinList; 0483 getPartition(*unassignedPins.begin(), &pinList, &unassignedPins); 0484 pinListList.append(pinList); 0485 } 0486 0487 // qCDebug(KTL_LOG) << "pinListList.size()="<<pinListList.size(); 0488 0489 // Stage 2: Split up each partition into circuits by ground pins 0490 const PinListList::iterator nllEnd = pinListList.end(); 0491 for (PinListList::iterator it = pinListList.begin(); it != nllEnd; ++it) 0492 splitIntoCircuits(&*it); 0493 0494 // Stage 3: Initialize the circuits 0495 m_circuitList.removeAll(nullptr); 0496 CircuitList::iterator circuitListEnd = m_circuitList.end(); 0497 for (CircuitList::iterator it = m_circuitList.begin(); it != circuitListEnd; ++it) 0498 (*it)->init(); 0499 0500 m_switchList.clear(); 0501 m_componentList.clear(); 0502 const ItemMap::const_iterator cilEnd = m_itemList.end(); 0503 for (ItemMap::const_iterator it = m_itemList.begin(); it != cilEnd; ++it) { 0504 Component *component = dynamic_cast<Component *>(*it); 0505 0506 if (!component) 0507 continue; 0508 0509 m_componentList << component; 0510 component->initElements(0); 0511 m_switchList += component->switchList(); 0512 } 0513 0514 circuitListEnd = m_circuitList.end(); 0515 for (CircuitList::iterator it = m_circuitList.begin(); it != circuitListEnd; ++it) 0516 (*it)->createMatrixMap(); 0517 0518 for (ItemMap::const_iterator it = m_itemList.begin(); it != cilEnd; ++it) { 0519 Component *component = dynamic_cast<Component *>(*it); 0520 0521 if (component) 0522 component->initElements(1); 0523 } 0524 0525 circuitListEnd = m_circuitList.end(); 0526 for (CircuitList::iterator it = m_circuitList.begin(); it != circuitListEnd; ++it) { 0527 (*it)->initCache(); 0528 Simulator::self()->attachCircuit(*it); 0529 } 0530 } 0531 0532 void CircuitDocument::getPartition(Pin *pin, PinList *pinList, PinList *unassignedPins, bool onlyGroundDependent) 0533 { 0534 if (!pin) 0535 return; 0536 0537 unassignedPins->removeAll(pin); 0538 0539 if (pinList->contains(pin)) 0540 return; 0541 0542 pinList->append(pin); 0543 0544 const PinList localConnectedPins = pin->localConnectedPins(); 0545 const PinList::const_iterator end = localConnectedPins.end(); 0546 for (PinList::const_iterator it = localConnectedPins.begin(); it != end; ++it) 0547 getPartition(*it, pinList, unassignedPins, onlyGroundDependent); 0548 0549 const PinList groundDependentPins = pin->groundDependentPins(); 0550 const PinList::const_iterator dEnd = groundDependentPins.end(); 0551 for (PinList::const_iterator it = groundDependentPins.begin(); it != dEnd; ++it) 0552 getPartition(*it, pinList, unassignedPins, onlyGroundDependent); 0553 0554 if (!onlyGroundDependent) { 0555 PinList circuitDependentPins = pin->circuitDependentPins(); 0556 const PinList::const_iterator dEnd = circuitDependentPins.end(); 0557 for (PinList::const_iterator it = circuitDependentPins.begin(); it != dEnd; ++it) 0558 getPartition(*it, pinList, unassignedPins, onlyGroundDependent); 0559 } 0560 } 0561 0562 void CircuitDocument::splitIntoCircuits(PinList *pinList) 0563 { 0564 // First: identify ground 0565 PinList unassignedPins = *pinList; 0566 typedef QList<PinList> PinListList; 0567 PinListList pinListList; 0568 0569 while (!unassignedPins.isEmpty()) { 0570 PinList tempPinList; 0571 getPartition(*unassignedPins.begin(), &tempPinList, &unassignedPins, true); 0572 pinListList.append(tempPinList); 0573 } 0574 0575 const PinListList::iterator nllEnd = pinListList.end(); 0576 for (PinListList::iterator it = pinListList.begin(); it != nllEnd; ++it) 0577 Circuit::identifyGround(*it); 0578 0579 while (!pinList->isEmpty()) { 0580 PinList::iterator end = pinList->end(); 0581 PinList::iterator it = pinList->begin(); 0582 0583 while (it != end && (*it)->eqId() == -1) 0584 ++it; 0585 0586 if (it == end) 0587 break; 0588 else { 0589 Circuitoid *circuitoid = new Circuitoid; 0590 recursivePinAdd(*it, circuitoid, pinList); 0591 0592 if (!tryAsLogicCircuit(circuitoid)) 0593 m_circuitList += createCircuit(circuitoid); 0594 0595 delete circuitoid; 0596 } 0597 } 0598 0599 // Remaining pins are ground; tell them about it 0600 // TODO This is a bit hacky.... 0601 const PinList::iterator end = pinList->end(); 0602 for (PinList::iterator it = pinList->begin(); it != end; ++it) { 0603 (*it)->setVoltage(0.0); 0604 ElementList elements = (*it)->elements(); 0605 const ElementList::iterator eEnd = elements.end(); 0606 for (ElementList::iterator it = elements.begin(); it != eEnd; ++it) { 0607 if (LogicIn *logicIn = dynamic_cast<LogicIn *>(*it)) { 0608 logicIn->setLastState(false); 0609 logicIn->callCallback(); 0610 } 0611 } 0612 } 0613 } 0614 0615 void CircuitDocument::recursivePinAdd(Pin *pin, Circuitoid *circuitoid, PinList *unassignedPins) 0616 { 0617 if (!pin) 0618 return; 0619 0620 if (pin->eqId() != -1) 0621 unassignedPins->removeAll(pin); 0622 0623 if (circuitoid->contains(pin)) 0624 return; 0625 0626 circuitoid->addPin(pin); 0627 0628 if (pin->eqId() == -1) 0629 return; 0630 0631 const PinList localConnectedPins = pin->localConnectedPins(); 0632 const PinList::const_iterator end = localConnectedPins.end(); 0633 for (PinList::const_iterator it = localConnectedPins.begin(); it != end; ++it) 0634 recursivePinAdd(*it, circuitoid, unassignedPins); 0635 0636 const PinList groundDependentPins = pin->groundDependentPins(); 0637 const PinList::const_iterator gdEnd = groundDependentPins.end(); 0638 for (PinList::const_iterator it = groundDependentPins.begin(); it != gdEnd; ++it) 0639 recursivePinAdd(*it, circuitoid, unassignedPins); 0640 0641 const PinList circuitDependentPins = pin->circuitDependentPins(); 0642 const PinList::const_iterator cdEnd = circuitDependentPins.end(); 0643 for (PinList::const_iterator it = circuitDependentPins.begin(); it != cdEnd; ++it) 0644 recursivePinAdd(*it, circuitoid, unassignedPins); 0645 0646 const ElementList elements = pin->elements(); 0647 const ElementList::const_iterator eEnd = elements.end(); 0648 for (ElementList::const_iterator it = elements.begin(); it != eEnd; ++it) 0649 circuitoid->addElement(*it); 0650 } 0651 0652 bool CircuitDocument::tryAsLogicCircuit(Circuitoid *circuitoid) 0653 { 0654 if (!circuitoid) 0655 return false; 0656 0657 if (circuitoid->elementList.size() == 0) { 0658 // This doesn't quite belong here...but whatever. Initialize all 0659 // pins to voltage zero as they won't get set to zero otherwise 0660 const PinList::const_iterator pinListEnd = circuitoid->pinList.constEnd(); 0661 for (PinList::const_iterator it = circuitoid->pinList.constBegin(); it != pinListEnd; ++it) 0662 (*it)->setVoltage(0.0); 0663 0664 // A logic circuit only requires there to be no non-logic components, 0665 // and at most one LogicOut - so this qualifies 0666 return true; 0667 } 0668 0669 LogicInList logicInList; 0670 LogicOut *out = nullptr; 0671 0672 uint logicInCount = 0; 0673 uint logicOutCount = 0; 0674 ElementList::const_iterator end = circuitoid->elementList.end(); 0675 for (ElementList::const_iterator it = circuitoid->elementList.begin(); it != end; ++it) { 0676 if ((*it)->type() == Element::Element_LogicOut) { 0677 logicOutCount++; 0678 out = static_cast<LogicOut *>(*it); 0679 } else if ((*it)->type() == Element::Element_LogicIn) { 0680 logicInCount++; 0681 logicInList += static_cast<LogicIn *>(*it); 0682 } else 0683 return false; 0684 } 0685 0686 if (logicOutCount > 1) 0687 return false; 0688 else if (logicOutCount == 1) 0689 Simulator::self()->createLogicChain(out, logicInList, circuitoid->pinList); 0690 else { 0691 // We have ourselves stranded LogicIns...so lets set them all to low 0692 0693 const PinList::const_iterator pinListEnd = circuitoid->pinList.constEnd(); 0694 for (PinList::const_iterator it = circuitoid->pinList.constBegin(); it != pinListEnd; ++it) 0695 (*it)->setVoltage(0.0); 0696 0697 for (ElementList::const_iterator it = circuitoid->elementList.begin(); it != end; ++it) { 0698 LogicIn *logicIn = static_cast<LogicIn *>(*it); 0699 logicIn->setNextLogic(nullptr); 0700 logicIn->setElementSet(nullptr); 0701 if (logicIn->isHigh()) { 0702 logicIn->setLastState(false); 0703 logicIn->callCallback(); 0704 } 0705 } 0706 } 0707 0708 return true; 0709 } 0710 0711 Circuit *CircuitDocument::createCircuit(Circuitoid *circuitoid) 0712 { 0713 if (!circuitoid) 0714 return nullptr; 0715 0716 Circuit *circuit = new Circuit(); 0717 0718 const PinList::const_iterator nEnd = circuitoid->pinList.end(); 0719 for (PinList::const_iterator it = circuitoid->pinList.begin(); it != nEnd; ++it) 0720 circuit->addPin(*it); 0721 0722 const ElementList::const_iterator eEnd = circuitoid->elementList.end(); 0723 for (ElementList::const_iterator it = circuitoid->elementList.begin(); it != eEnd; ++it) 0724 circuit->addElement(*it); 0725 0726 return circuit; 0727 } 0728 0729 void CircuitDocument::createSubcircuit() 0730 { 0731 ItemList itemList = m_selectList->items(); 0732 const ItemList::iterator itemListEnd = itemList.end(); 0733 for (ItemList::iterator it = itemList.begin(); it != itemListEnd; ++it) { 0734 if (!dynamic_cast<Component *>(static_cast<Item *>(*it))) 0735 *it = nullptr; 0736 } 0737 0738 itemList.removeAll(static_cast<Item *>(nullptr)); 0739 0740 if (itemList.isEmpty()) { 0741 KMessageBox::error(activeView(), i18n("No components were found in the selection.")); 0742 return; 0743 } 0744 0745 // Number of external connections 0746 const int extConCount = countExtCon(itemList); 0747 if (extConCount == 0) { 0748 KMessageBox::error(activeView(), i18n("No External Connection components were found in the selection.")); 0749 return; 0750 } 0751 0752 bool ok; 0753 const QString name = QInputDialog::getText(activeView(), "Subcircuit", "Name", QLineEdit::Normal, QString(), &ok); 0754 if (!ok) 0755 return; 0756 0757 SubcircuitData subcircuit; 0758 subcircuit.addItems(itemList); 0759 subcircuit.addNodes(getCommonNodes(itemList)); 0760 subcircuit.addConnectors(getCommonConnectors(itemList)); 0761 0762 Subcircuits::addSubcircuit(name, subcircuit.toXML()); 0763 } 0764 0765 int CircuitDocument::countExtCon(const ItemList &itemList) const 0766 { 0767 int count = 0; 0768 const ItemList::const_iterator end = itemList.end(); 0769 for (ItemList::const_iterator it = itemList.begin(); it != end; ++it) { 0770 Item *item = *it; 0771 0772 if (item && item->type() == "ec/external_connection") 0773 count++; 0774 } 0775 return count; 0776 } 0777 0778 bool CircuitDocument::isValidItem(const QString &itemId) 0779 { 0780 return itemId.startsWith("ec/") || itemId.startsWith("dp/") || itemId.startsWith("sc/"); 0781 } 0782 0783 bool CircuitDocument::isValidItem(Item *item) 0784 { 0785 return (dynamic_cast<Component *>(item) || dynamic_cast<DrawPart *>(item)); 0786 } 0787 0788 void CircuitDocument::displayEquations() 0789 { 0790 qCDebug(KTL_LOG) << "######################################################"; 0791 const CircuitList::iterator end = m_circuitList.end(); 0792 int i = 1; 0793 for (CircuitList::iterator it = m_circuitList.begin(); it != end; ++it) { 0794 qCDebug(KTL_LOG) << "Equation set " << i << ":"; 0795 (*it)->displayEquations(); 0796 i++; 0797 } 0798 qCDebug(KTL_LOG) << "######################################################"; 0799 } 0800 0801 #include "moc_circuitdocument.cpp"