wiRedPanda
Logic Circuit Simulator
Loading...
Searching...
No Matches
BaseHandler.cpp
Go to the documentation of this file.
1// Copyright 2015 - 2026, GIBIS-UNIFESP and the wiRedPanda contributors
2// SPDX-License-Identifier: GPL-3.0-or-later
3
5
6#include <cmath>
7
8#include <QJsonObject>
9
12#include "App/Scene/Scene.h"
13#include "App/Scene/Workspace.h"
14#include "App/UI/MainWindow.h"
15#include "App/Wiring/Port.h"
16
17BaseHandler::BaseHandler(MainWindow *mainWindow, const MCPValidator *validator)
18 : m_mainWindow(mainWindow)
19 , m_validator(validator)
20{
21}
22
23QJsonObject BaseHandler::createSuccessResponse(const QJsonObject &result, const QJsonValue &requestId) const
24{
25 QJsonObject response;
26 response["jsonrpc"] = "2.0";
27 response["result"] = result;
28 if (!requestId.isNull()) {
29 response["id"] = requestId;
30 }
31 return response;
32}
33
34QJsonObject BaseHandler::createErrorResponse(const QString &error, const QJsonValue &requestId, int code) const
35{
36 QJsonObject response;
37 response["jsonrpc"] = "2.0";
38
39 QJsonObject errorObject;
40 errorObject["code"] = code;
41 errorObject["message"] = error;
42 response["error"] = errorObject;
43
44 if (!requestId.isNull()) {
45 response["id"] = requestId;
46 }
47 return response;
48}
49
50bool BaseHandler::validateParameters(const QJsonObject &params, const QStringList &required) const
51{
52 for (const QString &param : required) {
53 if (!params.contains(param)) {
54 return false;
55 }
56 }
57 return true;
58}
59
61{
62 if (!m_mainWindow) {
63 return nullptr;
64 }
65
66 WorkSpace *workspace = m_mainWindow->currentTab();
67 if (!workspace) {
68 return nullptr;
69 }
70
71 return workspace->scene();
72}
73
75{
76 if (!m_mainWindow) {
77 return nullptr;
78 }
79
80 const WorkSpace *workspace = m_mainWindow->currentTab();
81 if (!workspace) {
82 return nullptr;
83 }
84
85 return workspace->scene();
86}
87
88bool BaseHandler::validatePositiveInteger(const QJsonValue &value, const QString &paramName, QString &errorMsg) const
89{
90 if (!value.isDouble()) {
91 errorMsg = QString("Parameter '%1' must be an integer").arg(paramName);
92 return false;
93 }
94
95 int intVal = value.toInt();
96 if (intVal <= 0) {
97 errorMsg = QString("Parameter '%1' must be a positive integer (got %2)").arg(paramName).arg(intVal);
98 return false;
99 }
100
101 return true;
102}
103
104bool BaseHandler::validateNonNegativeInteger(const QJsonValue &value, const QString &paramName, QString &errorMsg) const
105{
106 if (!value.isDouble()) {
107 errorMsg = QString("Parameter '%1' must be an integer").arg(paramName);
108 return false;
109 }
110
111 int intVal = value.toInt();
112 if (intVal < 0) {
113 errorMsg = QString("Parameter '%1' must be non-negative (got %2)").arg(paramName).arg(intVal);
114 return false;
115 }
116
117 return true;
118}
119
120bool BaseHandler::validateNonEmptyString(const QJsonValue &value, const QString &paramName, QString &errorMsg) const
121{
122 if (!value.isString()) {
123 errorMsg = QString("Parameter '%1' must be a string").arg(paramName);
124 return false;
125 }
126
127 QString str = value.toString();
128 if (str.isEmpty()) {
129 errorMsg = QString("Parameter '%1' cannot be empty").arg(paramName);
130 return false;
131 }
132
133 return true;
134}
135
136bool BaseHandler::validateElementId(int elementId, const QString &paramName, QString &errorMsg) const
137{
138 if (elementId <= 0) {
139 errorMsg = QString("Parameter '%1' must be a positive integer (got %2)").arg(paramName).arg(elementId);
140 return false;
141 }
142
143 const Scene *scene = currentScene();
144 if (!scene) {
145 errorMsg = "No active circuit scene available";
146 return false;
147 }
148
149 auto *item = scene->itemById(elementId);
150 if (!item) {
151 errorMsg = QString("Element not found: %1").arg(elementId);
152 return false;
153 }
154
155 return true;
156}
157
158bool BaseHandler::validateNumeric(const QJsonValue &value, const QString &paramName, QString &errorMsg) const
159{
160 if (!value.isDouble()) {
161 errorMsg = QString("Parameter '%1' must be a numeric value").arg(paramName);
162 return false;
163 }
164
165 double numVal = value.toDouble();
166 if (std::isnan(numVal) || std::isinf(numVal)) {
167 errorMsg = QString("Parameter '%1' must be a finite numeric value").arg(paramName);
168 return false;
169 }
170
171 return true;
172}
173
174bool BaseHandler::validatePortRange(GraphicElement *element, int portIndex, bool isOutput, const QString &paramName, QString &errorMsg) const
175{
176 if (!element) {
177 errorMsg = QString("Invalid element for port validation");
178 return false;
179 }
180
181 // Use the safe QVector size() instead of calling outputPort()/inputPort()
182 qsizetype maxPorts = 0;
183 if (isOutput) {
184 maxPorts = element->outputs().size();
185 } else {
186 maxPorts = element->inputs().size();
187 }
188
189 if (portIndex >= maxPorts) {
190 QString portType = isOutput ? "output" : "input";
191 errorMsg = QString("Parameter '%1' port index %2 is out of range (element has %3 %4 ports)")
192 .arg(paramName)
193 .arg(portIndex)
194 .arg(maxPorts)
195 .arg(portType);
196 return false;
197 }
198
199 return true;
200}
201
202GraphicElement *BaseHandler::validatedElement(const QJsonObject &params, const QString &paramName, QString &errorMsg)
203{
204 if (!validatePositiveInteger(params.value(paramName), paramName, errorMsg)) {
205 return nullptr;
206 }
207 const int elementId = params.value(paramName).toInt();
208 if (!validateElementId(elementId, paramName, errorMsg)) {
209 return nullptr;
210 }
211 auto *item = currentScene()->itemById(elementId);
212 auto *element = dynamic_cast<GraphicElement *>(item);
213 if (!element) {
214 errorMsg = QString("Item %1 is not a graphic element").arg(elementId);
215 return nullptr;
216 }
217 return element;
218}
219
220bool BaseHandler::inputPortByLabel(GraphicElement *element, const QString &label, int &portIndex, QString &errorMsg) const
221{
222 if (!element) {
223 errorMsg = "Element is null";
224 return false;
225 }
226
227 int inputCount = element->inputSize();
228 for (int i = 0; i < inputCount; ++i) {
229 auto *port = element->inputPort(i);
230 if (port && port->name() == label) {
231 portIndex = i;
232 return true;
233 }
234 }
235
236 errorMsg = QString("Input port '%1' not found on element '%2'. Available input ports: %3")
237 .arg(label, element->objectName(), availableInputPorts(element));
238 return false;
239}
240
241bool BaseHandler::outputPortByLabel(GraphicElement *element, const QString &label, int &portIndex, QString &errorMsg) const
242{
243 if (!element) {
244 errorMsg = "Element is null";
245 return false;
246 }
247
248 int outputCount = element->outputSize();
249 for (int i = 0; i < outputCount; ++i) {
250 auto *port = element->outputPort(i);
251 if (port && port->name() == label) {
252 portIndex = i;
253 return true;
254 }
255 }
256
257 errorMsg = QString("Output port '%1' not found on element '%2'. Available output ports: %3")
258 .arg(label, element->objectName(), availableOutputPorts(element));
259 return false;
260}
261
262QString BaseHandler::availablePorts(GraphicElement *element, bool isOutput) const
263{
264 if (!element) {
265 return "(element is null)";
266 }
267
268 const int count = isOutput ? element->outputSize() : element->inputSize();
269
270 if (count == 0) {
271 return isOutput ? "(no output ports)" : "(no input ports)";
272 }
273
274 QStringList ports;
275 for (int i = 0; i < count; ++i) {
276 auto *port = isOutput ? static_cast<Port *>(element->outputPort(i))
277 : static_cast<Port *>(element->inputPort(i));
278 if (port) {
279 QString portName = port->name().isEmpty() ? "(unnamed)" : port->name();
280 ports.append(QString("[%1] %2").arg(i).arg(portName));
281 } else {
282 ports.append(QString("[%1] (null)").arg(i));
283 }
284 }
285
286 return ports.join(", ");
287}
288
290{
291 return availablePorts(element, false);
292}
293
295{
296 return availablePorts(element, true);
297}
Singleton factory for all circuit element types.
Abstract base class for all graphical circuit elements.
Main application window providing menus, toolbars, and tab management.
Port classes: Port (base), InputPort, and OutputPort.
Main circuit editing scene with undo/redo and user interaction.
WorkSpace widget: the complete circuit editing environment for one tab.
bool validatePortRange(GraphicElement *element, int portIndex, bool isOutput, const QString &paramName, QString &errorMsg) const
const MCPValidator * m_validator
Definition BaseHandler.h:86
QString availableOutputPorts(GraphicElement *element) const
QJsonObject createSuccessResponse(const QJsonObject &result={}, const QJsonValue &requestId=QJsonValue()) const
bool validateElementId(int elementId, const QString &paramName, QString &errorMsg) const
QString availablePorts(GraphicElement *element, bool isOutput) const
GraphicElement * validatedElement(const QJsonObject &params, const QString &paramName, QString &errorMsg)
Validates paramName in params, looks up the element, and returns it.
bool validateNonNegativeInteger(const QJsonValue &value, const QString &paramName, QString &errorMsg) const
BaseHandler(MainWindow *mainWindow, const MCPValidator *validator)
QJsonObject createErrorResponse(const QString &error, const QJsonValue &requestId=QJsonValue(), int code=JsonRpcError::InternalError) const
MainWindow * m_mainWindow
Definition BaseHandler.h:85
bool outputPortByLabel(GraphicElement *element, const QString &label, int &portIndex, QString &errorMsg) const
bool validateParameters(const QJsonObject &params, const QStringList &required) const
bool inputPortByLabel(GraphicElement *element, const QString &label, int &portIndex, QString &errorMsg) const
bool validatePositiveInteger(const QJsonValue &value, const QString &paramName, QString &errorMsg) const
QString availableInputPorts(GraphicElement *element) const
bool validateNonEmptyString(const QJsonValue &value, const QString &paramName, QString &errorMsg) const
bool validateNumeric(const QJsonValue &value, const QString &paramName, QString &errorMsg) const
Scene * currentScene()
Abstract base class for all graphical circuit elements in wiRedPanda.
int inputSize() const
Returns the current number of input ports.
InputPort * inputPort(const int index=0) const
Returns the input port at index (default 0).
int outputSize() const
Returns the current number of output ports.
OutputPort * outputPort(const int index=0) const
Returns the output port at index (default 0).
const QVector< OutputPort * > & outputs() const
Returns a const reference to the vector of all output ports.
const QVector< InputPort * > & inputs() const
Returns a const reference to the vector of all input ports.
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.
Definition MainWindow.h:47
Abstract base class for circuit element ports (connection endpoints).
Definition Port.h:39
Main circuit editing scene.
Definition Scene.h:56
ItemWithId * itemById(int id) const
Returns the item registered under id, or nullptr if not found.
Definition Scene.cpp:171
A widget containing a complete circuit editing environment.
Definition Workspace.h:33
Scene * scene()
Returns the Scene embedded in this workspace.