25 if (command ==
"connect_elements") {
26 return handleConnectElements(params, requestId);
27 }
else if (command ==
"disconnect_elements") {
28 return handleDisconnectElements(params, requestId);
29 }
else if (command ==
"list_connections") {
30 return handleListConnections(params, requestId);
31 }
else if (command ==
"split_connection") {
32 return handleSplitConnection(params, requestId);
39QJsonObject ConnectionHandler::handleConnectElements(
const QJsonObject ¶ms,
const QJsonValue &requestId)
58 if (!resolvePort(params,
"source", sourceElement,
true, sourcePort, errorMsg)) {
63 if (!resolvePort(params,
"target", targetElement,
false, targetPort, errorMsg)) {
67 Port *outputPort = sourceElement->outputPort(sourcePort);
68 Port *inputPort = targetElement->inputPort(targetPort);
70 if (!outputPort || !inputPort) {
74 auto *startPort =
dynamic_cast<OutputPort *
>(outputPort);
75 auto *endPort =
dynamic_cast<InputPort *
>(inputPort);
82 return createErrorResponse(QString(
"Connection from element %1 port %2 to element %3 port %4 is not allowed "
83 "(duplicate, occupied input, or wireless port)")
84 .arg(sourceElement->id()).arg(sourcePort).arg(targetElement->id()).arg(targetPort),
93 auto connection = std::make_unique<Connection>();
94 connection->setStartPort(startPort);
95 connection->setEndPort(endPort);
96 connection->updatePath();
99 scene->
receiveCommand(
new AddItemsCommand({connection.get()}, scene));
100 connection.release();
101 }
catch (
const std::exception &e) {
112QJsonObject ConnectionHandler::handleDisconnectElements(
const QJsonObject ¶ms,
const QJsonValue &requestId)
120 if (!sourceElement) {
124 if (!targetElement) {
133 const auto connections = scene->
items();
134 for (
auto *item : connections) {
135 auto *connection = qgraphicsitem_cast<Connection *>(item);
140 Port *port1 = connection->startPort();
141 Port *port2 = connection->endPort();
143 if (!port1 || !port2) {
150 if ((elem1 == sourceElement && elem2 == targetElement) ||
151 (elem1 == targetElement && elem2 == sourceElement)) {
153 scene->
receiveCommand(
new DeleteItemsCommand({connection}, scene));
155 },
"disconnect elements", requestId);
159 return createErrorResponse(QString(
"No connection found between elements %1 and %2").arg(sourceElement->id()).arg(targetElement->id()),
163QJsonObject ConnectionHandler::handleListConnections(
const QJsonObject &,
const QJsonValue &requestId)
170 QJsonArray connections;
171 const auto sceneItems = scene->
items();
173 for (
const auto *item : sceneItems) {
174 const auto *connection = qgraphicsitem_cast<const Connection *>(item);
179 const Port *startPort = connection->startPort();
180 const Port *endPort = connection->endPort();
182 if (!startPort || !endPort) {
189 if (!startElement || !endElement) {
193 QJsonObject connectionObj;
194 connectionObj[
"source_id"] = startElement->
id();
195 connectionObj[
"source_port"] = startPort->
index();
196 connectionObj[
"target_id"] = endElement->
id();
197 connectionObj[
"target_port"] = endPort->
index();
199 connections.append(connectionObj);
203 result[
"connections"] = connections;
208QJsonObject ConnectionHandler::handleSplitConnection(
const QJsonObject ¶ms,
const QJsonValue &requestId)
210 if (!
validateParameters(params, {
"source_id",
"source_port",
"target_id",
"target_port",
"x",
"y"})) {
211 return createErrorResponse(
"Missing required parameters: source_id, source_port, target_id, target_port, x, y",
230 const int sourcePort = params.value(
"source_port").toInt();
231 const int targetPort = params.value(
"target_port").toInt();
232 const double x = params.value(
"x").toDouble();
233 const double y = params.value(
"y").toDouble();
236 if (!sourceElement) {
240 if (!targetElement) {
250 Connection *connectionToSplit =
nullptr;
251 const auto sceneItems = scene->
items();
253 for (
auto *item : sceneItems) {
254 auto *connection = qgraphicsitem_cast<Connection *>(item);
255 if (!connection) {
continue; }
257 Port *port1 = connection->startPort();
258 Port *port2 = connection->endPort();
260 if (!port1 || !port2) {
267 if (!elem1 || !elem2) {
272 if (elem1 == sourceElement && elem2 == targetElement &&
273 port1->
index() == sourcePort && port2->
index() == targetPort) {
274 connectionToSplit = connection;
279 if (!connectionToSplit) {
286 scene->
receiveCommand(
new SplitCommand(connectionToSplit, QPointF(x, y), scene));
288 },
"split connection", requestId);
291bool ConnectionHandler::resolvePort(
const QJsonObject ¶ms,
const QString &prefix,
293 int &portIndex, QString &errorMsg)
295 const QString labelParam = prefix +
"_port_label";
296 const QString indexParam = prefix +
"_port";
298 if (!params.contains(labelParam) && !params.contains(indexParam)) {
299 errorMsg = QString(
"Missing %1 port: provide either '%2' (index) or '%3' (name)")
300 .arg(prefix, indexParam, labelParam);
304 if (params.contains(labelParam)) {
308 const QString label = params.value(labelParam).toString();
316 portIndex = params.value(indexParam).toInt();
All QUndoCommand subclasses and the CommandUtils helper namespace.
ConnectionManager: manages wire creation, deletion, validation and hover feedback.
Connection: a wire that connects an output port to an input port in the circuit scene.
Singleton factory for all circuit element types.
Abstract base class for all graphical circuit elements.
Port classes: Port (base), InputPort, and OutputPort.
Main circuit editing scene with undo/redo and user interaction.
bool validatePortRange(GraphicElement *element, int portIndex, bool isOutput, const QString ¶mName, QString &errorMsg) const
QJsonObject createSuccessResponse(const QJsonObject &result={}, const QJsonValue &requestId=QJsonValue()) const
QJsonObject tryCommand(Fn &&fn, const QString &action, const QJsonValue &requestId=QJsonValue())
Wraps fn in a try/catch, returning an error response on exception.
GraphicElement * validatedElement(const QJsonObject ¶ms, const QString ¶mName, QString &errorMsg)
Validates paramName in params, looks up the element, and returns it.
bool validateNonNegativeInteger(const QJsonValue &value, const QString ¶mName, QString &errorMsg) const
BaseHandler(MainWindow *mainWindow, const MCPValidator *validator)
QJsonObject createErrorResponse(const QString &error, const QJsonValue &requestId=QJsonValue(), int code=JsonRpcError::InternalError) const
bool outputPortByLabel(GraphicElement *element, const QString &label, int &portIndex, QString &errorMsg) const
bool validateParameters(const QJsonObject ¶ms, const QStringList &required) const
bool inputPortByLabel(GraphicElement *element, const QString &label, int &portIndex, QString &errorMsg) const
bool validateNonEmptyString(const QJsonValue &value, const QString ¶mName, QString &errorMsg) const
bool validateNumeric(const QJsonValue &value, const QString ¶mName, QString &errorMsg) const
QJsonObject handleCommand(const QString &command, const QJsonObject ¶ms, const QJsonValue &requestId) override
ConnectionHandler(MainWindow *mainWindow, const MCPValidator *validator)
static bool isConnectionAllowed(OutputPort *startPort, InputPort *endPort)
Returns true if a wire from startPort to endPort is permitted.
Abstract base class for all graphical circuit elements in wiRedPanda.
int id() const
Returns the unique integer identifier of this item, or -1 if unassigned.
JSON Schema validator for MCP commands and responses using native json-schema-validator.
The top-level application window hosting the tab bar, menus, element palette, and editor.
GraphicElement * graphicElement()
Returns the graphic element that owns this port.
int index() const
Returns the port's visual/logical index within the element.
void receiveCommand(QUndoCommand *cmd)
Pushes cmd onto the undo stack (immediately executes its redo()).
QList< QGraphicsItem * > items(Qt::SortOrder order=Qt::AscendingOrder) const
constexpr int SceneNotAvailable
No active circuit scene to operate on.
constexpr int PortNotFound
Port lookup by index or label did not match.
constexpr int MethodNotFound
The requested method does not exist or is unavailable.
constexpr int ConnectionFailed
Connect/disconnect or port-mismatch failure.
constexpr int ElementNotFound
Referenced element id does not exist in the scene.
constexpr int ValidationError
Semantic validation failure (e.g. port index out of range, enum value not allowed).
constexpr int InvalidParams
Invalid method parameters (missing required, wrong type, etc.).