/*
Copyright 2006-2026 The QElectroTech Team
This file is part of QElectroTech.
QElectroTech 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 2 of the License, or
(at your option) any later version.
QElectroTech 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 QElectroTech. If not, see .
*/
#include "realterminal.h"
#include "terminalstrip.h"
#include "../qetgraphicsitem/terminalelement.h"
#include "physicalterminal.h"
#include "terminalcolor.h"
#include "terminalcatalog.h"
#include "../qetgraphicsitem/conductor.h"
#include "../qetinformation.h"
/**
* @brief RealTerminal
* @param parent_strip : parent terminal strip
* @param terminal : terminal element (if any) in a folio
*/
RealTerminal::RealTerminal(Element *terminal) :
m_element(terminal)
{}
/**
* @brief RealTerminal::createSpare
* Create a spare terminal (a reserved terminal) : a terminal of a strip
* which is not drawn on any folio. Its data (label, type, section,
* width...) are its own, stored with the terminal strip.
* @param data : the data of the terminal, only the terminal type,
* function, led and the informations are used.
* @param uuid : the uuid of the spare terminal
* @return the new spare terminal, not yet added to a strip
*/
QSharedPointer RealTerminal::createSpare(const ElementData &data, const QUuid &uuid)
{
auto raw_ = new RealTerminal(nullptr);
raw_->m_spare = true;
raw_->m_spare_uuid = uuid.isNull() ? QUuid::createUuid() : uuid;
raw_->m_spare_data.m_type = ElementData::Terminal;
raw_->m_spare_data.setTerminalType(data.terminalType());
raw_->m_spare_data.setTerminalFunction(data.terminalFunction());
raw_->m_spare_data.setTerminalLED(data.terminalLed());
raw_->m_spare_data.m_informations = data.m_informations;
return raw_->sharedRef();
}
/**
* @brief RealTerminal::spareFromXml
* @param xml_element : a real terminal saved by toXml()
* @return the spare terminal saved in @a xml_element,
* or a null pointer if @a xml_element is not a spare terminal.
*/
QSharedPointer RealTerminal::spareFromXml(const QDomElement &xml_element)
{
if (xml_element.tagName() != xmlTagName() ||
!xml_element.hasAttribute(QStringLiteral("spare_uuid"))) {
return QSharedPointer();
}
ElementData data_;
data_.setTerminalType(ElementData::terminalTypeFromString(
xml_element.attribute(QStringLiteral("type"))));
data_.setTerminalFunction(ElementData::terminalFunctionFromString(
xml_element.attribute(QStringLiteral("function"))));
data_.setTerminalLED(QET::infoFlagIsTrue(xml_element.attribute(QStringLiteral("led"))));
data_.m_informations.fromXml(xml_element.firstChildElement(QStringLiteral("informations")),
QStringLiteral("information"));
return createSpare(data_, QUuid(xml_element.attribute(QStringLiteral("spare_uuid"))));
}
/**
* @brief RealTerminal::isSpare
* @return true if this terminal is a spare terminal, not drawn on any folio
* @sa createSpare
*/
bool RealTerminal::isSpare() const {
return m_spare;
}
/**
* @brief RealTerminal::spareData
* @return the data of this spare terminal, an empty ElementData
* if this terminal is not a spare terminal.
*/
ElementData RealTerminal::spareData() const {
return m_spare_data;
}
/**
* @brief RealTerminal::setSpareData
* Set the data of this spare terminal (nothing is done if this terminal
* is not a spare terminal) and tell the strip, so that it is redrawn.
* @param data
*/
void RealTerminal::setSpareData(const ElementData &data)
{
if (!m_spare) {
return;
}
m_spare_data.setTerminalType(data.terminalType());
m_spare_data.setTerminalFunction(data.terminalFunction());
m_spare_data.setTerminalLED(data.terminalLed());
m_spare_data.m_informations = data.m_informations;
if (auto strip_ = parentStrip()) {
strip_->terminalDataChanged();
}
}
/**
* @brief RealTerminal::hasData
* @return true if this terminal has data : it is linked to an element,
* or it is a spare terminal.
*/
bool RealTerminal::hasData() const {
return m_spare || !m_element.isNull();
}
/**
* @brief RealTerminal::terminalData
* @return the data of this terminal : the data of its element,
* or its own data if it is a spare terminal.
*/
ElementData RealTerminal::terminalData() const
{
if (m_spare) {
return m_spare_data;
}
if (m_element) {
return m_element->elementData();
}
return ElementData();
}
RealTerminal::~RealTerminal()
{
if (m_physical_terminal) {
m_physical_terminal->removeTerminal(sharedRef());
}
}
/**
* @brief RealTerminal::sharedRef
* @return a QSharedPointer of this
*/
QSharedPointer RealTerminal::sharedRef()
{
QSharedPointer this_shared(this->weakRef());
if (this_shared.isNull())
{
this_shared = QSharedPointer(this);
m_this_weak = this_shared.toWeakRef();
}
return this_shared;
}
/**
* @brief RealTerminal::sharedRef
* @return a shared reference of this, not that because
* this method is const, the shared reference can be null if not already
* used in another part of the code.
*/
QSharedPointer RealTerminal::sharedRef() const {
return QSharedPointer(m_this_weak);
}
/**
* @brief RealTerminal::weakRef
* @return a QWeakPointer of this, weak pointer can be bull
*/
QWeakPointer RealTerminal::weakRef() {
return m_this_weak;
}
/**
* @brief toXml
* @param parent_document
* @return this real terminal to xml
*/
QDomElement RealTerminal::toXml(QDomDocument &parent_document) const
{
auto root_elmt = parent_document.createElement(this->xmlTagName());
if (m_spare)
{
//A spare terminal is on no folio : its data are saved here
root_elmt.setAttribute(QStringLiteral("spare_uuid"), m_spare_uuid.toString());
root_elmt.setAttribute(QStringLiteral("type"), ElementData::terminalTypeToString(m_spare_data.terminalType()));
root_elmt.setAttribute(QStringLiteral("function"), ElementData::terminalFunctionToString(m_spare_data.terminalFunction()));
if (m_spare_data.terminalLed()) {
root_elmt.setAttribute(QStringLiteral("led"), QStringLiteral("true"));
}
auto xml_info = parent_document.createElement(QStringLiteral("informations"));
m_spare_data.m_informations.toXml(xml_info, QStringLiteral("information"));
root_elmt.appendChild(xml_info);
}
else if (m_element) {
root_elmt.setAttribute(QStringLiteral("element_uuid"), m_element->uuid().toString());
}
return root_elmt;
}
/**
* @brief RealTerminal::setPhysicalTerminal
* Set the parent physical terminal of this real terminal
* @param phy_t
*/
void RealTerminal::setPhysicalTerminal(const QSharedPointer &phy_t) {
m_physical_terminal = phy_t;
}
/**
* @brief parentStrip
* @return parent terminal strip or nullptr
*/
TerminalStrip *RealTerminal::parentStrip() const noexcept {
if (m_physical_terminal) {
return m_physical_terminal->terminalStrip();
} else {
return nullptr;
}
}
/**
* @brief RealTerminal::physicalTerminal
* @return The parent physical terminal of this terminal.
* The returned QSharedPointer can be null
*/
QSharedPointer RealTerminal::physicalTerminal() const noexcept{
return m_physical_terminal;
}
/**
* @brief RealTerminal::level
* @return
*/
int RealTerminal::level() const
{
if (m_physical_terminal &&
sharedRef()) {
return m_physical_terminal->levelOf(sharedRef());
}
return -1;
}
/**
* @brief label
* @return the label of this real terminal
*/
QString RealTerminal::label() const {
if (m_spare) {
return m_spare_data.m_informations.value(QStringLiteral("label")).toString();
} else if (!m_element.isNull()) {
return m_element->actualLabel();
} else {
return QLatin1String();
}
}
/**
* @brief RealTerminal::Xref
* @return Convenient method to get the XRef
* formatted to string
*/
QString RealTerminal::Xref() const
{
if (!m_element.isNull()) {
return autonum::AssignVariables::genericXref(m_element.data());
} else {
return QString();
}
}
/**
* @brief RealTerminal::cable
* @return The name of the cable (hose) connected to this terminal.
* The value is stored in the informations of the element.
*/
QString RealTerminal::cable() const {
if (hasData()) {
return terminalData().m_informations.value(cableInfoKey()).toString();
}
return QString();
}
/**
* @brief RealTerminal::cableWire
* @return The color / number of the wire of the cable, connected to this terminal.
*/
QString RealTerminal::cableWire() const {
if (hasData()) {
return terminalData().m_informations.value(cableWireInfoKey()).toString();
}
return QString();
}
/**
* @brief RealTerminal::isShield
* @return true if this terminal is the shield of the cable
* and not one of its wires.
*/
bool RealTerminal::isShield() const {
if (hasData()) {
return QET::infoFlagIsTrue(
terminalData().m_informations.value(shieldInfoKey()).toString());
}
return false;
}
/**
* @brief RealTerminal::color
* @return The color set by the user for this terminal, drawn in the
* terminal strip. An invalid color mean that the user did not set a color.
* The value is stored in the informations of the element.
* @sa stripeColor
*/
QColor RealTerminal::color() const
{
QColor main_, stripe_;
if (hasData()) {
TerminalColor::parse(terminalData().m_informations.value(colorInfoKey()).toString(),
&main_, &stripe_);
}
return main_;
}
/**
* @brief RealTerminal::stripeColor
* @return The second color of a two-tone color (green/yellow for example)
* or an invalid color if the color of this terminal is plain or not set.
*/
QColor RealTerminal::stripeColor() const
{
QColor main_, stripe_;
if (hasData()) {
TerminalColor::parse(terminalData().m_informations.value(colorInfoKey()).toString(),
&main_, &stripe_);
}
return stripe_;
}
/**
* @brief RealTerminal::section
* @return The section in mm2 set by the user for this terminal
* or 0 if no section was set.
* The value is stored in the informations of the element,
* with the dot as decimal separator, whatever the locale.
*/
qreal RealTerminal::section() const
{
if (hasData())
{
bool ok_ = false;
const auto section_ = terminalData().m_informations.value(sectionInfoKey()).toString().toDouble(&ok_);
if (ok_ && section_ > 0) {
return section_;
}
}
return 0;
}
/**
* @brief RealTerminal::widthMm
* @return The width in mm of this terminal (the width of the terminal
* sold by the manufacturer), set by the user, or 0 if no width was set.
* The value is stored in the informations of the element,
* with the dot as decimal separator, whatever the locale.
*/
qreal RealTerminal::widthMm() const
{
if (hasData())
{
bool ok_ = false;
const auto width_ = terminalData().m_informations.value(widthInfoKey()).toString().toDouble(&ok_);
if (ok_ && width_ > 0) {
return width_;
}
}
return 0;
}
/**
* @brief RealTerminal::catalogEntry
* @return the label of the terminal chosen in the catalog
* (TerminalCatalog::WidthEntry::label), for example "Phoenix Contact ST 4",
* or an empty string if the width was typed or not set.
* Several terminals of the catalog can have the same width,
* the width alone doesn't say which one was chosen.
*/
QString RealTerminal::catalogEntry() const
{
if (hasData()) {
return terminalData().m_informations.value(catalogEntryInfoKey()).toString();
}
return QString();
}
/**
* @brief RealTerminal::applyCatalogEntry
* Store in @a informations the terminal chosen in the catalog,
* and write its manufacturer and its reference in the manufacturer and
* manufacturer reference informations (used by the bill of materials).
* These two informations are only written when they are empty,
* or when they still hold what the previous entry of the catalog wrote :
* a value typed by the user is never overwritten.
* @param informations : the informations of the element
* @param entry_label : label of an entry of the catalog,
* an empty string (or a label unknown by the catalog) removes the entry.
*/
void RealTerminal::applyCatalogEntry(DiagramContext &informations, const QString &entry_label)
{
const auto *old_entry = TerminalCatalog::entryByLabel(
informations.value(catalogEntryInfoKey()).toString());
const auto *new_entry = TerminalCatalog::entryByLabel(entry_label);
if (new_entry) {
informations.addValue(catalogEntryInfoKey(), QString::fromUtf8(new_entry->label));
} else if (informations.contains(catalogEntryInfoKey())) {
informations.remove(catalogEntryInfoKey());
}
const auto update_ = [&informations](const QString &key, const QString &old_value, const QString &new_value)
{
const auto current_ = informations.value(key).toString();
if (!current_.isEmpty() && current_ != old_value) {
return; //Typed by the user, keep it
}
if (!new_value.isEmpty()) {
informations.addValue(key, new_value);
} else if (!current_.isEmpty()) {
informations.addValue(key, QString());
}
};
update_(QETInformation::ELMT_MANUFACTURER,
old_entry ? QString::fromUtf8(old_entry->manufacturer) : QString(),
new_entry ? QString::fromUtf8(new_entry->manufacturer) : QString());
update_(QETInformation::ELMT_MANUFACTURER_REF,
old_entry ? TerminalCatalog::modelName(*old_entry) : QString(),
new_entry ? TerminalCatalog::modelName(*new_entry) : QString());
}
/**
* @brief RealTerminal::hasSeparatorAfter
* @return true if a separator plate (partition plate) is mounted
* after this terminal, between this terminal and the next one of the strip.
* Used to separate groups of terminals (voltages, functions...).
*/
bool RealTerminal::hasSeparatorAfter() const {
if (hasData()) {
return QET::infoFlagIsTrue(
terminalData().m_informations.value(separatorInfoKey()).toString());
}
return false;
}
/**
* @brief RealTerminal::conductor
* @return
*/
QString RealTerminal::conductor() const {
if (m_element)
{
const auto conductors_{m_element->conductors()};
if (conductors_.size()) {
return conductors_.first()->properties().text;
}
}
return QString();
}
/**
* @brief RealTerminal::type
* @return
*/
ElementData::TerminalType RealTerminal::type() const {
if (hasData()) {
return terminalData().terminalType();
} else {
return ElementData::TTGeneric;
}
}
/**
* @brief RealTerminal::function
* @return
*/
ElementData::TerminalFunction RealTerminal::function() const {
if (hasData()) {
return terminalData().terminalFunction();
} else {
return ElementData::TFGeneric;
}
}
/**
* @brief RealTerminal::isLed
* @return
*/
bool RealTerminal::isLed() const {
if (hasData()) {
return terminalData().terminalLed();
} else {
return false;
}
}
/**
* @brief isElement
* @return true if this real terminal is linked to a terminal element
*/
bool RealTerminal::isElement() const {
return m_element.isNull() ? false : true;
}
/**
* @brief RealTerminal::isBridged
* @return true if is bridged.
* @sa TerminalStrip::isBridged
*/
bool RealTerminal::isBridged() const
{
if (parentStrip()) {
return !parentStrip()->isBridged(m_this_weak.toStrongRef()).isNull();
} else {
return false;
}
}
/**
* @brief RealTerminal::bridge
* @return
*/
QSharedPointer RealTerminal::bridge() const
{
if (parentStrip()) {
return parentStrip()->isBridged(m_this_weak.toStrongRef());
} else {
return QSharedPointer();
}
}
/**
* @brief element
* @return the element linked to this real terminal
* or nullptr if not linked to an Element.
*/
Element *RealTerminal::element() const {
return m_element.data();
}
/**
* @brief elementUuid
* @return if this real terminal is an element
* in a folio, return the uuid of the element
* else return a null uuid.
*/
QUuid RealTerminal::elementUuid() const {
if (m_spare) {
return m_spare_uuid; //Used to find the terminal (bridges...) like an element uuid
}
if (!m_element.isNull()) {
return m_element->uuid();
} else {
return QUuid();
}
}
/**
* @brief RealTerminal::RealTerminal::xmlTagName
* @return
*/
QString RealTerminal::RealTerminal::xmlTagName() {
return QStringLiteral("real_terminal");
}