mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-05-06 23:29:00 +08:00
Merge branch 'master' into cloud-cluster-discovery-mvp
This commit is contained in:
commit
5bb56e06a4
12
.gitlab-ci.yml
Normal file
12
.gitlab-ci.yml
Normal file
@ -0,0 +1,12 @@
|
||||
image: registry.gitlab.com/ultimaker/cura/cura-build-environment:centos7
|
||||
|
||||
stages:
|
||||
- build
|
||||
|
||||
build-and-test:
|
||||
stage: build
|
||||
script:
|
||||
- docker/build.sh
|
||||
artifacts:
|
||||
paths:
|
||||
- build
|
@ -1,11 +1,10 @@
|
||||
project(cura NONE)
|
||||
cmake_minimum_required(VERSION 2.8.12)
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/
|
||||
${CMAKE_MODULE_PATH})
|
||||
project(cura)
|
||||
cmake_minimum_required(VERSION 3.6)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
|
||||
|
||||
set(URANIUM_DIR "${CMAKE_SOURCE_DIR}/../Uranium" CACHE DIRECTORY "The location of the Uranium repository")
|
||||
set(URANIUM_SCRIPTS_DIR "${URANIUM_DIR}/scripts" CACHE DIRECTORY "The location of the scripts directory of the Uranium repository")
|
||||
|
||||
@ -28,6 +27,26 @@ set(CURA_CLOUD_API_VERSION "" CACHE STRING "Alternative Cura cloud API version")
|
||||
configure_file(${CMAKE_SOURCE_DIR}/cura.desktop.in ${CMAKE_BINARY_DIR}/cura.desktop @ONLY)
|
||||
configure_file(cura/CuraVersion.py.in CuraVersion.py @ONLY)
|
||||
|
||||
|
||||
# FIXME: Remove the code for CMake <3.12 once we have switched over completely.
|
||||
# FindPython3 is a new module since CMake 3.12. It deprecates FindPythonInterp and FindPythonLibs. The FindPython3
|
||||
# module is copied from the CMake repository here so in CMake <3.12 we can still use it.
|
||||
if(${CMAKE_VERSION} VERSION_LESS 3.12)
|
||||
# Use FindPythonInterp and FindPythonLibs for CMake <3.12
|
||||
find_package(PythonInterp 3 REQUIRED)
|
||||
|
||||
set(Python3_EXECUTABLE ${PYTHON_EXECUTABLE})
|
||||
|
||||
set(Python3_VERSION ${PYTHON_VERSION_STRING})
|
||||
set(Python3_VERSION_MAJOR ${PYTHON_VERSION_MAJOR})
|
||||
set(Python3_VERSION_MINOR ${PYTHON_VERSION_MINOR})
|
||||
set(Python3_VERSION_PATCH ${PYTHON_VERSION_PATCH})
|
||||
else()
|
||||
# Use FindPython3 for CMake >=3.12
|
||||
find_package(Python3 REQUIRED COMPONENTS Interpreter Development)
|
||||
endif()
|
||||
|
||||
|
||||
if(NOT ${URANIUM_DIR} STREQUAL "")
|
||||
set(CMAKE_MODULE_PATH "${URANIUM_DIR}/cmake")
|
||||
endif()
|
||||
@ -40,12 +59,12 @@ if(NOT ${URANIUM_SCRIPTS_DIR} STREQUAL "")
|
||||
CREATE_TRANSLATION_TARGETS()
|
||||
endif()
|
||||
|
||||
find_package(PythonInterp 3.5.0 REQUIRED)
|
||||
|
||||
install(DIRECTORY resources
|
||||
DESTINATION ${CMAKE_INSTALL_DATADIR}/cura)
|
||||
install(DIRECTORY plugins
|
||||
DESTINATION lib${LIB_SUFFIX}/cura)
|
||||
|
||||
if(NOT APPLE AND NOT WIN32)
|
||||
install(FILES cura_app.py
|
||||
DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
@ -53,16 +72,16 @@ if(NOT APPLE AND NOT WIN32)
|
||||
RENAME cura)
|
||||
if(EXISTS /etc/debian_version)
|
||||
install(DIRECTORY cura
|
||||
DESTINATION lib${LIB_SUFFIX}/python${PYTHON_VERSION_MAJOR}/dist-packages
|
||||
DESTINATION lib${LIB_SUFFIX}/python${Python3_VERSION_MAJOR}/dist-packages
|
||||
FILES_MATCHING PATTERN *.py)
|
||||
install(FILES ${CMAKE_BINARY_DIR}/CuraVersion.py
|
||||
DESTINATION lib${LIB_SUFFIX}/python${PYTHON_VERSION_MAJOR}/dist-packages/cura)
|
||||
DESTINATION lib${LIB_SUFFIX}/python${Python3_VERSION_MAJOR}/dist-packages/cura)
|
||||
else()
|
||||
install(DIRECTORY cura
|
||||
DESTINATION lib${LIB_SUFFIX}/python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}/site-packages
|
||||
DESTINATION lib${LIB_SUFFIX}/python${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR}/site-packages
|
||||
FILES_MATCHING PATTERN *.py)
|
||||
install(FILES ${CMAKE_BINARY_DIR}/CuraVersion.py
|
||||
DESTINATION lib${LIB_SUFFIX}/python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}/site-packages/cura)
|
||||
DESTINATION lib${LIB_SUFFIX}/python${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR}/site-packages/cura)
|
||||
endif()
|
||||
install(FILES ${CMAKE_BINARY_DIR}/cura.desktop
|
||||
DESTINATION ${CMAKE_INSTALL_DATADIR}/applications)
|
||||
@ -78,8 +97,8 @@ else()
|
||||
DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
|
||||
install(DIRECTORY cura
|
||||
DESTINATION lib${LIB_SUFFIX}/python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}/site-packages
|
||||
DESTINATION lib${LIB_SUFFIX}/python${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR}/site-packages
|
||||
FILES_MATCHING PATTERN *.py)
|
||||
install(FILES ${CMAKE_BINARY_DIR}/CuraVersion.py
|
||||
DESTINATION lib${LIB_SUFFIX}/python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}/site-packages/cura)
|
||||
DESTINATION lib${LIB_SUFFIX}/python${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR}/site-packages/cura)
|
||||
endif()
|
||||
|
@ -1,10 +1,21 @@
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
enable_testing()
|
||||
include(CTest)
|
||||
include(CMakeParseArguments)
|
||||
|
||||
find_package(PythonInterp 3.5.0 REQUIRED)
|
||||
# FIXME: Remove the code for CMake <3.12 once we have switched over completely.
|
||||
# FindPython3 is a new module since CMake 3.12. It deprecates FindPythonInterp and FindPythonLibs. The FindPython3
|
||||
# module is copied from the CMake repository here so in CMake <3.12 we can still use it.
|
||||
if(${CMAKE_VERSION} VERSION_LESS 3.12)
|
||||
# Use FindPythonInterp and FindPythonLibs for CMake <3.12
|
||||
find_package(PythonInterp 3 REQUIRED)
|
||||
|
||||
set(Python3_EXECUTABLE ${PYTHON_EXECUTABLE})
|
||||
else()
|
||||
# Use FindPython3 for CMake >=3.12
|
||||
find_package(Python3 REQUIRED COMPONENTS Interpreter Development)
|
||||
endif()
|
||||
|
||||
add_custom_target(test-verbose COMMAND ${CMAKE_CTEST_COMMAND} --verbose)
|
||||
|
||||
@ -36,7 +47,7 @@ function(cura_add_test)
|
||||
if (NOT ${test_exists})
|
||||
add_test(
|
||||
NAME ${_NAME}
|
||||
COMMAND ${PYTHON_EXECUTABLE} -m pytest --verbose --full-trace --capture=no --no-print-log --junitxml=${CMAKE_BINARY_DIR}/junit-${_NAME}.xml ${_DIRECTORY}
|
||||
COMMAND ${Python3_EXECUTABLE} -m pytest --verbose --full-trace --capture=no --no-print-log --junitxml=${CMAKE_BINARY_DIR}/junit-${_NAME}.xml ${_DIRECTORY}
|
||||
)
|
||||
set_tests_properties(${_NAME} PROPERTIES ENVIRONMENT LANG=C)
|
||||
set_tests_properties(${_NAME} PROPERTIES ENVIRONMENT "PYTHONPATH=${_PYTHONPATH}")
|
||||
@ -59,13 +70,13 @@ endforeach()
|
||||
#Add code style test.
|
||||
add_test(
|
||||
NAME "code-style"
|
||||
COMMAND ${PYTHON_EXECUTABLE} run_mypy.py
|
||||
COMMAND ${Python3_EXECUTABLE} run_mypy.py
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
)
|
||||
|
||||
#Add test for whether the shortcut alt-keys are unique in every translation.
|
||||
add_test(
|
||||
NAME "shortcut-keys"
|
||||
COMMAND ${PYTHON_EXECUTABLE} scripts/check_shortcut_keys.py
|
||||
COMMAND ${Python3_EXECUTABLE} scripts/check_shortcut_keys.py
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
)
|
||||
)
|
||||
|
@ -50,6 +50,7 @@ class AuthorizationHelpers:
|
||||
# \param refresh_token:
|
||||
# \return An AuthenticationResponse object.
|
||||
def getAccessTokenUsingRefreshToken(self, refresh_token: str) -> "AuthenticationResponse":
|
||||
Logger.log("d", "Refreshing the access token.")
|
||||
data = {
|
||||
"client_id": self._settings.CLIENT_ID if self._settings.CLIENT_ID is not None else "",
|
||||
"redirect_uri": self._settings.CALLBACK_URL if self._settings.CALLBACK_URL is not None else "",
|
||||
|
@ -68,6 +68,7 @@ class AuthorizationService:
|
||||
self._user_profile = self._parseJWT()
|
||||
except requests.exceptions.ConnectionError:
|
||||
# Unable to get connection, can't login.
|
||||
Logger.logException("w", "Unable to validate user data with the remote server.")
|
||||
return None
|
||||
|
||||
if not self._user_profile and self._auth_data:
|
||||
@ -83,6 +84,7 @@ class AuthorizationService:
|
||||
def _parseJWT(self) -> Optional["UserProfile"]:
|
||||
if not self._auth_data or self._auth_data.access_token is None:
|
||||
# If no auth data exists, we should always log in again.
|
||||
Logger.log("d", "There was no auth data or access token")
|
||||
return None
|
||||
user_data = self._auth_helpers.parseJWT(self._auth_data.access_token)
|
||||
if user_data:
|
||||
@ -90,9 +92,11 @@ class AuthorizationService:
|
||||
return user_data
|
||||
# The JWT was expired or invalid and we should request a new one.
|
||||
if self._auth_data.refresh_token is None:
|
||||
Logger.log("w", "There was no refresh token in the auth data.")
|
||||
return None
|
||||
self._auth_data = self._auth_helpers.getAccessTokenUsingRefreshToken(self._auth_data.refresh_token)
|
||||
if not self._auth_data or self._auth_data.access_token is None:
|
||||
Logger.log("w", "Unable to use the refresh token to get a new access token.")
|
||||
# The token could not be refreshed using the refresh token. We should login again.
|
||||
return None
|
||||
|
||||
|
43
docker/build.sh
Executable file
43
docker/build.sh
Executable file
@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Abort at the first error.
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
PROJECT_DIR="$( cd "${SCRIPT_DIR}/.." && pwd )"
|
||||
|
||||
# Make sure that environment variables are set properly
|
||||
source /opt/rh/devtoolset-7/enable
|
||||
export PATH="${CURA_BUILD_ENV_PATH}/bin:${PATH}"
|
||||
export PKG_CONFIG_PATH="${CURA_BUILD_ENV_PATH}/lib/pkgconfig:${PKG_CONFIG_PATH}"
|
||||
|
||||
cd "${PROJECT_DIR}"
|
||||
|
||||
#
|
||||
# Clone Uranium and set PYTHONPATH first
|
||||
#
|
||||
|
||||
# Check the branch to use:
|
||||
# 1. Use the Uranium branch with the branch same if it exists.
|
||||
# 2. Otherwise, use the default branch name "master"
|
||||
URANIUM_BRANCH="${CI_COMMIT_REF_NAME:-master}"
|
||||
output="$(git ls-remote --heads https://github.com/Ultimaker/Uranium.git "${URANIUM_BRANCH}")"
|
||||
if [ -z "${output}" ]; then
|
||||
echo "Could not find Uranium banch ${URANIUM_BRANCH}, fallback to use master."
|
||||
URANIUM_BRANCH="master"
|
||||
fi
|
||||
|
||||
echo "Using Uranium branch ${URANIUM_BRANCH} ..."
|
||||
git clone --depth=1 -b "${URANIUM_BRANCH}" https://github.com/Ultimaker/Uranium.git "${PROJECT_DIR}"/Uranium
|
||||
export PYTHONPATH="${PROJECT_DIR}/Uranium:.:${PYTHONPATH}"
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
cmake3 \
|
||||
-DCMAKE_BUILD_TYPE=Debug \
|
||||
-DCMAKE_PREFIX_PATH="${CURA_BUILD_ENV_PATH}" \
|
||||
-DURANIUM_DIR="${PROJECT_DIR}/Uranium" \
|
||||
-DBUILD_TESTS=ON \
|
||||
..
|
||||
make
|
||||
ctest3 --verbose --output-on-failure -T Test
|
@ -1,31 +1,33 @@
|
||||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
|
||||
import math
|
||||
import re
|
||||
from typing import Dict, List, NamedTuple, Optional, Union
|
||||
|
||||
import numpy
|
||||
|
||||
from UM.Backend import Backend
|
||||
from UM.Job import Job
|
||||
from UM.Logger import Logger
|
||||
from UM.Math.Vector import Vector
|
||||
from UM.Message import Message
|
||||
from cura.Scene.CuraSceneNode import CuraSceneNode
|
||||
from UM.i18n import i18nCatalog
|
||||
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
from cura.CuraApplication import CuraApplication
|
||||
from cura.LayerDataBuilder import LayerDataBuilder
|
||||
from cura.LayerDataDecorator import LayerDataDecorator
|
||||
from cura.LayerPolygon import LayerPolygon
|
||||
from cura.Scene.CuraSceneNode import CuraSceneNode
|
||||
from cura.Scene.GCodeListDecorator import GCodeListDecorator
|
||||
from cura.Settings.ExtruderManager import ExtruderManager
|
||||
|
||||
import numpy
|
||||
import math
|
||||
import re
|
||||
from typing import Dict, List, NamedTuple, Optional, Union
|
||||
catalog = i18nCatalog("cura")
|
||||
|
||||
PositionOptional = NamedTuple("Position", [("x", Optional[float]), ("y", Optional[float]), ("z", Optional[float]), ("f", Optional[float]), ("e", Optional[float])])
|
||||
Position = NamedTuple("Position", [("x", float), ("y", float), ("z", float), ("f", float), ("e", List[float])])
|
||||
|
||||
|
||||
## This parser is intended to interpret the common firmware codes among all the
|
||||
# different flavors
|
||||
class FlavorParser:
|
||||
@ -33,7 +35,7 @@ class FlavorParser:
|
||||
def __init__(self) -> None:
|
||||
CuraApplication.getInstance().hideMessageSignal.connect(self._onHideMessage)
|
||||
self._cancelled = False
|
||||
self._message = None
|
||||
self._message = None # type: Optional[Message]
|
||||
self._layer_number = 0
|
||||
self._extruder_number = 0
|
||||
self._clearValues()
|
||||
@ -425,7 +427,8 @@ class FlavorParser:
|
||||
|
||||
if line.startswith("M"):
|
||||
M = self._getInt(line, "M")
|
||||
self.processMCode(M, line, current_position, current_path)
|
||||
if M is not None:
|
||||
self.processMCode(M, line, current_position, current_path)
|
||||
|
||||
# "Flush" leftovers. Last layer paths are still stored
|
||||
if len(current_path) > 1:
|
||||
@ -463,7 +466,7 @@ class FlavorParser:
|
||||
Logger.log("w", "File doesn't contain any valid layers")
|
||||
|
||||
settings = CuraApplication.getInstance().getGlobalContainerStack()
|
||||
if not settings.getProperty("machine_center_is_zero", "value"):
|
||||
if settings is not None and not settings.getProperty("machine_center_is_zero", "value"):
|
||||
machine_width = settings.getProperty("machine_width", "value")
|
||||
machine_depth = settings.getProperty("machine_depth", "value")
|
||||
scene_node.setPosition(Vector(-machine_width / 2, 0, machine_depth / 2))
|
||||
|
@ -162,7 +162,7 @@ class PostProcessingPlugin(QObject, Extension):
|
||||
loaded_script = importlib.util.module_from_spec(spec)
|
||||
if spec.loader is None:
|
||||
continue
|
||||
spec.loader.exec_module(loaded_script)
|
||||
spec.loader.exec_module(loaded_script) # type: ignore
|
||||
sys.modules[script_name] = loaded_script #TODO: This could be a security risk. Overwrite any module with a user-provided name?
|
||||
|
||||
loaded_class = getattr(loaded_script, script_name)
|
||||
|
@ -38,5 +38,5 @@ class UFPReader(MeshReader):
|
||||
|
||||
# Open the GCodeReader to parse the data
|
||||
gcode_reader = PluginRegistry.getInstance().getPluginObject("GCodeReader") # type: ignore
|
||||
gcode_reader.preReadFromStream(gcode_stream)
|
||||
return gcode_reader.readFromStream(gcode_stream)
|
||||
gcode_reader.preReadFromStream(gcode_stream) # type: ignore
|
||||
return gcode_reader.readFromStream(gcode_stream) # type: ignore
|
||||
|
@ -93,10 +93,11 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice):
|
||||
|
||||
# We use the Cura Connect monitor tab to get most functionality right away.
|
||||
if PluginRegistry.getInstance() is not None:
|
||||
self._monitor_view_qml_path = os.path.join(
|
||||
PluginRegistry.getInstance().getPluginPath("UM3NetworkPrinting"),
|
||||
"resources", "qml", "MonitorStage.qml"
|
||||
)
|
||||
plugin_path = PluginRegistry.getInstance().getPluginPath("UM3NetworkPrinting")
|
||||
if plugin_path is None:
|
||||
Logger.log("e", "Cloud not find plugin path for plugin UM3NetworkPrnting")
|
||||
raise RuntimeError("Cloud not find plugin path for plugin UM3NetworkPrnting")
|
||||
self._monitor_view_qml_path = os.path.join(plugin_path, "resources", "qml", "MonitorStage.qml")
|
||||
|
||||
# Trigger the printersChanged signal when the private signal is triggered.
|
||||
self.printersChanged.connect(self._clusterPrintersChanged)
|
||||
@ -158,7 +159,7 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice):
|
||||
# the host name should then be "ultimakersystem-aabbccdd0011"
|
||||
if network_key.startswith(self.clusterData.host_name):
|
||||
return True
|
||||
|
||||
|
||||
# However, for manually added printers, the local IP address is used in lieu of a proper
|
||||
# network key, so check for that as well
|
||||
if self.clusterData.host_internal_ip is not None and network_key.find(self.clusterData.host_internal_ip):
|
||||
|
@ -32,8 +32,8 @@ class CloudOutputDeviceManager:
|
||||
# The translation catalog for this device.
|
||||
I18N_CATALOG = i18nCatalog("cura")
|
||||
|
||||
addedCloudCluster = Signal(CloudOutputDevice)
|
||||
removedCloudCluster = Signal(CloudOutputDevice)
|
||||
addedCloudCluster = Signal()
|
||||
removedCloudCluster = Signal()
|
||||
|
||||
def __init__(self) -> None:
|
||||
# Persistent dict containing the remote clusters for the authenticated user.
|
||||
|
@ -66,10 +66,11 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
|
||||
self._received_print_jobs = False # type: bool
|
||||
|
||||
if PluginRegistry.getInstance() is not None:
|
||||
self._monitor_view_qml_path = os.path.join(
|
||||
PluginRegistry.getInstance().getPluginPath("UM3NetworkPrinting"),
|
||||
"resources", "qml", "MonitorStage.qml"
|
||||
)
|
||||
plugin_path = PluginRegistry.getInstance().getPluginPath("UM3NetworkPrinting")
|
||||
if plugin_path is None:
|
||||
Logger.log("e", "Cloud not find plugin path for plugin UM3NetworkPrnting")
|
||||
raise RuntimeError("Cloud not find plugin path for plugin UM3NetworkPrnting")
|
||||
self._monitor_view_qml_path = os.path.join(plugin_path, "resources", "qml", "MonitorStage.qml")
|
||||
|
||||
# Trigger the printersChanged signal when the private signal is triggered
|
||||
self.printersChanged.connect(self._clusterPrintersChanged)
|
||||
|
@ -1,4 +1,4 @@
|
||||
from unittest.mock import MagicMock
|
||||
from unittest.mock import MagicMock, PropertyMock
|
||||
|
||||
import pytest
|
||||
|
||||
@ -12,6 +12,8 @@ def discovered_printer_model(application) -> DiscoveredPrintersModel:
|
||||
|
||||
def test_discoveredPrinters(discovered_printer_model):
|
||||
mocked_device = MagicMock()
|
||||
cluster_size = PropertyMock(return_value = 1)
|
||||
type(mocked_device).clusterSize = cluster_size
|
||||
|
||||
mocked_callback = MagicMock()
|
||||
discovered_printer_model.addDiscoveredPrinter("ip", "key", "name", mocked_callback, "machine_type", mocked_device)
|
||||
|
Loading…
x
Reference in New Issue
Block a user