#ifndef PLCDOCUMENT_H #define PLCDOCUMENT_H #include "basedocument.h" #include "plcitems.h" #include #include #include #include #include #include #include #include #include #include #include #include #include const int DEFAULT_ROWS = 11; const int DEFAULT_COLUMNS = 24; const int DEFAULT_CELL_SIZE = 70; class PLCItem; class ConnectionLine; class PLCDocument : public BaseDocument { Q_OBJECT public: enum TerminalType { NoTerminal = 0, LeftTerminal, RightTerminal, RowTerminal }; enum ConnectionSourceType { ConnectionNone, // 无连接 ConnectionFromItem, // 起点是元件的端子 ConnectionFromRowTerminal // 起点是行触点 }; explicit PLCDocument(QWidget *parent = nullptr); ~PLCDocument() override; QString title() const override; QGraphicsView *view() const { return m_view; } QGraphicsScene *scene() const { return m_scene; } bool saveToFile(const QString &filePath) override; bool loadFromFile(const QString &filePath) override; void createPLCItem(const QString &type, const QPointF &pos); void startConnection(PLCItem *startItem, TerminalType startTerminal); void startConnectionFromRowTerminal(int row); void clearCurrentConnection(); bool tryEndConnection(PLCItem *endItem, TerminalType endTerminal); void setSimulationRunning(bool running); void resetSimulation(); void setCurrentTool(const QString &tool) { m_currentTool = tool; } int rowCount() const { return m_rows; } int columnCount() const { return m_columns; } int cellSize() const { return m_cellSize; } QPointF snapToCellCenter(const QPointF &pos) const; QPointF constrainToTable(const QPointF &pos) const; void setTitle(const QString &title) { m_title = title; } void deleteSelectedItems(); void copySelectedItem(); void pasteItem(); protected: bool eventFilter(QObject *obj, QEvent *event) override; void keyPressEvent(QKeyEvent* event) override; private slots: void handleSceneChanged(); void onRowTerminalConnection(); private: QList findConnectionsUnderItem(PLCItem* item) const; bool lineIntersectsRect(const QLineF& line, const QRectF& rect) const; bool splitConnection(ConnectionLine* originalLine, PLCItem* insertItem); bool isPointOnLineSegment(const QPointF& point, const QLineF& line) const; void createRealTable(); TerminalType whichTerminal(const QPointF& scenePos, PLCItem* item) const; bool isTerminalUsed(PLCItem* item, TerminalType terminal) const; void handleItemPositionChange(PLCItem *item); bool isCellOccupied(int col, int row, PLCItem* excludeItem) const; void updateConnections(); void createContextMenu(QPoint globalPos); PLCItem* createItemByType(PLCItem::ItemType type); void removeInvalidConnectionsForItem(PLCItem* movedItem); QList allPLCItems() const; // 新增:获取元件在连线上的位置比例(0-1) qreal getPositionRatioOnLine(PLCItem* item, ConnectionLine* line) const; QGraphicsScene *m_scene; QGraphicsView *m_view; QString m_currentTool; ConnectionSourceType m_connectionSourceType = ConnectionNone; PLCItem *m_connectionStartItem = nullptr; TerminalType m_startTerminal = NoTerminal; int m_connectionStartRow = -1; QGraphicsLineItem *m_tempLine = nullptr; QList m_connections; QMap> terminalConnections; bool m_simulationRunning = false; QSet m_activeItems; int m_rows = DEFAULT_ROWS; int m_columns = DEFAULT_COLUMNS; int m_cellSize = DEFAULT_CELL_SIZE; QGraphicsRectItem* m_tableFrame = nullptr; QVector m_horizontalLines; QVector m_verticalLines; QString m_title; QVector m_rowTerminals; QMap m_rowTerminalMap; QMap m_lastValidPositions; QSet m_processingItems; bool m_loadingFile = false; PLCItem* m_copiedItem = nullptr; }; class ConnectionLine : public QGraphicsLineItem { public: ConnectionLine( PLCItem *startItem, PLCDocument::TerminalType startTerminal, PLCItem *endItem, PLCDocument::TerminalType endTerminal, QGraphicsItem *parent = nullptr ); void updatePosition(); PLCItem* startItem() const { return m_startItem; } PLCItem* endItem() const { return m_endItem; } PLCDocument::TerminalType startTerminal() const { return m_startTerminal; } PLCDocument::TerminalType endTerminal() const { return m_endTerminal; } void setRowTerminalTargetRow(int row) { m_rowTerminalTargetRow = row; } int rowTerminalTargetRow() const { return m_rowTerminalTargetRow; } void setRowTerminalSourceRow(int row) { m_rowTerminalSourceRow = row; } int rowTerminalSourceRow() const { return m_rowTerminalSourceRow; } void setStartTerminalPoint(const QPointF& point) { m_startTerminalPoint = point; } void setEndTerminalPoint(const QPointF& point) { m_endTerminalPoint = point; } // 新增:设置这条连线是否正在被处理 void setProcessing(bool processing) { m_processing = processing; } bool isProcessing() const { return m_processing; } private: PLCItem *m_startItem; PLCDocument::TerminalType m_startTerminal; PLCItem *m_endItem; PLCDocument::TerminalType m_endTerminal; QPointF m_startTerminalPoint; QPointF m_endTerminalPoint; int m_rowTerminalTargetRow = -1; int m_rowTerminalSourceRow = -1; bool m_processing = false; // 用于防止递归处理 }; #endif // PLCDOCUMENT_H