Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
8c610eeceThis file is pinned to the selected release. Download the original to open it in your design tool.
/*
* LibrePCB - Professional EDA for everyone!
* Copyright (C) 2013 LibrePCB Developers, see AUTHORS.md for contributors.
* https://librepcb.org/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*******************************************************************************
* Includes
******************************************************************************/
#include "componentinstance.h"
#include "../../exceptions.h"
#include "../../library/cmp/component.h"
#include "../../library/dev/device.h"
#include "../../utils/scopeguardlist.h"
#include "../board/board.h"
#include "../board/items/bi_device.h"
#include "../project.h"
#include "../projectlibrary.h"
#include "../schematic/items/si_symbol.h"
#include "../schematic/schematic.h"
#include "circuit.h"
#include "componentsignalinstance.h"
#include <QtCore>
/*******************************************************************************
* Namespace
******************************************************************************/
namespace librepcb {
/*******************************************************************************
* Constructors / Destructor
******************************************************************************/
ComponentInstance::ComponentInstance(Circuit& circuit, const Uuid& uuid,
const Component& cmp, const Uuid& symbVar,
const CircuitIdentifier& name)
: QObject(&circuit),
mCircuit(circuit),
mIsAddedToCircuit(false),
mUuid(uuid),
mName(name),
mValue(cmp.getDefaultValue()),
mLibComponent(cmp),
mCompSymbVar(cmp.getSymbolVariants().get(symbVar).get()), // can throw
mAttributes(new AttributeList(cmp.getAttributes())),
mLockAssembly(circuit.getProject().getDefaultLockComponentAssembly()),
mPrimaryDevice(nullptr) {
Q_ASSERT(mCompSymbVar);
// add signal map
for (const ComponentSignal& signal : cmp.getSignals()) {
ComponentSignalInstance* signalInstance =
new ComponentSignalInstance(mCircuit, *this, signal, nullptr);
mSignals.insert(signalInstance->getCompSignal().getUuid(), signalInstance);
}
// Update primary device when the primary board has changed.
connect(&mCircuit.getProject(), &Project::primaryBoardChanged, this,
&ComponentInstance::updatePrimaryDevice);
// emit the "attributesChanged" signal when the project has emitted it
connect(&mCircuit.getProject(), &Project::attributesChanged, this,
&ComponentInstance::attributesChanged);
}
ComponentInstance::~ComponentInstance() noexcept {
Q_ASSERT(!mIsAddedToCircuit);
Q_ASSERT(!isUsed());
qDeleteAll(mSignals);
mSignals.clear();
}
/*******************************************************************************
* Getters
******************************************************************************/
bool ComponentInstance::isPureSchematicOnly() const noexcept {
if (!mIsPureSchematicOnly) {
// Maybe we should also check for the existence of {{NAME}} text elements
// (or any text on the text layer), but that feels a bit wrong at this
// place (texts are schematic elements, not circuit elements).
mIsPureSchematicOnly =
mLibComponent.isSchematicOnly() && mAssemblyOptions.isEmpty();
}
return *mIsPureSchematicOnly;
}
QSet<Uuid> ComponentInstance::getCompatibleDevices() const noexcept {
QSet<Uuid> result;
for (const ComponentAssemblyOption& option : mAssemblyOptions) {
result.insert(option.getDevice());
}
return result;
}
QVector<std::shared_ptr<const Part>> ComponentInstance::getParts(
const std::optional<Uuid>& assemblyVariant) const noexcept {
QVector<std::shared_ptr<const Part>> parts;
for (const ComponentAssemblyOption& opt : mAssemblyOptions) {
if (((!assemblyVariant) ||
(opt.getAssemblyVariants().contains(*assemblyVariant)))) {
for (auto it = opt.getParts().begin(); it != opt.getParts().end(); ++it) {
parts.append(it.ptr());
}
if (opt.getParts().isEmpty()) {
parts.append(std::make_shared<Part>(SimpleString(""), SimpleString(""),
opt.getAttributes()));
}
}
}
return parts;
}
QSet<Uuid> ComponentInstance::getUsedDeviceUuids() const noexcept {
QSet<Uuid> uuids;
foreach (const BI_Device* device, mRegisteredDevices) {
uuids.insert(device->getLibDevice().getUuid());
}
return uuids;
}
int ComponentInstance::getRegisteredElementsCount() const noexcept {
int count = 0;
count += mRegisteredSymbols.count();
count += mRegisteredDevices.count();
return count;
}
bool ComponentInstance::isUsed() const noexcept {
if (getRegisteredElementsCount() > 0) {
return true;
}
foreach (const ComponentSignalInstance* signal, mSignals) {
if (signal->isUsed()) {
return true;
}
}
return false;
}
/*******************************************************************************
* Setters
******************************************************************************/
void ComponentInstance::setName(const CircuitIdentifier& name) noexcept {
if (name != mName) {
mName = name;
emit attributesChanged();
}
}
void ComponentInstance::setValue(const QString& value) noexcept {
if (value != mValue) {
mValue = value;
emit attributesChanged();
}
}
void ComponentInstance::setAttributes(
const AttributeList& attributes) noexcept {
if (attributes != *mAttributes) {
*mAttributes = attributes;
emit attributesChanged();
}
}
void ComponentInstance::setAssemblyOptions(
const ComponentAssemblyOptionList& options) noexcept {
if (options != mAssemblyOptions) {
mAssemblyOptions = options;
mIsPureSchematicOnly = std::nullopt; // Invalidate cache.
emit attributesChanged();
}
}
/*******************************************************************************
* General Methods
******************************************************************************/
void ComponentInstance::addToCircuit() {
if (mIsAddedToCircuit || isUsed()) {
throw LogicError(__FILE__, __LINE__);
}
ScopeGuardList sgl(mSignals.count());
foreach (ComponentSignalInstance* signal, mSignals) {
signal->addToCircuit(); // can throw
sgl.add([signal]() { signal->removeFromCircuit(); });
}
mIsAddedToCircuit = true;
sgl.dismiss();
}
void ComponentInstance::removeFromCircuit() {
if (!mIsAddedToCircuit) {
throw LogicError(__FILE__, __LINE__);
}
if (isUsed()) {
throw RuntimeError(__FILE__, __LINE__,
tr("The component \"%1\" cannot be removed "
"because it is still in use!")
.arg(*mName));
}
ScopeGuardList sgl(mSignals.count());
foreach (ComponentSignalInstance* signal, mSignals) {
signal->removeFromCircuit(); // can throw
sgl.add([signal]() { signal->addToCircuit(); });
}
mIsAddedToCircuit = false;
sgl.dismiss();
}
void ComponentInstance::registerSymbol(SI_Symbol& symbol) {
if ((!mIsAddedToCircuit) || (symbol.getCircuit() != mCircuit)) {
throw LogicError(__FILE__, __LINE__);
}
Uuid itemUuid = symbol.getCompSymbVarItem().getUuid();
if (!mCompSymbVar->getSymbolItems().find(itemUuid)) {
throw RuntimeError(__FILE__, __LINE__,
QString("Invalid symbol item in circuit: \"%1\".")
.arg(itemUuid.toStr()));
}
if (mRegisteredSymbols.contains(itemUuid)) {
throw RuntimeError(
__FILE__, __LINE__,
QString("Symbol item UUID already exists in circuit: \"%1\".")
.arg(itemUuid.toStr()));
}
if (!mRegisteredSymbols.isEmpty()) {
if (symbol.getSchematic() !=
mRegisteredSymbols.values().first()->getSchematic()) {
// Actually it would be possible to place the symbols of a component on
// different schematics. But maybe some time this will no longer be
// possible due to the concept of hierarchical sheets, sub-circuits or
// something like that. To make the later project upgrade process (as
// simple as) possible, we introduce this restriction already from now on.
throw RuntimeError(__FILE__, __LINE__,
tr("All symbols of a component must be placed in the "
"same schematic."));
}
}
mRegisteredSymbols.insert(itemUuid, &symbol);
}
void ComponentInstance::unregisterSymbol(SI_Symbol& symbol) {
Uuid itemUuid = symbol.getCompSymbVarItem().getUuid();
if ((!mIsAddedToCircuit) || (!mRegisteredSymbols.contains(itemUuid)) ||
(&symbol != mRegisteredSymbols.value(itemUuid))) {
throw LogicError(__FILE__, __LINE__);
}
mRegisteredSymbols.remove(itemUuid);
}
void ComponentInstance::registerDevice(BI_Device& device) {
if ((!mIsAddedToCircuit) || (device.getCircuit() != mCircuit) ||
(mRegisteredDevices.contains(&device)) ||
(mLibComponent.isSchematicOnly())) {
throw LogicError(__FILE__, __LINE__);
}
mRegisteredDevices.append(&device);
updatePrimaryDevice();
emit attributesChanged(); // parent attribute provider may have changed!
}
void ComponentInstance::unregisterDevice(BI_Device& device) {
if ((!mIsAddedToCircuit) || (!mRegisteredDevices.contains(&device))) {
throw LogicError(__FILE__, __LINE__);
}
mRegisteredDevices.removeOne(&device);
updatePrimaryDevice();
emit attributesChanged(); // parent attribute provider may have changed!
}
void ComponentInstance::serialize(SExpression& root) const {
if (!checkAttributesValidity()) throw LogicError(__FILE__, __LINE__);
root.appendChild(mUuid);
root.ensureLineBreak();
root.appendChild("lib_component", mLibComponent.getUuid());
root.ensureLineBreak();
root.appendChild("lib_variant", mCompSymbVar->getUuid());
root.ensureLineBreak();
root.appendChild("name", mName);
root.appendChild("value", mValue);
root.ensureLineBreak();
root.appendChild("lock_assembly", mLockAssembly);
root.ensureLineBreak();
mAttributes->serialize(root);
root.ensureLineBreak();
mAssemblyOptions.serialize(root);
root.ensureLineBreak();
for (const ComponentSignalInstance* obj : mSignals) {
obj->serialize(root.appendList("signal"));
root.ensureLineBreak();
}
root.ensureLineBreak();
}
/*******************************************************************************
* Private Methods
******************************************************************************/
void ComponentInstance::updatePrimaryDevice() noexcept {
const BI_Device* primary = nullptr;
if (Board* board = mCircuit.getProject().getPrimaryBoard()) {
foreach (BI_Device* device, mRegisteredDevices) {
if (&device->getBoard() == board) {
primary = device;
}
}
}
if (primary != mPrimaryDevice) {
mPrimaryDevice = primary;
emit primaryDeviceChanged(mPrimaryDevice);
}
}
bool ComponentInstance::checkAttributesValidity() const noexcept {
if (mCompSymbVar == nullptr) return false;
return true;
}
const QStringList& ComponentInstance::getLocaleOrder() const noexcept {
return mCircuit.getProject().getLocaleOrder();
}
/*******************************************************************************
* End of File
******************************************************************************/
} // namespace librepcb