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);
72 if (!outputPort || !inputPort) {
76 auto *startPort =
dynamic_cast<OutputPort *
>(outputPort);
77 auto *endPort =
dynamic_cast<InputPort *
>(inputPort);
84 return createErrorResponse(QString(
"Connection from element %1 port %2 to element %3 port %4 is not allowed "
85 "(duplicate, occupied input, or wireless port)")
86 .arg(sourceElement->id()).arg(sourcePort).arg(targetElement->id()).arg(targetPort),
97 auto connection = std::make_unique<Connection>();
98 connection->setStartPort(startPort);
99 connection->setEndPort(endPort);
100 connection->updatePath();
107 scene->
receiveCommand(
new AddItemsCommand({connection.get()}, scene));
108 connection.release();
109 }
catch (
const std::exception &e) {
120QJsonObject ConnectionHandler::handleDisconnectElements(
const QJsonObject ¶ms,
const QJsonValue &requestId)
128 if (!sourceElement) {
132 if (!targetElement) {
143 const auto connections = scene->
items();
144 for (
auto *item : connections) {
145 auto *connection = qgraphicsitem_cast<Connection *>(item);
150 Port *port1 = connection->startPort();
151 Port *port2 = connection->endPort();
153 if (!port1 || !port2) {
160 if ((elem1 == sourceElement && elem2 == targetElement) ||
161 (elem1 == targetElement && elem2 == sourceElement)) {
163 scene->
receiveCommand(
new DeleteItemsCommand({connection}, scene));
165 },
"disconnect elements", requestId);
169 return createErrorResponse(QString(
"No connection found between elements %1 and %2").arg(sourceElement->id()).arg(targetElement->id()),
173QJsonObject ConnectionHandler::handleListConnections(
const QJsonObject &,
const QJsonValue &requestId)
180 QJsonArray connections;
181 const auto sceneItems = scene->
items();
183 for (
const auto *item : sceneItems) {
184 const auto *connection = qgraphicsitem_cast<const Connection *>(item);
189 const Port *startPort = connection->startPort();
190 const Port *endPort = connection->endPort();
192 if (!startPort || !endPort) {
201 if (!startElement || !endElement) {
205 QJsonObject connectionObj;
206 connectionObj[
"source_id"] = startElement->
id();
207 connectionObj[
"source_port"] = startPort->
index();
208 connectionObj[
"target_id"] = endElement->
id();
209 connectionObj[
"target_port"] = endPort->
index();
211 connections.append(connectionObj);
215 result[
"connections"] = connections;
220QJsonObject ConnectionHandler::handleSplitConnection(
const QJsonObject ¶ms,
const QJsonValue &requestId)
222 if (!
validateParameters(params, {
"source_id",
"source_port",
"target_id",
"target_port",
"x",
"y"})) {
223 return createErrorResponse(
"Missing required parameters: source_id, source_port, target_id, target_port, x, y",
242 const int sourcePort = params.value(
"source_port").toInt();
243 const int targetPort = params.value(
"target_port").toInt();
244 const double x = params.value(
"x").toDouble();
245 const double y = params.value(
"y").toDouble();
248 if (!sourceElement) {
252 if (!targetElement) {
264 Connection *connectionToSplit =
nullptr;
265 const auto sceneItems = scene->
items();
267 for (
auto *item : sceneItems) {
268 auto *connection = qgraphicsitem_cast<Connection *>(item);
269 if (!connection) {
continue; }
271 Port *port1 = connection->startPort();
272 Port *port2 = connection->endPort();
274 if (!port1 || !port2) {
282 if (!elem1 || !elem2) {
287 if (elem1 == sourceElement && elem2 == targetElement &&
288 port1->
index() == sourcePort && port2->
index() == targetPort) {
289 connectionToSplit = connection;
294 if (!connectionToSplit) {
301 scene->
receiveCommand(
new SplitCommand(connectionToSplit, QPointF(x, y), scene));
303 },
"split connection", requestId);
306bool ConnectionHandler::resolvePort(
const QJsonObject ¶ms,
const QString &prefix,
308 int &portIndex, QString &errorMsg)
310 const QString labelParam = prefix +
"_port_label";
311 const QString indexParam = prefix +
"_port";
313 if (!params.contains(labelParam) && !params.contains(indexParam)) {
314 errorMsg = QString(
"Missing %1 port: provide either '%2' (index) or '%3' (name)")
315 .arg(prefix, indexParam, labelParam);
319 if (params.contains(labelParam)) {
323 const QString label = params.value(labelParam).toString();
331 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.).