commit f91c9f1a6ff48504f785e072df0f1d9c8fafd1a5 Author: uvos Date: Sun Jan 29 18:45:42 2023 +0100 inital commit 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