diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 0ab2ddcff..3ad5dc4ec 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -20,7 +20,7 @@ jobs: - name: Install dependencies run: | sudo apt-get update - sudo apt-get install -y gcc g++ cmake golang uuid-dev + sudo apt-get install -y gcc g++ cmake golang uuid-dev libssl-dev - name: Run pre-build script run: | diff --git a/ACT/LibMC.xml b/ACT/LibMC.xml index 4096d0a73..60794069a 100644 --- a/ACT/LibMC.xml +++ b/ACT/LibMC.xml @@ -705,6 +705,12 @@ + + + + + + @@ -838,6 +844,11 @@ + + + + + diff --git a/ACT/LibMCEnv.xml b/ACT/LibMCEnv.xml index 143e3f965..7489871d4 100644 --- a/ACT/LibMCEnv.xml +++ b/ACT/LibMCEnv.xml @@ -290,11 +290,10 @@ + + - - - @@ -793,9 +792,25 @@ + + + + + + + + + + + + + + + + @@ -4819,10 +4834,6 @@ - - @@ -4831,22 +4842,14 @@ - - + + + + @@ -4858,7 +4861,6 @@ - @@ -4866,7 +4868,6 @@ - @@ -4913,13 +4914,21 @@ - + - + + + + + + + + + @@ -4962,6 +4971,14 @@ + + + + + + + + diff --git a/Artifacts/build_client_clean.sh b/Artifacts/build_client_clean.sh new file mode 100755 index 000000000..50ac16c44 --- /dev/null +++ b/Artifacts/build_client_clean.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +basepath=$(cd "$(dirname "$0")" && pwd) +echo "$basepath" +cd "$basepath" + +mkdir -p ../build_client/Client/public +mkdir -p ../build_client/Client/src/common +mkdir -p ../build_client/Client/src/modules +mkdir -p ../build_client/Client/src/dialogs +mkdir -p ../build_client/Client/dist + +cp ../Client/public/* ../build_client/Client/public/ 2>/dev/null || true +cp ../Client/src/*.* ../build_client/Client/src/ 2>/dev/null || true +cp ../Client/src/common/* ../build_client/Client/src/common/ 2>/dev/null || true +cp ../Client/src/modules/* ../build_client/Client/src/modules/ 2>/dev/null || true +cp ../Client/src/dialogs/* ../build_client/Client/src/dialogs/ 2>/dev/null || true +cp ../Client/*.js ../build_client/Client/ 2>/dev/null || true +cp ../Client/*.json ../build_client/Client/ 2>/dev/null || true + +cd .. +git log -n 1 --format="%H" -- "Client" > "build_client/Client/dist/_githash_client.txt" +git log -n 1 --format="%H" -- "Client" > "Artifacts/clientdist/_githash_client.txt" +CLIENTDIRHASH=$(cat "build_client/Client/dist/_githash_client.txt") + +cat > build_client/Client/src/AMCGitHash.js < +#include +#include +#include +#include +#include +#include +#include +#include + +#define CPPHTTPLIB_OPENSSL_SUPPORT + +#include "common_utils.hpp" +#include "common_exportstream_native.hpp" +#include "common_importstream_native.hpp" + +#include "Libraries/cpp-httplib/httplib.h" +#include "Libraries/PugiXML/pugixml.hpp" +#include "Libraries/RapidJSON/document.h" +#include "Libraries/PicoSHA2/picosha2.h" // SHA-256 (header-only) + + +struct ParsedURL { + std::string scheme; // "http" or "https" + std::string host; + int port = -1; + std::string path; // starts with '/' +}; + +// Very small URL parser for forms like: http(s)://host[:port][/path...] +static bool parse_url(const std::string& url, ParsedURL& out) { + auto pos_scheme = url.find("://"); + if (pos_scheme == std::string::npos) return false; + out.scheme = url.substr(0, pos_scheme); + auto rest = url.substr(pos_scheme + 3); + + auto pos_slash = rest.find('/'); + std::string hostport = (pos_slash == std::string::npos) ? rest : rest.substr(0, pos_slash); + out.path = (pos_slash == std::string::npos) ? "/" : rest.substr(pos_slash); + + auto pos_colon = hostport.find(':'); + if (pos_colon == std::string::npos) { + out.host = hostport; + out.port = -1; + } else { + out.host = hostport.substr(0, pos_colon); + out.port = std::stoi(hostport.substr(pos_colon + 1)); + } + + if (out.scheme != "http" && out.scheme != "https") + return false; + if (out.path.empty()) out.path = "/"; + return !out.host.empty(); +} + +static std::string join_url_path(const std::string& base_path, const std::string& segment) { + if (base_path.empty() || base_path.back() != '/') { + if (!segment.empty() && segment.front() == '/') return base_path + segment; + return base_path + "/" + segment; + } else { + if (!segment.empty() && segment.front() == '/') return base_path + segment.substr(1); + return base_path + segment; + } +} + +template void download_with_client(ClientT& client, const std::string& path, const std::string& out_path) { + + client.set_follow_location (true); + + AMCCommon::CExportStream_Native ofs_stream (out_path); + + size_t nReceivedSize = 0; + + auto res = client.Get(path.c_str(), [&ofs_stream, &nReceivedSize](const char* data, size_t len) { + + size_t nOldReceivedMB = nReceivedSize / (1024 * 1024); + + ofs_stream.writeBuffer(data, len); + + nReceivedSize += len; + + size_t nNewReceivedMB = nReceivedSize / (1024 * 1024); + + if (nNewReceivedMB != nOldReceivedMB) { + std::cout << "Received " << nNewReceivedMB << " MB...\n"; + } + + + return true; + }); + + if (!res) { + throw std::runtime_error ("HTTP request failed (network/connection error)."); + } + + if (res->status != 200) { + throw std::runtime_error("HTTP error status: " + std::to_string(res->status)); + } + +} + + +// Download to file using cpp-httplib. +void download_to_file(const std::string& full_url, const std::string& out_path) +{ + ParsedURL u; + if (!parse_url(full_url, u)) + { + throw std::runtime_error("Invalid URL: " + full_url); + } + + std::vector receivedBuffer; + + if (u.scheme == "http") { + httplib::Client client(u.host.c_str(), (u.port > 0 ? u.port : 80)); + + download_with_client(client, u.path, out_path); + + } else { + + httplib::SSLClient sslclient(u.host.c_str(), (u.port > 0 ? u.port : 443)); + + sslclient.enable_server_certificate_verification(true); + download_with_client(sslclient, u.path, out_path); + + } + + +} + + +// ---------------------- Main ---------------------- +int main(int argc, char* argv[]) { + + try { + + if (argc < 2) { + std::cerr << "Please start with: packageManager ....\n"; + std::cerr << "for example: packageManager download \n"; + return 1; + } + + std::string command = argv[1]; + + if (command == "download") { + if (argc < 6) { + std::cerr << "Please start with: packageManager download \n"; + return 1; + } + + const std::string localFileName = argv[2]; + const std::string downloadURLBase = argv[3]; + const std::string gitHash = argv[4]; + const std::string shaCheckSum = argv[5]; + + std::cout << "---------------------------------------------------------------------------------\n"; + std::cout << "-- AMCF Package Manager --\n"; + std::cout << "---------------------------------------------------------------------------------\n"; + std::cout << "Repository URL: " << downloadURLBase << "\n"; + std::cout << "Local Filename: " << localFileName << "\n"; + std::cout << "GIT Hash: " << gitHash << "\n"; + std::cout << "SHA 256 Checksum: " << shaCheckSum << "\n"; + std::cout << "---------------------------------------------------------------------------------\n"; + + std::cout << "Verifying " << localFileName << "...\n"; + + std::string calculatedChecksum; + + if (!AMCCommon::CUtils::fileOrPathExistsOnDisk (localFileName)) { + std::cout << "Local package does not exist...\n"; + } + else { + try { + std::vector fileBytes; + AMCCommon::CImportStream_Native ifs_stream(localFileName); + ifs_stream.readIntoMemory(fileBytes); + + calculatedChecksum = picosha2::hash256_hex_string(fileBytes); + std::cout << "Calculated Checksum: " << calculatedChecksum << "\n"; + if (calculatedChecksum != shaCheckSum) { + std::cout << "Checksum mismatch!\n"; + } + } + catch (const std::exception& ex) { + std::cerr << "Error reading file: " << ex.what() << "\n"; + return 1; + } + } + + if (calculatedChecksum != shaCheckSum) { + std::cout << "Creating " << localFileName << "...\n"; + + const std::string fileSegment = "amcf_" + gitHash + ".zip"; + + ParsedURL baseParsed; + if (!parse_url(downloadURLBase, baseParsed)) { + std::cerr << "Invalid repository URL: " << downloadURLBase << "\n"; + return 1; + } + std::string finalPath = join_url_path(baseParsed.path, fileSegment); + std::string finalURL = baseParsed.scheme + "://" + baseParsed.host; + if (baseParsed.port > 0) finalURL += ":" + std::to_string(baseParsed.port); + finalURL += finalPath; + + std::cout << "Downloading package from " << finalURL << "\n"; + std::string err; + download_to_file(finalURL, localFileName); + + try { + std::vector fileBytes; + AMCCommon::CImportStream_Native ifs_stream(localFileName); + ifs_stream.readIntoMemory(fileBytes); + + auto finalChecksum = picosha2::hash256_hex_string(fileBytes); + std::cout << "Final Checksum: " << finalChecksum << "\n"; + if (finalChecksum != shaCheckSum) { + std::cerr << "Checksum mismatch!\n"; + return 1; + } + } + catch (const std::exception& ex) { + std::cerr << "Error reading downloaded file: " << ex.what() << "\n"; + return 1; + } + } + + std::cout << "done..\n"; + return 0; + + } + else { + std::cerr << "Unknown command. Valid commands are download.\n"; + return 1; + } + } + catch (const std::exception& ex) { + std::cerr << "Fatal error: " << ex.what() << "\n"; + return 1; + } + +} diff --git a/BuildScripts/packageManager.go b/BuildScripts/packageManager.go deleted file mode 100644 index e45c43841..000000000 --- a/BuildScripts/packageManager.go +++ /dev/null @@ -1,123 +0,0 @@ -package main - -import ( - "log" - "net/http" - "io/ioutil" - "encoding/hex" - "io" - "crypto/sha256" - "os" -) - - -func main() { - - argsWithProg := os.Args; - - - if (len (argsWithProg) < 2) { - log.Fatal ("Please start with packageManager ...."); - } - - commandToUse := argsWithProg[1]; - - if (commandToUse == "download") { - - - if (len (argsWithProg) < 6) { - log.Fatal ("Please start with packageManager download "); - } - - downloadURLBase := argsWithProg[3]; - localFileName := argsWithProg[2]; - gitHash := argsWithProg[4]; - shaCheckSum := argsWithProg[5]; - - log.Println ("---------------------------------------------------------------------------------"); - log.Println ("-- AMCF Package Manager --"); - log.Println ("---------------------------------------------------------------------------------"); - log.Println ("Repository URL:" + downloadURLBase); - log.Println ("Local Filename:" + localFileName); - log.Println ("GIT Hash:" + gitHash); - log.Println ("SHA 256 Checksum:" + shaCheckSum); - log.Println ("---------------------------------------------------------------------------------"); - - log.Println ("Verifying " + localFileName + "..."); - - calculatedCheckSum := ""; - _, err := os.Stat(localFileName); - if err != nil { - if !os.IsNotExist(err) { - log.Fatal(err); - } - - log.Println ("Local package does not exist..."); - - } else { - - hasher := sha256.New() - fileContent, err := ioutil.ReadFile(localFileName) - hasher.Write(fileContent) - - if err != nil { - log.Fatal(err) - } - calculatedCheckSum = hex.EncodeToString(hasher.Sum(nil)); - log.Println ("Calculated Checksum: " + calculatedCheckSum); - - if (calculatedCheckSum != shaCheckSum) { - log.Println ("Checksum mismatch!"); - } - - } - - - if (calculatedCheckSum != shaCheckSum) { - - log.Println ("Creating " + localFileName + "..."); - targetFile, err := os.Create(localFileName) - if err != nil { - log.Fatal(err) - } - defer targetFile.Close() - - downloadURL := downloadURLBase + "/amcf_" + gitHash + ".zip"; - log.Println ("Downloading package from " + downloadURL); - - response, err := http.Get(downloadURL); - defer response.Body.Close() - - _, err = io.Copy(targetFile, response.Body) - if err != nil { - log.Fatal(err) - } - - hasher := sha256.New() - fileContent, err := ioutil.ReadFile(localFileName) - hasher.Write(fileContent) - - if err != nil { - log.Fatal(err) - } - finalCheckSum := hex.EncodeToString(hasher.Sum(nil)); - log.Println ("Final Checksum: " + finalCheckSum); - - - if (finalCheckSum != shaCheckSum) { - log.Fatal ("Checksum mismatch!"); - } - - } - - - log.Println ("done.."); - - - } else { - - log.Fatal ("Unknown command. Valid commands are download and extract."); - } - - -} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 6780bc676..06fdfd12a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -901,3 +901,78 @@ if(UNIX) endif() + + +#[[++ + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +++ packageManager Target +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +]] + + +if(UNIX AND NOT APPLE) + # If you ever want to force static later, set this to TRUE before find_package. + # set(OPENSSL_USE_STATIC_LIBS TRUE) + find_package(OpenSSL REQUIRED) # provides OpenSSL::SSL and OpenSSL::Crypto +endif() + +file(GLOB PACKAGE_MANAGER_SRC + ${CMAKE_CURRENT_SOURCE_DIR}/BuildScripts/packageManager.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Implementation/Common/common_utils.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Implementation/Common/common_importstream_native.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Implementation/Common/common_exportstream_native.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Libraries/crossguid/guid.cpp +) + +add_executable(package_manager ${PACKAGE_MANAGER_SRC} ${LIBMC_SRC_DEP_PUGIXML}) + +target_include_directories(package_manager PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/Implementation/Common) +target_include_directories(package_manager PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/Libraries/PugiXML) +target_include_directories(package_manager PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/Libraries) +target_include_directories(package_manager PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/Libraries/PicoSHA2) +if(MSVC) +target_include_directories(package_manager PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/Libraries/libressl) +endif() +target_include_directories(package_manager PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/) + +set_target_properties(package_manager + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/DevPackage/Framework" + RUNTIME_OUTPUT_DIRECTORY_DEBUG "${PROJECT_BINARY_DIR}/DevPackage/Framework" + RUNTIME_OUTPUT_DIRECTORY_RELEASE "${PROJECT_BINARY_DIR}/DevPackage/Framework" + LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/DevPackage/Framework" + + OUTPUT_NAME "package_manager" + + VS_DEBUGGER_WORKING_DIRECTORY "${PROJECT_BINARY_DIR}/Output" +) + + +target_compile_options(package_manager PRIVATE "-D__GITHASH=${GLOBALGITHASH}") +target_compile_options(package_manager PRIVATE "-DUNICODE") +target_compile_options(package_manager PRIVATE "-D_UNICODE") + + +if(MSVC) + target_link_libraries(package_manager shlwapi.lib) + target_link_libraries(package_manager ws2_32.lib) + target_link_libraries(package_manager bcrypt.lib) + + target_link_libraries(package_manager ${CMAKE_CURRENT_SOURCE_DIR}/Libraries/libressl/win64/tls-24.lib) + target_link_libraries(package_manager ${CMAKE_CURRENT_SOURCE_DIR}/Libraries/libressl/win64/crypto-49.lib) + target_link_libraries(package_manager ${CMAKE_CURRENT_SOURCE_DIR}/Libraries/libressl/win64/ssl-52.lib) +endif() + +if(UNIX AND NOT APPLE) + + find_package(Threads REQUIRED) + + if(NOT LIBUUID_PATH) + message(FATAL_ERROR "libuuid not found") + endif() + target_link_libraries(package_manager ${LIBUUID_PATH} OpenSSL::SSL OpenSSL::Crypto Threads::Threads) +endif() + + diff --git a/Client/src/common/AMCApplication.js b/Client/src/common/AMCApplication.js index 5a03c4700..3bd168435 100644 --- a/Client/src/common/AMCApplication.js +++ b/Client/src/common/AMCApplication.js @@ -561,7 +561,7 @@ export default class AMCApplication extends Common.AMCObject { formData.append("size", chunkData.byteLength); formData.append("offset", uploadOffset); formData.append("data", new Blob([chunkData], { - type: "application/3mf" + type: uploadObject.getMimeType () }), uploadObject.getFileName ()); application.axiosPostFormData("/upload/" + uploadObject.streamuuid, formData) @@ -689,7 +689,7 @@ export default class AMCApplication extends Common.AMCObject { formData.append("size", chunkData.byteLength); formData.append("offset", uploadOffset); formData.append("data", new Blob([chunkData], { - type: "application/3mf" + type: uploadObject.getMimeType () }), uploadObject.getFileName ()); application.axiosPostFormData("/upload/" + uploadObject.streamuuid, formData) diff --git a/Client/src/common/AMCImplementation_LayerView.js b/Client/src/common/AMCImplementation_LayerView.js index 8653ef5e7..b0a392bfb 100644 --- a/Client/src/common/AMCImplementation_LayerView.js +++ b/Client/src/common/AMCImplementation_LayerView.js @@ -326,6 +326,28 @@ class LayerViewImpl { } + computeChannelColumnRange (pointsChannelName, pointsColumnName) + { + if (this[pointsChannelName] && this[pointsChannelName][pointsColumnName]) { + let dataArray = this[pointsChannelName][pointsColumnName]; + + let maxValue = Number.NEGATIVE_INFINITY; + let minValue = Number.POSITIVE_INFINITY; + + for (let i = 0; i < dataArray.length; i++) { + let value = dataArray[i]; + if (value > maxValue) { + maxValue = value; + } + if (value < minValue) { + minValue = value; + } + } + this[pointsChannelName][pointsColumnName].max = maxValue; + this[pointsChannelName][pointsColumnName].min = minValue; + } + } + makeLaserOnColors () { this.laserOnPointsColorArray = null; @@ -351,6 +373,37 @@ class LayerViewImpl { } + makeLaserPowerColors () + { + this.layerPointsColorArray = null; + + if (this.laser && this.laser.power && this.layerPointsArray) { + const powerRange = (this.laser.power.max - this.laser.power.min); + if (powerRange > 0) { + + let pointCount = this.laser.power.length; + let colors = []; + + for (let pointIndex = 0; pointIndex < pointCount; pointIndex++) { + let power = this.laser.power[pointIndex]; + let fraction = (power - this.laser.power.min) / powerRange; + if (fraction >= 0.0) { + if (fraction > 1.0) + fraction = 1.0; + } else { + fraction = 0.0; + } + + const hue = (fraction) * 240 / 360; + colors.push (this.hslToRgb (hue, 1.0, 0.5)); + } + + this.layerPointsColorArray = colors; + } + } + } + + hslToRgb(h, s, l) { // Ensure h, s, l are in the range [0, 1] h = h % 1; // Wrap around if h is greater than 1 @@ -429,6 +482,8 @@ class LayerViewImpl { // Assign the data array to the corresponding column this[pointsChannelName][pointsColumnName] = pointsChannelDataArray; + + this.computeChannelColumnRange (pointsChannelName, pointsColumnName); } clearPoints () @@ -469,6 +524,10 @@ class LayerViewImpl { if (this.layerPointsMode == "laseron") { this.makeLaserOnColors (); } + + if (this.layerPointsMode == "powerramp") { + this.makeLaserPowerColors (); + } } setColorMode (newColorMode) { @@ -587,6 +646,16 @@ class LayerViewImpl { } + getPointPower (pointIndex) + { + if (this.laser && this.laser.power) { + if (pointIndex >= 0 && pointIndex < this.laser.power.length) { + return this.laser.power[pointIndex]; + } + } + return null; + } + updateLoadedLayer () { if (!this.glInstance) return; diff --git a/Client/src/common/AMCImplementation_Upload.js b/Client/src/common/AMCImplementation_Upload.js index 217d59dde..77bfe5bac 100644 --- a/Client/src/common/AMCImplementation_Upload.js +++ b/Client/src/common/AMCImplementation_Upload.js @@ -191,7 +191,12 @@ export default class AMCUpload extends Common.AMCObject { { return this.sha256sum; } - + + getMimeType () + { + return this.itemState.getMimeType(); + } + checkIfUploadIsActive () { if (this.itemState) { diff --git a/Client/src/modules/AMCModule_ContentItem_Form.vue b/Client/src/modules/AMCModule_ContentItem_Form.vue index e45b81185..f41a2d4fc 100644 --- a/Client/src/modules/AMCModule_ContentItem_Form.vue +++ b/Client/src/modules/AMCModule_ContentItem_Form.vue @@ -35,7 +35,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.