From f91c9f1a6ff48504f785e072df0f1d9c8fafd1a5 Mon Sep 17 00:00:00 2001 From: uvos Date: Sun, 29 Jan 2023 18:45:42 +0100 Subject: [PATCH] inital commit --- CMakeLists.txt | 43 ++ backplotwidget.cpp | 337 +++++++++++++++ backplotwidget.h | 86 ++++ gcodetovhf.cpp | 544 ++++++++++++++++++++++++ gcodetovhf.h | 7 + led.cpp | 55 +++ led.h | 45 ++ main.cpp | 64 +++ mainobject.cpp | 33 ++ mainobject.h | 26 ++ mainwindow.cpp | 552 ++++++++++++++++++++++++ mainwindow.h | 70 ++++ mainwindow.ui | 862 ++++++++++++++++++++++++++++++++++++++ orbitcameracontroller.cpp | 164 ++++++++ orbitcameracontroller.h | 33 ++ vhfmill.cpp | 628 +++++++++++++++++++++++++++ vhfmill.h | 125 ++++++ vhfmillthread.cpp | 53 +++ vhfmillthread.h | 36 ++ 19 files changed, 3763 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 backplotwidget.cpp create mode 100644 backplotwidget.h create mode 100644 gcodetovhf.cpp create mode 100644 gcodetovhf.h create mode 100644 led.cpp create mode 100644 led.h create mode 100644 main.cpp create mode 100644 mainobject.cpp create mode 100644 mainobject.h create mode 100644 mainwindow.cpp create mode 100644 mainwindow.h create mode 100644 mainwindow.ui create mode 100644 orbitcameracontroller.cpp create mode 100644 orbitcameracontroller.h create mode 100644 vhfmill.cpp create mode 100644 vhfmill.h create mode 100644 vhfmillthread.cpp create mode 100644 vhfmillthread.h diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..c5278bd --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,43 @@ +cmake_minimum_required(VERSION 3.5) + +project(VHFMill VERSION 0.1 LANGUAGES CXX) + +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +find_package(QT NAMES Qt6 REQUIRED COMPONENTS Widgets) +find_package(Qt6 REQUIRED COMPONENTS Widgets SerialPort Network 3DCore 3DRender 3DInput 3DExtras) + +set(PROJECT_SOURCES + main.cpp + mainwindow.cpp + vhfmill.cpp + vhfmill.h + led.cpp + led.h + gcodetovhf.cpp + gcodetovhf.h + mainwindow.h + mainwindow.ui + backplotwidget.cpp + backplotwidget.h + orbitcameracontroller.cpp + orbitcameracontroller.h + vhfmillthread.cpp + vhfmillthread.h + mainobject.cpp + mainobject.h +) + +qt_add_executable(VHFMill MANUAL_FINALIZATION ${PROJECT_SOURCES}) + +target_link_libraries(VHFMill PRIVATE Qt6::Widgets Qt6::SerialPort Qt6::Network Qt6::3DCore Qt6::3DRender Qt6::3DExtras) +target_include_directories(VHFMill PRIVATE .) + +install(TARGETS VHFMill BUNDLE DESTINATION . LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) + +qt_finalize_executable(VHFMill) diff --git a/backplotwidget.cpp b/backplotwidget.cpp new file mode 100644 index 0000000..e8b7e9b --- /dev/null +++ b/backplotwidget.cpp @@ -0,0 +1,337 @@ +#include "backplotwidget.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +BackPlotWidget::BackPlotWidget(QWidget *parent, double xLimit, double yLimit, double zLimit) + : limits(xLimit, yLimit, zLimit), + QWidget(parent) +{ + + containerWdiget = QWidget::createWindowContainer(&view); + hLayout.addWidget(containerWdiget); + containerWdiget->setParent(this); + setLayout(&hLayout); + view.defaultFrameGraph()->setClearColor("Black"); + + reset(); +} + +void BackPlotWidget::reset() +{ + view.setRootEntity(nullptr); + + if(rootEntity) + delete rootEntity; + rootEntity = new Qt3DCore::QEntity(); + view.setRootEntity(rootEntity); + view.renderSettings()->setRenderPolicy(Qt3DRender::QRenderSettings::OnDemand); + + camController = new OrbitCameraController(QVector2D(100, 100), rootEntity); + camController->setLinearSpeed(200.0f); + camController->setLookSpeed(360.0f); + camController->setCamera(view.camera()); + + axisMaterial = new Qt3DExtras::QPhongMaterial(rootEntity); + rapidMaterial= new Qt3DExtras::QPhongMaterial(rootEntity); + pathMaterial = new Qt3DExtras::QPhongMaterial(rootEntity); + axisMaterial->setAmbient(QColorConstants::Red); + rapidMaterial->setAmbient(QColorConstants::Gray); + pathMaterial->setAmbient(QColorConstants::White); + + view.camera()->setPosition(QVector3D(limits.x()/2, limits.y()/2, limits.z()*4)); + view.camera()->setUpVector(QVector3D(0, 1, 0)); + view.camera()->setViewCenter(QVector3D(limits.x()/2, limits.y()/2, 0)); + + Qt3DCore::QEntity *lightEntity = new Qt3DCore::QEntity(rootEntity); + Qt3DRender::QPointLight *light = new Qt3DRender::QPointLight(lightEntity); + light->setColor(QColorConstants::White); + light->setIntensity(0.2); + lightEntity->addComponent(light); + Qt3DCore::QTransform *lightTransform = new Qt3DCore::QTransform(lightEntity); + lightTransform->setTranslation(QVector3D(0-limits.x()/2, 0-limits.y()/2, 1000.0f)); + lightEntity->addComponent(lightTransform); + + Qt3DCore::QEntity* xAxisEntity = new Qt3DCore::QEntity(rootEntity); + Qt3DCore::QEntity* yAxisEntity = new Qt3DCore::QEntity(rootEntity); + Qt3DCore::QEntity* zAxisEntity = new Qt3DCore::QEntity(rootEntity); + Qt3DExtras::QPhongMaterial* xAxisMaterial = new Qt3DExtras::QPhongMaterial(xAxisEntity); + Qt3DExtras::QPhongMaterial* yAxisMaterial = new Qt3DExtras::QPhongMaterial(yAxisEntity); + Qt3DExtras::QPhongMaterial* zAxisMaterial = new Qt3DExtras::QPhongMaterial(zAxisEntity); + xAxisMaterial->setAmbient(QColorConstants::Green); + yAxisMaterial->setAmbient(QColorConstants::Red); + zAxisMaterial->setAmbient(QColor(18,128,255)); + touchoffTransform = new Qt3DCore::QTransform(rootEntity); + touchoffTransform->setTranslation(limits); + xAxisEntity->addComponent(touchoffTransform); + yAxisEntity->addComponent(touchoffTransform); + zAxisEntity->addComponent(touchoffTransform); + drawLine(QVector3D(0,0,0), QVector3D(-15,0,0), xAxisMaterial, xAxisEntity); + drawLine(QVector3D(0,0,0), QVector3D(0,-15,0), yAxisMaterial, yAxisEntity); + drawLine(QVector3D(0,0,0), QVector3D(0,0,15), zAxisMaterial, zAxisEntity); + + drawBox(QVector3D(0, 0, 0), limits, axisMaterial, new Qt3DCore::QEntity(rootEntity)); + + Qt3DCore::QEntity* planeEntity = new Qt3DCore::QEntity(rootEntity); + Qt3DExtras::QPlaneMesh* plane = new Qt3DExtras::QPlaneMesh(planeEntity); + Qt3DCore::QTransform *planeTransform = new Qt3DCore::QTransform(planeEntity); + Qt3DExtras::QPhongMaterial* planeMaterial = new Qt3DExtras::QPhongMaterial(planeEntity); + planeMaterial->setDiffuse(QColorConstants::DarkGray); + planeTransform->setTranslation(QVector3D(limits.x()/2, limits.y()/2, -1)); + planeTransform->setRotationX(90); + plane->setHeight(limits.y()*2); + plane->setWidth(limits.x()*2); + planeEntity->addComponent(plane); + planeEntity->addComponent(planeMaterial); + planeEntity->addComponent(planeTransform); + + Qt3DCore::QEntity* planeRevEntity = new Qt3DCore::QEntity(rootEntity); + Qt3DExtras::QPlaneMesh* planeRev = new Qt3DExtras::QPlaneMesh(planeRevEntity); + Qt3DCore::QTransform *planeRevTransform = new Qt3DCore::QTransform(planeRevEntity); + planeRevTransform->setTranslation(QVector3D(limits.x()/2, limits.y()/2, -1)); + planeRevTransform->setRotationX(-90); + planeRev->setHeight(limits.y()*2); + planeRev->setWidth(limits.x()*2); + planeRevEntity->addComponent(planeRev); + planeRevEntity->addComponent(planeMaterial); + planeRevEntity->addComponent(planeRevTransform); + + toolEntity = new Qt3DCore::QEntity(rootEntity); + Qt3DExtras::QCylinderMesh* toolMesh = new Qt3DExtras::QCylinderMesh(toolEntity); + Qt3DExtras::QPhongMaterial* toolMaterial = new Qt3DExtras::QPhongMaterial(toolEntity); + toolTransform = new Qt3DCore::QTransform(toolEntity); + toolTransform->setTranslation(limits + QVector3D(0,0,5)); + toolTransform->setRotationX(90); + toolMaterial->setDiffuse(QColorConstants::White); + toolMaterial->setAmbient(QColor(64,64,64)); + toolMesh->setRadius(1); + toolMesh->setLength(10); + toolEntity->addComponent(toolMesh); + toolEntity->addComponent(toolMaterial); + toolEntity->addComponent(toolTransform); +} + +void BackPlotWidget::showView(ViewPos viewPos) +{ + if(viewPos == VIEW_TOP) + { + view.camera()->setPosition(QVector3D(limits.x()/2, limits.y()/2, limits.z()*4)); + view.camera()->setUpVector(QVector3D(0, 1, 0)); + } + else if(viewPos == VIEW_FRONT) + { + view.camera()->setPosition(QVector3D(limits.x()/2, 0-limits.y()*4, limits.z()/2)); + view.camera()->setUpVector(QVector3D(0, 0, 1)); + } + else if(viewPos == VIEW_LEFT) + { + view.camera()->setPosition(QVector3D(0-limits.x()*4, limits.y()/2, limits.z()/2)); + view.camera()->setUpVector(QVector3D(0, 0, 1)); + } + else if(viewPos == VIEW_RIGHT) + { + view.camera()->setPosition(QVector3D(limits.x()*4, limits.y()/2, limits.z()/2)); + view.camera()->setUpVector(QVector3D(0, 0, 1)); + } + + if(viewPos == VIEW_TOP) + view.camera()->setViewCenter(QVector3D(limits.x()/2, limits.y()/2, 0)); + else + view.camera()->setViewCenter(QVector3D(limits.x()/2, limits.y()/2, limits.z()/2)); + camController->setZoomFactor(1); +} + +void BackPlotWidget::positionUpdate(std::vector position) +{ + assert(position.size() >= 3); + toolTransform->setTranslation(limits - QVector3D(position[0]/1000.0, position[1]/1000.0, (position[2]/1000.0)-5)); +} + +void BackPlotWidget::resizeEvent(QResizeEvent *event) +{ + QWidget::resizeEvent(event); + double aspectRatio = static_cast(geometry().width())/geometry().height(); + double screenWidth; + double screenHeight; + if(aspectRatio > 1.0) + { + screenWidth = 500.0; + screenHeight = screenWidth/aspectRatio; + } + else + { + screenHeight = 500; + screenWidth = aspectRatio*screenHeight; + } + camController->setOrthoSize(QVector2D(screenWidth, screenHeight)); +} + +void BackPlotWidget::drawBox(const QVector3D& a, const QVector3D& b, Qt3DRender::QMaterial* material, Qt3DCore::QEntity* parent) +{ + drawLine(QVector3D(a.x(), a.y(), a.z()), QVector3D(b.x(), a.y(), a.z()), material, parent); + drawLine(QVector3D(a.x(), a.y(), a.z()), QVector3D(a.x(), b.y(), a.z()), material, parent); + drawLine(QVector3D(a.x(), a.y(), a.z()), QVector3D(a.x(), a.y(), b.z()), material, parent); + drawLine(QVector3D(b.x(), a.y(), a.z()), QVector3D(b.x(), b.y(), a.z()), material, parent); + drawLine(QVector3D(b.x(), a.y(), a.z()), QVector3D(b.x(), a.y(), b.z()), material, parent); + drawLine(QVector3D(a.x(), b.y(), a.z()), QVector3D(b.x(), b.y(), a.z()), material, parent); + drawLine(QVector3D(a.x(), b.y(), a.z()), QVector3D(a.x(), b.y(), b.z()), material, parent); + drawLine(QVector3D(a.x(), a.y(), b.z()), QVector3D(b.x(), a.y(), b.z()), material, parent); + drawLine(QVector3D(a.x(), a.y(), b.z()), QVector3D(a.x(), b.y(), b.z()), material, parent); + drawLine(QVector3D(b.x(), b.y(), a.z()), QVector3D(b.x(), b.y(), b.z()), material, parent); + drawLine(QVector3D(a.x(), b.y(), b.z()), QVector3D(b.x(), b.y(), b.z()), material, parent); + drawLine(QVector3D(b.x(), a.y(), b.z()), QVector3D(b.x(), b.y(), b.z()), material, parent); +} + +QByteArray BackPlotWidget::removeComments(const QByteArray& program) +{ + int commentCounter = 0; + QByteArray out; + for(char ch : program) + { + if(ch == '/') + ++commentCounter; + else if(ch == '\\' && commentCounter > 0) + --commentCounter; + else if(commentCounter == 0) + out.push_back(ch); + } + return out; +} + +QList BackPlotWidget::getFields(QByteArray command) +{ + command.remove(0, 2); + QList fieldsText = command.split(','); + QList fields; + for(const QByteArray& field : fieldsText) + { + if(field.size() == 0) + fields.push_back(NO_FIELD); + else + fields.push_back(field.toInt()); + } + return fields; +} + +void BackPlotWidget::programChanged(QByteArray program) +{ + for(Qt3DCore::QEntity* entitiy : pathEntitys) + delete entitiy; + pathEntitys.clear(); + + program = removeComments(program); + QList commands = program.split(';'); + + QVector3D lastPos = touchoffPosition; + + qDebug()<<__func__; + + for(QByteArray& command : commands) + { + command = command.trimmed().toUpper(); + if(command.size() < 2) + continue; + if(command[0] == 'P' || command[0] == 'G') + { + QList fields = getFields(command); + QVector3D pos; + + if(fields.size() <= 2 || fields[2] == NO_FIELD) + pos.setZ(command[1] != 'R' ? lastPos.z()-touchoffPosition.z() : 0); + else if(fields.size() > 2) + pos.setZ(fields[2]/1000.0); + + if(fields.size() <= 1 || fields[1] == NO_FIELD) + pos.setY(command[1] != 'R' ? lastPos.y()-touchoffPosition.y() : 0); + else if(fields.size() > 1) + pos.setY(fields[1]/1000.0); + + if(fields.size() <= 0 || fields[0] == NO_FIELD) + pos.setX(command[1] != 'R' ? lastPos.x()-touchoffPosition.x() : 0); + else if(fields.size() > 0) + pos.setX(fields[0]/1000.0); + + if(command[1] == 'A') + pos = pos+touchoffPosition; + else if(command[1] == 'R') + pos = pos+lastPos; + + pathEntitys.push_back(new Qt3DCore::QEntity(rootEntity)); + drawLine(limits-lastPos, limits-pos, command[0] == 'G' ? rapidMaterial : pathMaterial, pathEntitys.back()); + + lastPos = pos; + } + } +} + +void BackPlotWidget::touchoffUpdate(std::vector position) +{ + assert(position.size() >= 3); + touchoffPosition.setX(position[0]/1000.0); + touchoffPosition.setY(position[1]/1000.0); + touchoffPosition.setZ(position[2]/1000.0); + touchoffTransform->setTranslation(limits-touchoffPosition); +} + +void BackPlotWidget::drawLine(const QVector3D& start, const QVector3D& end, Qt3DRender::QMaterial* material, Qt3DCore::QEntity* parent) +{ + Qt3DCore::QGeometry* geometry = new Qt3DCore::QGeometry(parent); + + // position vertices (start and end) + QByteArray bufferBytes; + bufferBytes.resize(3 * 2 * sizeof(float)); // start.x, start.y, start.end, end.x, end.y, end.z + float* positions = reinterpret_cast(bufferBytes.data()); + *positions++ = start.x(); + *positions++ = start.y(); + *positions++ = start.z(); + *positions++ = end.x(); + *positions++ = end.y(); + *positions++ = end.z(); + + Qt3DCore::QBuffer* buf = new Qt3DCore::QBuffer(geometry); + buf->setData(bufferBytes); + + auto *positionAttribute = new Qt3DCore::QAttribute(geometry); + positionAttribute->setName(Qt3DCore::QAttribute::defaultPositionAttributeName()); + positionAttribute->setVertexBaseType(Qt3DCore::QAttribute::Float); + positionAttribute->setVertexSize(3); + positionAttribute->setAttributeType(Qt3DCore::QAttribute::VertexAttribute); + positionAttribute->setBuffer(buf); + positionAttribute->setByteStride(3 * sizeof(float)); + positionAttribute->setCount(2); + geometry->addAttribute(positionAttribute); // We add the vertices in the geometry + + // connectivity between vertices + QByteArray indexBytes; + indexBytes.resize(2 * sizeof(unsigned int)); // start to end + unsigned int *indices = reinterpret_cast(indexBytes.data()); + *indices++ = 0; + *indices++ = 1; + + Qt3DCore::QBuffer* indexBuffer = new Qt3DCore::QBuffer(geometry); + indexBuffer->setData(indexBytes); + + Qt3DCore::QAttribute *indexAttribute = new Qt3DCore::QAttribute(geometry); + indexAttribute->setVertexBaseType(Qt3DCore::QAttribute::UnsignedInt); + indexAttribute->setAttributeType(Qt3DCore::QAttribute::IndexAttribute); + indexAttribute->setBuffer(indexBuffer); + indexAttribute->setCount(2); + geometry->addAttribute(indexAttribute); // We add the indices linking the points in the geometry + + // mesh + auto *line = new Qt3DRender::QGeometryRenderer(parent); + line->setGeometry(geometry); + line->setPrimitiveType(Qt3DRender::QGeometryRenderer::Lines); + + // entity + auto *lineEntity = new Qt3DCore::QEntity(parent); + lineEntity->addComponent(line); + lineEntity->addComponent(material); +} diff --git a/backplotwidget.h b/backplotwidget.h new file mode 100644 index 0000000..102025f --- /dev/null +++ b/backplotwidget.h @@ -0,0 +1,86 @@ +#ifndef BACKPLOTWIDGET_H +#define BACKPLOTWIDGET_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include + +#include "orbitcameracontroller.h" + +class BackPlotWidget : public QWidget +{ + Q_OBJECT + + Qt3DExtras::Qt3DWindow view; + OrbitCameraController *camController; + Qt3DCore::QEntity* rootEntity = nullptr; + Qt3DExtras::QPhongMaterial* axisMaterial; + Qt3DExtras::QPhongMaterial* rapidMaterial; + Qt3DExtras::QPhongMaterial* pathMaterial; + QWidget *containerWdiget; + + Qt3DCore::QEntity* toolEntity; + Qt3DCore::QTransform *toolTransform; + std::vector pathEntitys; + + Qt3DCore::QTransform *touchoffTransform; + + QVector3D limits; + QVector3D touchoffPosition; + + QHBoxLayout hLayout; + + static constexpr int NO_FIELD = std::numeric_limits::min(); + +public: + + typedef int ViewPos; + static constexpr ViewPos VIEW_TOP = 0; + static constexpr ViewPos VIEW_FRONT = 1; + static constexpr ViewPos VIEW_LEFT = 2; + static constexpr ViewPos VIEW_RIGHT = 3; + +private: + + static void drawBox(const QVector3D& bottomCloseLeft, const QVector3D& topFarRight, Qt3DRender::QMaterial* material, Qt3DCore::QEntity* parent); + static void drawLine(const QVector3D& start, const QVector3D& end, Qt3DRender::QMaterial* material, Qt3DCore::QEntity* parent); + static QList getFields(QByteArray command); + QByteArray removeComments(const QByteArray& program); + + void reset(); + +protected: + virtual void resizeEvent(QResizeEvent *event) override; + +public slots: + void positionUpdate(std::vector position); + void touchoffUpdate(std::vector position); + void programChanged(const QByteArray program); + void showView(ViewPos view); + +public: + explicit BackPlotWidget(QWidget *parent = nullptr, double xLimits = 190, double yLimits = 92, double zLimits = 85); + +signals: + +}; + +#endif // BACKPLOTWIDGET_H + diff --git a/gcodetovhf.cpp b/gcodetovhf.cpp new file mode 100644 index 0000000..cb04c44 --- /dev/null +++ b/gcodetovhf.cpp @@ -0,0 +1,544 @@ +#include "gcodetovhf.h" + +#include +#include +#include +#include +#include +#include + +static constexpr double TOLLERANCE = 0.005; + +QByteArray removeComments(const QByteArray& gcodeCommand) +{ + size_t comment = 0; + QByteArray output; + for(size_t i = 0; i < gcodeCommand.size(); ++i) + { + if(gcodeCommand[i] == '(') + ++comment; + else if(gcodeCommand[i] == ')' && comment > 0) + --comment; + + if(comment == 0) + output.push_back(gcodeCommand[i]); + } + return output; +} + +class Gmove +{ +public: + int gmode = 0; + int coordmode = 54; + bool relative = false; + double xStart = 0; + double yStart = 0; + double zStart = 0; + double aStart = 0; + + bool x = false; + bool y = false; + bool z = false; + bool a = false; + bool i = false; + bool j = false; + bool p = false; + bool r = false; + bool l = false; + + double xVal; + double yVal; + double zVal; + double aVal; + double iVal; + double jVal; + double pVal; + double rVal; + double lVal; + + void fill() + { + if(!x) + xVal = relative ? 0 : xStart; + if(!y) + yVal = relative ? 0 : yStart; + if(!z) + zVal = relative ? 0 : zStart; + if(!a) + aVal = relative ? 0 : aStart; + + x = true; + y = true; + z = true; + } + void makeAbsolute() + { + if(!relative) + return; + if(x) + xVal = xVal+xStart; + if(y) + yVal = yVal+yStart; + if(z) + zVal = zVal+zStart; + if(a) + aVal = aVal+aStart; + relative = false; + } +}; + +static QString checkMove(Gmove gmove) +{ + if(gmove.gmode == 0 || gmove.gmode == 1) + { + if(!gmove.x && !gmove.y && !gmove.z) + return "At least one coordinate is required for linear move."; + else if(gmove.i || gmove.j || gmove.p || gmove.r) + return "Invalid parameter in linear move."; + } + else if(gmove.gmode == 2 || gmove.gmode == 3) + { + if(!gmove.x && !gmove.y && !gmove.z) + return "At least one coordinate is required for arc move."; + else if(!gmove.i && !gmove.j && !gmove.r) + return "No arc center or radius defined."; + else if((gmove.i || gmove.j) && gmove.r) + return "More than one arc center defined."; + + gmove.fill(); + gmove.makeAbsolute(); + QVector2D xyVect(gmove.xVal, gmove.yVal); + QVector2D startVect(gmove.xStart, gmove.yStart); + + if(gmove.r) + { + if(gmove.rVal < TOLLERANCE) + return "Arc radius must be larger than zero."; + if((xyVect-startVect).length()/2 > gmove.rVal+TOLLERANCE) + return "Arc radius to small to fit to provided sart and end points."; + } + else + { + /*QVector2D ijVect(gmove.i ? gmove.iVal : 0, gmove.j ? gmove.jVal : 0); + QVector2D center = startVect+ijVect; + double r = (center-xyVect).length(); + double r2 = (center-startVect).length(); + + if(abs(r-r2) > TOLLERANCE) + return "Arc start and end point radii differ";*/ + } + } + + return QString(); +} + +static QByteArray generateLinearCommand(const Gmove& gmove) +{ + assert(gmove.gmode == 0 || gmove.gmode == 1); + + QByteArray output; + if(gmove.gmode == 0) + { + if(gmove.relative) + output.append("GR"); + else if(gmove.coordmode != 53) + output.append("GA"); + else + output.append("GB"); + } + else if(gmove.gmode == 1) + { + if(gmove.relative) + output.append("PR"); + else if(gmove.coordmode != 53) + output.append("PA"); + else + output.append("PB"); + } + else + { + return output; + } + + if(gmove.x) + output.append(QByteArray::number(static_cast(gmove.xVal*1000))); + if(gmove.y || gmove.z || gmove.a) + output.append(','); + if(gmove.y) + output.append(QByteArray::number(static_cast(gmove.yVal*1000))); + if(gmove.z || gmove.a) + output.append(','); + if(gmove.z) + output.append(QByteArray::number(static_cast(gmove.zVal*-1000))); + if(gmove.a) + { + output.append(','); + output.append(QByteArray::number(static_cast(gmove.aVal*1000))); + } + + output.append(';'); + + return output; +} + +static bool findCircleCenter(QVector2D a, QVector2D b, double r, bool leftHanded, QVector2D& result) +{ float distance = (a-b).length(); + if(distance/2 > r+TOLLERANCE) + return false; + + QVector2D baseVect = (b-a).normalized(); + float loftDist = sqrt(pow(r, 2)-pow(distance/2, 2)); + QVector2D loftVect; + if(leftHanded) + { + loftVect.setX(baseVect.y()*-1); + loftVect.setY(baseVect.x()); + } + else + { + loftVect.setX(baseVect.y()); + loftVect.setY(baseVect.x()*-1); + } + loftVect = loftVect*loftDist; + baseVect = baseVect*(distance/2); + result = a+baseVect+loftVect; + + return true; +} + +static bool withinTollerance(double a, double b) +{ + return a+TOLLERANCE > b && a-TOLLERANCE < b; +} + +static QByteArray generateArcCommand(Gmove gmove, size_t resolution) +{ + assert(gmove.gmode == 2 || gmove.gmode == 3); + gmove.fill(); + gmove.makeAbsolute(); + + QVector2D startMove(gmove.xStart, gmove.yStart); + QVector2D endMove(gmove.xVal, gmove.yVal); + QVector2D center; + double radius; + if(gmove.i && gmove.j) + { + center.setX(gmove.xStart+gmove.iVal); + center.setY(gmove.yStart+gmove.jVal); + radius = (QVector2D(gmove.xStart, gmove.yStart)-center).length(); + } + else if(gmove.r) + { + findCircleCenter(QVector2D(gmove.xStart, gmove.yStart), QVector2D(gmove.xVal, gmove.yVal), gmove.rVal, gmove.gmode == 3, center); + radius = gmove.rVal; + } + else + { + qCritical()<<"Can't create arc move"; + return QByteArray(); + } + + QVector2D aVector = startMove-center; + QVector2D bVector = endMove-center; + double startAngle = acos(aVector.x()/(aVector.length())); + double endAngle = acos(bVector.x()/(bVector.length())); + + { + bool a = false; + bool b = false; + QVector2D computedEnpoint(cos(endAngle)*radius+center.x(), sin(endAngle)*radius+center.y()); + if((computedEnpoint-endMove).length() > TOLLERANCE) + endAngle = 0 - endAngle; + + QVector2D computedSart(cos(startAngle)*radius+center.x(), sin(startAngle)*radius+center.y()); + if((computedSart-startMove).length() > TOLLERANCE) + startAngle = 0 - startAngle; + + if((startAngle-endAngle) < 0) + startAngle = startAngle+2*M_PI; + } + { + /*QVector2D computedEnpoint(cos(endAngle)*radius+center.x(), sin(endAngle)*radius+center.y()); + assert((computedEnpoint-endMove).length() > TOLLERANCE); + + QVector2D computedSart(cos(startAngle)*radius+center.x(), sin(startAngle)*radius+center.y()); + assert((computedSart-startMove).length() > TOLLERANCE);*/ + } + + + if(gmove.p && gmove.pVal > 1) + endAngle = endAngle+std::copysign(1.0, endAngle-startAngle)*2*(gmove.pVal-1)*M_PI; + + size_t steps = (resolution*(fabs(endAngle-startAngle)))/(2*M_PI); + + double angleStep; + if(gmove.gmode == 2) + angleStep = (endAngle-startAngle)/steps; + else + angleStep = (2*M_PI-(startAngle-endAngle))/steps; + + QByteArray output; + double deltaZ = (gmove.zVal - gmove.zStart)/steps; + for(size_t i = 1; i < steps; ++i) + { + double workAngle = startAngle+angleStep*i; + QVector2D point; + point.setX(cos(workAngle)*radius); + point.setY(sin(workAngle)*radius); + point = point+center; + QByteArray command("PA"); + command.append(QByteArray::number(static_cast(point.x()*1000)) + "," + + QByteArray::number(static_cast(point.y()*1000)) + "," + + QByteArray::number(static_cast((gmove.zStart+deltaZ*i)*-1000)) + ";"); + output.append(command); + } + + QByteArray command("PA"); + command.append(QByteArray::number(static_cast(gmove.xVal*1000)) +"," + + QByteArray::number(static_cast(gmove.yVal*1000)) + "," + + QByteArray::number(static_cast(gmove.zVal*-1000)) + ";"); + output.append(command); + + return output; +} + +static QByteArray generateDrillingCycle(Gmove gmove) +{ + if(!gmove.r) + return QByteArray(); + if(!gmove.z) + return QByteArray(); + gmove.gmode = 0; + if(gmove.relative) + gmove.rVal = gmove.rVal+gmove.zVal; + gmove.makeAbsolute(); + + QByteArray output("GA,,"+QByteArray::number(static_cast(gmove.rVal*-1000))+"; "); + if(gmove.x || gmove.y) + { + gmove.z = false; + output.append(generateLinearCommand(gmove)); + } + + size_t count = gmove.l ? gmove.lVal : 1; + + for(size_t i = 0; i < count; ++i) + { + output.append("PA,,"+QByteArray::number(static_cast(gmove.zVal*-1000))+"; "); + output.append("GA,,"+QByteArray::number(static_cast(gmove.rVal*-1000))+"; "); + } + return output; +} + +static QByteArray generateMoveCommand(const Gmove& gmove) +{ + if(gmove.gmode == 0 || gmove.gmode == 1) + return generateLinearCommand(gmove); + else if(gmove.gmode == 2 || gmove.gmode == 3) + return generateArcCommand(gmove, 100); + else if(gmove.gmode == 81) + return generateDrillingCycle(gmove); + return QByteArray(); +} + +static Gmove doMove(Gmove gmove) +{ + gmove.fill(); + gmove.makeAbsolute(); + + Gmove out; + out.xStart = gmove.xVal; + out.yStart = gmove.yVal; + out.zStart = gmove.zVal; + out.aStart = gmove.aVal; + + return out; +} + +QByteArray gcodeToVhf(const QByteArray& gcode, bool* ok, QList* errors) +{ + int gmode = -1; + int coordmode = 54; + bool relmode = false; + double spinspeed = 1000; + double feedrate = std::numeric_limits::min(); + int tool = 0; + Gmove move; + bool clearMove; + + QByteArray output; + + if(gcode.size()< 2) + return output; + + QList gcodeCommands = gcode.toUpper().split('\n'); + + if(ok) + *ok = true; + + size_t line = 0; + for(QByteArray& command : gcodeCommands) + { + ++line; + move.gmode = gmode; + move.coordmode = coordmode; + move.relative = relmode; + bool startSpindle = false; + bool toolchange = false; + command = removeComments(command); + + QList subcommands = command.split(' '); + for(QByteArray& subcommand : subcommands) + { + subcommand = subcommand.trimmed(); + if(subcommand.size() < 1) + continue; + if(subcommand[0] == 'G') + { + subcommand.remove(0, 1); + int gcodeCode = subcommand.toInt(); + if(gcodeCode >= 0 && gcodeCode <= 3 || gcodeCode == 81) + gmode = gcodeCode; + else if(gcodeCode == 80 && gmode == 81) + gmode = 0; + else if(gcodeCode == 91) + relmode = true; + else if(gcodeCode == 90) + relmode = false; + else if(gcodeCode == 53) + move.coordmode = 53; + else if(gcodeCode > 54 && gcodeCode < 59) + move.coordmode = gcodeCode; + else if(gcodeCode == 28) + output.append("PB0,0,0,0; "); + else if(gcodeCode == 64) + clearMove = true; + + move.gmode = gmode; + if(move.coordmode != 53) + move.coordmode = coordmode; + move.relative = relmode; + } + else if(subcommand[0] == 'M') + { + subcommand.remove(0, 1); + int mCode = subcommand.toDouble(); + switch(mCode) + { + case 0: + case 1: + output.append("!S; "); + break; + case 3: + startSpindle = true; + break; + case 2: + case 5: + output.append("RVS0; "); + startSpindle = false; + break; + case 6: + toolchange = true; + default: + break; + } + } + else if(subcommand[0] == 'F') + { + subcommand.remove(0, 1); + double newFeedrate = subcommand.toDouble(); + if(abs(newFeedrate - feedrate) > TOLLERANCE) + { + feedrate = newFeedrate; + output.append("VS" + QByteArray::number(static_cast(feedrate*(1000.0/60.0))) + "; "); + } + } + else if(subcommand[0] == 'T') + { + subcommand.remove(0, 1); + tool = subcommand.toInt(); + } + else if(subcommand[0] == 'S') + { + subcommand.remove(0, 1); + spinspeed = subcommand.toDouble(); + } + else if(subcommand[0] == 'X') + { + subcommand.remove(0, 1); + move.x = true; + move.xVal = subcommand.toDouble(); + } + else if(subcommand[0] == 'Y') + { + subcommand.remove(0, 1); + move.y = true; + move.yVal = subcommand.toDouble(); + } + else if(subcommand[0] == 'Z') + { + subcommand.remove(0, 1); + move.z = true; + move.zVal = subcommand.toDouble(); + } + else if(subcommand[0] == 'P') + { + subcommand.remove(0, 1); + move.p = true; + move.pVal = subcommand.toDouble(); + } + else if(subcommand[0] == 'A') + { + subcommand.remove(0, 1); + move.a = true; + move.aVal = subcommand.toDouble(); + } + else if(subcommand[0] == 'I') + { + subcommand.remove(0, 1); + move.i = true; + move.iVal = subcommand.toDouble(); + } + else if(subcommand[0] == 'J') + { + subcommand.remove(0, 1); + move.j = true; + move.jVal = subcommand.toDouble(); + } + else if(subcommand[0] == 'R') + { + subcommand.remove(0, 1); + move.r = true; + move.rVal = subcommand.toDouble(); + } + } + + if(toolchange) + output.append("T" + QByteArray::number(static_cast(tool)) + "; "); + if(startSpindle) + output.append("RVS" + QByteArray::number(static_cast(spinspeed)) + "; "); + if(move.x || move.y || move.z || move.a) + { + QString error = checkMove(move); + if(error.isEmpty()) + { + output.append(generateMoveCommand(move)); + } + else + { + *ok = false; + if(errors) + errors->push_back("Error on line " + QString::number(line) + ": " + error); + qWarning()<<"Error on line"< 0 && output.back() != '\n') + output.push_back('\n'); + } + + return output; +} diff --git a/gcodetovhf.h b/gcodetovhf.h new file mode 100644 index 0000000..447bb8c --- /dev/null +++ b/gcodetovhf.h @@ -0,0 +1,7 @@ +#ifndef GCODETOVHF_H +#define GCODETOVHF_H +#include + +QByteArray gcodeToVhf(const QByteArray& gcode, bool* ok, QList* errors = nullptr); + +#endif // GCODETOVHF_H diff --git a/led.cpp b/led.cpp new file mode 100644 index 0000000..42871d1 --- /dev/null +++ b/led.cpp @@ -0,0 +1,55 @@ +/*UVOS*/ + +/* This file is part of MAClient copyright © 2021 Carl Philipp Klemm. + * + * MAClient is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License (GPL) version + * 3 as published by the Free Software Foundation. + * + * MAClient 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 MAClient. If not, see . +*/ +#include "led.h" +#include +#include + +Led::Led(QWidget* parent): QWidget(parent) +{ + setMinimumSize(QSize(40,40)); + setSizePolicy(QSizePolicy::Policy::Fixed, QSizePolicy::Policy::Fixed); +} + +bool Led::lit() const +{ + return lit_; +} + +void Led::setLit(bool lit) +{ + if(lit != lit_) + { + lit_ = lit; + stateChanged(lit_); + update(); + } +} + +void Led::paintEvent(QPaintEvent* event) +{ + Q_UNUSED(event) + QPainter ledPainter(this); + ledPainter.setRenderHint(QPainter::Antialiasing, true); + ledPainter.setPen(Qt::black); + if(lit_) + ledPainter.setBrush(Qt::green); + else + ledPainter.setBrush(Qt::red); + int size = std::min(rect().width(), rect().height()); + QRect ellipseRect(rect().x()+(rect().width()-size)/2+1, rect().y()+(rect().height()-size)/2+1, size-2, size-2); + ledPainter.drawEllipse(ellipseRect); +} diff --git a/led.h b/led.h new file mode 100644 index 0000000..6cd4cd1 --- /dev/null +++ b/led.h @@ -0,0 +1,45 @@ +/*UVOS*/ + +/* This file is part of MAClient copyright © 2021 Carl Philipp Klemm. + * + * MAClient is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License (GPL) version + * 3 as published by the Free Software Foundation. + * + * MAClient 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 MAClient. If not, see . +*/ +#ifndef LED_H +#define LED_H + +#include + +class Led : public QWidget +{ + Q_OBJECT + + bool lit_ = false; + + public: + + Led(QWidget* parent = nullptr); + bool lit() const; + + public slots: + + void setLit(bool lit); + + signals: + + void stateChanged(bool lit); + protected: + virtual void paintEvent(QPaintEvent* event) override; + + +}; +#endif // LED_H diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..09c792c --- /dev/null +++ b/main.cpp @@ -0,0 +1,64 @@ +#include "mainwindow.h" + +#include +#include +#include +#include +#include +#include + +#include "vhfmill.h" +#include "vhfmillthread.h" +#include "mainobject.h" + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + + QCoreApplication::setOrganizationName("UVOS"); + QCoreApplication::setOrganizationDomain("uvos.xyz"); + QCoreApplication::setApplicationName("VHFMill"); + QCoreApplication::setApplicationVersion("0.1"); + + QCommandLineParser parser; + parser.setApplicationDescription("VHF mill cnc interface"); + parser.addHelpOption(); + parser.addVersionOption(); + QCommandLineOption tcpOption(QStringList() << "t" << "tcp", QCoreApplication::translate("main", "Use Tcp connection")); + parser.addOption(tcpOption); + QCommandLineOption hostOption(QStringList() << "H" << "host", QCoreApplication::translate("main", + "Set server host ip addres"), "adress"); + parser.addOption(hostOption); + QCommandLineOption portOption(QStringList() << "p" << "port", QCoreApplication::translate("main", + "Set server Port in TCP mode or Serial port in serial mode"), "port"); + parser.addOption(portOption); + QCommandLineOption serialOption(QStringList() << "s" << "serial", QCoreApplication::translate("main", + "Use serial connection")); + parser.addOption(serialOption); + QCommandLineOption settingsPathOption(QStringList() << "c" << "config", QCoreApplication::translate("main", + "Set config file"), "configFilePath"); + parser.addOption(settingsPathOption); + QCommandLineOption secondaryOption(QStringList() << "e" << "secondary", QCoreApplication::translate("main", + "Set if instance is not main instance")); + parser.addOption(secondaryOption); + parser.process(a); + + int port = 6856; + QString serialPort("ttyUSB0"); + QString host("127.0.0.1"); + if(parser.isSet(hostOption)) + host = parser.value(hostOption); + if(parser.isSet(tcpOption) && parser.isSet(portOption)) + port = parser.value(portOption).toInt(); + else if(parser.isSet(portOption)) + serialPort = parser.value(portOption); + + VhfMillThread vhfMillTread(parser.isSet(tcpOption), port, host, serialPort); + MainObject mainObject(&a, &vhfMillTread); + vhfMillTread.start(); + + int ret = a.exec(); + + vhfMillTread.exit(); + return ret; +} diff --git a/mainobject.cpp b/mainobject.cpp new file mode 100644 index 0000000..b847d0f --- /dev/null +++ b/mainobject.cpp @@ -0,0 +1,33 @@ +#include "mainobject.h" +#include + +MainObject::MainObject(QApplication* appI, VhfMillThread* millThreadI, QObject *parent): + app(appI), + millThread(millThreadI), + QObject{parent} +{ + connect(millThread, &VhfMillThread::ready, this, &MainObject::activate); +} + +void MainObject::activate() +{ + if(millThread->ret != 0) + { + if(millThread->ret == -2) + QMessageBox::critical(nullptr, "Error", "Can not connect to to Server"); + if(millThread->ret == -3) + QMessageBox::critical(nullptr, "Error", "Can not open serial port read write"); + } + else + { + w = new MainWindow(millThread->mill); + app->installEventFilter(w); + w->show(); + } +} + +MainObject::~MainObject() +{ + if(w) + delete w; +} diff --git a/mainobject.h b/mainobject.h new file mode 100644 index 0000000..62a1de6 --- /dev/null +++ b/mainobject.h @@ -0,0 +1,26 @@ +#ifndef MAINOBJECT_H +#define MAINOBJECT_H + +#include +#include + +#include "mainwindow.h" +#include "vhfmillthread.h" + +class MainObject : public QObject +{ + Q_OBJECT +private: + MainWindow* w; + QApplication* app; + VhfMillThread* millThread; + +public: + explicit MainObject(QApplication* appI, VhfMillThread* millThreadI, QObject *parent = nullptr); + ~MainObject(); + +public slots: + void activate(); +}; + +#endif // MAINOBJECT_H diff --git a/mainwindow.cpp b/mainwindow.cpp new file mode 100644 index 0000000..2002096 --- /dev/null +++ b/mainwindow.cpp @@ -0,0 +1,552 @@ +#include "mainwindow.h" +#include "./ui_mainwindow.h" + +#include +#include +#include + +#include "gcodetovhf.h" + +MainWindow::MainWindow(VhfMill* mill, QWidget *parent) + : mill_(mill), QMainWindow(parent), + ui(new Ui::MainWindow), + viewTopAction(QKeySequence(Qt::Key_7), this, 0, 0, Qt::ApplicationShortcut), + viewFrontAction(QKeySequence(Qt::Key_1), this, 0, 0, Qt::ApplicationShortcut), + viewLeftAction(QKeySequence(Qt::Key_9), this, 0, 0, Qt::ApplicationShortcut), + viewRightAction(QKeySequence(Qt::Key_3), this, 0, 0, Qt::ApplicationShortcut) +{ + installEventFilter(this); + + ui->setupUi(this); + checkBlocks(); + + int splitHight = ui->splitter->size().height(); + ui->splitter->setSizes({3*splitHight/4, splitHight/4}); + + ui->horizontalSlider->setTracking(false); + ui->plainTextEdit_compiled->setVisible(false); + ui->plainTextEdit_compiled->setWordWrapMode(QTextOption::NoWrap); + ui->plainTextEdit->setWordWrapMode(QTextOption::NoWrap); + + connect(mill_, &VhfMill::raiseError, this, &MainWindow::raiseError); + connect(mill_, &VhfMill::positionUpdate, this, &MainWindow::positionUpdate); + connect(mill_, &VhfMill::gotToolNum, ui->lcd_tool, QOverload::of(&QLCDNumber::display)); + connect(mill_, &VhfMill::gotOutputs, this, &MainWindow::gotOutputs); + connect(mill_, &VhfMill::gotSindleSpeed, this, &MainWindow::gotSpindleSpeed); + connect(mill_, &VhfMill::gotProbeState, ui->led_probe, &Led::setLit); + connect(mill_, &VhfMill::gotPressureState, this, &MainWindow::gotPressureState); + connect(mill_, &VhfMill::initDone, this, &MainWindow::checkBlocks); + connect(mill_, &VhfMill::isHomed, this, &MainWindow::isHomed); + connect(mill_, &VhfMill::toolChangeDone, this, &MainWindow::toolChangeDone); + connect(mill_, &VhfMill::touchoffChanged, this, &MainWindow::touchoffChanged); + connect(mill_, &VhfMill::positionUpdate, ui->backPlot, &BackPlotWidget::positionUpdate); + + connect(ui->checkBox_oe1, &QCheckBox::toggled, mill_, + [this](bool checked) + { + QMetaObject::invokeMethod(mill_, "setOutput", Qt::QueuedConnection, Q_ARG(int, 1), Q_ARG(bool, checked)); + }); + connect(ui->checkBox_oe2, &QCheckBox::toggled, mill_, + [this](bool checked) + { + QMetaObject::invokeMethod(mill_, "setOutput", Qt::QueuedConnection, Q_ARG(int, 2), Q_ARG(bool, checked)); + }); + connect(ui->checkBox_oe3, &QCheckBox::toggled, mill_, + [this](bool checked) + { + QMetaObject::invokeMethod(mill_, "setOutput", Qt::QueuedConnection, Q_ARG(int, 3), Q_ARG(bool, checked)); + }); + connect(ui->checkBox_oe4, &QCheckBox::toggled, mill_, + [this](bool checked) + { + QMetaObject::invokeMethod(mill_, "setOutput", Qt::QueuedConnection, Q_ARG(int, 4), Q_ARG(bool, checked)); + }); + connect(ui->checkBox_oe5, &QCheckBox::toggled, mill_, + [this](bool checked) + { + QMetaObject::invokeMethod(mill_, "setOutput", Qt::QueuedConnection, Q_ARG(int, 5), Q_ARG(bool, checked)); + }); + connect(ui->checkBox_oe6, &QCheckBox::toggled, mill_, + [this](bool checked) + { + QMetaObject::invokeMethod(mill_, "setOutput", Qt::QueuedConnection, Q_ARG(int, 6), Q_ARG(bool, checked)); + }); + connect(ui->checkBox_oe7, &QCheckBox::toggled, mill_, + [this](bool checked) + { + QMetaObject::invokeMethod(mill_, "setOutput", Qt::QueuedConnection, Q_ARG(int, 7), Q_ARG(bool, checked)); + }); + connect(ui->pushButton_home, &QPushButton::clicked, this, &MainWindow::home); + connect(ui->pushButton_homeAll, &QPushButton::clicked, this, [this](){QMetaObject::invokeMethod(mill_, [this]() {mill_->home(VhfMill::AXIS_ALL);}, Qt::QueuedConnection);}); + connect(ui->pushButton_stopSpindle, &QPushButton::clicked, this, &MainWindow::stopSpindle); + connect(ui->horizontalSlider, &QSlider::valueChanged, mill_, &VhfMill::setSpindleSpeed); + connect(ui->pushButton_init, &QPushButton::clicked, mill_, &VhfMill::init); + connect(ui->pushButton_run, &QPushButton::clicked, this, &MainWindow::run); + connect(ui->pushButton_toolUnload, &QPushButton::clicked, this, &MainWindow::toolUnload); + connect(ui->pushButton_toolSwitch, &QPushButton::clicked, this, &MainWindow::toolSwitch); + connect(ui->pushButton_touchoff, &QPushButton::clicked, this, &MainWindow::touchoff); + connect(ui->pushButton_touchoffAll, &QPushButton::clicked, this, &MainWindow::touchoffAll); + connect(ui->pushButton_touchoffRst, &QPushButton::clicked, this, &MainWindow::touchoffRst); + connect(ui->pushButton_stop, &QPushButton::released, mill_, &VhfMill::stop); + connect(ui->actionOpen_Gcode, &QAction::triggered, this, &MainWindow::openGcode); + connect(ui->actionOpen, &QAction::triggered, this, &MainWindow::openVhfCode); + connect(ui->plainTextEdit, &QPlainTextEdit::textChanged, this, &MainWindow::textChanged); + connect(ui->radioButton_gcode, &QRadioButton::toggled, ui->plainTextEdit_compiled, &QPlainTextEdit::setVisible); + connect(ui->radioButton_gcode, &QRadioButton::toggled, this, &MainWindow::textChanged); + connect(ui->comboBox_jogStep, &QComboBox::currentIndexChanged, this, &MainWindow::selectedJogStepChanged); + + connect(ui->pushButton_jogXp, &QPushButton::pressed, this, [this](){jog(VhfMill::AXIS_X, 1);}); + connect(ui->pushButton_jogXn, &QPushButton::pressed, this, [this](){jog(VhfMill::AXIS_X, -1);}); + connect(ui->pushButton_jogYp, &QPushButton::pressed, this, [this](){jog(VhfMill::AXIS_Y, -1);}); + connect(ui->pushButton_jogYn, &QPushButton::pressed, this, [this](){jog(VhfMill::AXIS_Y, 1);}); + connect(ui->pushButton_jogUp, &QPushButton::pressed, this, [this](){jog(VhfMill::AXIS_Z, 1);}); + connect(ui->pushButton_jogDown, &QPushButton::pressed, this, [this](){jog(VhfMill::AXIS_Z, -1);}); + connect(ui->pushButton_jogAp, &QPushButton::pressed, this, [this](){jog(VhfMill::AXIS_A, 1);}); + connect(ui->pushButton_jogAn, &QPushButton::pressed, this, [this](){jog(VhfMill::AXIS_A, -1);}); + connect(ui->pushButton_jogXp, &QPushButton::released, this, [this](){jog(VhfMill::AXIS_X, 0);}); + connect(ui->pushButton_jogXn, &QPushButton::released, this, [this](){jog(VhfMill::AXIS_X, 0);}); + connect(ui->pushButton_jogYp, &QPushButton::released, this, [this](){jog(VhfMill::AXIS_Y, 0);}); + connect(ui->pushButton_jogYn, &QPushButton::released, this, [this](){jog(VhfMill::AXIS_Y, 0);}); + connect(ui->pushButton_jogUp, &QPushButton::released, this, [this](){jog(VhfMill::AXIS_Z, 0);}); + connect(ui->pushButton_jogDown, &QPushButton::released, this, [this](){jog(VhfMill::AXIS_Z, 0);}); + connect(ui->pushButton_jogAp, &QPushButton::released, this, [this](){jog(VhfMill::AXIS_A, 0);}); + connect(ui->pushButton_jogAn, &QPushButton::released, this, [this](){jog(VhfMill::AXIS_A, 0);}); + + BackPlotWidget* backplot = ui->backPlot; + connect(&viewTopAction, &QShortcut::activated, backplot, [backplot](){backplot->showView(BackPlotWidget::VIEW_TOP);}); + connect(&viewFrontAction, &QShortcut::activated, backplot, [backplot](){backplot->showView(BackPlotWidget::VIEW_FRONT);}); + connect(&viewLeftAction, &QShortcut::activated, backplot, [backplot](){backplot->showView(BackPlotWidget::VIEW_LEFT);}); + connect(&viewRightAction, &QShortcut::activated, backplot, [backplot](){backplot->showView(BackPlotWidget::VIEW_RIGHT);}); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + +void MainWindow::selectedJogStepChanged(int index) +{ + switch(index) + { + case 0: + QMetaObject::invokeMethod(mill_, [this](){mill_->setjogStep(-1);}, Qt::QueuedConnection); + break; + case 1: + QMetaObject::invokeMethod(mill_, [this](){mill_->setjogStep(5000);}, Qt::QueuedConnection); + break; + case 2: + QMetaObject::invokeMethod(mill_, [this](){mill_->setjogStep(1000);}, Qt::QueuedConnection); + break; + case 3: + QMetaObject::invokeMethod(mill_, [this](){mill_->setjogStep(250);}, Qt::QueuedConnection); + break; + case 4: + QMetaObject::invokeMethod(mill_, [this](){mill_->setjogStep(100);}, Qt::QueuedConnection); + break; + case 5: + QMetaObject::invokeMethod(mill_, [this](){mill_->setjogStep(50);}, Qt::QueuedConnection); + break; + default: + QMetaObject::invokeMethod(mill_, [this](){mill_->setjogStep(0);}, Qt::QueuedConnection); + break; + } +} + +void MainWindow::jog(VhfMill::Axis axis, int jogDirection) +{ + QMetaObject::invokeMethod(mill_, [this, axis, jogDirection](){mill_->jog(axis, jogDirection);}, Qt::QueuedConnection); +} + +bool MainWindow::eventFilter(QObject *object, QEvent *event) +{ + if(jogEnabled) + { + if(event->type() == QEvent::KeyPress) + { + QKeyEvent *ke = static_cast(event); + switch(ke->key()) + { + case Qt::Key_Up: + if(!ke->isAutoRepeat()) + jog(VhfMill::AXIS_Y, -1); + event->setAccepted(true); + return true; + case Qt::Key_Left: + if(!ke->isAutoRepeat()) + jog(VhfMill::AXIS_X, -1); + event->setAccepted(true); + return true; + case Qt::Key_Right: + if(!ke->isAutoRepeat()) + jog(VhfMill::AXIS_X, 1); + event->setAccepted(true); + return true; + case Qt::Key_Down: + if(!ke->isAutoRepeat()) + jog(VhfMill::AXIS_Y, 1); + event->setAccepted(true); + return true; + case Qt::Key_PageUp: + if(!ke->isAutoRepeat()) + jog(VhfMill::AXIS_Z, 1); + event->setAccepted(true); + return true; + case Qt::Key_PageDown: + if(!ke->isAutoRepeat()) + jog(VhfMill::AXIS_Z, -1); + event->setAccepted(true); + return true; + default: + break; + } + } + else if(event->type() == QEvent::KeyRelease) + { + QKeyEvent *ke = static_cast(event); + switch(ke->key()) + { + case Qt::Key_Up: + if(!ke->isAutoRepeat()) + jog(VhfMill::AXIS_Y, 0); + event->setAccepted(true); + return true; + case Qt::Key_Left: + if(!ke->isAutoRepeat()) + jog(VhfMill::AXIS_X, 0); + event->setAccepted(true); + return true; + case Qt::Key_Right: + if(!ke->isAutoRepeat()) + jog(VhfMill::AXIS_X, 0); + event->setAccepted(true); + return true; + case Qt::Key_Down: + if(!ke->isAutoRepeat()) + jog(VhfMill::AXIS_Y, 0); + event->setAccepted(true); + return true; + case Qt::Key_PageUp: + if(!ke->isAutoRepeat()) + jog(VhfMill::AXIS_Z, 0); + event->setAccepted(true); + return true; + case Qt::Key_PageDown: + if(!ke->isAutoRepeat()) + jog(VhfMill::AXIS_Z, 0); + event->setAccepted(true); + return true; + default: + break; + } + } + } + return false; +} + +void MainWindow::textChanged() +{ + bool ok = true; + QByteArray programm = ui->plainTextEdit->toPlainText().toLatin1(); + + if(ui->radioButton_gcode->isChecked()) + { + QList errors; + programm = gcodeToVhf(programm, &ok, &errors); + if(!ok) + ui->statusbar->showMessage(errors.back()); + ui->plainTextEdit_compiled->setPlainText(programm); + } + + ui->backPlot->programChanged(programm); +} + +void MainWindow::home() +{ + VhfMill::Axis axis = comboBoxToAxis(*ui->comboBox_homeAxis); + qDebug()<<__func__<home(axis);}, Qt::QueuedConnection); +} + +void MainWindow::toolUnload() +{ + QMetaObject::invokeMethod(mill_, [this](){mill_->setTool(0);}, Qt::QueuedConnection); +} + +void MainWindow::gotSpindleSpeed(int speed) +{ + ui->lcd_spindle->display(speed); +} + +void MainWindow::stopSpindle() +{ + QMetaObject::invokeMethod(mill_, [this](){mill_->setSpindleSpeed(0);}, Qt::QueuedConnection); + ui->horizontalSlider->setValue(0); +} + +void MainWindow::isHomed(VhfMill::Axis axis) +{ + ui->led_homeX->setLit(axis & VhfMill::AXIS_X); + ui->led_homeY->setLit(axis & VhfMill::AXIS_Y); + ui->led_homeZ->setLit(axis & VhfMill::AXIS_Z); + ui->led_homeA->setLit(axis & VhfMill::AXIS_A); + checkBlocks(); +} + +VhfMill::Axis MainWindow::comboBoxToAxis(const QComboBox& box) +{ + switch(box.currentIndex()) + { + case 0: + return VhfMill::AXIS_X; + case 1: + return VhfMill::AXIS_Y; + case 2: + return VhfMill::AXIS_Z; + case 3: + return VhfMill::AXIS_A; + case 4: + return VhfMill::AXIS_B; + default: + return VhfMill::AXIS_NONE; + } +} + +void MainWindow::run() +{ + QByteArray programm = ui->plainTextEdit->toPlainText().toLatin1(); + if(ui->radioButton_gcode->isChecked()) + { + bool ok; + programm = gcodeToVhf(programm, &ok); + if(!ok) + { + QMessageBox::critical(this, "Error", "failed to parse gcode"); + return; + } + } + QMetaObject::invokeMethod(mill_, [programm, this](){mill_->send(programm);}, Qt::QueuedConnection); +} + +void MainWindow::toolSwitch() +{ + QMetaObject::invokeMethod(mill_, [this](){mill_->setTool(ui->comboBox_tool->currentIndex()+1);}, Qt::QueuedConnection); +} + +void MainWindow::touchoff() +{ + bool ok; + double offset = QInputDialog::getDouble(this, "Offset", "offset in mm", 0, -1000, 1000, 2, &ok); + if(ok) + QMetaObject::invokeMethod(mill_, [this, offset](){mill_->touchOff(comboBoxToAxis(*ui->comboBox_TouchoffAxis), offset*1000);}, Qt::QueuedConnection); +} + +void MainWindow::touchoffAll() +{ + QMetaObject::invokeMethod(mill_, [this](){mill_->touchOff(VhfMill::AXIS_ALL, 0);}, Qt::QueuedConnection); +} + + +void MainWindow::touchoffRst() +{ + QMetaObject::invokeMethod(mill_, [this](){mill_->touchOffAbsolute({0,0,0,0});}, Qt::QueuedConnection); +} + +void MainWindow::toolChangeDone() +{ + QMetaObject::invokeMethod(mill_, &VhfMill::reqTool, Qt::QueuedConnection); + setToolchangeBlocked(false); +} + +void MainWindow::raiseError(int errorNum) +{ + QMessageBox::critical(this, "Error", VhfMill::textForErrno(errorNum)); + checkBlocks(); +} + +void MainWindow::positionUpdate(std::vector position) +{ + if(position.size() < 4) + return; + + std::vector touchoff; + if(ui->radioButton->isChecked()) + touchoff.assign(position.size(), 0); + else + touchoff = mill_->getLastTouchoffPosition(); + + size_t xIndex = VhfMill::axisToIndex(VhfMill::AXIS_X); + size_t yIndex = VhfMill::axisToIndex(VhfMill::AXIS_Y); + size_t zIndex = VhfMill::axisToIndex(VhfMill::AXIS_Z); + size_t aIndex = VhfMill::axisToIndex(VhfMill::AXIS_A); + + double xDpl = position[xIndex] - touchoff[xIndex]; + double yDpl = position[yIndex] - touchoff[yIndex]; + double zDpl = position[zIndex] - touchoff[zIndex]; + double aDpl = position[aIndex] - touchoff[aIndex]; + + ui->lcd_x->display(xDpl/1000.0); + ui->lcd_y->display(yDpl/1000.0); + ui->lcd_z->display(zDpl/1000.0); + ui->lcd_a->display(aDpl/1000.0); +} + +void MainWindow::openVhfCode() +{ + QString fileName = QFileDialog::getOpenFileName(this, "Open File", "~", "VhfCode (*.vhf)"); + if(!fileName.isEmpty()) + { + QFile gcodeFile(fileName); + if(!gcodeFile.open(QIODeviceBase::ReadOnly)) + { + QMessageBox::critical(this, "Error", "Could not open " + fileName + " for reading"); + return; + } + QByteArray vhfCode = gcodeFile.readAll(); + + ui->radioButton_vhfCode->setChecked(true); + ui->plainTextEdit->setPlainText(vhfCode); + } +} + +void MainWindow::openGcode() +{ + QString fileName = QFileDialog::getOpenFileName(this, "Open File", "~", "Gcode (*.ngc *.nc)"); + if(!fileName.isEmpty()) + { + QFile gcodeFile(fileName); + if(!gcodeFile.open(QIODeviceBase::ReadOnly)) + { + QMessageBox::critical(this, "Error", "Could not open " + fileName + " for reading"); + return; + } + + bool ok; + QByteArray gcode = gcodeFile.readAll(); + gcodeToVhf(gcode, &ok); + if(!ok) + { + QMessageBox::critical(this, "Error", "failed to parse gcode"); + return; + } + + ui->radioButton_gcode->setChecked(true); + ui->radioButton_vhfCode->setChecked(false); + ui->plainTextEdit->setPlainText(gcode); + } +} + +void MainWindow::touchoffChanged(std::vector position) +{ + if(position.size() < 4) + return; + ui->lcd_touchX->display(static_cast(position[VhfMill::axisToIndex(VhfMill::AXIS_X)])/1000.0); + ui->lcd_touchY->display(static_cast(position[VhfMill::axisToIndex(VhfMill::AXIS_Y)])/1000.0); + ui->lcd_touchZ->display(static_cast(position[VhfMill::axisToIndex(VhfMill::AXIS_Z)])/1000.0); + ui->lcd_touchA->display(static_cast(position[VhfMill::axisToIndex(VhfMill::AXIS_A)])/1000.0); + positionUpdate(mill_->getLastKnownPosition()); + ui->backPlot->touchoffUpdate(position); + textChanged(); +} + +void MainWindow::checkBlocks() +{ + setAllBlocked(true); + if(mill_->isInitDone()) + { + qDebug()<<__func__<<"unblocking home and spindle"; + setSpindleBlocked(false); + setHomeingBlocked(false); + setOutputsBlocked(false); + if(mill_->allHomed()) + { + qDebug()<<__func__<<"unblocking jog"; + setJogBlocked(false); + ui->pushButton_run->setEnabled(true); + if(mill_->presureReady()) + { + qDebug()<<__func__<<"unblocking tool change"; + setToolchangeBlocked(false); + } + } + } +} + +void MainWindow::gotOutputs(uint8_t outputs) +{ + ui->checkBox_oe1->setChecked(outputs & 1<<0); + ui->checkBox_oe2->setChecked(outputs & 1<<1); + ui->checkBox_oe3->setChecked(outputs & 1<<2); + ui->checkBox_oe4->setChecked(outputs & 1<<3); + ui->checkBox_oe5->setChecked(outputs & 1<<4); + ui->checkBox_oe6->setChecked(outputs & 1<<5); + ui->checkBox_oe7->setChecked(outputs & 1<<6); +} + +void MainWindow::gotPressureState(bool state) +{ + ui->led_pressure->setLit(!state); + checkBlocks(); +} + +void MainWindow::setAllBlocked(bool blocked) +{ + setOutputsBlocked(blocked); + setJogBlocked(blocked); + setToolchangeBlocked(blocked); + setSpindleBlocked(blocked); + setHomeingBlocked(blocked); + ui->pushButton_run->setEnabled(!blocked); +} + +void MainWindow::setOutputsBlocked(bool block) +{ + ui->checkBox_oe1->setEnabled(!block); + ui->checkBox_oe2->setEnabled(!block); + ui->checkBox_oe3->setEnabled(!block); + ui->checkBox_oe4->setEnabled(!block); + ui->checkBox_oe5->setEnabled(!block); + ui->checkBox_oe6->setEnabled(!block); + ui->checkBox_oe7->setEnabled(!block); +} + +void MainWindow::setJogBlocked(bool block) +{ + jogEnabled = !block; + ui->pushButton_jogDown->setEnabled(!block); + ui->pushButton_jogUp->setEnabled(!block); + ui->pushButton_jogXp->setEnabled(!block); + ui->pushButton_jogXn->setEnabled(!block); + ui->pushButton_jogYp->setEnabled(!block); + ui->pushButton_jogYn->setEnabled(!block); + ui->pushButton_jogAp->setEnabled(!block); + ui->pushButton_jogAn->setEnabled(!block); + ui->comboBox_jogStep->setEnabled(!block); +} + +void MainWindow::setToolchangeBlocked(bool block) +{ + ui->pushButton_toolSwitch->setEnabled(!block); + ui->pushButton_toolUnload->setEnabled(!block); + ui->comboBox_tool->setEnabled(!block); +} + +void MainWindow::setSpindleBlocked(bool block) +{ + ui->pushButton_stopSpindle->setEnabled(!block); + ui->horizontalSlider->setEnabled(!block); +} + +void MainWindow::setHomeingBlocked(bool block) +{ + ui->pushButton_home->setEnabled(!block); + ui->pushButton_homeAll->setEnabled(!block); + ui->comboBox_homeAxis->setEnabled(!block); +} + +void MainWindow::setTouchoffBlocked(bool block) +{ + ui->comboBox_TouchoffAxis->setEnabled(!block); + ui->pushButton_touchoff->setEnabled(!block); + ui->pushButton_touchoffAll->setEnabled(!block); +} + diff --git a/mainwindow.h b/mainwindow.h new file mode 100644 index 0000000..b27e987 --- /dev/null +++ b/mainwindow.h @@ -0,0 +1,70 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include +#include +#include "vhfmill.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class MainWindow; } +QT_END_NAMESPACE + +class MainWindow : public QMainWindow +{ + Q_OBJECT + VhfMill* mill_; + + QShortcut viewTopAction; + QShortcut viewFrontAction; + QShortcut viewLeftAction; + QShortcut viewRightAction; + bool jogEnabled = false; + +private slots: + void raiseError(int errorNum); + void positionUpdate(std::vector position); + void isHomed(VhfMill::Axis axis); + void gotOutputs(uint8_t outputs); + void gotPressureState(bool state); + void checkBlocks(); + void toolChangeDone(); + void gotSpindleSpeed(int speed); + void touchoffChanged(std::vector position); + void textChanged(); + void selectedJogStepChanged(int index); + void jog(VhfMill::Axis axis, int jogDirection); + + void home(); + void stopSpindle(); + void run(); + void toolSwitch(); + void touchoff(); + void touchoffAll(); + void touchoffRst(); + void toolUnload(); + void openGcode(); + void openVhfCode(); + +private: + void setAllBlocked(bool block); + void setOutputsBlocked(bool block); + void setJogBlocked(bool block); + void setToolchangeBlocked(bool block); + void setSpindleBlocked(bool block); + void setHomeingBlocked(bool block); + void setTouchoffBlocked(bool block); + + static VhfMill::Axis comboBoxToAxis(const QComboBox& box); + +protected: + virtual bool eventFilter(QObject *o, QEvent *e) override; + +public: + MainWindow(VhfMill* mill, QWidget *parent = nullptr); + ~MainWindow(); + +private: + Ui::MainWindow *ui = nullptr; +}; +#endif // MAINWINDOW_H diff --git a/mainwindow.ui b/mainwindow.ui new file mode 100644 index 0000000..019a57f --- /dev/null +++ b/mainwindow.ui @@ -0,0 +1,862 @@ + + + MainWindow + + + + 0 + 0 + 1048 + 687 + + + + VHF Mill + + + + + + + Qt::Vertical + + + 5 + + + + + 0 + + + 0 + + + + + 10 + + + + + Setup + + + + + + + x + + + + + y + + + + + z + + + + + a + + + + + + + + Touchoff Axis + + + + + + + + x + + + + + y + + + + + z + + + + + a + + + + + + + + init + + + + + + + Home All + + + + + + + Home Axis + + + + + + + Touchoff All + + + + + + + Reset Touchoff + + + + + + + + + + Jog + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Continues + + + + + 5 mm + + + + + 1 mm + + + + + 0.25 mm + + + + + 0.1 mm + + + + + 0.05 mm + + + + + + + + + + + + + 10 + + + + + 1 + + + + DRO + + + + + + + + QLCDNumber::Flat + + + + + + + Z + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 30 + 30 + + + + + + + + Position + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Y + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 50 + 30 + + + + + + + + Touchoff Position + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + + 50 + 30 + + + + + + + + QLCDNumber::Flat + + + + + + + QLCDNumber::Flat + + + + + + + Homed + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + QLCDNumber::Flat + + + + + + + X + + + Qt::AlignCenter + + + + + + + A + + + Qt::AlignCenter + + + + + + + QLCDNumber::Flat + + + + + + + QLCDNumber::Flat + + + + + + + QLCDNumber::Flat + + + + + + + QLCDNumber::Flat + + + + + + + + 0 + 0 + + + + + 50 + 30 + + + + + + + + Pressure + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Probe + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + + 0 + 30 + + + + + + + + + 0 + 0 + + + + + 0 + 30 + + + + + + + + + + 6 + + + 0 + + + + + Show Absolute + + + true + + + true + + + + + + + Show Relative + + + + + + + + + + 3D + + + + + + + + + + + + + + + Current Tool + + + + + + + QLCDNumber::Flat + + + + + + + + 1 + + + + + 2 + + + + + 3 + + + + + 4 + + + + + 5 + + + + + + + + Switch + + + + + + + Unload + + + + + + + + + 0 + + + + + Spindle + + + + + + + QLCDNumber::Flat + + + + + + + 0 + + + 60000 + + + 100 + + + 100 + + + 0 + + + false + + + Qt::Horizontal + + + + + + + Stop + + + + + + + + + 0 + + + 10 + + + + + Unclamp Tool + + + + + + + Bearing Air + + + + + + + Air Assist + + + + + + + Clamp Tools + + + + + + + Compressor + + + + + + + A Axis Clamp + + + + + + + Fixture Air + + + + + + + + + + + + Programm + + + + + + + + + + + true + + + + + + + + + + + VHF code + + + true + + + + + + + Gcode + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Stop + + + + + + + Run + + + + + + + + + + + + + + + 0 + 0 + 1048 + 22 + + + + + File + + + + + + + + + + + Open VHL + + + Ctrl+Shift+O + + + + + Serial Port + + + + + Open Gcode + + + Ctrl+O + + + + + Quit + + + + + Save VhfCode + + + + + + Led + QWidget +
led.h
+ 1 +
+ + BackPlotWidget + QWidget +
backplotwidget.h
+ 1 +
+
+ + +
diff --git a/orbitcameracontroller.cpp b/orbitcameracontroller.cpp new file mode 100644 index 0000000..180ca8f --- /dev/null +++ b/orbitcameracontroller.cpp @@ -0,0 +1,164 @@ +// Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB). +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "orbitcameracontroller.h" + +#include + +/*! + \class Qt3DExtras::OrbitCameraController + \ingroup qt3d-extras-cameracontrollers + \brief The OrbitCameraController class allows controlling the scene camera along orbital path. + \inmodule Qt3DExtras + \since 5.7 + \inherits Qt3DCore::QEntity + + The controls are: + \table + \header + \li Input + \li Action + \row + \li Left mouse button + \li While the left mouse button is pressed, mouse movement along x-axis moves the camera + left and right and movement along y-axis moves it up and down. + \row + \li Right mouse button + \li While the right mouse button is pressed, mouse movement along x-axis pans the camera + around the camera view center and movement along y-axis tilts it around the camera + view center. + \row + \li Both left and right mouse button + \li While both the left and the right mouse button are pressed, mouse movement along y-axis + zooms the camera in and out without changing the view center. + \row + \li Mouse scroll wheel + \li Zooms the camera in and out without changing the view center. + \row + \li Arrow keys + \li Move the camera vertically and horizontally relative to camera viewport. + \row + \li Page up and page down keys + \li Move the camera forwards and backwards. + \row + \li Shift key + \li Changes the behavior of the up and down arrow keys to zoom the camera in and out + without changing the view center. The other movement keys are disabled. + \row + \li Alt key + \li Changes the behovior of the arrow keys to pan and tilt the camera around the view + center. Disables the page up and page down keys. + \row + \li Escape + \li Moves the camera so that entire scene is visible in the camera viewport. + \endtable +*/ + +OrbitCameraController::OrbitCameraController(QVector2D orthoSize, Qt3DCore::QNode *parent) + : QAbstractCameraController(parent), + m_orthoSize(orthoSize) +{ +} + +OrbitCameraController::~OrbitCameraController() +{ +} + +/*! + \property OrbitCameraController::zoomInLimit + + Holds the current zoom-in limit. The zoom-in limit determines how close to the view center + the camera can be zoomed. +*/ +float OrbitCameraController::zoomInLimit() const +{ + return m_zoomInLimit; +} + +void OrbitCameraController::updateProjection() +{ + camera()->lens()->setOrthographicProjection(0-(m_orthoSize.x()/m_zoomFactor)/2, (m_orthoSize.x()/m_zoomFactor)/2, + 0-(m_orthoSize.y()/m_zoomFactor)/2, (m_orthoSize.y()/m_zoomFactor)/2, 0.01, 100000); +} + +void OrbitCameraController::setZoomInLimit(float zoomInLimit) +{ + if (m_zoomInLimit != zoomInLimit) + { + m_zoomInLimit = zoomInLimit; + emit zoomInLimitChanged(); + } +} + +void OrbitCameraController::setZoomFactor(float factor) +{ + m_zoomFactor = factor; + updateProjection(); +} + +void OrbitCameraController::setOrthoSize(QVector2D orthoSize) +{ + m_orthoSize = orthoSize; + updateProjection(); +} + +inline float clampInputs(float input1, float input2) +{ + float axisValue = input1 + input2; + return (axisValue < -1) ? -1 : (axisValue > 1) ? 1 : axisValue; +} + +inline float zoomDistance(QVector3D firstPoint, QVector3D secondPoint) +{ + return (secondPoint - firstPoint).lengthSquared(); +} + +void OrbitCameraController::moveCamera(const QAbstractCameraController::InputState &state, float dt) +{ + Qt3DRender::QCamera *theCamera = camera(); + + if (theCamera == nullptr) + return; + + const QVector3D upVector(0.0f, 0.0f, 1.0f); + + if(state.tzAxisValue != 0) + { + m_zoomFactor+=0.1*state.tzAxisValue; + if(m_zoomFactor < 1) + m_zoomFactor = 1; + else if(m_zoomFactor > m_zoomInLimit) + m_zoomFactor = m_zoomInLimit; + updateProjection(); + } + + // Mouse input + if (state.rightMouseButtonActive) + { + // Translate + theCamera->translate(QVector3D(clampInputs(0-state.rxAxisValue, 0-state.txAxisValue) * linearSpeed(), + clampInputs(0-state.ryAxisValue, 0-state.tyAxisValue) * linearSpeed(), + 0) * dt); + } + else if (state.leftMouseButtonActive) + { + // Orbit + theCamera->panAboutViewCenter((state.rxAxisValue * -1 * lookSpeed()) * dt, upVector); + theCamera->tiltAboutViewCenter((state.ryAxisValue * -1 * lookSpeed()) * dt); + } + + // Keyboard Input + if (state.altKeyActive) + { + // Orbit + theCamera->panAboutViewCenter((state.txAxisValue * lookSpeed()) * dt, upVector); + theCamera->tiltAboutViewCenter((state.tyAxisValue * lookSpeed()) * dt); + } + else + { + // Translate + theCamera->translate(QVector3D(clampInputs(state.leftMouseButtonActive ? state.rxAxisValue : 0, state.txAxisValue) * linearSpeed(), + clampInputs(state.leftMouseButtonActive ? state.ryAxisValue : 0, state.tyAxisValue) * linearSpeed(), + state.tzAxisValue * linearSpeed()) * dt); + } +} diff --git a/orbitcameracontroller.h b/orbitcameracontroller.h new file mode 100644 index 0000000..30c27ca --- /dev/null +++ b/orbitcameracontroller.h @@ -0,0 +1,33 @@ +// Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB). +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#pragma once +#include +#include + +class OrbitCameraController : public Qt3DExtras::QAbstractCameraController +{ + Q_OBJECT + + float m_zoomInLimit = 10.0; + float m_zoomFactor = 1.0; + QVector2D m_orthoSize; + + void updateProjection(); + +public: + explicit OrbitCameraController(QVector2D orthoSize, Qt3DCore::QNode *parent = nullptr); + ~OrbitCameraController(); + + float zoomInLimit() const; + + void setZoomInLimit(float zoomInLimit); + void setZoomFactor(float factor); + void setOrthoSize(QVector2D orthoSize); + +signals: + void zoomInLimitChanged(); + +private: + virtual void moveCamera(const Qt3DExtras::QAbstractCameraController::InputState &state, float dt) override; +}; diff --git a/vhfmill.cpp b/vhfmill.cpp new file mode 100644 index 0000000..a569f0d --- /dev/null +++ b/vhfmill.cpp @@ -0,0 +1,628 @@ +#include "vhfmill.h" +#include + +VhfMill::VhfMill(QIODevice* serial, uint8_t nAxis, QObject *parent) + : serial_(serial), nAxis_(nAxis), QObject{parent} +{ + QObject::connect(serial_, &QIODevice::readyRead, this, &VhfMill::serialReadyRead); + + qDebug()<<__func__<<" object created"; + + lastKnownPos_.assign(5, 0); + touchoffPos_.assign(5, 0); + + initTimer.callOnTimeout(this, &VhfMill::initTimerFn); + initTimer.setInterval(3000); + + stateTimer.callOnTimeout(this, &VhfMill::stateTimerFn); + stateTimer.setInterval(1000); + + positionTimer.callOnTimeout(this, &VhfMill::reqPosition); + positionTimer.setInterval(100); + + limits_ = {190000, 92000, 85000, 100000}; +} + +void VhfMill::serialReadyRead() +{ + char charBuf; + while(serial_->getChar(&charBuf)) + { + lineBuffer_.push_back(charBuf); + if(lineBuffer_.endsWith(';') ) + { + lineBuffer_.chop(1); + processIncomeingLine(); + gotLine(lineBuffer_); + lineBuffer_.clear(); + } + } +} + +void VhfMill::init() +{ + initTimerFn(); + initTimer.start(); +} + +void VhfMill::setSpindleSpeed(int speed) +{ + serial_->write("RVS"+QByteArray::number(speed)+";\n"); + serial_->write("?RV;"); +} + +void VhfMill::setjogStep(int step) +{ + jogStep_ = step; + jogStepChanged(jogStep_); +} + +VhfMill::Axis VhfMill::homed() const +{ + return axisHomed_; +} + +void VhfMill::jog(VhfMill::Axis axis, int jogDirection) +{ + if(homed() & axis) + { + if(jogStep_ > 0 && jogDirection != 0 && jogAxis_ == AXIS_NONE) + { + QByteArray command("GR"); + if(axis == AXIS_X) + command.append(QByteArray::number(jogStep_*jogDirection*-1)); + else + command.push_back('0'); + command.push_back(','); + if(axis == AXIS_Y) + command.append(QByteArray::number(jogStep_*jogDirection)); + else + command.push_back('0'); + command.push_back(','); + if(axis == AXIS_Z) + command.append(QByteArray::number(jogStep_*jogDirection*-1)); + else + command.push_back('0'); + command.push_back(','); + if(axis == AXIS_A) + command.append(QByteArray::number(jogStep_*jogDirection*-1)); + else + command.push_back('0'); + command.push_back(';'); + send(command); + } + else + { + if(jogAxis_ == axis && jogDirection == 0) + { + serial_->write("!M;"); + jogDirection_ = 0; + jogAxis_ = AXIS_NONE; + } + else if(jogAxis_ == AXIS_NONE && jogDirection != 0) + { + jogAxis_ = axis; + jogDirection_ = jogDirection; + jogDirection = jogDirection == -1 ? 1 : 0; + QByteArray command("GA"); + if(axis == AXIS_X) + command.append(QByteArray::number(getLimits()[0]*jogDirection)); + else + command.push_back(QByteArray::number(getLastKnownPosition()[0])); + command.push_back(','); + if(axis == AXIS_Y) + command.append(QByteArray::number(getLimits()[1]*jogDirection)); + else + command.push_back(QByteArray::number(getLastKnownPosition()[1])); + command.push_back(','); + if(axis == AXIS_Z) + command.append(QByteArray::number(getLimits()[2]*jogDirection)); + else + command.push_back(QByteArray::number(getLastKnownPosition()[2])); + command.push_back(','); + if(axis == AXIS_A) + command.append(QByteArray::number(getLimits()[3]*jogDirection)); + else + command.push_back(QByteArray::number(getLastKnownPosition()[3])); + command.push_back(';'); + send(command); + } + } + } +} + +const std::vector& VhfMill::getLimits() const +{ + return limits_; +} + +QByteArray VhfMill::generateCmdForOffset(QByteArray command) +{ + QByteArray output = command.first(2); + command.remove(0, 2); + QList values = command.split(','); + + for(size_t i = 0; i < values.size(); ++i) + { + if(values[i].size() != 0) + { + bool ok; + int value = values[i].toInt(&ok); + if(!ok) + { + raiseError(10); + return QByteArray(); + } + output.append(QByteArray::number(value+touchoffPos_[i])); + } + output.push_back(','); + } + + output.back() == ','; + output.chop(1); + return output; +} + +void VhfMill::send(const QByteArray& cmd) +{ + QList commands = cmd.split(';'); + + for(const QByteArray& command : commands) + { + QByteArray trimmedCmd = command.trimmed().toUpper(); + if(trimmedCmd.size() < 2) + continue; + if(trimmedCmd.first(2) == "PA" || trimmedCmd.first(2) == "GA") + trimmedCmd = generateCmdForOffset(trimmedCmd); + else if(trimmedCmd.first(2) == "PB") + trimmedCmd[1] = 'A'; + else if(trimmedCmd.first(2) == "GB") + trimmedCmd[1] = 'A'; + qDebug()<<"Writeing"<<(trimmedCmd + ";\n"); + serial_->write(trimmedCmd + ";\n"); + } + +} + +void VhfMill::stop() +{ + send("!B;"); + send("CONT;"); +} + +void VhfMill::home(VhfMill::Axis axis) +{ + if(mode_ != MODE_NORMAL || homeAxis_ != AXIS_NONE) + { + raiseError(3); + return; + } + if(axis == AXIS_ALL) + serial_->write("RF;\n"); + else if(axis == AXIS_NONE) + { + return; + } + else + { + int axisNum = axisToMashineNumber(axis); + qDebug()<<__func__<write("RF" + QByteArray::number(axisNum) + ";\n"); + } + homeAxis_ = axis; + mode_ = MODE_HOME; + axisHomed_ &= ~axis; + isHomed(axisHomed_); +} + +void VhfMill::stateTimerFn() +{ + reqOutputs(); + reqSensors(); + reqSpindleSpeed(); +} + +void VhfMill::setOutput(int output, bool state) +{ + serial_->write("OA" + QByteArray::number(static_cast(output)) + "," + QByteArray::number(static_cast(state)) + ";\n"); + reqOutputs(); +} + +void VhfMill::setTool(int tool) +{ + if(mode_ != MODE_NORMAL) + { + VhfMill::raiseError(3); + return; + } + mode_ = MODE_TOOLCHANGE; + serial_->write("T" + QByteArray::number(tool) + ";\n"); + reqTool(); +} + +void VhfMill::reqTool() +{ + serial_->write("?T;\n"); +} + +void VhfMill::reqOutputs() +{ + serial_->write("?OA;\n"); +} + +void VhfMill::reqPosition() +{ + serial_->write("?PA;\n"); +} + +void VhfMill::reqSpindleSpeed() +{ + serial_->write("?RV;\n"); +} + +void VhfMill::reqSensors() +{ + serial_->write("?IA;\n"); +} + +void VhfMill::enablePosUpdates(bool enable) +{ + if(enable && !positionTimer.isActive()) + positionTimer.start(); + else + positionTimer.stop(); +} + +void VhfMill::enableStateUpdates(bool enable) +{ + if(enable && !stateTimer.isActive()) + stateTimer.start(); + else + stateTimer.stop(); +} + +void VhfMill::reinit() +{ + mode_ = MODE_PREINIT; + enablePosUpdates(false); + enableStateUpdates(false); + serial_->write("!N;\n"); +} + +void VhfMill::initTimerFn() +{ + if(mode_ != MODE_PREINIT) + { + initTimer.stop(); + enablePosUpdates(true); + enableStateUpdates(true); + return; + } + + reinit(); +} + +void VhfMill::processIncomeingLine() +{ + if(lineBuffer_.isEmpty()) + { + if(mode_ == MODE_PREINIT) + { + qDebug()<<"init done signal"; + mode_ = MODE_NORMAL; + initDone(); + } + else if(mode_ == MODE_TOOLCHANGE) + { + qDebug()<<"toolchange done signal"; + mode_ = MODE_NORMAL; + toolChangeDone(); + } + else if(mode_ == MODE_HOME) + { + qDebug()<<"homeing done"; + axisHomed_ = axisHomed_ | homeAxis_; + mode_ = MODE_NORMAL; + homeAxis_ = AXIS_NONE; + isHomed(axisHomed_); + } + else + { + return; + } + } + + if(lineBuffer_.size() == 0) + return; + + if(lineBuffer_[0] == 'E') + { + lineBuffer_.remove(0,1); + bool ok; + int errorNumber = lineBuffer_.toInt(&ok); + if(!ok) + errorNumber = 90; + VhfMill::raiseError(errorNumber); + } + else if(lineBuffer_.startsWith("PA=")) + { + lineBuffer_.remove(0,3); + QList list = lineBuffer_.split(','); + if(list.size() != nAxis_) + { + VhfMill::raiseError(22); + return; + } + + std::vector postion(5, 0); + for(int i = 0; i < list.size(); ++i) + { + bool ok; + postion[i] = list[i].toInt(&ok); + if(!ok) + { + VhfMill::raiseError(90); + return; + } + lastKnownPos_ = postion; + positionUpdate(lastKnownPos_); + } + } + else if(lineBuffer_.startsWith("T=")) + { + lineBuffer_.remove(0,2); + bool ok; + int toolNumber = lineBuffer_.toInt(&ok); + if(!ok) + { + VhfMill::raiseError(90); + return; + } + gotToolNum(toolNumber); + } + else if(lineBuffer_.startsWith("O=")) + { + lineBuffer_.remove(0,2); + bool ok; + int outputs = lineBuffer_.toInt(&ok); + if(!ok) + { + VhfMill::raiseError(90); + return; + } + if(outputs_ != outputs) + { + outputs_ = outputs; + gotOutputs(outputs_); + } + } + else if(lineBuffer_.startsWith("RV=")) + { + lineBuffer_.remove(0,3); + bool ok; + int speed = lineBuffer_.toInt(&ok); + if(!ok) + { + VhfMill::raiseError(90); + return; + } + if(spindleSpeed_ != speed) + { + spindleSpeed_ = speed; + gotSindleSpeed(spindleSpeed_); + } + } + else if(lineBuffer_.startsWith("I=")) + { + lineBuffer_.remove(0,2); + bool ok; + unsigned int sensorBits = lineBuffer_.toUInt(&ok); + if(!ok) + { + VhfMill::raiseError(90); + return; + } + if(sensors_ != sensorBits) + { + sensors_ = sensorBits; + gotProbeState(sensors_ & 1U); + gotPressureState(sensors_ & 1U<<1); + } + } +} + +bool VhfMill::presureReady() const +{ + return !(sensors_ & 1<<1); +} + +bool VhfMill::isInitDone() const +{ + return mode_ != MODE_PREINIT; +} + +int VhfMill::getLastSpindleSpeed() const +{ + return spindleSpeed_; +} + +bool VhfMill::allHomed() const +{ + bool allHomed = !(~axisHomed_ & (AXIS_X | AXIS_Y | AXIS_Z | AXIS_A | (nAxis_ > 4 ? AXIS_B : 0))); + qDebug()<<__func__< touchoffPos) +{ + for(size_t i = 0; i < touchoffPos.size() && i < touchoffPos_.size(); ++i) + touchoffPos_[i] = touchoffPos[i]; + touchoffChanged(touchoffPos_); +} + +size_t VhfMill::axisToIndex(Axis axis) +{ + return axisToMashineNumber(axis)-1; +} + +std::vector VhfMill::getLastTouchoffPosition() const +{ + return touchoffPos_; +} + +std::vector VhfMill::getLastKnownPosition() const +{ + return lastKnownPos_; +} + +int VhfMill::axisToMashineNumber(Axis axis) +{ + switch(axis) + { + case AXIS_X: + return 1; + case AXIS_Y: + return 2; + case AXIS_Z: + return 3; + case AXIS_A: + return 4; + case AXIS_B: + return 5; + default: + return 0; + } +} + +const QString VhfMill::textForErrno(int errorNumber) +{ + switch(errorNumber) + { + case 0: + return "No error"; + case 1: + return "unsupported command"; + case 2: + return "invalid command"; + case 3: + return "command not allowed in this state"; + case 4: + return "syntax error"; + case 5: + return "invalid feature"; + case 6: + return "stopped by user"; + case 7: + return "unknown error"; + case 10: + return "invalid parameter"; + case 12: + return "missing parameter"; + case 13: + return "parameter is out of range"; + case 14: + return "no customer profile"; + case 20: + return "axis not defined"; + case 21: + return "axis not combinable"; + case 22: + return "axis not referenced"; + case 23: + return "no referenzpoint found"; + case 24: + return "no measurepoint found"; + case 25: + return "axis range ended"; + case 26: + return "tool too long"; + case 27: + return "tool too short"; + case 30: + return "no spindle defined"; + case 31: + return "no spindle response"; + case 32: + return "spindle input active"; + case 40: + return "can not record macro"; + case 41: + return "macro too long"; + case 42: + return "error in last macro"; + case 43: + return "macro not found"; + case 50: + return "initial stop"; + case 51: + return "external stop"; + case 52: + return "power driver stop"; + case 53: + return "external spindle stop"; + case 54: + return "internal spindle stop"; + case 55: + return "hbox stop"; + case 56: + return "powerfail stop"; + case 57: + return "fpga confdone stop"; + case 58: + return "refswitch stop"; + case 59: + return "fpga error stop"; + case 60: + return "overcurrent spindle stop"; + case 61: + return "overload spindle stop"; + case 62: + return "wait for input stop"; + case 63: + return "unexpected input stop"; + case 70: + return "leveloffset too high"; + case 71: + return "internal error"; + case 72: + return "error opening/reading file"; + case 73: + return "no answer from device"; + case 74: + return "error while loading fpga"; + case 75: + return "update not feasible"; + case 76: + return "update failed"; + case 77: + return "wait for input failed"; + case 90: + return "return parse error"; + default: + return "errno not valid"; + } +} diff --git a/vhfmill.h b/vhfmill.h new file mode 100644 index 0000000..2008e1e --- /dev/null +++ b/vhfmill.h @@ -0,0 +1,125 @@ +#ifndef VHFMILL_H +#define VHFMILL_H + +#include +#include +#include +#include +#include +#include +#include + +class VhfMill : public QObject +{ + Q_OBJECT + +public: + enum { + AXIS_NONE = 0, + AXIS_X = 1, + AXIS_Y = 1<<1, + AXIS_Z = 1<<2, + AXIS_A = 1<<3, + AXIS_B = 1<<4, + AXIS_ALL = AXIS_X | AXIS_Y | AXIS_Z | AXIS_A | AXIS_B, + }; + typedef int Axis; + +private: + + typedef enum { + MODE_PREINIT, + MODE_HOME, + MODE_NORMAL, + MODE_TOOLCHANGE, + MODE_JOG, + } Mode; + + std::vector limits_; + + QIODevice* serial_; + uint8_t nAxis_; + + uint8_t outputs_ = 0; + int spindleSpeed_ = 0; + uint16_t sensors_; + std::vector lastKnownPos_; + std::vector touchoffPos_; + Mode mode_ = MODE_PREINIT; + Axis axisHomed_ = AXIS_NONE; + Axis homeAxis_ = AXIS_NONE; + + Axis jogAxis_ = AXIS_NONE; + int jogStep_ = -1; + int jogDirection_ = 0; + + QTimer positionTimer; + QTimer stateTimer; + QTimer initTimer; + + QByteArray lineBuffer_; + + void processIncomeingLine(); + static int axisToMashineNumber(Axis axis); + QByteArray generateCmdForOffset(QByteArray command); + +private slots: + void serialReadyRead(); + void initTimerFn(); + void stateTimerFn(); + void reinit(); + +public: + explicit VhfMill(QIODevice* serial, uint8_t nAxis = 4, QObject *parent = nullptr); + std::vector getLastKnownPosition() const; + Axis homed() const; + bool allHomed() const; + uint8_t axisCount() const; + uint16_t getLastKnownSensors() const; + bool presureReady() const; + bool isInitDone() const; + int getLastSpindleSpeed() const; + std::vector getLastTouchoffPosition() const; + const std::vector& getLimits() const; + + static const QString textForErrno(int errorNumber); + static size_t axisToIndex(Axis axis); + +public slots: + void setSpindleSpeed(int speed); + void send(const QByteArray& cmd); + void stop(); + void home(VhfMill::Axis axis); + void jog(VhfMill::Axis axis, int jogDirection); + void setOutput(int output, bool state); + void setTool(int tool); + void reqTool(); + void reqOutputs(); + void reqPosition(); + void reqSensors(); + void reqSpindleSpeed(); + void enablePosUpdates(bool enable); + void enableStateUpdates(bool enable); + void touchOff(VhfMill::Axis axis, int offset); + void touchOffAbsolute(std::vector touchoffPos); + void setjogStep(int step); + void init(); + +signals: + void raiseError(int errorNumber); + void positionUpdate(std::vector position); + void isHomed(VhfMill::Axis axis); + void gotLine(QByteArray string); + void gotToolNum(int tool); + void gotOutputs(uint8_t outputs); + void gotSindleSpeed(int speed); + void gotProbeState(bool state); + void gotPressureState(bool state); + void touchOffChanged(); + void initDone(); + void toolChangeDone(); + void touchoffChanged(std::vector touchoffPos); + void jogStepChanged(int step); +}; + +#endif // VHFMILL_H diff --git a/vhfmillthread.cpp b/vhfmillthread.cpp new file mode 100644 index 0000000..4d1722c --- /dev/null +++ b/vhfmillthread.cpp @@ -0,0 +1,53 @@ +#include "vhfmillthread.h" + +VhfMillThread::VhfMillThread(bool tcpI, int portI, const QString& hostI, const QString& serialPortI, QObject *parent): + tcp(tcpI), + port(portI), + serialPort(serialPortI), + host(hostI), + QThread(parent) +{ +} + +void VhfMillThread::run() +{ + if(tcp) + { + QTcpSocket* microSocket = new QTcpSocket; + microSocket->connectToHost(host, port, QIODevice::ReadWrite); + if(!microSocket->waitForConnected(1000)) + { + ret = -2; + ready(); + return; + } + masterIODevice = microSocket; + } + else + { + QSerialPort* microPort = new QSerialPort; + microPort->setPortName(serialPort); + microPort->setBaudRate(115200); + + if(!microPort->open(QIODevice::ReadWrite)) + { + qDebug()<<"could not open serial port "< +#include +#include + +#include "vhfmill.h" + +class VhfMillThread : public QThread +{ + Q_OBJECT +private: + const bool tcp; + const int port; + const QString serialPort; + const QString host; + +public: + VhfMill* mill = nullptr; + QIODevice* masterIODevice = nullptr; + int ret = -1; + +public: + VhfMillThread(bool tcpI, int portI, const QString& hostI, const QString& serialPortI, QObject *parent = nullptr); + ~VhfMillThread(); + +signals: + void ready(); + +protected: + virtual void run() override; +}; + + +#endif // VHFMILLTHREAD_H