From 7d8c847e7781a19550fd4f64c05b90a6a5dee488 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Oct 2024 03:57:18 +0000 Subject: [PATCH 01/15] [desktop]: Bump electron-builder from 25.1.7 to 25.1.8 in /desktop Bumps [electron-builder](https://github.com/electron-userland/electron-builder/tree/HEAD/packages/electron-builder) from 25.1.7 to 25.1.8. - [Release notes](https://github.com/electron-userland/electron-builder/releases) - [Changelog](https://github.com/electron-userland/electron-builder/blob/master/packages/electron-builder/CHANGELOG.md) - [Commits](https://github.com/electron-userland/electron-builder/commits/electron-builder@25.1.8/packages/electron-builder) --- updated-dependencies: - dependency-name: electron-builder dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- desktop/package-lock.json | 525 +++++++++----------------------------- desktop/package.json | 2 +- 2 files changed, 119 insertions(+), 408 deletions(-) diff --git a/desktop/package-lock.json b/desktop/package-lock.json index 171634fca..865f95632 100644 --- a/desktop/package-lock.json +++ b/desktop/package-lock.json @@ -46,7 +46,7 @@ "@types/leaflet": "1.9.12", "@types/node": "22.7.5", "electron": "32.2.0", - "electron-builder": "25.1.7", + "electron-builder": "25.1.8", "eslint": "9.12.0", "node-polyfill-webpack-plugin": "4.0.0", "npm-run-all": "4.1.5", @@ -675,6 +675,34 @@ "typescript": ">=5.4 <5.6" } }, + "node_modules/@angular/compiler-cli/node_modules/chokidar": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz", + "integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==", + "dev": true, + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@angular/compiler-cli/node_modules/readdirp": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", + "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==", + "dev": true, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@angular/core": { "version": "18.2.8", "resolved": "https://registry.npmjs.org/@angular/core/-/core-18.2.8.tgz", @@ -2761,7 +2789,6 @@ "resolved": "https://registry.npmjs.org/@develar/schema-utils/-/schema-utils-2.6.5.tgz", "integrity": "sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig==", "dev": true, - "license": "MIT", "dependencies": { "ajv": "^6.12.0", "ajv-keywords": "^3.4.1" @@ -2779,7 +2806,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, - "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -2796,7 +2822,6 @@ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "dev": true, - "license": "MIT", "peerDependencies": { "ajv": "^6.9.1" } @@ -2805,8 +2830,7 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@discoveryjs/json-ext": { "version": "0.6.1", @@ -2850,7 +2874,6 @@ "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.2.13.tgz", "integrity": "sha512-pY5z2qQSwbFzJsBdgfJIzXf5ElHTVMutC2dxh0FD60njknMu3n1NnTABOcQwbb5/v5soqE79m9UjaJryBf3epg==", "dev": true, - "license": "MIT", "dependencies": { "@types/glob": "^7.1.0", "commander": "^5.0.0", @@ -2869,7 +2892,6 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2880,7 +2902,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -2925,7 +2946,6 @@ "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.5.0.tgz", "integrity": "sha512-jNT8nwH1f9X5GEITXaQ8IF/KdskvIkOFfB2CvwumsveVidzpSc+mvhhTMdAGSYF3O+Nq49lJ7y+ssODRXu06+A==", "dev": true, - "license": "MIT", "dependencies": { "debug": "^4.1.1", "fs-extra": "^9.0.1", @@ -2940,7 +2960,6 @@ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, - "license": "MIT", "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", @@ -2956,7 +2975,6 @@ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, - "license": "MIT", "dependencies": { "universalify": "^2.0.0" }, @@ -2969,7 +2987,6 @@ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, - "license": "MIT", "engines": { "node": ">= 10.0.0" } @@ -2979,7 +2996,6 @@ "resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-1.3.1.tgz", "integrity": "sha512-BAfviURMHpmb1Yb50YbCxnOY0wfwaLXH5KJ4+80zS0gUkzDX3ec23naTlEqKsN+PwYn+a1cCzM7BJ4Wcd3sGzw==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "compare-version": "^0.1.2", "debug": "^4.3.4", @@ -3001,7 +3017,6 @@ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, - "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -3016,7 +3031,6 @@ "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", "dev": true, - "license": "MIT", "engines": { "node": ">= 8.0.0" }, @@ -3029,7 +3043,6 @@ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, - "license": "MIT", "dependencies": { "universalify": "^2.0.0" }, @@ -3042,7 +3055,6 @@ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, - "license": "MIT", "engines": { "node": ">= 10.0.0" } @@ -3052,7 +3064,6 @@ "resolved": "https://registry.npmjs.org/@electron/rebuild/-/rebuild-3.6.1.tgz", "integrity": "sha512-f6596ZHpEq/YskUd8emYvOUne89ij8mQgjYFA5ru25QwbrRO+t1SImofdDv7kKOuWCmVOuU5tvfkbgGxIl3E/w==", "dev": true, - "license": "MIT", "dependencies": { "@malept/cross-spawn-promise": "^2.0.0", "chalk": "^4.0.0", @@ -3081,7 +3092,6 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -3097,7 +3107,6 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -3114,7 +3123,6 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -3126,15 +3134,13 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@electron/rebuild/node_modules/fs-extra": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, - "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -3149,7 +3155,6 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -3159,7 +3164,6 @@ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, - "license": "MIT", "dependencies": { "universalify": "^2.0.0" }, @@ -3172,7 +3176,6 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -3185,7 +3188,6 @@ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, - "license": "MIT", "engines": { "node": ">= 10.0.0" } @@ -3195,7 +3197,6 @@ "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-2.0.1.tgz", "integrity": "sha512-fKpv9kg4SPmt+hY7SVBnIYULE9QJl8L3sCfcBsnqbJwwBwAeTLokJ9TRt9y7bK0JAzIW2y78TVVjvnQEms/yyA==", "dev": true, - "license": "MIT", "dependencies": { "@electron/asar": "^3.2.7", "@malept/cross-spawn-promise": "^2.0.0", @@ -3214,7 +3215,6 @@ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", "dev": true, - "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -3229,7 +3229,6 @@ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, - "license": "MIT", "dependencies": { "universalify": "^2.0.0" }, @@ -3242,7 +3241,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -3258,7 +3256,6 @@ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, - "license": "MIT", "engines": { "node": ">= 10.0.0" } @@ -3881,8 +3878,7 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@hapi/hoek": { "version": "9.3.0", @@ -4549,7 +4545,6 @@ "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" } ], - "license": "Apache-2.0", "dependencies": { "cross-spawn": "^7.0.1" }, @@ -4562,7 +4557,6 @@ "resolved": "https://registry.npmjs.org/@malept/flatpak-bundler/-/flatpak-bundler-0.4.0.tgz", "integrity": "sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q==", "dev": true, - "license": "MIT", "dependencies": { "debug": "^4.1.1", "fs-extra": "^9.0.0", @@ -4578,7 +4572,6 @@ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, - "license": "MIT", "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", @@ -4594,7 +4587,6 @@ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, - "license": "MIT", "dependencies": { "universalify": "^2.0.0" }, @@ -4607,7 +4599,6 @@ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, - "license": "MIT", "engines": { "node": ">= 10.0.0" } @@ -4801,7 +4792,6 @@ "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz", "integrity": "sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==", "dev": true, - "license": "ISC", "dependencies": { "@gar/promisify": "^1.1.3", "semver": "^7.3.5" @@ -4887,7 +4877,6 @@ "integrity": "sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==", "deprecated": "This functionality has been moved to @npmcli/fs", "dev": true, - "license": "MIT", "dependencies": { "mkdirp": "^1.0.4", "rimraf": "^3.0.2" @@ -5960,7 +5949,6 @@ "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", "dev": true, - "license": "MIT", "engines": { "node": ">= 10" } @@ -6093,7 +6081,6 @@ "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", "dev": true, - "license": "MIT", "dependencies": { "@types/ms": "*" } @@ -6170,7 +6157,6 @@ "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==", "dev": true, - "license": "MIT", "dependencies": { "@types/node": "*" } @@ -6187,7 +6173,6 @@ "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", "dev": true, - "license": "MIT", "dependencies": { "@types/minimatch": "*", "@types/node": "*" @@ -6255,15 +6240,13 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@types/ms": { "version": "0.7.34", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@types/mute-stream": { "version": "0.0.4", @@ -6300,7 +6283,6 @@ "resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.5.tgz", "integrity": "sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA==", "dev": true, - "license": "MIT", "optional": true, "dependencies": { "@types/node": "*", @@ -6386,7 +6368,6 @@ "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.10.tgz", "integrity": "sha512-l4MM0Jppn18hb9xmM6wwD1uTdShpf9Pn80aXTStnK1C94gtPvJcV2FrDmbOQUAQfJ1cKZHktkQUDwEqaAKXMMg==", "dev": true, - "license": "MIT", "optional": true }, "node_modules/@types/wrap-ansi": { @@ -6815,7 +6796,6 @@ "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", "dev": true, - "license": "MIT", "engines": { "node": ">=10.0.0" } @@ -6845,15 +6825,13 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.2.0.tgz", "integrity": "sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true, - "license": "ISC" + "dev": true }, "node_modules/abort-controller": { "version": "3.0.0", @@ -6975,7 +6953,6 @@ "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", "dev": true, - "license": "MIT", "dependencies": { "humanize-ms": "^1.2.1" }, @@ -7147,15 +7124,13 @@ "version": "5.0.0-alpha.10", "resolved": "https://registry.npmjs.org/app-builder-bin/-/app-builder-bin-5.0.0-alpha.10.tgz", "integrity": "sha512-Ev4jj3D7Bo+O0GPD2NMvJl+PGiBAfS7pUGawntBNpCbxtpncfUixqFj9z9Jme7V7s3LBGqsWZZP54fxBX3JKJw==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/app-builder-lib": { - "version": "25.1.7", - "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-25.1.7.tgz", - "integrity": "sha512-JxmN+D/Dn7BLQoN+cTFO+zbMHcpI10v/xjyjFO1FKpHbApOG+OQt/xUyVjKWp4FYplIfuHdpxqTXo1PN/Wzm/A==", + "version": "25.1.8", + "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-25.1.8.tgz", + "integrity": "sha512-pCqe7dfsQFBABC1jeKZXQWhGcCPF3rPCXDdfqVKjIeWBcXzyC1iOWZdfFhGl+S9MyE/k//DFmC6FzuGAUudNDg==", "dev": true, - "license": "MIT", "dependencies": { "@develar/schema-utils": "~2.6.5", "@electron/notarize": "2.5.0", @@ -7194,8 +7169,8 @@ "node": ">=14.0.0" }, "peerDependencies": { - "dmg-builder": "25.1.7", - "electron-builder-squirrel-windows": "25.1.7" + "dmg-builder": "25.1.8", + "electron-builder-squirrel-windows": "25.1.8" } }, "node_modules/app-builder-lib/node_modules/fs-extra": { @@ -7203,7 +7178,6 @@ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, - "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -7218,7 +7192,6 @@ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, - "license": "MIT", "dependencies": { "universalify": "^2.0.0" }, @@ -7231,7 +7204,6 @@ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, - "license": "MIT", "engines": { "node": ">= 10.0.0" } @@ -7240,15 +7212,13 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", - "dev": true, - "license": "ISC" + "dev": true }, "node_modules/archiver": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz", "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==", "dev": true, - "license": "MIT", "peer": true, "dependencies": { "archiver-utils": "^2.1.0", @@ -7268,7 +7238,6 @@ "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", "dev": true, - "license": "MIT", "peer": true, "dependencies": { "glob": "^7.1.4", @@ -7291,7 +7260,6 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", "dev": true, - "license": "MIT", "peer": true }, "node_modules/archiver-utils/node_modules/readable-stream": { @@ -7299,7 +7267,6 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, - "license": "MIT", "peer": true, "dependencies": { "core-util-is": "~1.0.0", @@ -7316,7 +7283,6 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true, - "license": "MIT", "peer": true }, "node_modules/archiver-utils/node_modules/string_decoder": { @@ -7324,7 +7290,6 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, - "license": "MIT", "peer": true, "dependencies": { "safe-buffer": "~5.1.0" @@ -7336,7 +7301,6 @@ "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", "deprecated": "This package is no longer supported.", "dev": true, - "license": "ISC", "dependencies": { "delegates": "^1.0.0", "readable-stream": "^3.6.0" @@ -7444,7 +7408,6 @@ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", "dev": true, - "license": "MIT", "optional": true, "engines": { "node": ">=0.8" @@ -7455,7 +7418,6 @@ "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true, - "license": "MIT", "optional": true, "engines": { "node": ">=8" @@ -7465,15 +7427,13 @@ "version": "3.2.6", "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/async-exit-hook": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-2.0.1.tgz", "integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.12.0" } @@ -7490,7 +7450,6 @@ "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", "dev": true, - "license": "ISC", "engines": { "node": ">= 4.0.0" } @@ -7711,15 +7670,13 @@ "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/bluebird-lst": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/bluebird-lst/-/bluebird-lst-1.0.9.tgz", "integrity": "sha512-7B1Rtx82hjnSD4PGLAjVWeYH3tHAcVUmChh85a3lltKQm6FresXh9ErQo6oAv6CqxttczC3/kEg8SY5NluPuUw==", "dev": true, - "license": "MIT", "dependencies": { "bluebird": "^3.5.5" } @@ -8073,7 +8030,6 @@ "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-25.1.7.tgz", "integrity": "sha512-7jPjzBwEGRbwNcep0gGNpLXG9P94VA3CPAZQCzxkFXiV2GMQKlziMbY//rXPI7WKfhsvGgFXjTcXdBEwgXw9ww==", "dev": true, - "license": "MIT", "dependencies": { "@types/debug": "^4.1.6", "7zip-bin": "~5.2.0", @@ -8098,7 +8054,6 @@ "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.10.tgz", "integrity": "sha512-6p/gfG1RJSQeIbz8TK5aPNkoztgY1q5TgmGFMAXcY8itsGW6Y2ld1ALsZ5UJn8rog7hKF3zHx5iQbNQ8uLcRlw==", "dev": true, - "license": "MIT", "dependencies": { "debug": "^4.3.4", "sax": "^1.2.4" @@ -8112,7 +8067,6 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -8128,7 +8082,6 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -8145,7 +8098,6 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -8157,15 +8109,13 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/builder-util/node_modules/fs-extra": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, - "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -8180,7 +8130,6 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -8190,7 +8139,6 @@ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, - "license": "MIT", "dependencies": { "universalify": "^2.0.0" }, @@ -8203,7 +8151,6 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -8216,7 +8163,6 @@ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, - "license": "MIT", "engines": { "node": ">= 10.0.0" } @@ -8259,7 +8205,6 @@ "resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.3.tgz", "integrity": "sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==", "dev": true, - "license": "ISC", "dependencies": { "@npmcli/fs": "^2.1.0", "@npmcli/move-file": "^2.0.0", @@ -8290,7 +8235,6 @@ "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, - "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -8310,7 +8254,6 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "dev": true, - "license": "ISC", "engines": { "node": ">=12" } @@ -8320,7 +8263,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -8455,19 +8397,39 @@ } }, "node_modules/chokidar": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz", - "integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, - "license": "MIT", "dependencies": { - "readdirp": "^4.0.1" + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" }, "engines": { - "node": ">= 14.16.0" + "node": ">= 8.10.0" }, "funding": { "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" } }, "node_modules/chownr": { @@ -8494,8 +8456,7 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz", "integrity": "sha512-1R5Fho+jBq0DDydt+/vHWj5KJNJCKdARKOCwZUen84I5BreWoLqRLANH1U87eJy1tiASPtMnGqJJq0ZsLoRPOw==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/ci-info": { "version": "3.9.0", @@ -8508,7 +8469,6 @@ "url": "https://github.com/sponsors/sibiraj-s" } ], - "license": "MIT", "engines": { "node": ">=8" } @@ -8568,7 +8528,6 @@ "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", "dev": true, - "license": "MIT", "optional": true, "dependencies": { "slice-ansi": "^3.0.0", @@ -8720,7 +8679,6 @@ "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", "dev": true, - "license": "ISC", "bin": { "color-support": "bin.js" } @@ -8750,7 +8708,6 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 6" } @@ -8767,7 +8724,6 @@ "resolved": "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz", "integrity": "sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -8777,7 +8733,6 @@ "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz", "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==", "dev": true, - "license": "MIT", "peer": true, "dependencies": { "buffer-crc32": "^0.2.13", @@ -8857,7 +8812,6 @@ "resolved": "https://registry.npmjs.org/config-file-ts/-/config-file-ts-0.2.8-rc1.tgz", "integrity": "sha512-GtNECbVI82bT4RiDIzBSVuTKoSHufnU7Ce7/42bkWZJZFLjmDF2WBpVsvRkhKCfKBnTBb3qZrBwPpFBU/Myvhg==", "dev": true, - "license": "MIT", "dependencies": { "glob": "^10.3.12", "typescript": "^5.4.3" @@ -8868,7 +8822,6 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, - "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", @@ -8889,7 +8842,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -8905,7 +8857,6 @@ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, - "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" } @@ -8930,8 +8881,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", - "dev": true, - "license": "ISC" + "dev": true }, "node_modules/constants-browserify": { "version": "1.0.0", @@ -9078,7 +9028,6 @@ "resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz", "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==", "dev": true, - "license": "MIT", "optional": true, "dependencies": { "buffer": "^5.1.0" @@ -9089,7 +9038,6 @@ "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", "dev": true, - "license": "Apache-2.0", "peer": true, "bin": { "crc32": "bin/crc32.njs" @@ -9103,7 +9051,6 @@ "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz", "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==", "dev": true, - "license": "MIT", "peer": true, "dependencies": { "crc-32": "^1.2.0", @@ -9678,8 +9625,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/depd": { "version": "2.0.0", @@ -9764,7 +9710,6 @@ "resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-4.2.0.tgz", "integrity": "sha512-2xMCmOoMrdQIPHdsTawECdNPwlVFB9zGcz3kuhmBO6U3oU+UQjsue0i8ayLKpgBcm+hcXPMVSGUN9d+pvJ6+VQ==", "dev": true, - "license": "MIT", "dependencies": { "minimatch": "^3.0.5", "p-limit": "^3.1.0 " @@ -9775,7 +9720,6 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -9786,7 +9730,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -9795,13 +9738,12 @@ } }, "node_modules/dmg-builder": { - "version": "25.1.7", - "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-25.1.7.tgz", - "integrity": "sha512-Hac0AfXQrAV62JT99Had6bvUJb/f7vjJTaLOsmA/gAQcrc/cLmNAqCJ0ZZDqwKy2+LKXnxx45TvMXvovKd4iMg==", + "version": "25.1.8", + "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-25.1.8.tgz", + "integrity": "sha512-NoXo6Liy2heSklTI5OIZbCgXC1RzrDQsZkeEwXhdOro3FT1VBOvbubvscdPnjVuQ4AMwwv61oaH96AbiYg9EnQ==", "dev": true, - "license": "MIT", "dependencies": { - "app-builder-lib": "25.1.7", + "app-builder-lib": "25.1.8", "builder-util": "25.1.7", "builder-util-runtime": "9.2.10", "fs-extra": "^10.1.0", @@ -9817,7 +9759,6 @@ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, - "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -9832,7 +9773,6 @@ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, - "license": "MIT", "dependencies": { "universalify": "^2.0.0" }, @@ -9845,7 +9785,6 @@ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, - "license": "MIT", "engines": { "node": ">= 10.0.0" } @@ -9855,7 +9794,6 @@ "resolved": "https://registry.npmjs.org/dmg-license/-/dmg-license-1.0.11.tgz", "integrity": "sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q==", "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" @@ -9882,7 +9820,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, - "license": "MIT", "optional": true, "dependencies": { "fast-deep-equal": "^3.1.1", @@ -9900,7 +9837,6 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true, - "license": "MIT", "optional": true }, "node_modules/dns-packet": { @@ -9993,7 +9929,6 @@ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", "dev": true, - "license": "BSD-2-Clause", "engines": { "node": ">=12" }, @@ -10006,7 +9941,6 @@ "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-11.0.6.tgz", "integrity": "sha512-8NHi73otpWsZGBSZwwknTXS5pqMOrk9+Ssrna8xCaxkzEpU9OTf9R5ArQGVw03//Zmk9MOwLPng9WwndvpAJ5g==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "dotenv": "^16.4.4" }, @@ -10036,7 +9970,6 @@ "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", "dev": true, - "license": "Apache-2.0", "dependencies": { "jake": "^10.8.5" }, @@ -10067,17 +10000,16 @@ } }, "node_modules/electron-builder": { - "version": "25.1.7", - "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-25.1.7.tgz", - "integrity": "sha512-lsKtX93GSHWnmuteNRvBzgJIjRiiYB0qrJVRjShwBi75Ns+mRdWeOGZiXItqOWj+3g5UyY722kgoq2WlqCB87A==", + "version": "25.1.8", + "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-25.1.8.tgz", + "integrity": "sha512-poRgAtUHHOnlzZnc9PK4nzG53xh74wj2Jy7jkTrqZ0MWPoHGh1M2+C//hGeYdA+4K8w4yiVCNYoLXF7ySj2Wig==", "dev": true, - "license": "MIT", "dependencies": { - "app-builder-lib": "25.1.7", + "app-builder-lib": "25.1.8", "builder-util": "25.1.7", "builder-util-runtime": "9.2.10", "chalk": "^4.1.2", - "dmg-builder": "25.1.7", + "dmg-builder": "25.1.8", "fs-extra": "^10.1.0", "is-ci": "^3.0.0", "lazy-val": "^1.0.5", @@ -10093,14 +10025,13 @@ } }, "node_modules/electron-builder-squirrel-windows": { - "version": "25.1.7", - "resolved": "https://registry.npmjs.org/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-25.1.7.tgz", - "integrity": "sha512-nJMvw1FNy+6YP8HmjSb0JwMowpdlZpydZGab9KevKO/fIC9wTcr5rkhbLsTfEPOjdAqOTycRoK0mOJCFB/1uig==", + "version": "25.1.8", + "resolved": "https://registry.npmjs.org/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-25.1.8.tgz", + "integrity": "sha512-2ntkJ+9+0GFP6nAISiMabKt6eqBB0kX1QqHNWFWAXgi0VULKGisM46luRFpIBiU3u/TDmhZMM8tzvo2Abn3ayg==", "dev": true, - "license": "MIT", "peer": true, "dependencies": { - "app-builder-lib": "25.1.7", + "app-builder-lib": "25.1.8", "archiver": "^5.3.1", "builder-util": "25.1.7", "fs-extra": "^10.1.0" @@ -10111,7 +10042,6 @@ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, - "license": "MIT", "peer": true, "dependencies": { "graceful-fs": "^4.2.0", @@ -10127,7 +10057,6 @@ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, - "license": "MIT", "peer": true, "dependencies": { "universalify": "^2.0.0" @@ -10141,7 +10070,6 @@ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, - "license": "MIT", "peer": true, "engines": { "node": ">= 10.0.0" @@ -10266,7 +10194,6 @@ "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-25.1.7.tgz", "integrity": "sha512-+jbTkR9m39eDBMP4gfbqglDd6UvBC7RLh5Y0MhFSsc6UkGHj9Vj9TWobxevHYMMqmoujL11ZLjfPpMX+Pt6YEg==", "dev": true, - "license": "MIT", "dependencies": { "@types/fs-extra": "^9.0.11", "builder-util": "25.1.7", @@ -10282,7 +10209,6 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -10298,7 +10224,6 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -10315,7 +10240,6 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -10327,15 +10251,13 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/electron-publish/node_modules/fs-extra": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, - "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -10350,7 +10272,6 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -10360,7 +10281,6 @@ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, - "license": "MIT", "dependencies": { "universalify": "^2.0.0" }, @@ -10373,7 +10293,6 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -10386,7 +10305,6 @@ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, - "license": "MIT", "engines": { "node": ">= 10.0.0" } @@ -11309,7 +11227,6 @@ "engines": [ "node >=0.6.0" ], - "license": "MIT", "optional": true }, "node_modules/fast-deep-equal": { @@ -11421,7 +11338,6 @@ "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", "dev": true, - "license": "Apache-2.0", "dependencies": { "minimatch": "^5.0.1" } @@ -11431,7 +11347,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -11661,7 +11576,6 @@ "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", "dev": true, - "license": "MIT", "peer": true }, "node_modules/fs-extra": { @@ -11696,8 +11610,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, - "license": "ISC" + "dev": true }, "node_modules/fsevents": { "version": "2.3.3", @@ -11759,7 +11672,6 @@ "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", "deprecated": "This package is no longer supported.", "dev": true, - "license": "ISC", "dependencies": { "aproba": "^1.0.3 || ^2.0.0", "color-support": "^1.1.3", @@ -11778,8 +11690,7 @@ "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" + "dev": true }, "node_modules/gensync": { "version": "1.0.0-beta.2", @@ -11884,7 +11795,6 @@ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, - "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -11925,7 +11835,6 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -11936,7 +11845,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -12159,8 +12067,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", - "dev": true, - "license": "ISC" + "dev": true }, "node_modules/hash-base": { "version": "3.0.4", @@ -12217,7 +12124,6 @@ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", "dev": true, - "license": "ISC", "dependencies": { "lru-cache": "^6.0.0" }, @@ -12230,7 +12136,6 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, - "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, @@ -12242,8 +12147,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true, - "license": "ISC" + "dev": true }, "node_modules/hotkeys-js": { "version": "3.13.7", @@ -12479,7 +12383,6 @@ "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", "dev": true, - "license": "MIT", "dependencies": { "ms": "^2.0.0" } @@ -12499,7 +12402,6 @@ "resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz", "integrity": "sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==", "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" @@ -12660,8 +12562,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", - "dev": true, - "license": "ISC" + "dev": true }, "node_modules/inflight": { "version": "1.0.6", @@ -12669,7 +12570,6 @@ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, - "license": "ISC", "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -12833,7 +12733,6 @@ "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", "dev": true, - "license": "MIT", "dependencies": { "ci-info": "^3.2.0" }, @@ -13236,11 +13135,10 @@ "license": "MIT" }, "node_modules/isbinaryfile": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-5.0.2.tgz", - "integrity": "sha512-GvcjojwonMjWbTkfMpnVHVqXW/wKMYDfEpY94/8zy8HFMOqb/VL6oeONq9v87q4ttVlaTLnGXnJD4B5B1OTGIg==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-5.0.3.tgz", + "integrity": "sha512-VR4gNjFaDP8csJQvzInG20JvBj8MaHYLxNOMXysxRbGM7tcsHZwCjhch3FubFtZBkuDbN55i4dUukGeIrzF+6g==", "dev": true, - "license": "MIT", "engines": { "node": ">= 18.0.0" }, @@ -13313,7 +13211,6 @@ "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", "dev": true, - "license": "Apache-2.0", "dependencies": { "async": "^3.2.3", "chalk": "^4.0.2", @@ -13332,7 +13229,6 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -13348,7 +13244,6 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -13359,7 +13254,6 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -13376,7 +13270,6 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -13388,15 +13281,13 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/jake/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=8" } @@ -13406,7 +13297,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -13419,7 +13309,6 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -13681,15 +13570,13 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.5.tgz", "integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/lazystream": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", "dev": true, - "license": "MIT", "peer": true, "dependencies": { "readable-stream": "^2.0.5" @@ -13703,7 +13590,6 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", "dev": true, - "license": "MIT", "peer": true }, "node_modules/lazystream/node_modules/readable-stream": { @@ -13711,7 +13597,6 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, - "license": "MIT", "peer": true, "dependencies": { "core-util-is": "~1.0.0", @@ -13728,7 +13613,6 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true, - "license": "MIT", "peer": true }, "node_modules/lazystream/node_modules/string_decoder": { @@ -13736,7 +13620,6 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, - "license": "MIT", "peer": true, "dependencies": { "safe-buffer": "~5.1.0" @@ -14151,7 +14034,6 @@ "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", "dev": true, - "license": "MIT", "peer": true }, "node_modules/lodash.difference": { @@ -14159,7 +14041,6 @@ "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==", "dev": true, - "license": "MIT", "peer": true }, "node_modules/lodash.flatten": { @@ -14167,7 +14048,6 @@ "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==", "dev": true, - "license": "MIT", "peer": true }, "node_modules/lodash.isplainobject": { @@ -14175,7 +14055,6 @@ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", "dev": true, - "license": "MIT", "peer": true }, "node_modules/lodash.merge": { @@ -14190,7 +14069,6 @@ "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==", "dev": true, - "license": "MIT", "peer": true }, "node_modules/log-symbols": { @@ -14508,7 +14386,6 @@ "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz", "integrity": "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==", "dev": true, - "license": "ISC", "dependencies": { "agentkeepalive": "^4.2.1", "cacache": "^16.1.0", @@ -14536,7 +14413,6 @@ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "dev": true, - "license": "MIT", "dependencies": { "debug": "4" }, @@ -14549,7 +14425,6 @@ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", "dev": true, - "license": "MIT", "dependencies": { "@tootallnate/once": "2", "agent-base": "6", @@ -14564,7 +14439,6 @@ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "dev": true, - "license": "MIT", "dependencies": { "agent-base": "6", "debug": "4" @@ -14578,7 +14452,6 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "dev": true, - "license": "ISC", "engines": { "node": ">=12" } @@ -14752,7 +14625,6 @@ "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", "dev": true, - "license": "MIT", "bin": { "mime": "cli.js" }, @@ -14856,7 +14728,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -14895,7 +14766,6 @@ "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", "dev": true, - "license": "ISC", "dependencies": { "minipass": "^3.0.0" }, @@ -14908,7 +14778,6 @@ "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-2.1.2.tgz", "integrity": "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==", "dev": true, - "license": "MIT", "dependencies": { "minipass": "^3.1.6", "minipass-sized": "^1.0.3", @@ -15206,7 +15075,6 @@ "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.68.0.tgz", "integrity": "sha512-7vbj10trelExNjFSBm5kTvZXXa7pZyKWx9RCKIyqe6I9Ev3IzGpQoqBP3a+cOdxY+pWj6VkP28n/2wWysBHD/A==", "dev": true, - "license": "MIT", "dependencies": { "semver": "^7.3.5" }, @@ -15219,7 +15087,6 @@ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==", "dev": true, - "license": "MIT", "optional": true }, "node_modules/node-api-version": { @@ -15227,7 +15094,6 @@ "resolved": "https://registry.npmjs.org/node-api-version/-/node-api-version-0.2.0.tgz", "integrity": "sha512-fthTTsi8CxaBXMaBAD7ST2uylwvsnYxh2PfaScwpMhos6KlSFajXQPcM4ogNE1q2s3Lbz9GCGqeIHC+C6OZnKg==", "dev": true, - "license": "MIT", "dependencies": { "semver": "^7.3.5" } @@ -15247,7 +15113,6 @@ "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-9.4.1.tgz", "integrity": "sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ==", "dev": true, - "license": "MIT", "dependencies": { "env-paths": "^2.2.0", "exponential-backoff": "^3.1.1", @@ -15402,7 +15267,6 @@ "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz", "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==", "dev": true, - "license": "ISC", "dependencies": { "abbrev": "^1.0.0" }, @@ -15942,7 +15806,6 @@ "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", "deprecated": "This package is no longer supported.", "dev": true, - "license": "ISC", "dependencies": { "are-we-there-yet": "^3.0.0", "console-control-strings": "^1.1.0", @@ -16749,7 +16612,6 @@ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, - "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -16847,7 +16709,6 @@ "resolved": "https://registry.npmjs.org/pe-library/-/pe-library-0.4.1.tgz", "integrity": "sha512-eRWB5LBz7PpDu4PUlwT0PhnQfTQJlDDdPa35urV4Osrm0t0AqQFGn+UIkU3klZvwJ8KPO3VbBFsXquA6p6kqZw==", "dev": true, - "license": "MIT", "engines": { "node": ">=12", "npm": ">=6" @@ -17027,7 +16888,6 @@ "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", "integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==", "dev": true, - "license": "MIT", "dependencies": { "@xmldom/xmldom": "^0.8.8", "base64-js": "^1.5.1", @@ -17572,7 +17432,6 @@ "resolved": "https://registry.npmjs.org/read-binary-file-arch/-/read-binary-file-arch-1.0.6.tgz", "integrity": "sha512-BNg9EN3DD3GsDXX7Aa8O4p92sryjkmzYYgmgTAc6CA4uGLEDzFfxOxugu21akOxpcXHiEgsYkC6nPsQvLLLmEg==", "dev": true, - "license": "MIT", "dependencies": { "debug": "^4.3.4" }, @@ -17668,7 +17527,6 @@ "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", "dev": true, - "license": "Apache-2.0", "peer": true, "dependencies": { "minimatch": "^5.1.0" @@ -17679,7 +17537,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, - "license": "ISC", "peer": true, "dependencies": { "brace-expansion": "^2.0.1" @@ -17689,17 +17546,27 @@ } }, "node_modules/readdirp": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", - "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, - "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, "engines": { - "node": ">= 14.16.0" + "node": ">=8.10.0" + } + }, + "node_modules/readdirp/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" }, "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" + "url": "https://github.com/sponsors/jonschlinkert" } }, "node_modules/reflect-metadata": { @@ -17855,7 +17722,6 @@ "resolved": "https://registry.npmjs.org/resedit/-/resedit-1.7.2.tgz", "integrity": "sha512-vHjcY2MlAITJhC0eRD/Vv8Vlgmu9Sd3LX9zZvtGzU5ZImdTN3+d6e/4mnTyV8vEbyf1sgNIrWxhWlrys52OkEA==", "dev": true, - "license": "MIT", "dependencies": { "pe-library": "^0.4.1" }, @@ -18009,7 +17875,6 @@ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, - "license": "ISC", "dependencies": { "glob": "^7.1.3" }, @@ -18209,7 +18074,6 @@ "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz", "integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==", "dev": true, - "license": "WTFPL OR ISC", "dependencies": { "truncate-utf8-bytes": "^1.0.0" } @@ -18273,70 +18137,6 @@ } } }, - "node_modules/sass/node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/sass/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/sass/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/sass/node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, "node_modules/sax": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", @@ -18654,8 +18454,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "dev": true, - "license": "ISC" + "dev": true }, "node_modules/set-function-length": { "version": "1.2.2", @@ -18846,7 +18645,6 @@ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", "dev": true, - "license": "MIT", "optional": true, "dependencies": { "ansi-styles": "^4.0.0", @@ -18862,7 +18660,6 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "license": "MIT", "optional": true, "dependencies": { "color-convert": "^2.0.1" @@ -18879,7 +18676,6 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "license": "MIT", "optional": true, "dependencies": { "color-name": "~1.1.4" @@ -18893,7 +18689,6 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true, - "license": "MIT", "optional": true }, "node_modules/smart-buffer": { @@ -18939,7 +18734,6 @@ "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", "dev": true, - "license": "MIT", "dependencies": { "agent-base": "^6.0.2", "debug": "^4.3.3", @@ -18954,7 +18748,6 @@ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "dev": true, - "license": "MIT", "dependencies": { "debug": "4" }, @@ -19104,7 +18897,6 @@ "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz", "integrity": "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==", "dev": true, - "license": "ISC", "dependencies": { "minipass": "^3.1.1" }, @@ -19117,7 +18909,6 @@ "resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-1.0.0.tgz", "integrity": "sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==", "dev": true, - "license": "MIT", "engines": { "node": ">= 6" } @@ -19410,7 +19201,6 @@ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", "dev": true, - "license": "MIT", "peer": true, "dependencies": { "bl": "^4.0.3", @@ -19445,7 +19235,6 @@ "resolved": "https://registry.npmjs.org/temp-file/-/temp-file-3.4.0.tgz", "integrity": "sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg==", "dev": true, - "license": "MIT", "dependencies": { "async-exit-hook": "^2.0.1", "fs-extra": "^10.0.0" @@ -19456,7 +19245,6 @@ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, - "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -19471,7 +19259,6 @@ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, - "license": "MIT", "dependencies": { "universalify": "^2.0.0" }, @@ -19484,7 +19271,6 @@ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, - "license": "MIT", "engines": { "node": ">= 10.0.0" } @@ -19661,7 +19447,6 @@ "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz", "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==", "dev": true, - "license": "MIT", "dependencies": { "tmp": "^0.2.0" } @@ -19671,7 +19456,6 @@ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", "dev": true, - "license": "MIT", "engines": { "node": ">=14.14" } @@ -19741,7 +19525,6 @@ "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", "integrity": "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==", "dev": true, - "license": "WTFPL", "dependencies": { "utf8-byte-length": "^1.0.1" } @@ -20291,7 +20074,6 @@ "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-2.0.1.tgz", "integrity": "sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==", "dev": true, - "license": "ISC", "dependencies": { "unique-slug": "^3.0.0" }, @@ -20304,7 +20086,6 @@ "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-3.0.0.tgz", "integrity": "sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==", "dev": true, - "license": "ISC", "dependencies": { "imurmurhash": "^0.1.4" }, @@ -20398,8 +20179,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz", "integrity": "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==", - "dev": true, - "license": "(WTFPL OR MIT)" + "dev": true }, "node_modules/util": { "version": "0.12.5", @@ -20485,7 +20265,6 @@ "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", "dev": true, - "license": "MIT", "optional": true, "dependencies": { "assert-plus": "^1.0.0", @@ -21221,31 +21000,6 @@ } } }, - "node_modules/webpack-dev-server/node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, "node_modules/webpack-dev-server/node_modules/glob": { "version": "10.4.5", "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", @@ -21267,19 +21021,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/webpack-dev-server/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/webpack-dev-server/node_modules/http-proxy-middleware": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz", @@ -21331,32 +21072,6 @@ "node": ">=16 || 14 >=14.17" } }, - "node_modules/webpack-dev-server/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/webpack-dev-server/node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, "node_modules/webpack-dev-server/node_modules/rimraf": { "version": "5.0.10", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", @@ -21600,7 +21315,6 @@ "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", "dev": true, - "license": "ISC", "dependencies": { "string-width": "^1.0.2 || 2 || 3 || 4" } @@ -21762,7 +21476,6 @@ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", "dev": true, - "license": "MIT", "engines": { "node": ">=8.0" } @@ -21875,7 +21588,6 @@ "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz", "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==", "dev": true, - "license": "MIT", "peer": true, "dependencies": { "archiver-utils": "^3.0.4", @@ -21891,7 +21603,6 @@ "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz", "integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==", "dev": true, - "license": "MIT", "peer": true, "dependencies": { "glob": "^7.2.3", diff --git a/desktop/package.json b/desktop/package.json index e28ad8951..82e3db37e 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -71,7 +71,7 @@ "@types/leaflet": "1.9.12", "@types/node": "22.7.5", "electron": "32.2.0", - "electron-builder": "25.1.7", + "electron-builder": "25.1.8", "eslint": "9.12.0", "node-polyfill-webpack-plugin": "4.0.0", "npm-run-all": "4.1.5", From 68eae49568cd9b89c38ecfb1f56cb7376da24223 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Oct 2024 03:09:28 +0000 Subject: [PATCH 02/15] [api]: Bump ch.qos.logback:logback-classic from 1.5.9 to 1.5.10 Bumps [ch.qos.logback:logback-classic](https://github.com/qos-ch/logback) from 1.5.9 to 1.5.10. - [Commits](https://github.com/qos-ch/logback/compare/v_1.5.9...v_1.5.10) --- updated-dependencies: - dependency-name: ch.qos.logback:logback-classic dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- settings.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index 562cf73b5..27a4fafa5 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -21,7 +21,7 @@ dependencyResolutionManagement { library("retrofit", "com.squareup.retrofit2:retrofit:2.11.0") library("retrofit-jackson", "com.squareup.retrofit2:converter-jackson:2.11.0") library("rx", "io.reactivex.rxjava3:rxjava:3.1.9") - library("logback", "ch.qos.logback:logback-classic:1.5.9") + library("logback", "ch.qos.logback:logback-classic:1.5.10") library("eventbus", "org.greenrobot:eventbus-java:3.3.1") library("netty-transport", "io.netty:netty-transport:4.1.114.Final") library("netty-codec", "io.netty:netty-codec:4.1.114.Final") From 3ea0f8e7fb877f29af156f90b4382878c0c2cc01 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Oct 2024 03:56:47 +0000 Subject: [PATCH 03/15] [desktop]: Bump electron from 32.2.0 to 33.0.0 in /desktop Bumps [electron](https://github.com/electron/electron) from 32.2.0 to 33.0.0. - [Release notes](https://github.com/electron/electron/releases) - [Changelog](https://github.com/electron/electron/blob/main/docs/breaking-changes.md) - [Commits](https://github.com/electron/electron/compare/v32.2.0...v33.0.0) --- updated-dependencies: - dependency-name: electron dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- desktop/package-lock.json | 9 ++++----- desktop/package.json | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/desktop/package-lock.json b/desktop/package-lock.json index 865f95632..881dcae5a 100644 --- a/desktop/package-lock.json +++ b/desktop/package-lock.json @@ -45,7 +45,7 @@ "@types/eslint__js": "8.42.3", "@types/leaflet": "1.9.12", "@types/node": "22.7.5", - "electron": "32.2.0", + "electron": "33.0.0", "electron-builder": "25.1.8", "eslint": "9.12.0", "node-polyfill-webpack-plugin": "4.0.0", @@ -9981,12 +9981,11 @@ } }, "node_modules/electron": { - "version": "32.2.0", - "resolved": "https://registry.npmjs.org/electron/-/electron-32.2.0.tgz", - "integrity": "sha512-Xy82QBQrEiQysoxsv6lnhHAcWNNe6vV6QqH3OPFXhEj/T9oAsBHEhZuuYHINSSsUE7zRSj+J9sNwJYOjisT0Vw==", + "version": "33.0.0", + "resolved": "https://registry.npmjs.org/electron/-/electron-33.0.0.tgz", + "integrity": "sha512-OdLLR/zAVuNfKahSSYokZmSi7uK2wEYTbCoiIdqWLsOWmCqO9j0JC2XkYQmXQcAk2BJY0ri4lxwAfc5pzPDYsA==", "dev": true, "hasInstallScript": true, - "license": "MIT", "dependencies": { "@electron/get": "^2.0.0", "@types/node": "^20.9.0", diff --git a/desktop/package.json b/desktop/package.json index 82e3db37e..43c355251 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -70,7 +70,7 @@ "@types/eslint__js": "8.42.3", "@types/leaflet": "1.9.12", "@types/node": "22.7.5", - "electron": "32.2.0", + "electron": "33.0.0", "electron-builder": "25.1.8", "eslint": "9.12.0", "node-polyfill-webpack-plugin": "4.0.0", From d32c2b8cc95749f61aa016fccf9eda6b5d4f02f2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Oct 2024 03:56:17 +0000 Subject: [PATCH 04/15] [desktop]: Bump typescript-eslint from 8.8.1 to 8.9.0 in /desktop Bumps [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) from 8.8.1 to 8.9.0. - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.9.0/packages/typescript-eslint) --- updated-dependencies: - dependency-name: typescript-eslint dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- desktop/package-lock.json | 113 +++++++++++++++++--------------------- desktop/package.json | 2 +- 2 files changed, 51 insertions(+), 64 deletions(-) diff --git a/desktop/package-lock.json b/desktop/package-lock.json index 881dcae5a..9644e1cda 100644 --- a/desktop/package-lock.json +++ b/desktop/package-lock.json @@ -53,7 +53,7 @@ "prettier": "3.3.3", "ts-node": "10.9.2", "typescript": "5.5.4", - "typescript-eslint": "8.8.1", + "typescript-eslint": "8.9.0", "wait-on": "8.0.1" }, "engines": { @@ -6399,17 +6399,16 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.8.1.tgz", - "integrity": "sha512-xfvdgA8AP/vxHgtgU310+WBnLB4uJQ9XdyP17RebG26rLtDrQJV3ZYrcopX91GrHmMoH8bdSwMRh2a//TiJ1jQ==", + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.9.0.tgz", + "integrity": "sha512-Y1n621OCy4m7/vTXNlCbMVp87zSd7NH0L9cXD8aIpOaNlzeWxIK4+Q19A68gSmTNRZn92UjocVUWDthGxtqHFg==", "dev": true, - "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.8.1", - "@typescript-eslint/type-utils": "8.8.1", - "@typescript-eslint/utils": "8.8.1", - "@typescript-eslint/visitor-keys": "8.8.1", + "@typescript-eslint/scope-manager": "8.9.0", + "@typescript-eslint/type-utils": "8.9.0", + "@typescript-eslint/utils": "8.9.0", + "@typescript-eslint/visitor-keys": "8.9.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -6433,16 +6432,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.8.1.tgz", - "integrity": "sha512-hQUVn2Lij2NAxVFEdvIGxT9gP1tq2yM83m+by3whWFsWC+1y8pxxxHUFE1UqDu2VsGi2i6RLcv4QvouM84U+ow==", + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.9.0.tgz", + "integrity": "sha512-U+BLn2rqTTHnc4FL3FJjxaXptTxmf9sNftJK62XLz4+GxG3hLHm/SUNaaXP5Y4uTiuYoL5YLy4JBCJe3+t8awQ==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/scope-manager": "8.8.1", - "@typescript-eslint/types": "8.8.1", - "@typescript-eslint/typescript-estree": "8.8.1", - "@typescript-eslint/visitor-keys": "8.8.1", + "@typescript-eslint/scope-manager": "8.9.0", + "@typescript-eslint/types": "8.9.0", + "@typescript-eslint/typescript-estree": "8.9.0", + "@typescript-eslint/visitor-keys": "8.9.0", "debug": "^4.3.4" }, "engines": { @@ -6462,14 +6460,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.8.1.tgz", - "integrity": "sha512-X4JdU+66Mazev/J0gfXlcC/dV6JI37h+93W9BRYXrSn0hrE64IoWgVkO9MSJgEzoWkxONgaQpICWg8vAN74wlA==", + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.9.0.tgz", + "integrity": "sha512-bZu9bUud9ym1cabmOYH9S6TnbWRzpklVmwqICeOulTCZ9ue2/pczWzQvt/cGj2r2o1RdKoZbuEMalJJSYw3pHQ==", "dev": true, - "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.8.1", - "@typescript-eslint/visitor-keys": "8.8.1" + "@typescript-eslint/types": "8.9.0", + "@typescript-eslint/visitor-keys": "8.9.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6480,14 +6477,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.8.1.tgz", - "integrity": "sha512-qSVnpcbLP8CALORf0za+vjLYj1Wp8HSoiI8zYU5tHxRVj30702Z1Yw4cLwfNKhTPWp5+P+k1pjmD5Zd1nhxiZA==", + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.9.0.tgz", + "integrity": "sha512-JD+/pCqlKqAk5961vxCluK+clkppHY07IbV3vett97KOV+8C6l+CPEPwpUuiMwgbOz/qrN3Ke4zzjqbT+ls+1Q==", "dev": true, - "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.8.1", - "@typescript-eslint/utils": "8.8.1", + "@typescript-eslint/typescript-estree": "8.9.0", + "@typescript-eslint/utils": "8.9.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -6505,11 +6501,10 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.8.1.tgz", - "integrity": "sha512-WCcTP4SDXzMd23N27u66zTKMuEevH4uzU8C9jf0RO4E04yVHgQgW+r+TeVTNnO1KIfrL8ebgVVYYMMO3+jC55Q==", + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.9.0.tgz", + "integrity": "sha512-SjgkvdYyt1FAPhU9c6FiYCXrldwYYlIQLkuc+LfAhCna6ggp96ACncdtlbn8FmnG72tUkXclrDExOpEYf1nfJQ==", "dev": true, - "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -6519,14 +6514,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.8.1.tgz", - "integrity": "sha512-A5d1R9p+X+1js4JogdNilDuuq+EHZdsH9MjTVxXOdVFfTJXunKJR/v+fNNyO4TnoOn5HqobzfRlc70NC6HTcdg==", + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.9.0.tgz", + "integrity": "sha512-9iJYTgKLDG6+iqegehc5+EqE6sqaee7kb8vWpmHZ86EqwDjmlqNNHeqDVqb9duh+BY6WCNHfIGvuVU3Tf9Db0g==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "8.8.1", - "@typescript-eslint/visitor-keys": "8.8.1", + "@typescript-eslint/types": "8.9.0", + "@typescript-eslint/visitor-keys": "8.9.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -6552,7 +6546,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -6564,16 +6557,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.8.1.tgz", - "integrity": "sha512-/QkNJDbV0bdL7H7d0/y0qBbV2HTtf0TIyjSDTvvmQEzeVx8jEImEbLuOA4EsvE8gIgqMitns0ifb5uQhMj8d9w==", + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.9.0.tgz", + "integrity": "sha512-PKgMmaSo/Yg/F7kIZvrgrWa1+Vwn036CdNUvYFEkYbPwOH4i8xvkaRlu148W3vtheWK9ckKRIz7PBP5oUlkrvQ==", "dev": true, - "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.8.1", - "@typescript-eslint/types": "8.8.1", - "@typescript-eslint/typescript-estree": "8.8.1" + "@typescript-eslint/scope-manager": "8.9.0", + "@typescript-eslint/types": "8.9.0", + "@typescript-eslint/typescript-estree": "8.9.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6587,13 +6579,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.8.1.tgz", - "integrity": "sha512-0/TdC3aeRAsW7MDvYRwEc1Uwm0TIBfzjPFgg60UU2Haj5qsCs9cc3zNgY71edqE3LbWfF/WoZQd3lJoDXFQpag==", + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.9.0.tgz", + "integrity": "sha512-Ht4y38ubk4L5/U8xKUBfKNYGmvKvA1CANoxiTRMM+tOLk3lbF3DvzZCxJCRSE+2GdCMSh6zq9VZJc3asc1XuAA==", "dev": true, - "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.8.1", + "@typescript-eslint/types": "8.9.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -6609,7 +6600,6 @@ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, - "license": "Apache-2.0", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -11968,8 +11958,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/hammerjs": { "version": "2.0.8", @@ -19533,7 +19522,6 @@ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=16" }, @@ -19965,15 +19953,14 @@ } }, "node_modules/typescript-eslint": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.8.1.tgz", - "integrity": "sha512-R0dsXFt6t4SAFjUSKFjMh4pXDtq04SsFKCVGDP3ZOzNP7itF0jBcZYU4fMsZr4y7O7V7Nc751dDeESbe4PbQMQ==", + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.9.0.tgz", + "integrity": "sha512-AuD/FXGYRQyqyOBCpNLldMlsCGvmDNxptQ3Dp58/NXeB+FqyvTfXmMyba3PYa0Vi9ybnj7G8S/yd/4Cw8y47eA==", "dev": true, - "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.8.1", - "@typescript-eslint/parser": "8.8.1", - "@typescript-eslint/utils": "8.8.1" + "@typescript-eslint/eslint-plugin": "8.9.0", + "@typescript-eslint/parser": "8.9.0", + "@typescript-eslint/utils": "8.9.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" diff --git a/desktop/package.json b/desktop/package.json index 43c355251..a2ff9cc1b 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -78,7 +78,7 @@ "prettier": "3.3.3", "ts-node": "10.9.2", "typescript": "5.5.4", - "typescript-eslint": "8.8.1", + "typescript-eslint": "8.9.0", "wait-on": "8.0.1" }, "engines": { From f256f085df5f784a4bbca5db4a3a4d852781f3a8 Mon Sep 17 00:00:00 2001 From: tiagohm Date: Mon, 14 Oct 2024 11:15:57 -0300 Subject: [PATCH 05/15] [api]: Restore Thread interrupt status on catch --- .../nebulosa/alpaca/discovery/AlpacaDiscoveryProtocol.kt | 4 ++++ .../src/main/kotlin/nebulosa/guiding/phd2/PHD2Guider.kt | 1 + .../nebulosa/indi/protocol/parser/INDIProtocolReader.kt | 1 + .../src/main/kotlin/nebulosa/util/exec/CommandLine.kt | 9 ++++----- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/nebulosa-alpaca-discovery-protocol/src/main/kotlin/nebulosa/alpaca/discovery/AlpacaDiscoveryProtocol.kt b/nebulosa-alpaca-discovery-protocol/src/main/kotlin/nebulosa/alpaca/discovery/AlpacaDiscoveryProtocol.kt index fd73a2f14..adf50ab33 100644 --- a/nebulosa-alpaca-discovery-protocol/src/main/kotlin/nebulosa/alpaca/discovery/AlpacaDiscoveryProtocol.kt +++ b/nebulosa-alpaca-discovery-protocol/src/main/kotlin/nebulosa/alpaca/discovery/AlpacaDiscoveryProtocol.kt @@ -50,6 +50,7 @@ class AlpacaDiscoveryProtocol : Runnable, AutoCloseable { val message = DatagramPacket(discoveryMessage, discoveryMessage.size, address.broadcast, 32227) socket.send(message) } catch (_: InterruptedException) { + Thread.currentThread().interrupt() break } catch (e: IOException) { LOG.e("socket IPv4 send error", e) @@ -58,6 +59,7 @@ class AlpacaDiscoveryProtocol : Runnable, AutoCloseable { } } } catch (_: InterruptedException) { + Thread.currentThread().interrupt() } catch (e: SocketException) { LOG.e("socket error", e) } @@ -66,6 +68,7 @@ class AlpacaDiscoveryProtocol : Runnable, AutoCloseable { val message = DatagramPacket(discoveryMessage, discoveryMessage.size, InetAddress.getByName("ff12::00a1:9aca"), 32227) socket.send(message) } catch (_: InterruptedException) { + Thread.currentThread().interrupt() } catch (e: IOException) { LOG.e("socket IPv6 send error", e) } @@ -78,6 +81,7 @@ class AlpacaDiscoveryProtocol : Runnable, AutoCloseable { try { socket.receive(packet) } catch (_: InterruptedException) { + Thread.currentThread().interrupt() break } catch (e: IOException) { LOG.e("socket receive error", e) diff --git a/nebulosa-guiding-phd2/src/main/kotlin/nebulosa/guiding/phd2/PHD2Guider.kt b/nebulosa-guiding-phd2/src/main/kotlin/nebulosa/guiding/phd2/PHD2Guider.kt index fe9694c39..70a80cb0f 100644 --- a/nebulosa-guiding-phd2/src/main/kotlin/nebulosa/guiding/phd2/PHD2Guider.kt +++ b/nebulosa-guiding-phd2/src/main/kotlin/nebulosa/guiding/phd2/PHD2Guider.kt @@ -236,6 +236,7 @@ class PHD2Guider(private val client: PHD2Client) : Guider, PHD2EventListener { try { settling.await(settleTimeout) } catch (_: InterruptedException) { + Thread.currentThread().interrupt() LOG.dw("PHD2 did not send SettleDone message in expected time") } catch (e: Throwable) { LOG.e("an error occurrs while waiting for settle done", e) diff --git a/nebulosa-indi-protocol/src/main/kotlin/nebulosa/indi/protocol/parser/INDIProtocolReader.kt b/nebulosa-indi-protocol/src/main/kotlin/nebulosa/indi/protocol/parser/INDIProtocolReader.kt index 6618fc94f..a6f9b9d43 100644 --- a/nebulosa-indi-protocol/src/main/kotlin/nebulosa/indi/protocol/parser/INDIProtocolReader.kt +++ b/nebulosa-indi-protocol/src/main/kotlin/nebulosa/indi/protocol/parser/INDIProtocolReader.kt @@ -47,6 +47,7 @@ class INDIProtocolReader( listeners.onEach { it.onConnectionClosed() }.clear() parser.close() } catch (_: InterruptedException) { + currentThread().interrupt() running = false } catch (e: Throwable) { running = false diff --git a/nebulosa-util/src/main/kotlin/nebulosa/util/exec/CommandLine.kt b/nebulosa-util/src/main/kotlin/nebulosa/util/exec/CommandLine.kt index 40a84d3d0..a79dfc8d9 100644 --- a/nebulosa-util/src/main/kotlin/nebulosa/util/exec/CommandLine.kt +++ b/nebulosa-util/src/main/kotlin/nebulosa/util/exec/CommandLine.kt @@ -141,7 +141,8 @@ class CommandLine internal constructor( inputReader?.waitFor() errorReader?.waitFor() } - } catch (ignored: InterruptedException) { + } catch (_: InterruptedException) { + currentThread().interrupt() } finally { if (process.isAlive) { process.destroyForcibly() @@ -160,10 +161,7 @@ class CommandLine internal constructor( } } - private inner class StreamLineReader( - stream: InputStream, - private val isError: Boolean, - ) : Thread("Command Line ${if (isError) "Error" else "Input"} Stream Line Reader") { + private inner class StreamLineReader(stream: InputStream, isError: Boolean) : Thread("Command Line ${if (isError) "Error" else "Input"} Stream Line Reader") { private val reader = stream.bufferedReader() private val completable = CompletableFuture() @@ -182,6 +180,7 @@ class CommandLine internal constructor( } } } catch (e: InterruptedException) { + currentThread().interrupt() LOG.dw("command line interrupted") } catch (e: Throwable) { LOG.dw("command line exited: {}", e.message) From b1b7278f9ec13d81def29d1ca3b1f8fc4a1812db Mon Sep 17 00:00:00 2001 From: tiagohm Date: Tue, 15 Oct 2024 18:27:47 -0300 Subject: [PATCH 06/15] [api][desktop]: Refactor command line --- .../kotlin/nebulosa/api/image/ImageSolved.kt | 5 + .../main/kotlin/nebulosa/api/inject/Inject.kt | 2 +- .../api/platesolver/PlateSolverController.kt | 6 +- .../api/platesolver/PlateSolverService.kt | 36 ++- .../nebulosa/api/stacker/StackerController.kt | 26 +- .../nebulosa/api/stacker/StackerService.kt | 61 +++-- desktop/src/app/atlas/atlas.component.html | 4 + .../app/autofocus/autofocus.component.html | 1 + desktop/src/app/image/image.component.html | 9 +- desktop/src/app/image/image.component.ts | 8 +- .../src/app/rotator/rotator.component.html | 1 + desktop/src/app/stacker/stacker.component.ts | 8 +- desktop/src/shared/services/api.service.ts | 28 +- desktop/src/shared/types/image.types.ts | 2 + .../point/three/ThreePointPolarAlignment.kt | 6 +- nebulosa-astap/build.gradle.kts | 1 + .../astap/platesolver/AstapPlateSolver.kt | 55 ++-- .../astap/stardetector/AstapStarDetector.kt | 22 +- .../src/test/kotlin/AstapPlateSolverTest.kt | 41 +++ .../src/test/kotlin/AstapStarDetectorTest.kt | 17 ++ .../LibAstrometryNetPlateSolver.kt | 6 +- nebulosa-astrometrynet/build.gradle.kts | 1 + .../LocalAstrometryNetPlateSolver.kt | 78 +++--- .../NovaAstrometryNetPlateSolver.kt | 12 +- .../src/test/kotlin/AutoFocusTest.kt | 67 ++--- nebulosa-commandline/build.gradle.kts | 18 ++ .../commandline/CommandLineListener.kt | 10 + .../commandline/CommandLineListenerHandler.kt | 78 ++++++ .../nebulosa/commandline/LineReaderThread.kt | 28 ++ nebulosa-image/src/test/kotlin/HFDTest.kt | 48 ++-- .../src/main/kotlin/nebulosa/log/Log.kt | 8 + nebulosa-pixinsight/build.gradle.kts | 2 +- .../platesolver/PixInsightPlateSolver.kt | 21 +- .../script/AbstractPixInsightScript.kt | 55 +--- .../pixinsight/script/PixInsightAlign.kt | 15 +- .../PixInsightAutomaticBackgroundExtractor.kt | 15 +- .../pixinsight/script/PixInsightCalibrate.kt | 14 +- .../script/PixInsightDetectStars.kt | 29 +- .../script/PixInsightFileFormatConversion.kt | 14 +- .../script/PixInsightImageSolver.kt | 20 +- .../pixinsight/script/PixInsightIsRunning.kt | 25 +- .../script/PixInsightLRGBCombination.kt | 14 +- .../script/PixInsightLuminanceCombination.kt | 14 +- .../pixinsight/script/PixInsightPixelMath.kt | 15 +- .../pixinsight/script/PixInsightScript.kt | 26 +- .../script/PixInsightScriptOutput.kt | 8 + .../script/PixInsightScriptRunner.kt | 62 ++++- .../pixinsight/script/PixInsightStartup.kt | 14 +- .../stacker/PixInsightAutoStacker.kt | 23 +- .../test/kotlin/PixInsightAutoStackerTest.kt | 11 +- .../test/kotlin/PixInsightLiveStackerTest.kt | 9 +- .../src/test/kotlin/PixInsightScriptTest.kt | 55 ++-- .../src/test/kotlin/PixInsightStackerTest.kt | 17 +- nebulosa-platesolver/build.gradle.kts | 1 - .../nebulosa/platesolver/PlateSolver.kt | 2 - nebulosa-siril/build.gradle.kts | 1 + .../main/kotlin/nebulosa/siril/command/Cd.kt | 6 +- .../nebulosa/siril/command/DumpHeader.kt | 6 +- .../kotlin/nebulosa/siril/command/FindStar.kt | 4 +- .../nebulosa/siril/command/LiveStack.kt | 4 +- .../kotlin/nebulosa/siril/command/Load.kt | 6 +- .../nebulosa/siril/command/PlateSolve.kt | 6 +- .../siril/command/SirilCommandLine.kt | 58 ++-- .../kotlin/nebulosa/siril/command/StartLs.kt | 6 +- .../siril/livestacker/SirilLiveStacker.kt | 6 +- .../siril/platesolver/SirilPlateSolver.kt | 20 +- nebulosa-siril/src/test/kotlin/SirilTest.kt | 38 +-- nebulosa-stacker/build.gradle.kts | 1 - .../kotlin/nebulosa/stacker/AutoStacker.kt | 8 +- .../main/kotlin/nebulosa/stacker/Stacker.kt | 5 +- .../src/main/kotlin/nebulosa/test/Files.kt | 40 +-- .../kotlin/nebulosa/test/fits/Astrometry.kt | 8 + .../main/kotlin/nebulosa/test/fits/Focus.kt | 31 +++ .../kotlin/nebulosa/test/fits/Stacking.kt | 18 ++ .../kotlin/nebulosa/util/exec/CommandLine.kt | 256 ------------------ .../nebulosa/util/exec/CommandLineListener.kt | 18 -- .../src/test/kotlin/CommandLineTest.kt | 76 ------ .../watney/platesolver/WatneyPlateSolver.kt | 2 - settings.gradle.kts | 2 + 79 files changed, 868 insertions(+), 932 deletions(-) create mode 100644 nebulosa-astap/src/test/kotlin/AstapPlateSolverTest.kt create mode 100644 nebulosa-astap/src/test/kotlin/AstapStarDetectorTest.kt create mode 100644 nebulosa-commandline/build.gradle.kts create mode 100644 nebulosa-commandline/src/main/kotlin/nebulosa/commandline/CommandLineListener.kt create mode 100644 nebulosa-commandline/src/main/kotlin/nebulosa/commandline/CommandLineListenerHandler.kt create mode 100644 nebulosa-commandline/src/main/kotlin/nebulosa/commandline/LineReaderThread.kt create mode 100644 nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightScriptOutput.kt create mode 100644 nebulosa-test/src/main/kotlin/nebulosa/test/fits/Astrometry.kt create mode 100644 nebulosa-test/src/main/kotlin/nebulosa/test/fits/Focus.kt create mode 100644 nebulosa-test/src/main/kotlin/nebulosa/test/fits/Stacking.kt delete mode 100644 nebulosa-util/src/main/kotlin/nebulosa/util/exec/CommandLine.kt delete mode 100644 nebulosa-util/src/main/kotlin/nebulosa/util/exec/CommandLineListener.kt delete mode 100644 nebulosa-util/src/test/kotlin/CommandLineTest.kt diff --git a/api/src/main/kotlin/nebulosa/api/image/ImageSolved.kt b/api/src/main/kotlin/nebulosa/api/image/ImageSolved.kt index 04202b45f..e291be005 100644 --- a/api/src/main/kotlin/nebulosa/api/image/ImageSolved.kt +++ b/api/src/main/kotlin/nebulosa/api/image/ImageSolved.kt @@ -23,4 +23,9 @@ data class ImageSolved( solution.width.toArcmin, solution.height.toArcmin, solution.radius.toDegrees, ) + + companion object { + + @JvmStatic val NO_SOLUTION = ImageSolved(PlateSolution.NO_SOLUTION) + } } diff --git a/api/src/main/kotlin/nebulosa/api/inject/Inject.kt b/api/src/main/kotlin/nebulosa/api/inject/Inject.kt index dae46eae6..75c29dede 100644 --- a/api/src/main/kotlin/nebulosa/api/inject/Inject.kt +++ b/api/src/main/kotlin/nebulosa/api/inject/Inject.kt @@ -296,7 +296,7 @@ fun servicesModule() = module { single { CalibrationFrameService(get()) } single { FramingService(get(), get()) } single { ImageService(get(), get(), get(), get(), get(), get(), get(), get(), get()) } - single { PlateSolverService(get()) } + single { PlateSolverService(get(), get()) } single { FlatWizardExecutor(get(), get(), get()) } single { FlatWizardService(get(Named.capturesDir), get()) } single { StarDetectionService() } diff --git a/api/src/main/kotlin/nebulosa/api/platesolver/PlateSolverController.kt b/api/src/main/kotlin/nebulosa/api/platesolver/PlateSolverController.kt index 56542f54d..43284d7b0 100644 --- a/api/src/main/kotlin/nebulosa/api/platesolver/PlateSolverController.kt +++ b/api/src/main/kotlin/nebulosa/api/platesolver/PlateSolverController.kt @@ -21,12 +21,12 @@ class PlateSolverController( private fun start(ctx: Context) { val path = ctx.queryParam("path").notNullOrBlank().path().exists() + val key = ctx.queryParam("key").notNullOrBlank() val solver = ctx.bodyAsClass().valid() - ctx.json(plateSolverService.solveImage(solver, path)) + ctx.json(plateSolverService.start(solver, path, key)) } - @Suppress("UNUSED_PARAMETER") private fun stop(ctx: Context) { - plateSolverService.stopSolver() + plateSolverService.stop(ctx.queryParam("key").notNullOrBlank()) } } diff --git a/api/src/main/kotlin/nebulosa/api/platesolver/PlateSolverService.kt b/api/src/main/kotlin/nebulosa/api/platesolver/PlateSolverService.kt index e5fa0191a..bb43eedae 100644 --- a/api/src/main/kotlin/nebulosa/api/platesolver/PlateSolverService.kt +++ b/api/src/main/kotlin/nebulosa/api/platesolver/PlateSolverService.kt @@ -2,31 +2,45 @@ package nebulosa.api.platesolver import nebulosa.api.image.ImageBucket import nebulosa.api.image.ImageSolved -import nebulosa.util.concurrency.cancellation.CancellationToken +import nebulosa.platesolver.PlateSolution import java.nio.file.Path -import java.util.concurrent.atomic.AtomicReference +import java.util.concurrent.* class PlateSolverService( private val imageBucket: ImageBucket, + private val executor: ExecutorService, ) { - private val cancellationToken = AtomicReference() + private val tasks = ConcurrentHashMap>(4) + + fun start(request: PlateSolverRequest, path: Path, key: String): ImageSolved { + val calibration = try { + solve(request, path, key).get() + } catch (e: CancellationException) { + return ImageSolved.NO_SOLUTION + } - fun solveImage(request: PlateSolverRequest, path: Path): ImageSolved { - val calibration = solve(request, path) imageBucket.put(path, calibration) + + if (!calibration.solved) { + throw RuntimeException("no solution found!") + } + return ImageSolved(calibration) } - @Synchronized - fun solve(request: PlateSolverRequest, path: Path) = CancellationToken().use { - cancellationToken.set(it) + fun solve(request: PlateSolverRequest, path: Path, key: String): Future { val solver = request.get() val radius = if (request.blind) 0.0 else request.radius - solver.solve(path, null, request.centerRA, request.centerDEC, radius, request.downsampleFactor, request.timeout, it) + + val task = FutureTask { solver.solve(path, null, request.centerRA, request.centerDEC, radius, request.downsampleFactor, request.timeout) } + + tasks[key] = task + executor.submit(task) + return task } - fun stopSolver() { - cancellationToken.get()?.cancel() + fun stop(key: String) { + tasks.remove(key)?.cancel(true) } } diff --git a/api/src/main/kotlin/nebulosa/api/stacker/StackerController.kt b/api/src/main/kotlin/nebulosa/api/stacker/StackerController.kt index c5829c85a..e1264f094 100644 --- a/api/src/main/kotlin/nebulosa/api/stacker/StackerController.kt +++ b/api/src/main/kotlin/nebulosa/api/stacker/StackerController.kt @@ -4,20 +4,13 @@ import io.javalin.Javalin import io.javalin.http.Context import io.javalin.http.bodyAsClass import nebulosa.api.core.Controller -import nebulosa.api.validators.exists -import nebulosa.api.validators.notNull -import nebulosa.api.validators.path -import nebulosa.api.validators.valid -import nebulosa.util.concurrency.cancellation.CancellationToken -import java.util.concurrent.atomic.AtomicReference +import nebulosa.api.validators.* class StackerController( override val app: Javalin, private val stackerService: StackerService, ) : Controller { - private val cancellationToken = AtomicReference() - init { app.put("stacker/start", ::start) app.get("stacker/running", ::isRunning) @@ -26,24 +19,19 @@ class StackerController( } private fun start(ctx: Context) { + val key = ctx.queryParam("key").notNullOrBlank() val body = ctx.bodyAsClass().valid() - - if (cancellationToken.compareAndSet(null, CancellationToken())) { - try { - stackerService.stack(body, cancellationToken.get())?.also(ctx::json) - } finally { - cancellationToken.getAndSet(null)?.unlistenAll() - } - } + stackerService.stack(body, key)?.also(ctx::json) } private fun isRunning(ctx: Context) { - ctx.json(cancellationToken.get() != null) + val key = ctx.queryParam("key").notNullOrBlank() + ctx.json(stackerService.isRunning(key)) } - @Suppress("UNUSED_PARAMETER") private fun stop(ctx: Context) { - cancellationToken.get()?.cancel() + val key = ctx.queryParam("key").notNullOrBlank() + stackerService.stop(key) } private fun analyze(ctx: Context) { diff --git a/api/src/main/kotlin/nebulosa/api/stacker/StackerService.kt b/api/src/main/kotlin/nebulosa/api/stacker/StackerService.kt index ee6279215..aaada6b06 100644 --- a/api/src/main/kotlin/nebulosa/api/stacker/StackerService.kt +++ b/api/src/main/kotlin/nebulosa/api/stacker/StackerService.kt @@ -6,19 +6,21 @@ import nebulosa.fits.isFits import nebulosa.image.format.ImageHdu import nebulosa.stacker.AutoStacker import nebulosa.stacker.AutoStackerListener -import nebulosa.util.concurrency.cancellation.CancellationToken import nebulosa.xisf.isXisf import nebulosa.xisf.xisf import java.nio.file.Path +import java.util.concurrent.ConcurrentHashMap import kotlin.io.path.exists import kotlin.io.path.isDirectory import kotlin.io.path.isRegularFile class StackerService(private val messageService: MessageService?) { - @Synchronized - fun stack(request: StackingRequest, cancellationToken: CancellationToken = CancellationToken.NONE): Path? { + private val stackers = ConcurrentHashMap(1) + + fun stack(request: StackingRequest, key: String): Path? { require(request.outputDirectory != null && request.outputDirectory.exists() && request.outputDirectory.isDirectory()) + require(!stackers.containsKey(key)) { "stacking is already in progress" } val luminance = request.targets.filter { it.enabled && it.group == StackerGroupType.LUMINANCE } val red = request.targets.filter { it.enabled && it.group == StackerGroupType.RED } @@ -32,23 +34,20 @@ class StackerService(private val messageService: MessageService?) { // Combined LRGB return if (red.size + green.size + blue.size >= 1) { val stacker = request.get() + stackers[key] = stacker val autoStackerMessageHandler = AutoStackerMessageHandler(luminance, red, green, blue) try { stacker.registerAutoStackerListener(autoStackerMessageHandler) - val stackedLuminancePath = luminance.stack(request, stacker, name, StackerGroupType.LUMINANCE, cancellationToken) - val stackedRedPath = red.stack(request, stacker, name, StackerGroupType.RED, cancellationToken) - val stackedGreenPath = green.stack(request, stacker, name, StackerGroupType.GREEN, cancellationToken) - val stackedBluePath = blue.stack(request, stacker, name, StackerGroupType.BLUE, cancellationToken) + val stackedLuminancePath = luminance.stack(request, stacker, name, StackerGroupType.LUMINANCE) + val stackedRedPath = red.stack(request, stacker, name, StackerGroupType.RED) + val stackedGreenPath = green.stack(request, stacker, name, StackerGroupType.GREEN) + val stackedBluePath = blue.stack(request, stacker, name, StackerGroupType.BLUE) - if (cancellationToken.isCancelled) { - null - } else { - val combinedPath = Path.of("${request.outputDirectory}", "$name.LRGB.fits") - stacker.combineLRGB(combinedPath, stackedLuminancePath, stackedRedPath, stackedGreenPath, stackedBluePath) - combinedPath - } + val combinedPath = Path.of("${request.outputDirectory}", "$name.LRGB.fits") + stacker.combineLRGB(combinedPath, stackedLuminancePath, stackedRedPath, stackedGreenPath, stackedBluePath) + combinedPath } finally { messageService?.sendMessage(StackerEvent.IDLE) stacker.unregisterAutoStackerListener(autoStackerMessageHandler) @@ -57,17 +56,16 @@ class StackerService(private val messageService: MessageService?) { // LRGB else if (rgb.isNotEmpty()) { val stacker = request.get() + stackers[key] = stacker val autoStackerMessageHandler = AutoStackerMessageHandler(luminance, rgb = rgb) try { stacker.registerAutoStackerListener(autoStackerMessageHandler) - val stackedLuminancePath = luminance.stack(request, stacker, name, StackerGroupType.LUMINANCE, cancellationToken) - val stackedRGBPath = rgb.stack(request, stacker, name, StackerGroupType.RGB, cancellationToken) + val stackedLuminancePath = luminance.stack(request, stacker, name, StackerGroupType.LUMINANCE) + val stackedRGBPath = rgb.stack(request, stacker, name, StackerGroupType.RGB) - if (cancellationToken.isCancelled) { - null - } else if (stackedLuminancePath != null && stackedRGBPath != null) { + if (stackedLuminancePath != null && stackedRGBPath != null) { val combinedPath = Path.of("${request.outputDirectory}", "$name.LRGB.fits") stacker.combineLuminance(combinedPath, stackedLuminancePath, stackedRGBPath, false) combinedPath @@ -82,17 +80,16 @@ class StackerService(private val messageService: MessageService?) { // MONO else if (mono.isNotEmpty() || luminance.isNotEmpty()) { val stacker = request.get() + stackers[key] = stacker val autoStackerMessageHandler = AutoStackerMessageHandler(luminance, mono = mono) try { stacker.registerAutoStackerListener(autoStackerMessageHandler) - val stackedLuminancePath = luminance.stack(request, stacker, name, StackerGroupType.LUMINANCE, cancellationToken) - val stackedMonoPath = mono.stack(request, stacker, name, StackerGroupType.MONO, cancellationToken) + val stackedLuminancePath = luminance.stack(request, stacker, name, StackerGroupType.LUMINANCE) + val stackedMonoPath = mono.stack(request, stacker, name, StackerGroupType.MONO) - if (cancellationToken.isCancelled) { - null - } else if (stackedLuminancePath != null && stackedMonoPath != null) { + if (stackedLuminancePath != null && stackedMonoPath != null) { val combinedPath = Path.of("${request.outputDirectory}", "$name.LRGB.fits") stacker.combineLuminance(combinedPath, stackedLuminancePath, stackedMonoPath, true) combinedPath @@ -108,15 +105,21 @@ class StackerService(private val messageService: MessageService?) { } } + fun isRunning(key: String): Boolean { + return stackers.containsKey(key) + } + + fun stop(key: String) { + stackers.remove(key)?.stop() + } + private fun List.stack( request: StackingRequest, stacker: AutoStacker, - name: String, group: StackerGroupType, cancellationToken: CancellationToken, + name: String, group: StackerGroupType ): Path? { - return if (cancellationToken.isCancelled) { - null - } else if (size > 1) { + return if (size > 1) { val outputPath = Path.of("${request.outputDirectory}", "$name.$group.fits") - if (stacker.stack(map { it.path!! }, outputPath, request.referencePath!!, cancellationToken)) outputPath else null + if (stacker.stack(map { it.path!! }, outputPath, request.referencePath!!)) outputPath else null } else if (isNotEmpty()) { val outputPath = Path.of("${request.outputDirectory}", "$name.$group.fits") if (stacker.align(request.referencePath!!, this[0].path!!, outputPath)) outputPath else null diff --git a/desktop/src/app/atlas/atlas.component.html b/desktop/src/app/atlas/atlas.component.html index 34f2bd1ac..4e6fb831e 100644 --- a/desktop/src/app/atlas/atlas.component.html +++ b/desktop/src/app/atlas/atlas.component.html @@ -229,6 +229,7 @@ [max]="100" class="w-full" [step]="0.1" + [minFractionDigits]="1" [showButtons]="true" inputStyleClass="p-inputtext-sm border-0 w-full" locale="en" @@ -746,6 +747,7 @@ [max]="90" class="w-full" [step]="0.1" + [minFractionDigits]="1" [maxFractionDigits]="1" [showButtons]="true" inputStyleClass="p-inputtext-sm border-0 w-full" @@ -792,6 +794,7 @@ [max]="30" class="w-full" [step]="0.1" + [minFractionDigits]="1" [maxFractionDigits]="1" [showButtons]="true" inputStyleClass="p-inputtext-sm border-0 w-full" @@ -808,6 +811,7 @@ [max]="30" class="w-full" [step]="0.1" + [minFractionDigits]="1" [maxFractionDigits]="1" [showButtons]="true" inputStyleClass="p-inputtext-sm border-0 w-full" diff --git a/desktop/src/app/autofocus/autofocus.component.html b/desktop/src/app/autofocus/autofocus.component.html index d4b95892c..1effbb8f0 100644 --- a/desktop/src/app/autofocus/autofocus.component.html +++ b/desktop/src/app/autofocus/autofocus.component.html @@ -134,6 +134,7 @@ [min]="0" [max]="1" [step]="0.1" + [minFractionDigits]="1" (ngModelChange)="savePreference()" locale="en" spinnableNumber /> diff --git a/desktop/src/app/image/image.component.html b/desktop/src/app/image/image.component.html index b6a2d6d04..4d6a7b1e5 100644 --- a/desktop/src/app/image/image.component.html +++ b/desktop/src/app/image/image.component.html @@ -496,6 +496,7 @@ [min]="0" [max]="100" [step]="0.01" + [minFractionDigits]="1" styleClass="p-inputtext-sm border-0 w-full" [showButtons]="true" [(ngModel)]="solver.request.pixelSize" @@ -1169,7 +1170,7 @@ [min]="0" [max]="99.99" [step]="0.01" - [minFractionDigits]="0" + [minFractionDigits]="1" [maxFractionDigits]="2" [(ngModel)]="fov.selected.pixelSize.width" (ngModelChange)="saveFOV()" @@ -1186,7 +1187,7 @@ [min]="0" [max]="99.99" [step]="0.01" - [minFractionDigits]="0" + [minFractionDigits]="1" [maxFractionDigits]="2" [(ngModel)]="fov.selected.pixelSize.height" (ngModelChange)="saveFOV()" @@ -1203,7 +1204,7 @@ [min]="0.01" [max]="5" [step]="0.01" - [minFractionDigits]="0" + [minFractionDigits]="1" [maxFractionDigits]="2" [(ngModel)]="fov.selected.barlowReducer" (ngModelChange)="saveFOV()" @@ -1233,7 +1234,7 @@ [min]="0" [max]="360" [step]="0.01" - [minFractionDigits]="0" + [minFractionDigits]="1" [maxFractionDigits]="2" [(ngModel)]="fov.rotation" (ngModelChange)="saveFOV(false)" diff --git a/desktop/src/app/image/image.component.ts b/desktop/src/app/image/image.component.ts index 74b37d3b9..cd33d12fd 100644 --- a/desktop/src/app/image/image.component.ts +++ b/desktop/src/app/image/image.component.ts @@ -2,6 +2,7 @@ import { AfterViewInit, Component, ElementRef, HostListener, NgZone, OnDestroy, import { ActivatedRoute } from '@angular/router' import hotkeys from 'hotkeys-js' import { NgxLegacyMoveableComponent, OnDrag, OnResize, OnRotate } from 'ngx-moveable' +import { nuid } from 'nuid' import createPanZoom from 'panzoom' import { basename, dirname, extname } from 'path' import { ContextMenu } from 'primeng/contextmenu' @@ -516,6 +517,8 @@ export class ImageComponent implements AfterViewInit, OnDestroy { }) this.loadPreference() + + this.solver.key = nuid.next() } async ngAfterViewInit() { @@ -1125,9 +1128,10 @@ export class ImageComponent implements AfterViewInit, OnDestroy { const request: PlateSolverRequest = { ...this.solver.request, ...this.preferenceService.settings.get().plateSolver[this.solver.request.type], + type: this.solver.request.type, } - const solved = await this.api.solverStart(request, path) + const solved = await this.api.solverStart(request, path, this.solver.key) this.updateImageSolved(solved) } catch { @@ -1143,7 +1147,7 @@ export class ImageComponent implements AfterViewInit, OnDestroy { } protected solverStop() { - return this.api.solverStop() + return this.api.solverStop(this.solver.key) } private updateImageSolved(solved?: ImageSolved) { diff --git a/desktop/src/app/rotator/rotator.component.html b/desktop/src/app/rotator/rotator.component.html index 9cc577a9b..04ce4ca45 100644 --- a/desktop/src/app/rotator/rotator.component.html +++ b/desktop/src/app/rotator/rotator.component.html @@ -74,6 +74,7 @@ (`sky-atlas/twilight?${query}`) } @@ -740,13 +740,14 @@ export class ApiService { // SOLVER - solverStart(solver: PlateSolverRequest, path: string) { - const query = this.http.query({ path }) + solverStart(solver: PlateSolverRequest, path: string, key: string) { + const query = this.http.query({ path, key }) return this.http.put(`plate-solver/start?${query}`, solver) } - solverStop() { - return this.http.put('plate-solver/stop') + solverStop(key: string) { + const query = this.http.query({ key }) + return this.http.put(`plate-solver/stop?${query}`) } // AUTO FOCUS @@ -761,16 +762,19 @@ export class ApiService { // STACKER - stackerStart(request: StackingRequest) { - return this.http.put('stacker/start', request) + stackerStart(request: StackingRequest, key: string) { + const query = this.http.query({ key }) + return this.http.put(`stacker/start?${query}`, request) } - stackerIsRunning() { - return this.http.get('stacker/running') + stackerIsRunning( key: string) { + const query = this.http.query({ key }) + return this.http.get(`stacker/running?${query}`) } - stackerStop() { - return this.http.put('stacker/stop') + stackerStop( key: string) { + const query = this.http.query({ key }) + return this.http.put(`stacker/stop?${query}`) } stackerAnalyze(path: string) { diff --git a/desktop/src/shared/types/image.types.ts b/desktop/src/shared/types/image.types.ts index 8d41c305b..b1fb79293 100644 --- a/desktop/src/shared/types/image.types.ts +++ b/desktop/src/shared/types/image.types.ts @@ -212,6 +212,7 @@ export interface ImageStretchDialog { export interface ImageSolverDialog { showDialog: boolean + key: string running: boolean request: PlateSolverRequest readonly solved: ImageSolved @@ -381,6 +382,7 @@ export const DEFAULT_IMAGE_TRANSFORMATION: ImageTransformation = { export const DEFAULT_IMAGE_SOLVER_DIALOG: ImageSolverDialog = { showDialog: false, + key: '', running: false, request: DEFAULT_PLATE_SOLVER_REQUEST, solved: DEFAULT_IMAGE_SOLVED, diff --git a/nebulosa-alignment/src/main/kotlin/nebulosa/alignment/polar/point/three/ThreePointPolarAlignment.kt b/nebulosa-alignment/src/main/kotlin/nebulosa/alignment/polar/point/three/ThreePointPolarAlignment.kt index 8ecd32ee2..c7b3550a3 100644 --- a/nebulosa-alignment/src/main/kotlin/nebulosa/alignment/polar/point/three/ThreePointPolarAlignment.kt +++ b/nebulosa-alignment/src/main/kotlin/nebulosa/alignment/polar/point/three/ThreePointPolarAlignment.kt @@ -8,7 +8,6 @@ import nebulosa.platesolver.PlateSolver import nebulosa.platesolver.PlateSolverException import nebulosa.time.UTC import nebulosa.util.Resettable -import nebulosa.util.concurrency.cancellation.CancellationToken import java.nio.file.Path /** @@ -54,12 +53,13 @@ data class ThreePointPolarAlignment( path: Path, rightAscension: Angle, declination: Angle, radius: Angle = DEFAULT_RADIUS, compensateRefraction: Boolean = false, - cancellationToken: CancellationToken = CancellationToken.NONE, ): ThreePointPolarAlignmentResult { val solution = try { - solver.solve(path, null, rightAscension, declination, radius, cancellationToken = cancellationToken) + solver.solve(path, null, rightAscension, declination, radius) } catch (e: PlateSolverException) { return NoPlateSolution(e) + } catch (e: Throwable) { + return NoPlateSolution(null) } if (!solution.solved) { diff --git a/nebulosa-astap/build.gradle.kts b/nebulosa-astap/build.gradle.kts index aea4754a3..f40f0abc4 100644 --- a/nebulosa-astap/build.gradle.kts +++ b/nebulosa-astap/build.gradle.kts @@ -4,6 +4,7 @@ plugins { } dependencies { + api(project(":nebulosa-commandline")) api(project(":nebulosa-platesolver")) api(project(":nebulosa-stardetector")) api(project(":nebulosa-util")) diff --git a/nebulosa-astap/src/main/kotlin/nebulosa/astap/platesolver/AstapPlateSolver.kt b/nebulosa-astap/src/main/kotlin/nebulosa/astap/platesolver/AstapPlateSolver.kt index 80b1fb2e0..1f55cd377 100644 --- a/nebulosa-astap/src/main/kotlin/nebulosa/astap/platesolver/AstapPlateSolver.kt +++ b/nebulosa-astap/src/main/kotlin/nebulosa/astap/platesolver/AstapPlateSolver.kt @@ -13,8 +13,9 @@ import nebulosa.math.toHours import nebulosa.platesolver.PlateSolution import nebulosa.platesolver.PlateSolver import nebulosa.platesolver.PlateSolverException -import nebulosa.util.concurrency.cancellation.CancellationToken -import nebulosa.util.exec.commandLine +import org.apache.commons.exec.CommandLine +import org.apache.commons.exec.DefaultExecutor +import org.apache.commons.exec.ExecuteWatchdog import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths @@ -33,7 +34,6 @@ data class AstapPlateSolver(private val executablePath: Path) : PlateSolver { path: Path?, image: Image?, centerRA: Angle, centerDEC: Angle, radius: Angle, downsampleFactor: Int, timeout: Duration, - cancellationToken: CancellationToken, ): PlateSolution { requireNotNull(path) { "path is required" } @@ -41,35 +41,33 @@ data class AstapPlateSolver(private val executablePath: Path) : PlateSolver { val baseName = UUID.randomUUID().toString() val outFile = Paths.get("$basePath", baseName) - val cmd = commandLine { - executablePath(executablePath) - workingDirectory(path.parent) - - putArg("-o", outFile) - putArg("-z", downsampleFactor) - putArg("-fov", "0") // auto + val commandline = CommandLine.parse("$executablePath") + .addArgument("-o").addArgument("$outFile") + .addArgument("-z").addArgument("$downsampleFactor") + .addArgument("-fov").addArgument("0") // auto + + if (radius.toDegrees >= 0.1 && centerRA.isFinite() && centerDEC.isFinite()) { + commandline.addArgument("-ra").addArgument("${centerRA.toHours}") + commandline.addArgument("-spd").addArgument("${centerDEC.toDegrees + 90.0}") + commandline.addArgument("-r").addArgument("${ceil(radius.toDegrees)}") + } else { + commandline.addArgument("-r").addArgument("180.0") + } - if (radius.toDegrees >= 0.1 && centerRA.isFinite() && centerDEC.isFinite()) { - putArg("-ra", centerRA.toHours) - putArg("-spd", centerDEC.toDegrees + 90.0) - putArg("-r", ceil(radius.toDegrees)) - } else { - putArg("-r", "180.0") - } + commandline.addArgument("-f").addArgument("$path") - putArg("-f", path) - } + LOG.di("astap solving. command={}", commandline) - LOG.di("astap solving. command={}", cmd.command) + val executor = DefaultExecutor.builder() + .setWorkingDirectory(path.parent.toFile()) + .get() try { - val timeoutOrDefault = timeout.takeIf { it.toSeconds() > 0 } ?: Duration.ofMinutes(5) - cancellationToken.listen(cmd) - cmd.start(timeoutOrDefault) - - LOG.di("astap exited. code={}", cmd.get()) + executor.watchdog = ExecuteWatchdog.builder() + .setTimeout(timeout.takeIf { it.toSeconds() > 0 } ?: Duration.ofMinutes(5)) + .get() - if (cancellationToken.isCancelled) return PlateSolution.NO_SOLUTION + LOG.di("astap exited. code={}", executor.execute(commandline)) val ini = Properties() Paths.get("$basePath", "$baseName.ini").inputStream().use(ini::load) @@ -123,13 +121,10 @@ data class AstapPlateSolver(private val executablePath: Path) : PlateSolver { return solution } else { - val message = ini.getProperty("ERROR") - ?: ini.getProperty("WARNING") - ?: "plate solving failed" + val message = ini.getProperty("ERROR") ?: ini.getProperty("WARNING") ?: "plate solving failed" throw PlateSolverException(message) } } finally { - cancellationToken.unlisten(cmd) basePath.deleteRecursively() } } diff --git a/nebulosa-astap/src/main/kotlin/nebulosa/astap/stardetector/AstapStarDetector.kt b/nebulosa-astap/src/main/kotlin/nebulosa/astap/stardetector/AstapStarDetector.kt index bf4d5e67b..842e28b54 100644 --- a/nebulosa-astap/src/main/kotlin/nebulosa/astap/stardetector/AstapStarDetector.kt +++ b/nebulosa-astap/src/main/kotlin/nebulosa/astap/stardetector/AstapStarDetector.kt @@ -7,7 +7,8 @@ import nebulosa.log.e import nebulosa.log.loggerFor import nebulosa.stardetector.StarDetector import nebulosa.stardetector.StarPoint -import nebulosa.util.exec.commandLine +import org.apache.commons.exec.CommandLine +import org.apache.commons.exec.DefaultExecutor import java.io.InputStreamReader import java.nio.file.Path import kotlin.io.path.deleteIfExists @@ -29,18 +30,17 @@ data class AstapStarDetector( ) : StarPoint override fun detect(input: Path): List { - val cmd = commandLine { - executablePath(executablePath) - workingDirectory(input.parent) - - putArg("-f", input) - putArg("-z", "0") - putArg("-extract", "$minSNR") - } + val commandline = CommandLine.parse("$executablePath") + .addArgument("-f").addArgument("$input") + .addArgument("-z").addArgument("0") + .addArgument("-extract").addArgument("$minSNR") try { - cmd.start() - LOG.d("astap exited. code={}", cmd.get()) + val executor = DefaultExecutor.builder() + .setWorkingDirectory(input.parent.toFile()) + .get() + + LOG.d("astap exited. code={}", executor.execute(commandline)) } catch (e: Throwable) { LOG.e("astap failed", e) return emptyList() diff --git a/nebulosa-astap/src/test/kotlin/AstapPlateSolverTest.kt b/nebulosa-astap/src/test/kotlin/AstapPlateSolverTest.kt new file mode 100644 index 000000000..bf6dc6eb5 --- /dev/null +++ b/nebulosa-astap/src/test/kotlin/AstapPlateSolverTest.kt @@ -0,0 +1,41 @@ +import io.kotest.matchers.booleans.shouldBeTrue +import io.kotest.matchers.doubles.plusOrMinus +import io.kotest.matchers.shouldBe +import nebulosa.astap.platesolver.AstapPlateSolver +import nebulosa.math.* +import nebulosa.test.NonGitHubOnly +import nebulosa.test.fits.ASTROMETRY_GALACTIC_CENTER_FITS +import org.junit.jupiter.api.Test +import kotlin.io.path.Path + +@NonGitHubOnly +class AstapPlateSolverTest { + + @Test + fun blind() { + val solver = AstapPlateSolver(Path("astap")) + val solution = solver.solve(ASTROMETRY_GALACTIC_CENTER_FITS, null) + + solution.solved.shouldBeTrue() + solution.orientation.toDegrees shouldBe (-179.8412 plusOrMinus 0.1) + solution.scale.toArcsec shouldBe (2.3204 plusOrMinus 0.01) + solution.rightAscension.toDegrees shouldBe ("17 45 47.4".hours.toDegrees plusOrMinus (1.0 / 3600.0)) + solution.declination.toDegrees shouldBe ("-029 07 26.3".deg.toDegrees plusOrMinus (1.0 / 3600.0)) + solution.width.toArcmin shouldBe (49.50 plusOrMinus 0.01) + solution.height.toArcmin shouldBe (39.60 plusOrMinus 0.01) + } + + @Test + fun nearest() { + val solver = AstapPlateSolver(Path("astap")) + val solution = solver.solve(ASTROMETRY_GALACTIC_CENTER_FITS, null, "17 47 14.4".hours, "-029 01 06.9".deg, 4.deg) + + solution.solved.shouldBeTrue() + solution.orientation.toDegrees shouldBe (-179.8412 plusOrMinus 0.1) + solution.scale.toArcsec shouldBe (2.3204 plusOrMinus 0.01) + solution.rightAscension.toDegrees shouldBe ("17 45 47.4".hours.toDegrees plusOrMinus (1.0 / 3600.0)) + solution.declination.toDegrees shouldBe ("-029 07 26.3".deg.toDegrees plusOrMinus (1.0 / 3600.0)) + solution.width.toArcmin shouldBe (49.50 plusOrMinus 0.01) + solution.height.toArcmin shouldBe (39.60 plusOrMinus 0.01) + } +} diff --git a/nebulosa-astap/src/test/kotlin/AstapStarDetectorTest.kt b/nebulosa-astap/src/test/kotlin/AstapStarDetectorTest.kt new file mode 100644 index 000000000..a62812017 --- /dev/null +++ b/nebulosa-astap/src/test/kotlin/AstapStarDetectorTest.kt @@ -0,0 +1,17 @@ +import io.kotest.matchers.collections.shouldHaveSize +import nebulosa.astap.stardetector.AstapStarDetector +import nebulosa.test.NonGitHubOnly +import nebulosa.test.fits.ASTROMETRY_GALACTIC_CENTER_FITS +import org.junit.jupiter.api.Test +import kotlin.io.path.Path + +@NonGitHubOnly +class AstapStarDetectorTest { + + @Test + fun detect() { + val solver = AstapStarDetector(Path("astap")) + val detectedStars = solver.detect(ASTROMETRY_GALACTIC_CENTER_FITS) + detectedStars shouldHaveSize 429 + } +} diff --git a/nebulosa-astrometrynet-jna/src/main/kotlin/nebulosa/astrometrynet/platesolver/LibAstrometryNetPlateSolver.kt b/nebulosa-astrometrynet-jna/src/main/kotlin/nebulosa/astrometrynet/platesolver/LibAstrometryNetPlateSolver.kt index 7db19d419..773141199 100644 --- a/nebulosa-astrometrynet-jna/src/main/kotlin/nebulosa/astrometrynet/platesolver/LibAstrometryNetPlateSolver.kt +++ b/nebulosa-astrometrynet-jna/src/main/kotlin/nebulosa/astrometrynet/platesolver/LibAstrometryNetPlateSolver.kt @@ -4,7 +4,6 @@ import nebulosa.image.Image import nebulosa.math.Angle import nebulosa.platesolver.PlateSolution import nebulosa.platesolver.PlateSolver -import nebulosa.util.concurrency.cancellation.CancellationToken import java.nio.file.Path import java.time.Duration @@ -14,8 +13,5 @@ data class LibAstrometryNetPlateSolver(private val solver: LibAstrometryNet) : P path: Path?, image: Image?, centerRA: Angle, centerDEC: Angle, radius: Angle, downsampleFactor: Int, timeout: Duration, - cancellationToken: CancellationToken, - ): PlateSolution { - return PlateSolution.NO_SOLUTION - } + ) = PlateSolution.NO_SOLUTION } diff --git a/nebulosa-astrometrynet/build.gradle.kts b/nebulosa-astrometrynet/build.gradle.kts index 61aa6e3b8..3bac91b62 100644 --- a/nebulosa-astrometrynet/build.gradle.kts +++ b/nebulosa-astrometrynet/build.gradle.kts @@ -5,6 +5,7 @@ plugins { dependencies { api(project(":nebulosa-math")) + api(project(":nebulosa-commandline")) api(project(":nebulosa-platesolver")) api(project(":nebulosa-retrofit")) api(project(":nebulosa-util")) diff --git a/nebulosa-astrometrynet/src/main/kotlin/nebulosa/astrometrynet/platesolver/LocalAstrometryNetPlateSolver.kt b/nebulosa-astrometrynet/src/main/kotlin/nebulosa/astrometrynet/platesolver/LocalAstrometryNetPlateSolver.kt index 71ec3ddaf..387f44202 100644 --- a/nebulosa-astrometrynet/src/main/kotlin/nebulosa/astrometrynet/platesolver/LocalAstrometryNetPlateSolver.kt +++ b/nebulosa-astrometrynet/src/main/kotlin/nebulosa/astrometrynet/platesolver/LocalAstrometryNetPlateSolver.kt @@ -1,15 +1,16 @@ package nebulosa.astrometrynet.platesolver +import nebulosa.commandline.CommandLineListener +import nebulosa.commandline.CommandLineListenerHandler import nebulosa.image.Image import nebulosa.log.di -import nebulosa.log.e import nebulosa.log.loggerFor import nebulosa.math.* import nebulosa.platesolver.PlateSolution import nebulosa.platesolver.PlateSolver -import nebulosa.util.concurrency.cancellation.CancellationToken -import nebulosa.util.exec.CommandLineListener -import nebulosa.util.exec.commandLine +import org.apache.commons.exec.CommandLine +import org.apache.commons.exec.DefaultExecutor +import org.apache.commons.exec.ExecuteWatchdog import java.nio.file.Files import java.nio.file.Path import java.time.Duration @@ -26,57 +27,54 @@ data class LocalAstrometryNetPlateSolver(private val executablePath: Path) : Pla path: Path?, image: Image?, centerRA: Angle, centerDEC: Angle, radius: Angle, downsampleFactor: Int, timeout: Duration, - cancellationToken: CancellationToken, ): PlateSolution { requireNotNull(path) { "path is required" } val outFolder = Files.createTempDirectory("localplatesolver") - val cmd = commandLine { - executablePath(executablePath) - workingDirectory(path.parent) - - putArg("--out", UUID.randomUUID().toString()) - putArg("--overwrite") + val commandLine = CommandLine.parse("$executablePath") + .addArgument("--out").addArgument(UUID.randomUUID().toString()) + .addArgument("--overwrite") + .addArgument("--dir").addArgument("$outFolder") + .addArgument("--cpulimit").addArgument(timeout.takeIf { it.toSeconds() > 0 }?.toSeconds()?.toString() ?: "300") + .addArgument("--scale-units").addArgument("degwidth") + .addArgument("--guess-scale") + .addArgument("--crpix-center") + .addArgument("--downsample").addArgument("$downsampleFactor") + .addArgument("--no-verify") + .addArgument("--no-plots") + // .addArgument("--resort") + + if (radius.toDegrees >= 0.1 && centerRA.isFinite() && centerDEC.isFinite()) { + commandLine.addArgument("--ra").addArgument("${centerRA.toDegrees}") + .addArgument("--dec").addArgument("${centerDEC.toDegrees}") + .addArgument("--radius").addArgument("${radius.toDegrees}") + } - putArg("--dir", outFolder) + val solution = PlateSolutionLineReader() + val handler = CommandLineListenerHandler() - putArg("--cpulimit", timeout.takeIf { it.toSeconds() > 0 }?.toSeconds() ?: 300) - putArg("--scale-units", "degwidth") - putArg("--guess-scale") - putArg("--crpix-center") - putArg("--downsample", downsampleFactor) - putArg("--no-verify") - putArg("--no-plots") - // putArg("--resort") + val executor = DefaultExecutor.builder() + .setWorkingDirectory(path.parent.toFile()) + .setExecuteStreamHandler(handler) + .get() - if (radius.toDegrees >= 0.1 && centerRA.isFinite() && centerDEC.isFinite()) { - putArg("--ra", centerRA.toDegrees) - putArg("--dec", centerDEC.toDegrees) - putArg("--radius", radius.toDegrees) - } + executor.watchdog = ExecuteWatchdog.builder() + .setTimeout(timeout.takeIf { it.toSeconds() > 0 } ?: Duration.ofMinutes(5)) + .get() - putArg("$path") - } - - val solution = PlateSolutionLineReader() + commandLine.addArgument("$path") - try { - cancellationToken.listen(cmd) - cmd.registerCommandLineListener(solution) - cmd.start() - LOG.di("astrometry.net exited. code={}", cmd.get()) - return solution.get() - } catch (e: Throwable) { - LOG.e("astronomy.net failed.", e) - return PlateSolution.NO_SOLUTION + return try { + handler.registerCommandLineListener(solution) + LOG.di("astrometry.net exited. code={}", executor.execute(commandLine, handler)) + solution.get() } finally { - cancellationToken.unlisten(cmd) outFolder.deleteRecursively() } } - private class PlateSolutionLineReader : CommandLineListener.OnLineRead, Supplier { + private class PlateSolutionLineReader : CommandLineListener, Supplier { @Volatile private var fieldCenter: DoubleArray? = null @Volatile private var fieldRotation: Angle = 0.0 diff --git a/nebulosa-astrometrynet/src/main/kotlin/nebulosa/astrometrynet/platesolver/NovaAstrometryNetPlateSolver.kt b/nebulosa-astrometrynet/src/main/kotlin/nebulosa/astrometrynet/platesolver/NovaAstrometryNetPlateSolver.kt index 10dc9d4d3..2864ac510 100644 --- a/nebulosa-astrometrynet/src/main/kotlin/nebulosa/astrometrynet/platesolver/NovaAstrometryNetPlateSolver.kt +++ b/nebulosa-astrometrynet/src/main/kotlin/nebulosa/astrometrynet/platesolver/NovaAstrometryNetPlateSolver.kt @@ -14,7 +14,6 @@ import nebulosa.math.toDegrees import nebulosa.platesolver.PlateSolution import nebulosa.platesolver.PlateSolver import nebulosa.platesolver.PlateSolverException -import nebulosa.util.concurrency.cancellation.CancellationToken import java.nio.file.Path import java.time.Duration @@ -47,10 +46,7 @@ data class NovaAstrometryNetPlateSolver( path: Path?, image: Image?, centerRA: Angle, centerDEC: Angle, radius: Angle, downsampleFactor: Int, timeout: Duration, - cancellationToken: CancellationToken, ): PlateSolution { - renewSession() - val blind = radius.toDegrees < 0.1 || !centerRA.isFinite() || !centerDEC.isFinite() val upload = Upload( @@ -62,6 +58,8 @@ data class NovaAstrometryNetPlateSolver( tweakOrder = 2, ) + renewSession() + val call = path?.let { service.uploadFromFile(it, upload) } ?: image?.let { service.uploadFromImage(it, upload) } ?: throw PlateSolverException("failed to submit the file") @@ -74,13 +72,13 @@ data class NovaAstrometryNetPlateSolver( var timeLeft = timeout.takeIf { it.toSeconds() > 0 }?.toMillis() ?: 300000L - while (timeLeft >= 0L && !cancellationToken.isCancelled) { + while (timeLeft >= 0L && !Thread.currentThread().isInterrupted) { val startTime = System.currentTimeMillis() val status = service.submissionStatus(submission.subId).execute().body() ?: throw PlateSolverException("failed to retrieve submission status") - if (status.solved && !cancellationToken.isCancelled) { + if (status.solved && !Thread.currentThread().isInterrupted) { LOG.di("retrieving WCS from job. id={}", status.jobs[0]) val body = service.wcs(status.jobs[0]).execute().body() @@ -94,7 +92,7 @@ data class NovaAstrometryNetPlateSolver( return calibration ?: PlateSolution.NO_SOLUTION } - if (!cancellationToken.isCancelled) { + if (!Thread.currentThread().isInterrupted) { timeLeft -= System.currentTimeMillis() - startTime + 5000L Thread.sleep(5000L) diff --git a/nebulosa-autofocus/src/test/kotlin/AutoFocusTest.kt b/nebulosa-autofocus/src/test/kotlin/AutoFocusTest.kt index 399bb8c5c..48a517ba5 100644 --- a/nebulosa-autofocus/src/test/kotlin/AutoFocusTest.kt +++ b/nebulosa-autofocus/src/test/kotlin/AutoFocusTest.kt @@ -1,4 +1,5 @@ -import io.kotest.matchers.ints.shouldBeExactly +import io.kotest.matchers.ints.shouldBeGreaterThanOrEqual +import io.kotest.matchers.ints.shouldBeLessThanOrEqual import io.kotest.matchers.nulls.shouldNotBeNull import nebulosa.autofocus.AutoFocus import nebulosa.autofocus.AutoFocusFittingMode @@ -7,7 +8,8 @@ import nebulosa.autofocus.AutoFocusResult import nebulosa.curve.fitting.CurvePoint import nebulosa.stardetector.StarDetector import nebulosa.stardetector.StarPoint -import nebulosa.test.* +import nebulosa.test.AbstractTest +import nebulosa.test.fits.* import org.junit.jupiter.api.Test import java.nio.file.Path import kotlin.math.cosh @@ -19,42 +21,42 @@ class AutoFocusTest : AbstractTest() { @Test fun trendHyperbolic() { - executeAutoFocus().shouldNotBeNull().x.roundToInt() shouldBeExactly 798 - executeAutoFocus(700).shouldNotBeNull().x.roundToInt() shouldBeExactly 800 - executeAutoFocus(1200).shouldNotBeNull().x.roundToInt() shouldBeExactly 798 - executeAutoFocus(exposureAmount = 3).shouldNotBeNull().x.roundToInt() shouldBeExactly 798 + executeAutoFocus().shouldNotBeNull().x.roundToInt() shouldBeGreaterThanOrEqual 1295 shouldBeLessThanOrEqual 1305 + executeAutoFocus(1000).shouldNotBeNull().x.roundToInt() shouldBeGreaterThanOrEqual 1295 shouldBeLessThanOrEqual 1305 + executeAutoFocus(1600).shouldNotBeNull().x.roundToInt() shouldBeGreaterThanOrEqual 1295 shouldBeLessThanOrEqual 1305 + executeAutoFocus(exposureAmount = 3).shouldNotBeNull().x.roundToInt() shouldBeGreaterThanOrEqual 1295 shouldBeLessThanOrEqual 1305 } @Test fun trendParabolic() { - executeAutoFocus(fittingMode = TREND_PARABOLIC).shouldNotBeNull().x.roundToInt() shouldBeExactly 807 - executeAutoFocus(700, fittingMode = TREND_PARABOLIC).shouldNotBeNull().x.roundToInt() shouldBeExactly 800 - executeAutoFocus(1200, fittingMode = TREND_PARABOLIC).shouldNotBeNull().x.roundToInt() shouldBeExactly 805 - executeAutoFocus(exposureAmount = 3, fittingMode = TREND_PARABOLIC).shouldNotBeNull().x.roundToInt() shouldBeExactly 807 + executeAutoFocus(fittingMode = TREND_PARABOLIC).shouldNotBeNull().x.roundToInt() shouldBeGreaterThanOrEqual 1295 shouldBeLessThanOrEqual 1305 + executeAutoFocus(1000, fittingMode = TREND_PARABOLIC).shouldNotBeNull().x.roundToInt() shouldBeGreaterThanOrEqual 1295 shouldBeLessThanOrEqual 1305 + executeAutoFocus(1600, fittingMode = TREND_PARABOLIC).shouldNotBeNull().x.roundToInt() shouldBeGreaterThanOrEqual 1295 shouldBeLessThanOrEqual 1307 + executeAutoFocus(exposureAmount = 3, fittingMode = TREND_PARABOLIC).shouldNotBeNull().x.roundToInt() shouldBeGreaterThanOrEqual 1295 shouldBeLessThanOrEqual 1305 } @Test fun trendlines() { - executeAutoFocus(fittingMode = TRENDLINES).shouldNotBeNull().x.roundToInt() shouldBeExactly 800 - executeAutoFocus(700, fittingMode = TRENDLINES).shouldNotBeNull().x.roundToInt() shouldBeExactly 800 - executeAutoFocus(1200, fittingMode = TRENDLINES).shouldNotBeNull().x.roundToInt() shouldBeExactly 800 - executeAutoFocus(exposureAmount = 3, fittingMode = TRENDLINES).shouldNotBeNull().x.roundToInt() shouldBeExactly 800 + executeAutoFocus(fittingMode = TRENDLINES).shouldNotBeNull().x.roundToInt() shouldBeGreaterThanOrEqual 1295 shouldBeLessThanOrEqual 1305 + executeAutoFocus(1000, fittingMode = TRENDLINES).shouldNotBeNull().x.roundToInt() shouldBeGreaterThanOrEqual 1295 shouldBeLessThanOrEqual 1305 + executeAutoFocus(1600, fittingMode = TRENDLINES).shouldNotBeNull().x.roundToInt() shouldBeGreaterThanOrEqual 1295 shouldBeLessThanOrEqual 1305 + executeAutoFocus(exposureAmount = 3, fittingMode = TRENDLINES).shouldNotBeNull().x.roundToInt() shouldBeGreaterThanOrEqual 1295 shouldBeLessThanOrEqual 1305 } @Test fun hyperbolic() { - executeAutoFocus(fittingMode = HYPERBOLIC).shouldNotBeNull().x.roundToInt() shouldBeExactly 796 - executeAutoFocus(700, fittingMode = HYPERBOLIC).shouldNotBeNull().x.roundToInt() shouldBeExactly 800 - executeAutoFocus(1200, fittingMode = HYPERBOLIC).shouldNotBeNull().x.roundToInt() shouldBeExactly 795 - executeAutoFocus(exposureAmount = 3, fittingMode = HYPERBOLIC).shouldNotBeNull().x.roundToInt() shouldBeExactly 796 + executeAutoFocus(fittingMode = HYPERBOLIC).shouldNotBeNull().x.roundToInt() shouldBeGreaterThanOrEqual 1295 shouldBeLessThanOrEqual 1305 + executeAutoFocus(1000, fittingMode = HYPERBOLIC).shouldNotBeNull().x.roundToInt() shouldBeGreaterThanOrEqual 1295 shouldBeLessThanOrEqual 1305 + executeAutoFocus(1600, fittingMode = HYPERBOLIC).shouldNotBeNull().x.roundToInt() shouldBeGreaterThanOrEqual 1295 shouldBeLessThanOrEqual 1305 + executeAutoFocus(exposureAmount = 3, fittingMode = HYPERBOLIC).shouldNotBeNull().x.roundToInt() shouldBeGreaterThanOrEqual 1295 shouldBeLessThanOrEqual 1305 } @Test fun parabolic() { - executeAutoFocus(fittingMode = PARABOLIC).shouldNotBeNull().x.roundToInt() shouldBeExactly 814 - executeAutoFocus(700, fittingMode = PARABOLIC).shouldNotBeNull().x.roundToInt() shouldBeExactly 800 - executeAutoFocus(1200, fittingMode = PARABOLIC).shouldNotBeNull().x.roundToInt() shouldBeExactly 809 - executeAutoFocus(exposureAmount = 3, fittingMode = PARABOLIC).shouldNotBeNull().x.roundToInt() shouldBeExactly 814 + executeAutoFocus(fittingMode = PARABOLIC).shouldNotBeNull().x.roundToInt() shouldBeGreaterThanOrEqual 1295 shouldBeLessThanOrEqual 1305 + executeAutoFocus(1000, fittingMode = PARABOLIC).shouldNotBeNull().x.roundToInt() shouldBeGreaterThanOrEqual 1295 shouldBeLessThanOrEqual 1305 + executeAutoFocus(1600, fittingMode = PARABOLIC).shouldNotBeNull().x.roundToInt() shouldBeGreaterThanOrEqual 1295 shouldBeLessThanOrEqual 1315 + executeAutoFocus(exposureAmount = 3, fittingMode = PARABOLIC).shouldNotBeNull().x.roundToInt() shouldBeGreaterThanOrEqual 1295 shouldBeLessThanOrEqual 1305 } private data class DetectedStar( @@ -63,10 +65,10 @@ class AutoFocusTest : AbstractTest() { override val snr: Double = 0.0, override val flux: Double = 0.0, ) : StarPoint - private data class HyperbolicStarDetector(private val offset: Int = 8) : StarDetector { + private data class HyperbolicStarDetector(private val offset: Int = 13) : StarDetector { override fun detect(input: Path): List { - val index = STAR_FOCUS_LIST.indexOf(input) + val index = FOCUS_LIST.indexOf(input) val hfd = max(1.0, ln(cosh(((index - offset) * 2).toDouble()))) return listOf(DetectedStar(hfd)) } @@ -74,11 +76,13 @@ class AutoFocusTest : AbstractTest() { companion object { - @JvmStatic val STAR_FOCUS_LIST = listOf( - STAR_FOCUS_1, STAR_FOCUS_2, STAR_FOCUS_3, STAR_FOCUS_4, STAR_FOCUS_5, - STAR_FOCUS_6, STAR_FOCUS_7, STAR_FOCUS_8, STAR_FOCUS_9, STAR_FOCUS_10, - STAR_FOCUS_11, STAR_FOCUS_12, STAR_FOCUS_13, STAR_FOCUS_14, STAR_FOCUS_15, - STAR_FOCUS_16, STAR_FOCUS_17, + @JvmStatic val FOCUS_LIST = listOf( + FOCUS_01_FITS, FOCUS_02_FITS, FOCUS_03_FITS, FOCUS_04_FITS, FOCUS_05_FITS, + FOCUS_06_FITS, FOCUS_07_FITS, FOCUS_08_FITS, FOCUS_09_FITS, FOCUS_10_FITS, + FOCUS_11_FITS, FOCUS_12_FITS, FOCUS_13_FITS, FOCUS_14_FITS, FOCUS_15_FITS, + FOCUS_16_FITS, FOCUS_17_FITS, FOCUS_18_FITS, FOCUS_19_FITS, FOCUS_20_FITS, + FOCUS_21_FITS, FOCUS_22_FITS, FOCUS_23_FITS, FOCUS_24_FITS, FOCUS_25_FITS, + FOCUS_26_FITS, ) @JvmStatic @@ -91,8 +95,7 @@ class AutoFocusTest : AbstractTest() { rSquaredThreshold: Double = 0.0, reverse: Boolean = false, ): CurvePoint? { - val autoFocus = - AutoFocus(HyperbolicStarDetector(), exposureAmount, initialOffsetSteps, stepSize, fittingMode, rSquaredThreshold, reverse, 16000) + val autoFocus = AutoFocus(HyperbolicStarDetector(), exposureAmount, initialOffsetSteps, stepSize, fittingMode, rSquaredThreshold, reverse, 25000) var focusPosition = initialFocusPosition var focusPoint: CurvePoint? = null @@ -101,7 +104,7 @@ class AutoFocusTest : AbstractTest() { when (val result = autoFocus.determinate(focusPosition)) { AutoFocusResult.Determinate -> continue is AutoFocusResult.MoveFocuser -> focusPosition = if (result.relative) focusPosition + result.position else result.position - AutoFocusResult.TakeExposure -> autoFocus.add(STAR_FOCUS_LIST[focusPosition / 100]) + AutoFocusResult.TakeExposure -> autoFocus.add(FOCUS_LIST[focusPosition / 100]) is AutoFocusResult.Completed -> focusPoint = result.determinedFocusPoint is AutoFocusResult.Failed -> break } diff --git a/nebulosa-commandline/build.gradle.kts b/nebulosa-commandline/build.gradle.kts new file mode 100644 index 000000000..c4b71f106 --- /dev/null +++ b/nebulosa-commandline/build.gradle.kts @@ -0,0 +1,18 @@ +plugins { + kotlin("jvm") + id("maven-publish") +} + +dependencies { + api(libs.apache.exec) + implementation(project(":nebulosa-log")) + testImplementation(project(":nebulosa-test")) +} + +publishing { + publications { + create("pluginMaven") { + from(components["java"]) + } + } +} diff --git a/nebulosa-commandline/src/main/kotlin/nebulosa/commandline/CommandLineListener.kt b/nebulosa-commandline/src/main/kotlin/nebulosa/commandline/CommandLineListener.kt new file mode 100644 index 000000000..c1258cc07 --- /dev/null +++ b/nebulosa-commandline/src/main/kotlin/nebulosa/commandline/CommandLineListener.kt @@ -0,0 +1,10 @@ +package nebulosa.commandline + +interface CommandLineListener { + + fun onStarted() = Unit + + fun onLineRead(line: String) = Unit + + fun onExited(exitCode: Int, exception: Throwable?) = Unit +} diff --git a/nebulosa-commandline/src/main/kotlin/nebulosa/commandline/CommandLineListenerHandler.kt b/nebulosa-commandline/src/main/kotlin/nebulosa/commandline/CommandLineListenerHandler.kt new file mode 100644 index 000000000..f745e2787 --- /dev/null +++ b/nebulosa-commandline/src/main/kotlin/nebulosa/commandline/CommandLineListenerHandler.kt @@ -0,0 +1,78 @@ +package nebulosa.commandline + +import org.apache.commons.exec.ExecuteException +import org.apache.commons.exec.ExecuteResultHandler +import org.apache.commons.exec.ExecuteStreamHandler +import java.io.BufferedReader +import java.io.BufferedWriter +import java.io.InputStream +import java.io.OutputStream +import java.util.concurrent.ConcurrentHashMap + +class CommandLineListenerHandler : ExecuteResultHandler, ExecuteStreamHandler { + + private val listeners = ConcurrentHashMap.newKeySet(1) + + @Volatile private var errorStream: BufferedReader? = null + @Volatile private var inputStream: BufferedWriter? = null + @Volatile private var outputStream: BufferedReader? = null + @Volatile private var errorThread: Thread? = null + @Volatile private var outputThread: Thread? = null + + fun registerCommandLineListener(listener: CommandLineListener) { + listeners.add(listener) + } + + fun unregisterCommandLineListener(listener: CommandLineListener) { + listeners.remove(listener) + } + + override fun setProcessErrorStream(stream: InputStream) { + errorStream = stream.bufferedReader() + } + + override fun setProcessInputStream(stream: OutputStream) { + inputStream = stream.bufferedWriter() + } + + override fun setProcessOutputStream(stream: InputStream) { + outputStream = stream.bufferedReader() + } + + override fun start() { + errorThread?.interrupt() + errorThread = LineReaderThread(errorStream!!, listeners) + errorThread!!.start() + + outputThread?.interrupt() + outputThread = LineReaderThread(outputStream!!, listeners) + outputThread!!.start() + + listeners.forEach { it.onStarted() } + } + + override fun stop() { + errorThread?.interrupt() + errorThread = null + + outputThread?.interrupt() + outputThread = null + } + + override fun onProcessFailed(e: ExecuteException) { + listeners.forEach { it.onExited(-1, e) } + } + + override fun onProcessComplete(exitValue: Int) { + listeners.forEach { it.onExited(exitValue, null) } + } + + @Synchronized + fun write(text: String) { + with(inputStream ?: return) { + write(text) + newLine() + flush() + } + } +} diff --git a/nebulosa-commandline/src/main/kotlin/nebulosa/commandline/LineReaderThread.kt b/nebulosa-commandline/src/main/kotlin/nebulosa/commandline/LineReaderThread.kt new file mode 100644 index 000000000..f68160d4c --- /dev/null +++ b/nebulosa-commandline/src/main/kotlin/nebulosa/commandline/LineReaderThread.kt @@ -0,0 +1,28 @@ +package nebulosa.commandline + +import nebulosa.log.d +import nebulosa.log.loggerFor +import java.io.BufferedReader + +internal data class LineReaderThread( + private val reader: BufferedReader, + private val listeners: Iterable, +) : Thread("Command Line Reader") { + + init { + isDaemon = true + } + + override fun run() { + while (true) { + val line = reader.readLine() ?: break + LOG.d(line) + listeners.forEach { it.onLineRead(line) } + } + } + + companion object { + + @JvmStatic private val LOG = loggerFor() + } +} diff --git a/nebulosa-image/src/test/kotlin/HFDTest.kt b/nebulosa-image/src/test/kotlin/HFDTest.kt index 7092a5536..dc906cc43 100644 --- a/nebulosa-image/src/test/kotlin/HFDTest.kt +++ b/nebulosa-image/src/test/kotlin/HFDTest.kt @@ -1,41 +1,33 @@ import io.kotest.matchers.floats.plusOrMinus +import io.kotest.matchers.ints.shouldBeExactly import io.kotest.matchers.shouldBe import nebulosa.fits.fits import nebulosa.image.Image.Companion.asImage import nebulosa.image.algorithms.computation.hfd.HFD -import nebulosa.test.* +import nebulosa.test.fits.FOCUS_14_FITS import org.junit.jupiter.api.Test class HFDTest { @Test - fun focus() { - val starFocus = arrayOf( - STAR_FOCUS_1 to floatArrayOf(7.0f, 77.2f, 11950.023f), - STAR_FOCUS_2 to floatArrayOf(7.3f, 114.1f, 26055.076f), - STAR_FOCUS_3 to floatArrayOf(7.7f, 185.1f, 68531.1f), - STAR_FOCUS_4 to floatArrayOf(6.9f, 363.4f, 264183.53f), - STAR_FOCUS_5 to floatArrayOf(6.2f, 382.5f, 292700.53f), - STAR_FOCUS_6 to floatArrayOf(6.1f, 410.0f, 336331.9f), - STAR_FOCUS_7 to floatArrayOf(5.7f, 422.7f, 357451.03f), - STAR_FOCUS_8 to floatArrayOf(5.1f, 430.5f, 370737.5f), - STAR_FOCUS_9 to floatArrayOf(4.8f, 435.4f, 379284.03f), - STAR_FOCUS_10 to floatArrayOf(4.3f, 444.6f, 395406.34f), - STAR_FOCUS_11 to floatArrayOf(3.79f, 442.9f, 392338.5f), - STAR_FOCUS_12 to floatArrayOf(3.71f, 443.3f, 393080.38f), - STAR_FOCUS_13 to floatArrayOf(4.9f, 443.6f, 393603.97f), - STAR_FOCUS_14 to floatArrayOf(6.1f, 368.8f, 272057.78f), - STAR_FOCUS_15 to floatArrayOf(6.9f, 249.0f, 124078.05f), - STAR_FOCUS_16 to floatArrayOf(6.5f, 188.2f, 70905.97f), - STAR_FOCUS_17 to floatArrayOf(6.9f, 164.3f, 53994.75f), - ) + fun centered() { + val image = FOCUS_14_FITS.fits().asImage() + val star = image.compute(HFD(690, 276, 25)) + star.x shouldBeExactly 690 + star.y shouldBeExactly 276 + star.hfd shouldBe (2.88f plusOrMinus 0.1f) + star.snr shouldBe (683.3849f plusOrMinus 0.1f) + star.flux shouldBe (934029.75f plusOrMinus 0.1f) + } - for ((first, second) in starFocus) { - val focusImage = first.fits().asImage() - val star = focusImage.compute(HFD(focusImage.width / 2, focusImage.height / 2, 50)) - star.hfd shouldBe (second[0] plusOrMinus 0.1f) - star.snr shouldBe (second[1] plusOrMinus 0.1f) - star.flux shouldBe (second[2] plusOrMinus 0.1f) - } + @Test + fun outOfCenter() { + val image = FOCUS_14_FITS.fits().asImage() + val star = image.compute(HFD(695, 279, 25)) + star.x shouldBeExactly 690 + star.y shouldBeExactly 276 + star.hfd shouldBe (2.88f plusOrMinus 0.1f) + star.snr shouldBe (683.3849f plusOrMinus 0.1f) + star.flux shouldBe (934029.75f plusOrMinus 0.1f) } } diff --git a/nebulosa-log/src/main/kotlin/nebulosa/log/Log.kt b/nebulosa-log/src/main/kotlin/nebulosa/log/Log.kt index cf6c9399e..47117c2cc 100644 --- a/nebulosa-log/src/main/kotlin/nebulosa/log/Log.kt +++ b/nebulosa-log/src/main/kotlin/nebulosa/log/Log.kt @@ -29,6 +29,10 @@ inline fun Logger.i(message: String, a0: Any?, a1: Any?, a2: Any?) { if (isInfoEnabled) info(message, a0, a1, a2) } +inline fun Logger.i(message: String, a0: Any?, a1: Any?, a2: Any?, a3: Any?) { + if (isInfoEnabled) info(message, a0, a1, a2, a3) +} + // WARN inline fun Logger.w(message: String) { @@ -117,6 +121,10 @@ inline fun Logger.de(message: String, a0: Any?) { if (isDebugEnabled) error(message, a0) } +inline fun Logger.de(message: String, a0: Any?, a1: Any?) { + if (isDebugEnabled) error(message, a0, a1) +} + // DEBUG WARN inline fun Logger.dw(message: String) { diff --git a/nebulosa-pixinsight/build.gradle.kts b/nebulosa-pixinsight/build.gradle.kts index 57c654bb8..8b7c0e850 100644 --- a/nebulosa-pixinsight/build.gradle.kts +++ b/nebulosa-pixinsight/build.gradle.kts @@ -5,12 +5,12 @@ plugins { dependencies { api(project(":nebulosa-math")) + api(project(":nebulosa-commandline")) api(project(":nebulosa-platesolver")) api(project(":nebulosa-stardetector")) api(project(":nebulosa-stacker")) api(project(":nebulosa-livestacker")) api(project(":nebulosa-json")) - api(project(":nebulosa-util")) api(libs.apache.codec) implementation(project(":nebulosa-log")) testImplementation(project(":nebulosa-image")) diff --git a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/platesolver/PixInsightPlateSolver.kt b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/platesolver/PixInsightPlateSolver.kt index f414230bb..bdf50b820 100644 --- a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/platesolver/PixInsightPlateSolver.kt +++ b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/platesolver/PixInsightPlateSolver.kt @@ -9,8 +9,6 @@ import nebulosa.pixinsight.script.PixInsightScript import nebulosa.pixinsight.script.PixInsightScriptRunner import nebulosa.platesolver.PlateSolution import nebulosa.platesolver.PlateSolver -import nebulosa.util.concurrency.cancellation.CancellationListener -import nebulosa.util.concurrency.cancellation.CancellationToken import java.nio.file.Path import java.time.Duration @@ -30,33 +28,26 @@ data class PixInsightPlateSolver( path: Path?, image: Image?, centerRA: Angle, centerDEC: Angle, radius: Angle, downsampleFactor: Int, timeout: Duration, - cancellationToken: CancellationToken ): PlateSolution { require(path != null) { "path must be provided" } - val script = PixInsightImageSolver(slot, path, centerRA, centerDEC, pixelSize, resolution.toArcsec, focalLength, timeout, cancellationToken) - val cancellationListener = CancellationListener { runner.abort(script) } + val script = PixInsightImageSolver(slot, path, centerRA, centerDEC, pixelSize, resolution.toArcsec, focalLength, timeout) - val solver = try { - cancellationToken.listen(cancellationListener) - script.use { it.runSync(runner) } - } finally { - cancellationToken.unlisten(cancellationListener) - } + val solver = script.use { it.runSync(runner) } - if (solver.success) { + return if (solver.success) { val m = ROTATION_REGEX.find(solver.astrometricSolutionSummary) val rotation = m?.groupValues?.get(1)?.toDoubleOrNull()?.deg ?: 0.0 - return PlateSolution( + PlateSolution( true, rotation, solver.resolution, solver.rightAscension, solver.declination, solver.width, solver.height, widthInPixels = solver.imageWidth, heightInPixels = solver.imageHeight, ) + } else { + PlateSolution.NO_SOLUTION } - - return PlateSolution.NO_SOLUTION } companion object { diff --git a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/AbstractPixInsightScript.kt b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/AbstractPixInsightScript.kt index f1630b48a..95d5ffecd 100644 --- a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/AbstractPixInsightScript.kt +++ b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/AbstractPixInsightScript.kt @@ -3,53 +3,18 @@ package nebulosa.pixinsight.script import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.module.kotlin.jsonMapper import nebulosa.json.PathModule -import nebulosa.log.d import nebulosa.log.di -import nebulosa.log.e import nebulosa.log.loggerFor -import nebulosa.util.exec.CommandLine -import nebulosa.util.exec.CommandLineListener import org.apache.commons.codec.binary.Hex +import org.apache.commons.exec.DefaultExecutor import java.nio.file.Path -import java.util.concurrent.CompletableFuture import kotlin.io.path.readText -abstract class AbstractPixInsightScript : PixInsightScript, CommandLineListener, CompletableFuture() { +abstract class AbstractPixInsightScript : PixInsightScript { - override fun onLineRead(line: String) = Unit + @Volatile private var executor: DefaultExecutor? = null - override fun onExit(exitCode: Int, exception: Throwable?) = Unit - - protected open fun beforeRun() = Unit - - protected abstract fun processOnComplete(exitCode: Int): T? - - protected open fun waitOnComplete() = Unit - - final override fun run(runner: PixInsightScriptRunner) = runner.run(this) - - final override fun startCommandLine(commandLine: CommandLine) { - commandLine.whenComplete { exitCode, exception -> - try { - LOG.d("{} script finished. done={}, exitCode={}", this::class.simpleName, isDone, exitCode, exception) - - waitOnComplete() - - if (isDone) return@whenComplete - else if (exception != null) completeExceptionally(exception) - else complete(processOnComplete(exitCode).also { LOG.d("{} script processed. output={}", this::class.simpleName, it) }) - } catch (e: Throwable) { - LOG.e("{} finished with fatal exception. message={}", this::class.simpleName, e.message) - completeExceptionally(e) - } finally { - commandLine.unregisterCommandLineListener(this) - } - } - - commandLine.registerCommandLineListener(this) - beforeRun() - commandLine.start() - } + override val name = this::class.simpleName!! companion object { @@ -65,28 +30,26 @@ abstract class AbstractPixInsightScript : PixInsigh @JvmStatic internal fun PixInsightScript<*>.execute(scriptPath: Path, data: Any?, slot: Int = this.slot): String { - LOG.di("{} will be executed. slot={}, script={}, data={}", this::class.simpleName, slot, scriptPath, data) + LOG.di("{} will be executed. slot={}, script={}, data={}", name, slot, scriptPath, data) return buildString { if (slot > 0) append("$slot:") - append("\"$scriptPath") + append("$scriptPath") if (data != null) { append(',') when (data) { - is Path, is CharSequence -> append("'$data'") + is Path, is CharSequence -> append("$data") is Number -> append("$data") else -> append(Hex.encodeHexString(OBJECT_MAPPER.writeValueAsString(data).toByteArray(Charsets.UTF_16BE))) } } - - append('"') } } @JvmStatic - internal fun Path.parseStatus(type: Class): T? { + internal fun Path.parseStatus(type: Class): T? { val text = readText() return if (text.startsWith(START_FILE) && text.endsWith(END_FILE)) { @@ -96,7 +59,7 @@ abstract class AbstractPixInsightScript : PixInsigh } } - internal inline fun Path.parseStatus(): T? { + internal inline fun Path.parseStatus(): T? { return parseStatus(T::class.java) } } diff --git a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightAlign.kt b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightAlign.kt index a96fedaac..15897a0ff 100644 --- a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightAlign.kt +++ b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightAlign.kt @@ -2,8 +2,10 @@ package nebulosa.pixinsight.script import nebulosa.io.resource import nebulosa.io.transferAndClose +import nebulosa.pixinsight.script.PixInsightLRGBCombination.Output import java.nio.file.Files import java.nio.file.Path +import java.util.concurrent.CompletableFuture import kotlin.io.path.deleteIfExists import kotlin.io.path.outputStream @@ -44,7 +46,7 @@ data class PixInsightAlign( @JvmField val h31: Double = 0.0, @JvmField val h32: Double = 0.0, @JvmField val h33: Double = 0.0, - ) : PixInsightScript.Output { + ) : PixInsightScriptOutput { companion object { @@ -61,14 +63,17 @@ data class PixInsightAlign( override val arguments = listOf("-x=${execute(scriptPath, Input(referencePath, targetPath, workingDirectory, statusPath))}") - override fun processOnComplete(exitCode: Int): Output { + override fun processOnExit(exitCode: Int, output: CompletableFuture) { if (exitCode == 0) { - repeat(30) { - statusPath.parseStatus()?.also { return it } ?: Thread.sleep(1000) + repeat(60) { + if (output.isDone) return + Thread.sleep(1000) + val status = statusPath.parseStatus() ?: return@repeat + output.complete(status) } } - return Output.FAILED + output.complete(Output.FAILED) } override fun close() { diff --git a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightAutomaticBackgroundExtractor.kt b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightAutomaticBackgroundExtractor.kt index 7bd437755..c02e90d72 100644 --- a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightAutomaticBackgroundExtractor.kt +++ b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightAutomaticBackgroundExtractor.kt @@ -2,9 +2,9 @@ package nebulosa.pixinsight.script import nebulosa.io.resource import nebulosa.io.transferAndClose -import nebulosa.pixinsight.script.PixInsightImageSolver.Output import java.nio.file.Files import java.nio.file.Path +import java.util.concurrent.CompletableFuture import kotlin.io.path.deleteIfExists import kotlin.io.path.outputStream @@ -24,7 +24,7 @@ data class PixInsightAutomaticBackgroundExtractor( override val success: Boolean = false, override val errorMessage: String? = null, @JvmField val outputImage: Path? = null, - ) : PixInsightScript.Output { + ) : PixInsightScriptOutput { companion object { @@ -41,14 +41,17 @@ data class PixInsightAutomaticBackgroundExtractor( override val arguments = listOf("-x=${execute(scriptPath, Input(targetPath, outputPath, statusPath))}") - override fun processOnComplete(exitCode: Int): Output { + override fun processOnExit(exitCode: Int, output: CompletableFuture) { if (exitCode == 0) { - repeat(30) { - statusPath.parseStatus()?.also { return it } ?: Thread.sleep(1000) + repeat(60) { + if (output.isDone) return + Thread.sleep(1000) + val status = statusPath.parseStatus() ?: return@repeat + output.complete(status) } } - return Output.FAILED + output.complete(Output.FAILED) } override fun close() { diff --git a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightCalibrate.kt b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightCalibrate.kt index 21a29ca21..5ceef235b 100644 --- a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightCalibrate.kt +++ b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightCalibrate.kt @@ -4,6 +4,7 @@ import nebulosa.io.resource import nebulosa.io.transferAndClose import java.nio.file.Files import java.nio.file.Path +import java.util.concurrent.CompletableFuture import kotlin.io.path.deleteIfExists import kotlin.io.path.outputStream @@ -33,7 +34,7 @@ data class PixInsightCalibrate( override val success: Boolean = false, override val errorMessage: String? = null, @JvmField val outputImage: Path? = null, - ) : PixInsightScript.Output { + ) : PixInsightScriptOutput { companion object { @@ -51,14 +52,17 @@ data class PixInsightCalibrate( private val input = Input(targetPath, workingDirectory, statusPath, darkPath, flatPath, biasPath, compress, use32Bit) override val arguments = listOf("-x=${execute(scriptPath, input)}") - override fun processOnComplete(exitCode: Int): Output { + override fun processOnExit(exitCode: Int, output: CompletableFuture) { if (exitCode == 0) { - repeat(30) { - statusPath.parseStatus()?.also { return it } ?: Thread.sleep(1000) + repeat(60) { + if (output.isDone) return + Thread.sleep(1000) + val status = statusPath.parseStatus() ?: return@repeat + output.complete(status) } } - return Output.FAILED + output.complete(Output.FAILED) } override fun close() { diff --git a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightDetectStars.kt b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightDetectStars.kt index 6f45c2a92..4d190a312 100644 --- a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightDetectStars.kt +++ b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightDetectStars.kt @@ -6,9 +6,9 @@ import nebulosa.stardetector.StarPoint import java.nio.file.Files import java.nio.file.Path import java.time.Duration +import java.util.concurrent.CompletableFuture import kotlin.io.path.deleteIfExists import kotlin.io.path.outputStream -import kotlin.io.path.readText data class PixInsightDetectStars( override val slot: Int, @@ -29,7 +29,7 @@ data class PixInsightDetectStars( override val success: Boolean = false, override val errorMessage: String? = null, @JvmField val stars: List = emptyList(), - ) : PixInsightScript.Output { + ) : PixInsightScriptOutput { override fun toString() = "Output(success=$success, errorMessage=$errorMessage, stars=${stars.size})" @@ -64,28 +64,17 @@ data class PixInsightDetectStars( override val arguments = listOf("-x=${execute(scriptPath, Input(targetPath, statusPath, minSNR, invert))}") - override fun processOnComplete(exitCode: Int): Output { - val timeoutInMillis = timeout.toMillis() - + override fun processOnExit(exitCode: Int, output: CompletableFuture) { if (exitCode == 0) { - val startTime = System.currentTimeMillis() - - repeat(600) { - val text = statusPath.readText() - - if (text.startsWith(START_FILE) && text.endsWith(END_FILE)) { - return OBJECT_MAPPER.readValue(text.substring(1, text.length - 1), Output::class.java) - } - - if (timeoutInMillis == 0L || System.currentTimeMillis() - startTime < timeoutInMillis) { - Thread.sleep(500) - } else { - return@repeat - } + repeat(60) { + if (output.isDone) return + Thread.sleep(1000) + val status = statusPath.parseStatus() ?: return@repeat + output.complete(status) } } - return Output.FAILED + output.complete(Output.FAILED) } override fun close() { diff --git a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightFileFormatConversion.kt b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightFileFormatConversion.kt index 9192893f0..a28923f1b 100644 --- a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightFileFormatConversion.kt +++ b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightFileFormatConversion.kt @@ -4,6 +4,7 @@ import nebulosa.io.resource import nebulosa.io.transferAndClose import java.nio.file.Files import java.nio.file.Path +import java.util.concurrent.CompletableFuture import kotlin.io.path.deleteIfExists import kotlin.io.path.outputStream @@ -23,7 +24,7 @@ data class PixInsightFileFormatConversion( override val success: Boolean = false, override val errorMessage: String? = null, @JvmField val outputImage: Path? = null, - ) : PixInsightScript.Output { + ) : PixInsightScriptOutput { companion object { @@ -40,14 +41,17 @@ data class PixInsightFileFormatConversion( override val arguments = listOf("-x=${execute(scriptPath, Input(inputPath, outputPath, statusPath))}") - override fun processOnComplete(exitCode: Int): Output { + override fun processOnExit(exitCode: Int, output: CompletableFuture) { if (exitCode == 0) { - repeat(30) { - statusPath.parseStatus()?.also { return it } ?: Thread.sleep(1000) + repeat(60) { + if (output.isDone) return + Thread.sleep(1000) + val status = statusPath.parseStatus() ?: return@repeat + output.complete(status) } } - return Output.FAILED + output.complete(Output.FAILED) } override fun close() { diff --git a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightImageSolver.kt b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightImageSolver.kt index 0548313e7..6d500b29c 100644 --- a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightImageSolver.kt +++ b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightImageSolver.kt @@ -5,15 +5,14 @@ import nebulosa.io.resource import nebulosa.io.transferAndClose import nebulosa.math.Angle import nebulosa.math.toDegrees -import nebulosa.util.concurrency.cancellation.CancellationToken import java.nio.file.Files import java.nio.file.Path import java.time.Duration +import java.util.concurrent.CompletableFuture import kotlin.io.path.deleteIfExists import kotlin.io.path.inputStream import kotlin.io.path.outputStream import kotlin.math.max -import kotlin.math.min data class PixInsightImageSolver( override val slot: Int, @@ -24,7 +23,6 @@ data class PixInsightImageSolver( private val resolution: Double = 0.0, // arcsec/px private val focalLength: Double = 0.0, // mm private val timeout: Duration = Duration.ZERO, - private val cancellationToken: CancellationToken = CancellationToken.NONE, ) : AbstractPixInsightScript() { private data class Input( @@ -51,7 +49,7 @@ data class PixInsightImageSolver( @JvmField val imageWidth: Double = 0.0, @JvmField val imageHeight: Double = 0.0, @JvmField val astrometricSolutionSummary: String = "", - ) : PixInsightScript.Output { + ) : PixInsightScriptOutput { companion object { @@ -79,17 +77,19 @@ data class PixInsightImageSolver( private val input = Input(targetPath, statusPath, centerRA.toDegrees, centerDEC.toDegrees, pixelSize, resolution, focalLength) override val arguments = listOf("-x=${execute(scriptPath, input)}") - override fun processOnComplete(exitCode: Int): Output { + override fun processOnExit(exitCode: Int, output: CompletableFuture) { if (exitCode == 0) { - val seconds = timeout.toSeconds().toInt() + val count = max(60, timeout.toSeconds()).toInt() - repeat(max(30, min(seconds, 300))) { - if (cancellationToken.isCancelled) return@repeat - statusPath.parseStatus()?.also { return it } ?: Thread.sleep(1000) + repeat(count) { + if (output.isDone) return + Thread.sleep(1000) + val status = statusPath.parseStatus() ?: return@repeat + output.complete(status) } } - return Output.FAILED + output.complete(Output.FAILED) } override fun close() { diff --git a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightIsRunning.kt b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightIsRunning.kt index 769bfa4eb..01ef27bbb 100644 --- a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightIsRunning.kt +++ b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightIsRunning.kt @@ -2,13 +2,14 @@ package nebulosa.pixinsight.script import nebulosa.log.d import nebulosa.log.loggerFor +import java.util.concurrent.CompletableFuture -data class PixInsightIsRunning(override val slot: Int) : AbstractPixInsightScript() { +data class PixInsightIsRunning(override val slot: Int) : AbstractPixInsightScript() { data class Output( override val success: Boolean, override val errorMessage: String? = null, - ) : PixInsightScript.Output { + ) : PixInsightScriptOutput { companion object { @@ -23,25 +24,21 @@ data class PixInsightIsRunning(override val slot: Int) : AbstractPixInsightScrip private val slotCrashed = "The requested application instance #$slot has crashed" private val yieldedExecutionInstance = "$YIELDED_EXECUTION_INSTANCE$slot" - override fun onLineRead(line: String) { - processLine(line) - } - - private fun processLine(line: String) { - if (isDone) return + override fun processLine(line: String, output: CompletableFuture) { + if (output.isDone) return if (slot > 0) { if (line.contains(slotIsNotRunning, true) || line.contains(slotCrashed, true)) { - complete(Output.FAILED) + output.complete(Output.FAILED) } else if (line.contains(yieldedExecutionInstance, true)) { - complete(Output.SUCCESS) + output.complete(Output.SUCCESS) } else { return } } else if (line.contains(YIELDED_EXECUTION_INSTANCE, true)) { - complete(Output.SUCCESS) + output.complete(Output.SUCCESS) } else if (line.contains(NO_RUNNING_PROCESS, true)) { - complete(Output.FAILED) + output.complete(Output.FAILED) } else { return } @@ -49,7 +46,9 @@ data class PixInsightIsRunning(override val slot: Int) : AbstractPixInsightScrip LOG.d(line) } - override fun processOnComplete(exitCode: Int) = Output.FAILED + override fun processOnExit(exitCode: Int, output: CompletableFuture) { + if (exitCode != 0) output.complete(Output.FAILED) + } override fun close() = Unit diff --git a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightLRGBCombination.kt b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightLRGBCombination.kt index 3af4274c8..86de6d85e 100644 --- a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightLRGBCombination.kt +++ b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightLRGBCombination.kt @@ -4,6 +4,7 @@ import nebulosa.io.resource import nebulosa.io.transferAndClose import java.nio.file.Files import java.nio.file.Path +import java.util.concurrent.CompletableFuture import kotlin.io.path.deleteIfExists import kotlin.io.path.outputStream @@ -33,7 +34,7 @@ data class PixInsightLRGBCombination( override val success: Boolean = false, override val errorMessage: String? = null, @JvmField val outputImage: Path? = null, - ) : PixInsightScript.Output { + ) : PixInsightScriptOutput { companion object { @@ -52,14 +53,17 @@ data class PixInsightLRGBCombination( private val input = Input(outputPath, statusPath, luminancePath, redPath, greenPath, bluePath, weights) override val arguments = listOf("-x=${execute(scriptPath, input)}") - override fun processOnComplete(exitCode: Int): Output { + override fun processOnExit(exitCode: Int, output: CompletableFuture) { if (exitCode == 0) { - repeat(30) { - statusPath.parseStatus()?.also { return it } ?: Thread.sleep(1000) + repeat(60) { + if (output.isDone) return + Thread.sleep(1000) + val status = statusPath.parseStatus() ?: return@repeat + output.complete(status) } } - return Output.FAILED + output.complete(Output.FAILED) } override fun close() { diff --git a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightLuminanceCombination.kt b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightLuminanceCombination.kt index 3ae858446..bf7a1e222 100644 --- a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightLuminanceCombination.kt +++ b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightLuminanceCombination.kt @@ -4,6 +4,7 @@ import nebulosa.io.resource import nebulosa.io.transferAndClose import java.nio.file.Files import java.nio.file.Path +import java.util.concurrent.CompletableFuture import kotlin.io.path.deleteIfExists import kotlin.io.path.outputStream @@ -26,7 +27,7 @@ data class PixInsightLuminanceCombination( override val success: Boolean = false, override val errorMessage: String? = null, @JvmField val outputImage: Path? = null, - ) : PixInsightScript.Output { + ) : PixInsightScriptOutput { companion object { @@ -44,14 +45,17 @@ data class PixInsightLuminanceCombination( private val input = Input(outputPath, statusPath, luminancePath, targetPath, 1.0) override val arguments = listOf("-x=${execute(scriptPath, input)}") - override fun processOnComplete(exitCode: Int): Output { + override fun processOnExit(exitCode: Int, output: CompletableFuture) { if (exitCode == 0) { - repeat(30) { - statusPath.parseStatus()?.also { return it } ?: Thread.sleep(1000) + repeat(60) { + if (output.isDone) return + Thread.sleep(1000) + val status = statusPath.parseStatus() ?: return@repeat + output.complete(status) } } - return Output.FAILED + output.complete(Output.FAILED) } override fun close() { diff --git a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightPixelMath.kt b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightPixelMath.kt index 30b4820e3..ebfb45b8b 100644 --- a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightPixelMath.kt +++ b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightPixelMath.kt @@ -2,9 +2,9 @@ package nebulosa.pixinsight.script import nebulosa.io.resource import nebulosa.io.transferAndClose -import nebulosa.pixinsight.script.PixInsightImageSolver.Output import java.nio.file.Files import java.nio.file.Path +import java.util.concurrent.CompletableFuture import kotlin.io.path.deleteIfExists import kotlin.io.path.outputStream @@ -30,7 +30,7 @@ data class PixInsightPixelMath( override val success: Boolean = false, override val errorMessage: String? = null, @JvmField val outputImage: Path? = null, - ) : PixInsightScript.Output { + ) : PixInsightScriptOutput { companion object { @@ -48,14 +48,17 @@ data class PixInsightPixelMath( private val input = Input(statusPath, inputPaths, outputPath, expressionRK, expressionG, expressionB) override val arguments = listOf("-x=${execute(scriptPath, input)}") - override fun processOnComplete(exitCode: Int): Output { + override fun processOnExit(exitCode: Int, output: CompletableFuture) { if (exitCode == 0) { - repeat(30) { - statusPath.parseStatus()?.also { return it } ?: Thread.sleep(1000) + repeat(60) { + if (output.isDone) return + Thread.sleep(1000) + val status = statusPath.parseStatus() ?: return@repeat + output.complete(status) } } - return Output.FAILED + output.complete(Output.FAILED) } override fun close() { diff --git a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightScript.kt b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightScript.kt index 6e715cced..285c8312f 100644 --- a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightScript.kt +++ b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightScript.kt @@ -1,29 +1,27 @@ package nebulosa.pixinsight.script -import nebulosa.util.exec.CommandLine -import java.util.concurrent.Future +import java.util.concurrent.CompletableFuture +import java.util.concurrent.TimeUnit -sealed interface PixInsightScript : Future, AutoCloseable { +sealed interface PixInsightScript : AutoCloseable { val slot: Int - sealed interface Output { + val name: String - val success: Boolean + val arguments: Iterable - val errorMessage: String? - } + fun processOnStart(output: CompletableFuture) = Unit - val arguments: Iterable + fun processLine(line: String, output: CompletableFuture) = Unit - fun startCommandLine(commandLine: CommandLine) + fun processOnExit(exitCode: Int, output: CompletableFuture) = Unit - fun run(runner: PixInsightScriptRunner) + fun run(runner: PixInsightScriptRunner) = runner.run(this) - fun runSync(runner: PixInsightScriptRunner): T { - run(runner) - return get() - } + fun runSync(runner: PixInsightScriptRunner): T = run(runner).get() + + fun runSync(runner: PixInsightScriptRunner, timeout: Long, unit: TimeUnit): T = run(runner).get(timeout, unit) companion object { diff --git a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightScriptOutput.kt b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightScriptOutput.kt new file mode 100644 index 000000000..41985d09b --- /dev/null +++ b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightScriptOutput.kt @@ -0,0 +1,8 @@ +package nebulosa.pixinsight.script + +sealed interface PixInsightScriptOutput { + + val success: Boolean + + val errorMessage: String? +} diff --git a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightScriptRunner.kt b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightScriptRunner.kt index 01b4b48ac..8ff5b6e8a 100644 --- a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightScriptRunner.kt +++ b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightScriptRunner.kt @@ -1,25 +1,67 @@ package nebulosa.pixinsight.script +import nebulosa.commandline.CommandLineListener +import nebulosa.commandline.CommandLineListenerHandler import nebulosa.log.d +import nebulosa.log.de +import nebulosa.log.di import nebulosa.log.loggerFor -import nebulosa.util.exec.commandLine +import org.apache.commons.exec.CommandLine +import org.apache.commons.exec.DefaultExecutor +import org.apache.commons.exec.ExecuteWatchdog import java.nio.file.Path +import java.util.concurrent.CancellationException +import java.util.concurrent.CompletableFuture data class PixInsightScriptRunner(private val executablePath: Path) { - fun run(script: PixInsightScript<*>) { - val commandLine = commandLine { - executablePath(executablePath) - script.arguments.forEach(::putArg) - DEFAULT_ARGS.forEach(::putArg) + fun run(script: PixInsightScript): CompletableFuture { + val commandLine = CommandLine.parse("$executablePath") + script.arguments.forEach(commandLine::addArgument) + DEFAULT_ARGS.forEach(commandLine::addArgument) + + LOG.d("running {} script: {}", script.name, commandLine) + + val handler = CommandLineListenerHandler() + val completable = CompletableFuture() + + val executor = DefaultExecutor.builder() + .setExecuteStreamHandler(handler) + .get() + + executor.watchdog = ExecuteWatchdog.builder() + .setTimeout(ExecuteWatchdog.INFINITE_TIMEOUT_DURATION) + .get() + + completable.whenComplete { o, e -> + if (e is CancellationException) executor.watchdog.destroyProcess() + if (o != null) LOG.di("{} completed. output={}", script.name, o) + else LOG.de("{} completed with exception", script.name, e) } - LOG.d("running {} script: {}", script::class.simpleName, commandLine.command) + handler.registerCommandLineListener(object : CommandLineListener { - script.startCommandLine(commandLine) - } + override fun onStarted() { + script.processOnStart(completable) + } - fun abort(script: PixInsightScript<*>) = Unit + override fun onLineRead(line: String) { + script.processLine(line, completable) + } + + override fun onExited(exitCode: Int, exception: Throwable?) { + LOG.d("{} script finished. done={}, exitCode={}", script.name, completable.isDone, exitCode, exception) + + if (completable.isDone) return + if (exception != null) completable.completeExceptionally(exception) + else script.processOnExit(exitCode, completable) + } + }) + + executor.execute(commandLine, handler) + + return completable + } companion object { diff --git a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightStartup.kt b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightStartup.kt index 713170b19..4a4868507 100644 --- a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightStartup.kt +++ b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/script/PixInsightStartup.kt @@ -2,7 +2,9 @@ package nebulosa.pixinsight.script import nebulosa.io.resource import nebulosa.io.transferAndClose +import nebulosa.pixinsight.script.PixInsightPixelMath.Output import java.nio.file.Files +import java.util.concurrent.CompletableFuture import kotlin.concurrent.timer import kotlin.io.path.deleteIfExists import kotlin.io.path.outputStream @@ -13,7 +15,7 @@ data class PixInsightStartup(override val slot: Int) : AbstractPixInsightScript< data class Output( override val success: Boolean, override val errorMessage: String? = null, - ) : PixInsightScript.Output { + ) : PixInsightScriptOutput { companion object { @@ -31,15 +33,15 @@ data class PixInsightStartup(override val slot: Int) : AbstractPixInsightScript< override val arguments = listOf("-r=${execute(scriptPath, outputPath, 0)}", if (slot > 0) "-n=$slot" else "-n") - override fun beforeRun() { + override fun processOnStart(output: CompletableFuture) { var count = 0 timer("PixInsight Startup Timer", true, 1000L, 500L) { if (outputPath.readText() == "STARTED") { - complete(Output.SUCCESS) + output.complete(Output.SUCCESS) cancel() - } else if (count >= 60) { - complete(Output.FAILED) + } else if (count >= 120) { + output.complete(Output.FAILED) cancel() } @@ -47,8 +49,6 @@ data class PixInsightStartup(override val slot: Int) : AbstractPixInsightScript< } } - override fun processOnComplete(exitCode: Int) = Output.FAILED - override fun close() { scriptPath.deleteIfExists() outputPath.deleteIfExists() diff --git a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/stacker/PixInsightAutoStacker.kt b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/stacker/PixInsightAutoStacker.kt index c5e2ee52b..8d5ec5bed 100644 --- a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/stacker/PixInsightAutoStacker.kt +++ b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/stacker/PixInsightAutoStacker.kt @@ -4,9 +4,9 @@ import nebulosa.pixinsight.script.PixInsightScript import nebulosa.pixinsight.script.PixInsightScriptRunner import nebulosa.stacker.AutoStacker import nebulosa.stacker.AutoStackerListener -import nebulosa.util.concurrency.cancellation.CancellationToken import java.nio.file.Path import java.util.concurrent.CancellationException +import java.util.concurrent.atomic.AtomicBoolean import kotlin.io.path.deleteIfExists data class PixInsightAutoStacker( @@ -20,6 +20,7 @@ data class PixInsightAutoStacker( private val stacker = PixInsightStacker(runner, workingDirectory, slot) private val listeners = HashSet() + private val stopped = AtomicBoolean() override fun registerAutoStackerListener(listener: AutoStackerListener) { listeners.add(listener) @@ -29,19 +30,21 @@ data class PixInsightAutoStacker( listeners.remove(listener) } - override fun stack(targetPaths: Collection, outputPath: Path, referencePath: Path, cancellationToken: CancellationToken): Boolean { + override fun stack(targetPaths: Collection, outputPath: Path, referencePath: Path): Boolean { if (targetPaths.isEmpty()) return false val calibratedPath = Path.of("$workingDirectory", "calibrated.xisf") val alignedPath = Path.of("$workingDirectory", "aligned.xisf") + stopped.set(false) + try { var stackCount = 0 for (path in targetPaths) { var targetPath = path - if (cancellationToken.isCancelled) return false + if (stopped.get()) return false listeners.forEach { it.onCalibrationStarted(stackCount, path) } @@ -50,7 +53,7 @@ data class PixInsightAutoStacker( targetPath = calibratedPath } - if (cancellationToken.isCancelled) return false + if (stopped.get()) return false if (stackCount > 0) { listeners.forEach { it.onAlignStarted(stackCount, path) } @@ -58,7 +61,7 @@ data class PixInsightAutoStacker( if (align(referencePath, targetPath, alignedPath)) { listeners.forEach { it.onAlignFinished(stackCount, path, alignedPath) } - if (cancellationToken.isCancelled) return false + if (stopped.get()) return false listeners.forEach { it.onIntegrationStarted(stackCount, path) } integrate(stackCount, outputPath, alignedPath, outputPath) @@ -72,11 +75,11 @@ data class PixInsightAutoStacker( if (align(referencePath, targetPath, alignedPath)) { listeners.forEach { it.onAlignFinished(stackCount, path, alignedPath) } - if (cancellationToken.isCancelled) return false + if (stopped.get()) return false saveAs(alignedPath, outputPath) - if (cancellationToken.isCancelled) return false + if (stopped.get()) return false listeners.forEach { it.onIntegrationStarted(0, path) } integrate(0, outputPath, alignedPath, outputPath) @@ -91,7 +94,7 @@ data class PixInsightAutoStacker( stackCount = 1 } - if (cancellationToken.isCancelled) return false + if (stopped.get()) return false } } catch (_: CancellationException) { return false @@ -103,6 +106,10 @@ data class PixInsightAutoStacker( return true } + override fun stop() { + stopped.set(true) + } + override fun calibrate(targetPath: Path, outputPath: Path, darkPath: Path?, flatPath: Path?, biasPath: Path?): Boolean { return stacker.calibrate(targetPath, outputPath, darkPath, flatPath, biasPath) } diff --git a/nebulosa-pixinsight/src/test/kotlin/PixInsightAutoStackerTest.kt b/nebulosa-pixinsight/src/test/kotlin/PixInsightAutoStackerTest.kt index 75fc0736a..69514950c 100644 --- a/nebulosa-pixinsight/src/test/kotlin/PixInsightAutoStackerTest.kt +++ b/nebulosa-pixinsight/src/test/kotlin/PixInsightAutoStackerTest.kt @@ -4,7 +4,10 @@ import io.kotest.matchers.booleans.shouldBeTrue import io.kotest.matchers.shouldBe import nebulosa.image.algorithms.transformation.AutoScreenTransformFunction import nebulosa.pixinsight.stacker.PixInsightAutoStacker -import nebulosa.test.* +import nebulosa.test.AbstractTest +import nebulosa.test.NonGitHubOnly +import nebulosa.test.fits.* +import nebulosa.test.save import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test @@ -13,7 +16,7 @@ class PixInsightAutoStackerTest : AbstractTest() { @Test fun stack() { - val files = listOf(PI_01_LIGHT, PI_02_LIGHT, PI_03_LIGHT, PI_04_LIGHT, PI_05_LIGHT, PI_06_LIGHT, PI_07_LIGHT, PI_08_LIGHT) + val files = listOf(STACKING_LIGHT_MONO_01_FITS, STACKING_LIGHT_MONO_02_FITS, STACKING_LIGHT_MONO_03_FITS, STACKING_LIGHT_MONO_04_FITS, STACKING_LIGHT_MONO_05_FITS, STACKING_LIGHT_MONO_06_FITS, STACKING_LIGHT_MONO_07_FITS, STACKING_LIGHT_MONO_08_FITS) val workingDirectory = tempDirectory("pi-") val outputPath = tempPath("pi-", ".fits") @@ -27,11 +30,11 @@ class PixInsightAutoStackerTest : AbstractTest() { @Test @Disabled fun calibratedStack() { - val files = listOf(PI_01_LIGHT, PI_02_LIGHT, PI_03_LIGHT, PI_04_LIGHT, PI_05_LIGHT, PI_06_LIGHT, PI_07_LIGHT, PI_08_LIGHT) + val files = listOf(STACKING_LIGHT_MONO_01_FITS, STACKING_LIGHT_MONO_02_FITS, STACKING_LIGHT_MONO_03_FITS, STACKING_LIGHT_MONO_04_FITS, STACKING_LIGHT_MONO_05_FITS, STACKING_LIGHT_MONO_06_FITS, STACKING_LIGHT_MONO_07_FITS, STACKING_LIGHT_MONO_08_FITS) val workingDirectory = tempDirectory("pi-") val outputPath = tempPath("pi-", ".fits") - val stacker = PixInsightAutoStacker(RUNNER, workingDirectory, PI_DARK, PI_FLAT, PI_BIAS) + val stacker = PixInsightAutoStacker(RUNNER, workingDirectory, STACKING_DARK_MONO_FITS, STACKING_FLAT_MONO_FITS, STACKING_BIAS_MONO_FITS) stacker.stack(files, outputPath).shouldBeTrue() outputPath.openAsImage().transform(AutoScreenTransformFunction) diff --git a/nebulosa-pixinsight/src/test/kotlin/PixInsightLiveStackerTest.kt b/nebulosa-pixinsight/src/test/kotlin/PixInsightLiveStackerTest.kt index 231367299..15d08e604 100644 --- a/nebulosa-pixinsight/src/test/kotlin/PixInsightLiveStackerTest.kt +++ b/nebulosa-pixinsight/src/test/kotlin/PixInsightLiveStackerTest.kt @@ -6,14 +6,17 @@ import io.kotest.matchers.nulls.shouldNotBeNull import io.kotest.matchers.shouldBe import nebulosa.image.algorithms.transformation.AutoScreenTransformFunction import nebulosa.pixinsight.livestacker.PixInsightLiveStacker -import nebulosa.test.* +import nebulosa.test.AbstractTest +import nebulosa.test.NonGitHubOnly +import nebulosa.test.fits.* +import nebulosa.test.save import org.junit.jupiter.api.Test import java.nio.file.Path @NonGitHubOnly class PixInsightLiveStackerTest : AbstractTest() { - private val files = listOf(PI_01_LIGHT, PI_02_LIGHT, PI_03_LIGHT, PI_04_LIGHT, PI_05_LIGHT, PI_06_LIGHT, PI_07_LIGHT, PI_08_LIGHT) + private val files = listOf(STACKING_LIGHT_MONO_01_FITS, STACKING_LIGHT_MONO_02_FITS, STACKING_LIGHT_MONO_03_FITS, STACKING_LIGHT_MONO_04_FITS, STACKING_LIGHT_MONO_05_FITS, STACKING_LIGHT_MONO_06_FITS, STACKING_LIGHT_MONO_07_FITS, STACKING_LIGHT_MONO_08_FITS) @Test fun stack() { @@ -49,7 +52,7 @@ class PixInsightLiveStackerTest : AbstractTest() { it.isRunning.shouldBeTrue() for (file in files) { - outputPath = stacker.add(file, PI_04_LIGHT) + outputPath = stacker.add(file, STACKING_LIGHT_MONO_04_FITS) } outputPath.shouldNotBeNull().openAsImage().transform(AutoScreenTransformFunction) diff --git a/nebulosa-pixinsight/src/test/kotlin/PixInsightScriptTest.kt b/nebulosa-pixinsight/src/test/kotlin/PixInsightScriptTest.kt index 3098a6b02..836bfe93c 100644 --- a/nebulosa-pixinsight/src/test/kotlin/PixInsightScriptTest.kt +++ b/nebulosa-pixinsight/src/test/kotlin/PixInsightScriptTest.kt @@ -10,12 +10,17 @@ import nebulosa.image.algorithms.transformation.AutoScreenTransformFunction import nebulosa.math.* import nebulosa.pixinsight.script.* import nebulosa.pixinsight.script.PixInsightScript.Companion.UNSPECIFIED_SLOT -import nebulosa.test.* +import nebulosa.test.AbstractTest +import nebulosa.test.NonGitHubOnly +import nebulosa.test.download +import nebulosa.test.fits.* +import nebulosa.test.save import nebulosa.xisf.isXisf import nebulosa.xisf.xisf import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test import java.nio.file.Path +import java.time.Duration import kotlin.math.roundToInt @NonGitHubOnly @@ -38,7 +43,7 @@ class PixInsightScriptTest : AbstractTest() { @Test fun calibrate() { val workingDirectory = tempDirectory("pi-") - PixInsightCalibrate(UNSPECIFIED_SLOT, workingDirectory, PI_01_LIGHT, PI_DARK, PI_FLAT, PI_BIAS) + PixInsightCalibrate(UNSPECIFIED_SLOT, workingDirectory, STACKING_LIGHT_MONO_01_FITS, STACKING_DARK_MONO_FITS, STACKING_FLAT_MONO_FITS, STACKING_BIAS_MONO_FITS) .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().openAsImage() } .transform(AutoScreenTransformFunction).save("pi-calibrate").second shouldBe "731562ee12f45bf7c1095f4773f70e71" } @@ -46,33 +51,33 @@ class PixInsightScriptTest : AbstractTest() { @Test fun align() { val workingDirectory = tempDirectory("pi-") - PixInsightAlign(UNSPECIFIED_SLOT, workingDirectory, PI_01_LIGHT, PI_02_LIGHT) + PixInsightAlign(UNSPECIFIED_SLOT, workingDirectory, STACKING_LIGHT_MONO_01_FITS, STACKING_LIGHT_MONO_02_FITS) .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().openAsImage() } .transform(AutoScreenTransformFunction).save("pi-align").second shouldBe "483ebaf15afa5957fe099f3ee2beff78" } @Test fun detectStars() { - PixInsightDetectStars(UNSPECIFIED_SLOT, PI_FOCUS_0) + PixInsightDetectStars(UNSPECIFIED_SLOT, FOCUS_09_FITS) .use { it.runSync(RUNNER).stars } .map { it.hfd } - .average() shouldBe (8.43 plusOrMinus 1e-2) + .average() shouldBe (9.07 plusOrMinus 1e-2) - PixInsightDetectStars(UNSPECIFIED_SLOT, PI_FOCUS_30000) + PixInsightDetectStars(UNSPECIFIED_SLOT, FOCUS_14_FITS) .use { it.runSync(RUNNER).stars } .map { it.hfd } - .average() shouldBe (1.85 plusOrMinus 1e-2) + .average() shouldBe (2.92 plusOrMinus 1e-2) - PixInsightDetectStars(UNSPECIFIED_SLOT, PI_FOCUS_100000) + PixInsightDetectStars(UNSPECIFIED_SLOT, FOCUS_26_FITS) .use { it.runSync(RUNNER).stars } .map { it.hfd } - .average() shouldBe (18.35 plusOrMinus 1e-2) + .average() shouldBe (17.90 plusOrMinus 1e-2) } @Test fun pixelMath() { val outputPath = tempPath("pi-stacked-", ".fits") - PixInsightPixelMath(UNSPECIFIED_SLOT, listOf(PI_01_LIGHT, PI_02_LIGHT), outputPath, "{{0}} + {{1}}") + PixInsightPixelMath(UNSPECIFIED_SLOT, listOf(STACKING_LIGHT_MONO_01_FITS, STACKING_LIGHT_MONO_02_FITS), outputPath, "{{0}} + {{1}}") .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().openAsImage() } .transform(AutoScreenTransformFunction).save("pi-pixelmath").second shouldBe "cafc8138e2ce17614dcfa10edf410b07" } @@ -80,7 +85,7 @@ class PixInsightScriptTest : AbstractTest() { @Test fun abe() { val outputPath = tempPath("pi-abe-", ".fits") - PixInsightAutomaticBackgroundExtractor(UNSPECIFIED_SLOT, PI_01_LIGHT, outputPath) + PixInsightAutomaticBackgroundExtractor(UNSPECIFIED_SLOT, STACKING_LIGHT_MONO_01_FITS, outputPath) .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().openAsImage() } .transform(AutoScreenTransformFunction).save("pi-abe").second shouldBe "bf62207dc17190009ba215da7c011297" } @@ -88,48 +93,48 @@ class PixInsightScriptTest : AbstractTest() { @Test fun lrgbCombination() { val outputPath = tempPath("pi-lrgb-", ".fits") - PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, PI_01_LIGHT, PI_01_LIGHT, PI_01_LIGHT, PI_01_LIGHT) + PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, STACKING_LIGHT_MONO_01_FITS, STACKING_LIGHT_MONO_01_FITS, STACKING_LIGHT_MONO_01_FITS, STACKING_LIGHT_MONO_01_FITS) .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().openAsImage() } .transform(AutoScreenTransformFunction).save("pi-lrgb").second shouldBe "99db35d78f7b360e7592217f4179b189" val weights = doubleArrayOf(1.0, 0.2470588, 0.31764705, 0.709803921) // LRGB #3F51B5 - PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, PI_01_LIGHT, PI_01_LIGHT, PI_01_LIGHT, PI_01_LIGHT, weights) + PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, STACKING_LIGHT_MONO_01_FITS, STACKING_LIGHT_MONO_01_FITS, STACKING_LIGHT_MONO_01_FITS, STACKING_LIGHT_MONO_01_FITS, weights) .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().openAsImage() } .transform(AutoScreenTransformFunction).save("pi-weighted-lrgb").second shouldBe "1148ee222fbfb382ad2d708df5b0f79f" - PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, PI_01_LIGHT, PI_01_LIGHT, null, null) + PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, STACKING_LIGHT_MONO_01_FITS, STACKING_LIGHT_MONO_01_FITS, null, null) .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().openAsImage() } .transform(AutoScreenTransformFunction).save("pi-lr").second shouldBe "9100d3ce892f05f4b832b2fb5f35b5a1" - PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, PI_01_LIGHT, null, PI_01_LIGHT, null) + PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, STACKING_LIGHT_MONO_01_FITS, null, STACKING_LIGHT_MONO_01_FITS, null) .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().openAsImage() } .transform(AutoScreenTransformFunction).save("pi-lg").second shouldBe "b4e8d8f7e289db60b41ba2bbe0035344" - PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, PI_01_LIGHT, null, null, PI_01_LIGHT) + PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, STACKING_LIGHT_MONO_01_FITS, null, null, STACKING_LIGHT_MONO_01_FITS) .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().openAsImage() } .transform(AutoScreenTransformFunction).save("pi-lb").second shouldBe "1760e7cb1d139b63022dd975fe84897d" - PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, null, PI_01_LIGHT, PI_01_LIGHT, null) + PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, null, STACKING_LIGHT_MONO_01_FITS, STACKING_LIGHT_MONO_01_FITS, null) .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().openAsImage() } .transform(AutoScreenTransformFunction).save("pi-rg").second shouldBe "8c59307b5943932aefdf2dedfe1c8178" - PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, null, PI_01_LIGHT, null, PI_01_LIGHT) + PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, null, STACKING_LIGHT_MONO_01_FITS, null, STACKING_LIGHT_MONO_01_FITS) .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().openAsImage() } .transform(AutoScreenTransformFunction).save("pi-rb").second shouldBe "1bdf9cada6a33f76dceaccdaacf30fef" - PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, null, null, PI_01_LIGHT, PI_01_LIGHT) + PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, null, null, STACKING_LIGHT_MONO_01_FITS, STACKING_LIGHT_MONO_01_FITS) .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().openAsImage() } .transform(AutoScreenTransformFunction).save("pi-bg").second shouldBe "4a9c81c71fd37546fd300d1037742fa2" - PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, PI_01_LIGHT, PI_01_LIGHT, PI_01_LIGHT, null) + PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, STACKING_LIGHT_MONO_01_FITS, STACKING_LIGHT_MONO_01_FITS, STACKING_LIGHT_MONO_01_FITS, null) .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().openAsImage() } .transform(AutoScreenTransformFunction).save("pi-lrg").second shouldBe "06c32c8679d409302423baa3a07fb241" - PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, PI_01_LIGHT, PI_01_LIGHT, null, PI_01_LIGHT) + PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, STACKING_LIGHT_MONO_01_FITS, STACKING_LIGHT_MONO_01_FITS, null, STACKING_LIGHT_MONO_01_FITS) .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().openAsImage() } .transform(AutoScreenTransformFunction).save("pi-lrb").second shouldBe "f6d026cb63f7a58fc325e422c277ff89" - PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, PI_01_LIGHT, null, PI_01_LIGHT, PI_01_LIGHT) + PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, STACKING_LIGHT_MONO_01_FITS, null, STACKING_LIGHT_MONO_01_FITS, STACKING_LIGHT_MONO_01_FITS) .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().openAsImage() } .transform(AutoScreenTransformFunction).save("pi-lbg").second shouldBe "67f961110fb4b9f0033b3b8dbc8b1638" } @@ -137,7 +142,7 @@ class PixInsightScriptTest : AbstractTest() { @Test fun fileFormatConversion() { val xisfPath = tempPath("pi-ffc", ".xisf") - PixInsightFileFormatConversion(UNSPECIFIED_SLOT, PI_01_LIGHT, xisfPath) + PixInsightFileFormatConversion(UNSPECIFIED_SLOT, STACKING_LIGHT_MONO_01_FITS, xisfPath) .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().isXisf().shouldBeTrue() } val fitsPath = tempPath("pi-ffc", ".fits") @@ -157,7 +162,7 @@ class PixInsightScriptTest : AbstractTest() { val focalDistance = 200.0 // mm val pixelSize = 6.58 - with(PixInsightImageSolver(UNSPECIFIED_SLOT, path, centerRA, centerDEC, pixelSize = pixelSize, resolution = resolution) + with(PixInsightImageSolver(UNSPECIFIED_SLOT, path, centerRA, centerDEC, pixelSize = pixelSize, resolution = resolution, timeout = Duration.ofMinutes(5)) .use { it.runSync(RUNNER) }) { success.shouldBeTrue() this.focalLength shouldBe (200.355 plusOrMinus 1e-5) @@ -171,7 +176,7 @@ class PixInsightScriptTest : AbstractTest() { imageHeight.roundToInt() shouldBeExactly 526 } - with(PixInsightImageSolver(UNSPECIFIED_SLOT, path, centerRA, centerDEC, pixelSize = pixelSize, focalLength = focalDistance) + with(PixInsightImageSolver(UNSPECIFIED_SLOT, path, centerRA, centerDEC, pixelSize = pixelSize, focalLength = focalDistance, timeout = Duration.ofMinutes(5)) .use { it.runSync(RUNNER) }) { success.shouldBeTrue() this.focalLength shouldBe (200.355 plusOrMinus 1e-5) diff --git a/nebulosa-pixinsight/src/test/kotlin/PixInsightStackerTest.kt b/nebulosa-pixinsight/src/test/kotlin/PixInsightStackerTest.kt index c04e0a364..1ab1d8fa2 100644 --- a/nebulosa-pixinsight/src/test/kotlin/PixInsightStackerTest.kt +++ b/nebulosa-pixinsight/src/test/kotlin/PixInsightStackerTest.kt @@ -4,7 +4,12 @@ import io.kotest.matchers.booleans.shouldBeTrue import io.kotest.matchers.shouldBe import nebulosa.image.algorithms.transformation.AutoScreenTransformFunction import nebulosa.pixinsight.stacker.PixInsightStacker -import nebulosa.test.* +import nebulosa.test.AbstractTest +import nebulosa.test.NonGitHubOnly +import nebulosa.test.fits.STACKING_DARK_MONO_FITS +import nebulosa.test.fits.STACKING_LIGHT_MONO_01_FITS +import nebulosa.test.fits.STACKING_LIGHT_MONO_03_FITS +import nebulosa.test.save import org.junit.jupiter.api.Test @NonGitHubOnly @@ -15,7 +20,7 @@ class PixInsightStackerTest : AbstractTest() { val outputPath = tempPath("pi-", ".fits") val workingDirectory = tempDirectory("pi-") val stacker = PixInsightStacker(RUNNER, workingDirectory) - stacker.align(PI_01_LIGHT, PI_03_LIGHT, outputPath).shouldBeTrue() + stacker.align(STACKING_LIGHT_MONO_01_FITS, STACKING_LIGHT_MONO_03_FITS, outputPath).shouldBeTrue() outputPath.openAsImage().transform(AutoScreenTransformFunction) .save("pi-aligned").second shouldBe "106651a7c1e640852384284ec12e0977" @@ -26,7 +31,7 @@ class PixInsightStackerTest : AbstractTest() { val outputPath = tempPath("pi-", ".fits") val workingDirectory = tempDirectory("pi-") val stacker = PixInsightStacker(RUNNER, workingDirectory) - stacker.calibrate(PI_01_LIGHT, outputPath, PI_DARK).shouldBeTrue() + stacker.calibrate(STACKING_LIGHT_MONO_01_FITS, outputPath, STACKING_DARK_MONO_FITS).shouldBeTrue() outputPath.openAsImage().transform(AutoScreenTransformFunction) .save("pi-calibrated").second shouldBe "8f5a2632c701680b41fcfe170c9cf468" @@ -37,7 +42,7 @@ class PixInsightStackerTest : AbstractTest() { val outputPath = tempPath("pi-", ".fits") val workingDirectory = tempDirectory("pi-") val stacker = PixInsightStacker(RUNNER, workingDirectory) - stacker.integrate(1, PI_01_LIGHT, PI_01_LIGHT, outputPath).shouldBeTrue() + stacker.integrate(1, STACKING_LIGHT_MONO_01_FITS, STACKING_LIGHT_MONO_01_FITS, outputPath).shouldBeTrue() outputPath.openAsImage().transform(AutoScreenTransformFunction) .save("pi-integrated").second shouldBe "bf62207dc17190009ba215da7c011297" @@ -48,7 +53,7 @@ class PixInsightStackerTest : AbstractTest() { val outputPath = tempPath("pi-", ".fits") val workingDirectory = tempDirectory("pi-") val stacker = PixInsightStacker(RUNNER, workingDirectory) - stacker.combineLRGB(outputPath, PI_01_LIGHT, PI_01_LIGHT).shouldBeTrue() + stacker.combineLRGB(outputPath, STACKING_LIGHT_MONO_01_FITS, STACKING_LIGHT_MONO_01_FITS).shouldBeTrue() outputPath.openAsImage().transform(AutoScreenTransformFunction) .save("pi-lrgb-combined").second shouldBe "9100d3ce892f05f4b832b2fb5f35b5a1" @@ -59,7 +64,7 @@ class PixInsightStackerTest : AbstractTest() { val outputPath = tempPath("pi-", ".fits") val workingDirectory = tempDirectory("pi-") val stacker = PixInsightStacker(RUNNER, workingDirectory) - stacker.combineLuminance(outputPath, PI_01_LIGHT, PI_01_LIGHT, true).shouldBeTrue() + stacker.combineLuminance(outputPath, STACKING_LIGHT_MONO_01_FITS, STACKING_LIGHT_MONO_01_FITS, true).shouldBeTrue() outputPath.openAsImage().transform(AutoScreenTransformFunction) .save("pi-mono-luminance-combined").second shouldBe "85de365a9895234222acdc6e9feb7009" diff --git a/nebulosa-platesolver/build.gradle.kts b/nebulosa-platesolver/build.gradle.kts index a44c013cc..39c2d0141 100644 --- a/nebulosa-platesolver/build.gradle.kts +++ b/nebulosa-platesolver/build.gradle.kts @@ -7,7 +7,6 @@ dependencies { api(project(":nebulosa-math")) api(project(":nebulosa-wcs")) api(project(":nebulosa-image")) - api(project(":nebulosa-util")) implementation(project(":nebulosa-log")) testImplementation(project(":nebulosa-test")) } diff --git a/nebulosa-platesolver/src/main/kotlin/nebulosa/platesolver/PlateSolver.kt b/nebulosa-platesolver/src/main/kotlin/nebulosa/platesolver/PlateSolver.kt index 23b53cba3..6c1a6861d 100644 --- a/nebulosa-platesolver/src/main/kotlin/nebulosa/platesolver/PlateSolver.kt +++ b/nebulosa-platesolver/src/main/kotlin/nebulosa/platesolver/PlateSolver.kt @@ -2,7 +2,6 @@ package nebulosa.platesolver import nebulosa.image.Image import nebulosa.math.Angle -import nebulosa.util.concurrency.cancellation.CancellationToken import java.nio.file.Path import java.time.Duration @@ -12,6 +11,5 @@ interface PlateSolver { path: Path?, image: Image?, centerRA: Angle = 0.0, centerDEC: Angle = 0.0, radius: Angle = 0.0, downsampleFactor: Int = 0, timeout: Duration = Duration.ZERO, - cancellationToken: CancellationToken = CancellationToken.NONE, ): PlateSolution } diff --git a/nebulosa-siril/build.gradle.kts b/nebulosa-siril/build.gradle.kts index 8fd972b46..6be91e10c 100644 --- a/nebulosa-siril/build.gradle.kts +++ b/nebulosa-siril/build.gradle.kts @@ -5,6 +5,7 @@ plugins { dependencies { api(project(":nebulosa-math")) + api(project(":nebulosa-commandline")) api(project(":nebulosa-livestacker")) api(project(":nebulosa-platesolver")) api(project(":nebulosa-stardetector")) diff --git a/nebulosa-siril/src/main/kotlin/nebulosa/siril/command/Cd.kt b/nebulosa-siril/src/main/kotlin/nebulosa/siril/command/Cd.kt index 5e9a3fc62..deb05a3c9 100644 --- a/nebulosa-siril/src/main/kotlin/nebulosa/siril/command/Cd.kt +++ b/nebulosa-siril/src/main/kotlin/nebulosa/siril/command/Cd.kt @@ -1,8 +1,8 @@ package nebulosa.siril.command +import nebulosa.commandline.CommandLineListener import nebulosa.siril.command.SirilCommand.Companion.SCRIPT_EXECUTION_FAILED import nebulosa.util.concurrency.latch.CountUpDownLatch -import nebulosa.util.exec.CommandLineListener import java.nio.file.Path import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicBoolean @@ -27,6 +27,10 @@ data class Cd(@JvmField val directory: Path) : SirilCommand, CommandLin latch.reset() } + override fun onExited(exitCode: Int, exception: Throwable?) { + latch.reset() + } + override fun write(commandLine: SirilCommandLine): Boolean { return try { commandLine.registerCommandLineListener(this) diff --git a/nebulosa-siril/src/main/kotlin/nebulosa/siril/command/DumpHeader.kt b/nebulosa-siril/src/main/kotlin/nebulosa/siril/command/DumpHeader.kt index 8205083b1..a6fb720b9 100644 --- a/nebulosa-siril/src/main/kotlin/nebulosa/siril/command/DumpHeader.kt +++ b/nebulosa-siril/src/main/kotlin/nebulosa/siril/command/DumpHeader.kt @@ -1,10 +1,10 @@ package nebulosa.siril.command +import nebulosa.commandline.CommandLineListener import nebulosa.fits.FitsHeader import nebulosa.fits.FitsHeaderCard import nebulosa.image.format.Header import nebulosa.util.concurrency.latch.CountUpDownLatch -import nebulosa.util.exec.CommandLineListener import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicBoolean @@ -36,6 +36,10 @@ data class DumpHeader(private val header: Header = FitsHeader()) : SirilCommand< } } + override fun onExited(exitCode: Int, exception: Throwable?) { + latch.reset() + } + override fun write(commandLine: SirilCommandLine): Header { return try { commandLine.registerCommandLineListener(this) diff --git a/nebulosa-siril/src/main/kotlin/nebulosa/siril/command/FindStar.kt b/nebulosa-siril/src/main/kotlin/nebulosa/siril/command/FindStar.kt index 26f77c775..aba29926e 100644 --- a/nebulosa-siril/src/main/kotlin/nebulosa/siril/command/FindStar.kt +++ b/nebulosa-siril/src/main/kotlin/nebulosa/siril/command/FindStar.kt @@ -1,10 +1,10 @@ package nebulosa.siril.command +import nebulosa.commandline.CommandLineListener import nebulosa.fits.height import nebulosa.log.loggerFor import nebulosa.stardetector.StarPoint import nebulosa.util.concurrency.latch.CountUpDownLatch -import nebulosa.util.exec.CommandLineListener import java.nio.file.Files import java.nio.file.Path import java.util.concurrent.TimeUnit @@ -44,7 +44,7 @@ data class FindStar( } } - override fun onExit(exitCode: Int, exception: Throwable?) { + override fun onExited(exitCode: Int, exception: Throwable?) { latch.reset() } diff --git a/nebulosa-siril/src/main/kotlin/nebulosa/siril/command/LiveStack.kt b/nebulosa-siril/src/main/kotlin/nebulosa/siril/command/LiveStack.kt index b5e287d3c..3ee3d4999 100644 --- a/nebulosa-siril/src/main/kotlin/nebulosa/siril/command/LiveStack.kt +++ b/nebulosa-siril/src/main/kotlin/nebulosa/siril/command/LiveStack.kt @@ -1,7 +1,7 @@ package nebulosa.siril.command +import nebulosa.commandline.CommandLineListener import nebulosa.util.concurrency.latch.CountUpDownLatch -import nebulosa.util.exec.CommandLineListener import java.nio.file.Path import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicBoolean @@ -26,7 +26,7 @@ data class LiveStack(@JvmField val path: Path) : SirilCommand, CommandL latch.reset() } - override fun onExit(exitCode: Int, exception: Throwable?) { + override fun onExited(exitCode: Int, exception: Throwable?) { latch.reset() } diff --git a/nebulosa-siril/src/main/kotlin/nebulosa/siril/command/Load.kt b/nebulosa-siril/src/main/kotlin/nebulosa/siril/command/Load.kt index 67d48e7c3..f456dea97 100644 --- a/nebulosa-siril/src/main/kotlin/nebulosa/siril/command/Load.kt +++ b/nebulosa-siril/src/main/kotlin/nebulosa/siril/command/Load.kt @@ -1,8 +1,8 @@ package nebulosa.siril.command +import nebulosa.commandline.CommandLineListener import nebulosa.siril.command.SirilCommand.Companion.SCRIPT_EXECUTION_FAILED import nebulosa.util.concurrency.latch.CountUpDownLatch -import nebulosa.util.exec.CommandLineListener import java.nio.file.Path import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicBoolean @@ -27,6 +27,10 @@ data class Load(@JvmField val path: Path) : SirilCommand, CommandLineLi latch.reset() } + override fun onExited(exitCode: Int, exception: Throwable?) { + latch.reset() + } + override fun write(commandLine: SirilCommandLine): Boolean { return try { commandLine.registerCommandLineListener(this) diff --git a/nebulosa-siril/src/main/kotlin/nebulosa/siril/command/PlateSolve.kt b/nebulosa-siril/src/main/kotlin/nebulosa/siril/command/PlateSolve.kt index f627d0f1e..0a22fd54f 100644 --- a/nebulosa-siril/src/main/kotlin/nebulosa/siril/command/PlateSolve.kt +++ b/nebulosa-siril/src/main/kotlin/nebulosa/siril/command/PlateSolve.kt @@ -1,12 +1,12 @@ package nebulosa.siril.command +import nebulosa.commandline.CommandLineListener import nebulosa.log.di import nebulosa.log.loggerFor import nebulosa.math.* import nebulosa.platesolver.Parity import nebulosa.platesolver.PlateSolution import nebulosa.util.concurrency.latch.CountUpDownLatch -import nebulosa.util.exec.CommandLineListener import java.nio.file.Path import java.time.Duration import java.util.concurrent.atomic.AtomicBoolean @@ -70,7 +70,7 @@ data class PlateSolve( } } - override fun onExit(exitCode: Int, exception: Throwable?) { + override fun onExited(exitCode: Int, exception: Throwable?) { LOG.di("plate solver finished. exitCode={}", exitCode, exception) exited.set(true) latch.reset() @@ -78,7 +78,7 @@ data class PlateSolve( override fun write(commandLine: SirilCommandLine): PlateSolution { if (commandLine.execute(Load(path))) { - LOG.di("plate solver started. pid={}", commandLine.pid) + LOG.di("plate solver started") try { commandLine.registerCommandLineListener(this) diff --git a/nebulosa-siril/src/main/kotlin/nebulosa/siril/command/SirilCommandLine.kt b/nebulosa-siril/src/main/kotlin/nebulosa/siril/command/SirilCommandLine.kt index f524fe3be..ef18cc01a 100644 --- a/nebulosa-siril/src/main/kotlin/nebulosa/siril/command/SirilCommandLine.kt +++ b/nebulosa-siril/src/main/kotlin/nebulosa/siril/command/SirilCommandLine.kt @@ -1,42 +1,58 @@ package nebulosa.siril.command +import nebulosa.commandline.CommandLineListener +import nebulosa.commandline.CommandLineListenerHandler +import nebulosa.log.di +import nebulosa.log.loggerFor import nebulosa.util.concurrency.cancellation.CancellationListener import nebulosa.util.concurrency.cancellation.CancellationSource -import nebulosa.util.exec.CommandLineListener -import nebulosa.util.exec.commandLine +import org.apache.commons.exec.CommandLine +import org.apache.commons.exec.DefaultExecutor +import org.apache.commons.exec.ExecuteWatchdog import java.nio.file.Path -data class SirilCommandLine(private val executablePath: Path) : Runnable, CancellationListener, AutoCloseable { +class SirilCommandLine(executablePath: Path) : Runnable, CancellationListener, AutoCloseable { - private val commandLine = commandLine { - executablePath(executablePath) - putArg("-s", "-") + private val handler = CommandLineListenerHandler() + + private val executor = DefaultExecutor.builder() + .setExecuteStreamHandler(handler) + .get() + + private val commandLine = CommandLine.parse("$executablePath") + .addArgument("-s").addArgument("-") + + @Volatile private var started = false + + init { + executor.watchdog = ExecuteWatchdog.builder() + .setTimeout(ExecuteWatchdog.INFINITE_TIMEOUT_DURATION) + .get() } val isRunning - get() = commandLine.isRunning - - val pid - get() = commandLine.pid + get() = started && executor.watchdog.isWatching fun registerCommandLineListener(listener: CommandLineListener) { - commandLine.registerCommandLineListener(listener) + handler.registerCommandLineListener(listener) } fun unregisterCommandLineListener(listener: CommandLineListener) { - commandLine.unregisterCommandLineListener(listener) + handler.unregisterCommandLineListener(listener) } override fun run() { - if (!commandLine.isRunning) { - commandLine.start() + if (!isRunning) { + started = true + executor.execute(commandLine, handler) execute(Requires) } } internal fun write(command: String) { - if (commandLine.isRunning) { - commandLine.writer.println(command) + if (isRunning) { + LOG.di(command) + handler.write(command) } } @@ -50,6 +66,14 @@ data class SirilCommandLine(private val executablePath: Path) : Runnable, Cancel override fun close() { execute(Exit) - commandLine.stop() + + if (isRunning) { + executor.watchdog.destroyProcess() + } + } + + companion object { + + @JvmStatic private val LOG = loggerFor() } } diff --git a/nebulosa-siril/src/main/kotlin/nebulosa/siril/command/StartLs.kt b/nebulosa-siril/src/main/kotlin/nebulosa/siril/command/StartLs.kt index 2711b06dc..4d4a97390 100644 --- a/nebulosa-siril/src/main/kotlin/nebulosa/siril/command/StartLs.kt +++ b/nebulosa-siril/src/main/kotlin/nebulosa/siril/command/StartLs.kt @@ -1,7 +1,7 @@ package nebulosa.siril.command +import nebulosa.commandline.CommandLineListener import nebulosa.util.concurrency.latch.CountUpDownLatch -import nebulosa.util.exec.CommandLineListener import java.nio.file.Path import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicBoolean @@ -41,6 +41,10 @@ data class StartLs( latch.reset() } + override fun onExited(exitCode: Int, exception: Throwable?) { + latch.reset() + } + override fun write(commandLine: SirilCommandLine): Boolean { return try { commandLine.registerCommandLineListener(this) diff --git a/nebulosa-siril/src/main/kotlin/nebulosa/siril/livestacker/SirilLiveStacker.kt b/nebulosa-siril/src/main/kotlin/nebulosa/siril/livestacker/SirilLiveStacker.kt index 29eb28544..4b8230301 100644 --- a/nebulosa-siril/src/main/kotlin/nebulosa/siril/livestacker/SirilLiveStacker.kt +++ b/nebulosa-siril/src/main/kotlin/nebulosa/siril/livestacker/SirilLiveStacker.kt @@ -1,11 +1,11 @@ package nebulosa.siril.livestacker +import nebulosa.commandline.CommandLineListener import nebulosa.livestacker.LiveStacker import nebulosa.log.d import nebulosa.log.loggerFor import nebulosa.siril.command.* import nebulosa.util.concurrency.latch.CountUpDownLatch -import nebulosa.util.exec.CommandLineListener import java.nio.file.Path import kotlin.io.path.deleteIfExists import kotlin.io.path.listDirectoryEntries @@ -39,7 +39,7 @@ data class SirilLiveStacker( commandLine.registerCommandLineListener(this) commandLine.run() - LOG.d("live stacking started. pid={}", commandLine.pid) + LOG.d("live stacking started") try { check(commandLine.execute(Cd(workingDirectory))) { "failed to run cd command" } @@ -72,7 +72,7 @@ data class SirilLiveStacker( workingDirectory.deleteStackingFiles() } - override fun onExit(exitCode: Int, exception: Throwable?) { + override fun onExited(exitCode: Int, exception: Throwable?) { LOG.d("live stacking finished. exitCode={}", exitCode, exception) } diff --git a/nebulosa-siril/src/main/kotlin/nebulosa/siril/platesolver/SirilPlateSolver.kt b/nebulosa-siril/src/main/kotlin/nebulosa/siril/platesolver/SirilPlateSolver.kt index 5e9877b8a..60c4ae988 100644 --- a/nebulosa-siril/src/main/kotlin/nebulosa/siril/platesolver/SirilPlateSolver.kt +++ b/nebulosa-siril/src/main/kotlin/nebulosa/siril/platesolver/SirilPlateSolver.kt @@ -3,11 +3,9 @@ package nebulosa.siril.platesolver import nebulosa.image.Image import nebulosa.math.Angle import nebulosa.math.toDegrees -import nebulosa.platesolver.PlateSolution import nebulosa.platesolver.PlateSolver import nebulosa.siril.command.PlateSolve import nebulosa.siril.command.SirilCommandLine -import nebulosa.util.concurrency.cancellation.CancellationToken import java.nio.file.Path import java.time.Duration @@ -20,18 +18,10 @@ data class SirilPlateSolver( override fun solve( path: Path?, image: Image?, centerRA: Angle, centerDEC: Angle, radius: Angle, - downsampleFactor: Int, timeout: Duration, cancellationToken: CancellationToken - ): PlateSolution { - val commandLine = SirilCommandLine(executablePath) - - return try { - commandLine.run() - cancellationToken.listen(commandLine) - val useCenterCoordinates = radius > 0.0 && radius.toDegrees >= 0.1 && centerRA.isFinite() && centerDEC.isFinite() - commandLine.execute(PlateSolve(path!!, focalLength, pixelSize, useCenterCoordinates, centerRA, centerDEC, downsampleFactor, timeout)) - } finally { - cancellationToken.unlisten(commandLine) - commandLine.close() - } + downsampleFactor: Int, timeout: Duration, + ) = SirilCommandLine(executablePath).use { + it.run() + val useCenterCoordinates = radius > 0.0 && radius.toDegrees >= 0.1 && centerRA.isFinite() && centerDEC.isFinite() + it.execute(PlateSolve(path!!, focalLength, pixelSize, useCenterCoordinates, centerRA, centerDEC, downsampleFactor, timeout)) } } diff --git a/nebulosa-siril/src/test/kotlin/SirilTest.kt b/nebulosa-siril/src/test/kotlin/SirilTest.kt index 26d489523..498ff5ee1 100644 --- a/nebulosa-siril/src/test/kotlin/SirilTest.kt +++ b/nebulosa-siril/src/test/kotlin/SirilTest.kt @@ -1,4 +1,5 @@ import io.kotest.matchers.booleans.shouldBeTrue +import io.kotest.matchers.collections.shouldBeEmpty import io.kotest.matchers.collections.shouldHaveSize import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.doubles.shouldBeExactly @@ -9,7 +10,11 @@ import nebulosa.platesolver.Parity import nebulosa.siril.livestacker.SirilLiveStacker import nebulosa.siril.platesolver.SirilPlateSolver import nebulosa.siril.stardetector.SirilStarDetector -import nebulosa.test.* +import nebulosa.test.AbstractTest +import nebulosa.test.NonGitHubOnly +import nebulosa.test.concat +import nebulosa.test.dataDirectory +import nebulosa.test.fits.* import org.junit.jupiter.api.Test import java.nio.file.Path import kotlin.io.path.copyTo @@ -27,10 +32,10 @@ class SirilTest : AbstractTest() { val inputDir = tempDirectory("ls-") - PI_01_LIGHT.copyTo(inputDir.concat("01.fits")) - PI_02_LIGHT.copyTo(inputDir.concat("02.fits")) - PI_03_LIGHT.copyTo(inputDir.concat("03.fits")) - PI_04_LIGHT.copyTo(inputDir.concat("04.fits")) + STACKING_LIGHT_MONO_01_FITS.copyTo(inputDir.concat("01.fits")) + STACKING_LIGHT_MONO_02_FITS.copyTo(inputDir.concat("02.fits")) + STACKING_LIGHT_MONO_03_FITS.copyTo(inputDir.concat("03.fits")) + STACKING_LIGHT_MONO_04_FITS.copyTo(inputDir.concat("04.fits")) for (fits in inputDir.listDirectoryEntries().shouldHaveSize(4).sorted()) { it.add(fits).shouldNotBeNull() @@ -39,13 +44,12 @@ class SirilTest : AbstractTest() { workingDirectory.listDirectoryEntries().shouldHaveSize(5) } - workingDirectory.listDirectoryEntries().shouldHaveSize(1) + workingDirectory.listDirectoryEntries().shouldBeEmpty() } @Test fun plateSolver() { - val solution = SOLVER.solve(PI_01_LIGHT, null) - + val solution = SOLVER.solve(STACKING_LIGHT_MONO_01_FITS, null) solution.solved.shouldBeTrue() solution.orientation.toDegrees shouldBe (-90.02 plusOrMinus 1e-2) solution.rightAscension.formatHMS() shouldBe "00h06m46.0s" @@ -61,21 +65,9 @@ class SirilTest : AbstractTest() { @Test fun starDetector() { val detector = SirilStarDetector(EXECUTABLE_PATH) - - with(detector.detect(PI_FOCUS_0)) { - this shouldHaveSize 307 - map { it.hfd }.average() shouldBe (7.9 plusOrMinus 1e-1) - } - - with(detector.detect(PI_FOCUS_30000)) { - this shouldHaveSize 258 - map { it.hfd }.average() shouldBe (1.1 plusOrMinus 1e-1) - } - - with(detector.detect(PI_FOCUS_100000)) { - this shouldHaveSize 82 - map { it.hfd }.average() shouldBe (22.4 plusOrMinus 1e-1) - } + val detectedStars = detector.detect(ASTROMETRY_GALACTIC_CENTER_FITS) + detectedStars shouldHaveSize 425 + (detectedStars.sumOf { it.hfd } / detectedStars.size) shouldBe (2.1 plusOrMinus 0.1) } companion object { diff --git a/nebulosa-stacker/build.gradle.kts b/nebulosa-stacker/build.gradle.kts index 80f7624d3..c0dd5826a 100644 --- a/nebulosa-stacker/build.gradle.kts +++ b/nebulosa-stacker/build.gradle.kts @@ -4,7 +4,6 @@ plugins { } dependencies { - api(project(":nebulosa-util")) implementation(project(":nebulosa-log")) } diff --git a/nebulosa-stacker/src/main/kotlin/nebulosa/stacker/AutoStacker.kt b/nebulosa-stacker/src/main/kotlin/nebulosa/stacker/AutoStacker.kt index 5ba9bcedb..e48e8c355 100644 --- a/nebulosa-stacker/src/main/kotlin/nebulosa/stacker/AutoStacker.kt +++ b/nebulosa-stacker/src/main/kotlin/nebulosa/stacker/AutoStacker.kt @@ -1,6 +1,5 @@ package nebulosa.stacker -import nebulosa.util.concurrency.cancellation.CancellationToken import java.nio.file.Path interface AutoStacker : Stacker { @@ -9,8 +8,7 @@ interface AutoStacker : Stacker { fun unregisterAutoStackerListener(listener: AutoStackerListener) - fun stack( - targetPaths: Collection, outputPath: Path, referencePath: Path = targetPaths.first(), - cancellationToken: CancellationToken = CancellationToken.NONE - ): Boolean + fun stack(targetPaths: Collection, outputPath: Path, referencePath: Path = targetPaths.first()): Boolean + + fun stop() } diff --git a/nebulosa-stacker/src/main/kotlin/nebulosa/stacker/Stacker.kt b/nebulosa-stacker/src/main/kotlin/nebulosa/stacker/Stacker.kt index 31cf7f719..4a3b8fb4d 100644 --- a/nebulosa-stacker/src/main/kotlin/nebulosa/stacker/Stacker.kt +++ b/nebulosa-stacker/src/main/kotlin/nebulosa/stacker/Stacker.kt @@ -4,10 +4,7 @@ import java.nio.file.Path interface Stacker { - fun calibrate( - targetPath: Path, outputPath: Path, - darkPath: Path? = null, flatPath: Path? = null, biasPath: Path? = null, - ): Boolean + fun calibrate(targetPath: Path, outputPath: Path, darkPath: Path? = null, flatPath: Path? = null, biasPath: Path? = null): Boolean fun align(referencePath: Path, targetPath: Path, outputPath: Path): Boolean diff --git a/nebulosa-test/src/main/kotlin/nebulosa/test/Files.kt b/nebulosa-test/src/main/kotlin/nebulosa/test/Files.kt index ce19039cf..0d1f44248 100644 --- a/nebulosa-test/src/main/kotlin/nebulosa/test/Files.kt +++ b/nebulosa-test/src/main/kotlin/nebulosa/test/Files.kt @@ -6,7 +6,7 @@ import nebulosa.math.deg import nebulosa.math.hours const val ASTROPY_PHOTOMETRY_URL = "https://www.astropy.org/astropy-data/photometry" -const val GITHUB_FITS_URL = "https://github.com/tiagohm/nebulosa.data/raw/main/test/fits" +const val GITHUB_FITS_URL = "https://github.com/tiagohm/nebulosa.data/raw/main/fits" const val GITHUB_XISF_URL = "https://github.com/tiagohm/nebulosa.data/raw/main/test/xisf" val M82_MONO_8_LZ4_XISF by lazy { download("$GITHUB_XISF_URL/M82.Mono.8.LZ4.xisf") } @@ -69,41 +69,3 @@ val PALETTE_COLOR_F64_XISF by lazy { download("$GITHUB_XISF_URL/PALETTE.Color.F6 val DEBAYER_FITS by lazy { download("$GITHUB_FITS_URL/Debayer.fits") } val M6707HH by lazy { download("$ASTROPY_PHOTOMETRY_URL/M6707HH.fits") } val M31_FITS by lazy { downloadFits("00 42 44.3".hours, "41 16 9".deg, 3.deg) } - -val STAR_FOCUS_1 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.1.fits") } -val STAR_FOCUS_2 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.2.fits") } -val STAR_FOCUS_3 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.3.fits") } -val STAR_FOCUS_4 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.4.fits") } -val STAR_FOCUS_5 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.5.fits") } -val STAR_FOCUS_6 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.6.fits") } -val STAR_FOCUS_7 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.7.fits") } -val STAR_FOCUS_8 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.8.fits") } -val STAR_FOCUS_9 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.9.fits") } -val STAR_FOCUS_10 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.10.fits") } -val STAR_FOCUS_11 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.11.fits") } -val STAR_FOCUS_12 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.12.fits") } -val STAR_FOCUS_13 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.13.fits") } -val STAR_FOCUS_14 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.14.fits") } -val STAR_FOCUS_15 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.15.fits") } -val STAR_FOCUS_16 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.16.fits") } -val STAR_FOCUS_17 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.17.fits") } - -val PI_01_LIGHT by lazy { download("$GITHUB_FITS_URL/PI.01.LIGHT.fits") } -val PI_02_LIGHT by lazy { download("$GITHUB_FITS_URL/PI.02.LIGHT.fits") } -val PI_03_LIGHT by lazy { download("$GITHUB_FITS_URL/PI.03.LIGHT.fits") } -val PI_04_LIGHT by lazy { download("$GITHUB_FITS_URL/PI.04.LIGHT.fits") } -val PI_05_LIGHT by lazy { download("$GITHUB_FITS_URL/PI.05.LIGHT.fits") } -val PI_06_LIGHT by lazy { download("$GITHUB_FITS_URL/PI.06.LIGHT.fits") } -val PI_07_LIGHT by lazy { download("$GITHUB_FITS_URL/PI.07.LIGHT.fits") } -val PI_08_LIGHT by lazy { download("$GITHUB_FITS_URL/PI.08.LIGHT.fits") } -val PI_09_LIGHT by lazy { download("$GITHUB_FITS_URL/PI.09.LIGHT.fits") } -val PI_10_LIGHT by lazy { download("$GITHUB_FITS_URL/PI.10.LIGHT.fits") } -val PI_BIAS by lazy { download("$GITHUB_FITS_URL/PI.BIAS.fits") } -val PI_DARK by lazy { download("$GITHUB_FITS_URL/PI.DARK.fits") } -val PI_FLAT by lazy { download("$GITHUB_FITS_URL/PI.FLAT.fits") } - -val PI_FOCUS_0 by lazy { download("$GITHUB_FITS_URL/PI.FOCUS.0.fits") } -val PI_FOCUS_10000 by lazy { download("$GITHUB_FITS_URL/PI.FOCUS.10000.fits") } -val PI_FOCUS_20000 by lazy { download("$GITHUB_FITS_URL/PI.FOCUS.20000.fits") } -val PI_FOCUS_30000 by lazy { download("$GITHUB_FITS_URL/PI.FOCUS.30000.fits") } -val PI_FOCUS_100000 by lazy { download("$GITHUB_FITS_URL/PI.FOCUS.100000.fits") } diff --git a/nebulosa-test/src/main/kotlin/nebulosa/test/fits/Astrometry.kt b/nebulosa-test/src/main/kotlin/nebulosa/test/fits/Astrometry.kt new file mode 100644 index 000000000..f49a64275 --- /dev/null +++ b/nebulosa-test/src/main/kotlin/nebulosa/test/fits/Astrometry.kt @@ -0,0 +1,8 @@ +package nebulosa.test.fits + +import nebulosa.test.GITHUB_FITS_URL +import nebulosa.test.download + +val ASTROMETRY_GALACTIC_CENTER_FITS by lazy { download("$GITHUB_FITS_URL/astrometry/galactic-center.fits") } +val ASTROMETRY_SOUTH_POLE_FITS by lazy { download("$GITHUB_FITS_URL/astrometry/south-celestial-pole.fits") } +val ASTROMETRY_NORTH_POLE_FITS by lazy { download("$GITHUB_FITS_URL/astrometry/north-celestial-pole.fits") } diff --git a/nebulosa-test/src/main/kotlin/nebulosa/test/fits/Focus.kt b/nebulosa-test/src/main/kotlin/nebulosa/test/fits/Focus.kt new file mode 100644 index 000000000..55c91bf22 --- /dev/null +++ b/nebulosa-test/src/main/kotlin/nebulosa/test/fits/Focus.kt @@ -0,0 +1,31 @@ +package nebulosa.test.fits + +import nebulosa.test.GITHUB_FITS_URL +import nebulosa.test.download + +val FOCUS_01_FITS by lazy { download("$GITHUB_FITS_URL/focus/galactic-center-0.fits") } +val FOCUS_02_FITS by lazy { download("$GITHUB_FITS_URL/focus/galactic-center-1000.fits") } +val FOCUS_03_FITS by lazy { download("$GITHUB_FITS_URL/focus/galactic-center-2000.fits") } +val FOCUS_04_FITS by lazy { download("$GITHUB_FITS_URL/focus/galactic-center-3000.fits") } +val FOCUS_05_FITS by lazy { download("$GITHUB_FITS_URL/focus/galactic-center-4000.fits") } +val FOCUS_06_FITS by lazy { download("$GITHUB_FITS_URL/focus/galactic-center-5000.fits") } +val FOCUS_07_FITS by lazy { download("$GITHUB_FITS_URL/focus/galactic-center-6000.fits") } +val FOCUS_08_FITS by lazy { download("$GITHUB_FITS_URL/focus/galactic-center-7000.fits") } +val FOCUS_09_FITS by lazy { download("$GITHUB_FITS_URL/focus/galactic-center-8000.fits") } +val FOCUS_10_FITS by lazy { download("$GITHUB_FITS_URL/focus/galactic-center-9000.fits") } +val FOCUS_11_FITS by lazy { download("$GITHUB_FITS_URL/focus/galactic-center-10000.fits") } +val FOCUS_12_FITS by lazy { download("$GITHUB_FITS_URL/focus/galactic-center-11000.fits") } +val FOCUS_13_FITS by lazy { download("$GITHUB_FITS_URL/focus/galactic-center-12000.fits") } +val FOCUS_14_FITS by lazy { download("$GITHUB_FITS_URL/focus/galactic-center-13000.fits") } // best focus +val FOCUS_15_FITS by lazy { download("$GITHUB_FITS_URL/focus/galactic-center-14000.fits") } +val FOCUS_16_FITS by lazy { download("$GITHUB_FITS_URL/focus/galactic-center-15000.fits") } +val FOCUS_17_FITS by lazy { download("$GITHUB_FITS_URL/focus/galactic-center-16000.fits") } +val FOCUS_18_FITS by lazy { download("$GITHUB_FITS_URL/focus/galactic-center-17000.fits") } +val FOCUS_19_FITS by lazy { download("$GITHUB_FITS_URL/focus/galactic-center-18000.fits") } +val FOCUS_20_FITS by lazy { download("$GITHUB_FITS_URL/focus/galactic-center-19000.fits") } +val FOCUS_21_FITS by lazy { download("$GITHUB_FITS_URL/focus/galactic-center-20000.fits") } +val FOCUS_22_FITS by lazy { download("$GITHUB_FITS_URL/focus/galactic-center-21000.fits") } +val FOCUS_23_FITS by lazy { download("$GITHUB_FITS_URL/focus/galactic-center-22000.fits") } +val FOCUS_24_FITS by lazy { download("$GITHUB_FITS_URL/focus/galactic-center-23000.fits") } +val FOCUS_25_FITS by lazy { download("$GITHUB_FITS_URL/focus/galactic-center-24000.fits") } +val FOCUS_26_FITS by lazy { download("$GITHUB_FITS_URL/focus/galactic-center-25000.fits") } diff --git a/nebulosa-test/src/main/kotlin/nebulosa/test/fits/Stacking.kt b/nebulosa-test/src/main/kotlin/nebulosa/test/fits/Stacking.kt new file mode 100644 index 000000000..8b9186c3e --- /dev/null +++ b/nebulosa-test/src/main/kotlin/nebulosa/test/fits/Stacking.kt @@ -0,0 +1,18 @@ +package nebulosa.test.fits + +import nebulosa.test.GITHUB_FITS_URL +import nebulosa.test.download + +val STACKING_LIGHT_MONO_01_FITS by lazy { download("$GITHUB_FITS_URL/stacking/light-mono-01.fits") } +val STACKING_LIGHT_MONO_02_FITS by lazy { download("$GITHUB_FITS_URL/stacking/light-mono-02.fits") } +val STACKING_LIGHT_MONO_03_FITS by lazy { download("$GITHUB_FITS_URL/stacking/light-mono-03.fits") } +val STACKING_LIGHT_MONO_04_FITS by lazy { download("$GITHUB_FITS_URL/stacking/light-mono-04.fits") } +val STACKING_LIGHT_MONO_05_FITS by lazy { download("$GITHUB_FITS_URL/stacking/light-mono-05.fits") } +val STACKING_LIGHT_MONO_06_FITS by lazy { download("$GITHUB_FITS_URL/stacking/light-mono-06.fits") } +val STACKING_LIGHT_MONO_07_FITS by lazy { download("$GITHUB_FITS_URL/stacking/light-mono-07.fits") } +val STACKING_LIGHT_MONO_08_FITS by lazy { download("$GITHUB_FITS_URL/stacking/light-mono-08.fits") } +val STACKING_LIGHT_MONO_09_FITS by lazy { download("$GITHUB_FITS_URL/stacking/light-mono-09.fits") } +val STACKING_LIGHT_MONO_10_FITS by lazy { download("$GITHUB_FITS_URL/stacking/light-mono-10.fits") } +val STACKING_BIAS_MONO_FITS by lazy { download("$GITHUB_FITS_URL/stacking/bias-mono.fits") } +val STACKING_DARK_MONO_FITS by lazy { download("$GITHUB_FITS_URL/stacking/dark-mono.fits") } +val STACKING_FLAT_MONO_FITS by lazy { download("$GITHUB_FITS_URL/stacking/flat-mono.fits") } diff --git a/nebulosa-util/src/main/kotlin/nebulosa/util/exec/CommandLine.kt b/nebulosa-util/src/main/kotlin/nebulosa/util/exec/CommandLine.kt deleted file mode 100644 index a79dfc8d9..000000000 --- a/nebulosa-util/src/main/kotlin/nebulosa/util/exec/CommandLine.kt +++ /dev/null @@ -1,256 +0,0 @@ -package nebulosa.util.exec - -import nebulosa.log.dw -import nebulosa.log.loggerFor -import nebulosa.util.concurrency.cancellation.CancellationListener -import nebulosa.util.concurrency.cancellation.CancellationSource -import java.io.InputStream -import java.io.OutputStream -import java.io.PrintStream -import java.nio.file.Path -import java.time.Duration -import java.util.concurrent.CompletableFuture -import java.util.concurrent.TimeUnit -import java.util.function.Supplier - -inline fun commandLine(action: CommandLine.Builder.() -> Unit): CommandLine { - return CommandLine.Builder().also(action).get() -} - -class CommandLine internal constructor( - private val builder: ProcessBuilder, - private val listeners: LinkedHashSet, -) : CompletableFuture(), CancellationListener { - - @Volatile private lateinit var process: Process - @Volatile private var waiter: ProcessWaiter? = null - @Volatile private var inputReader: StreamLineReader? = null - @Volatile private var errorReader: StreamLineReader? = null - - val command: List - get() = builder.command() - - val pid - get() = process.pid() - - val exitCode - get() = process.takeIf { !it.isAlive }?.exitValue() ?: -1 - - val isRunning - get() = ::process.isInitialized && process.isAlive - - val writer = PrintStream(object : OutputStream() { - - override fun write(b: Int) { - process.outputStream?.write(b) - } - - override fun write(b: ByteArray) { - process.outputStream?.write(b) - } - - override fun write(b: ByteArray, off: Int, len: Int) { - process.outputStream?.write(b, off, len) - } - - override fun flush() { - process.outputStream?.flush() - } - - override fun close() { - process.outputStream?.close() - } - }, true) - - fun registerCommandLineListener(listener: CommandLineListener) { - synchronized(listeners) { listeners.add(listener) } - } - - fun unregisterCommandLineListener(listener: CommandLineListener) { - synchronized(listeners) { listeners.remove(listener) } - } - - @Synchronized - fun start(timeout: Duration = Duration.ZERO): CommandLine { - require(!::process.isInitialized) { "process has already executed" } - - process = try { - builder.start() - } catch (e: Throwable) { - completeExceptionally(e) - listeners.forEach { it.onExit(-1, e) } - return this - } - - inputReader = StreamLineReader(process.inputStream, false) - inputReader!!.start() - - errorReader = StreamLineReader(process.errorStream, true) - errorReader!!.start() - - waiter = ProcessWaiter(process, timeout.toMillis()) - waiter!!.start() - - return this - } - - @Synchronized - fun stop() { - waiter?.interrupt() - waiter = null - - inputReader?.interrupt() - inputReader = null - - errorReader?.interrupt() - errorReader = null - - process.destroyForcibly() - process.waitFor() - - listeners.clear() - } - - fun get(timeout: Duration): Int { - return get(timeout.toNanos(), TimeUnit.NANOSECONDS) - } - - override fun onCancel(source: CancellationSource) { - stop() - } - - private inner class ProcessWaiter( - private val process: Process, - private val timeout: Long, - ) : Thread("Command Line Process Waiter") { - - init { - isDaemon = true - } - - override fun run() { - try { - val exited = if (timeout > 0L) { - process.waitFor(timeout, TimeUnit.MILLISECONDS) - } else { - process.waitFor() - true - } - - if (exited) { - inputReader?.waitFor() - errorReader?.waitFor() - } - } catch (_: InterruptedException) { - currentThread().interrupt() - } finally { - if (process.isAlive) { - process.destroyForcibly() - process.waitFor() - } - - with(process.exitValue()) { - complete(this) - synchronized(listeners) { listeners.forEach { it.onExit(this, null) } } - } - - waiter = null - inputReader = null - errorReader = null - } - } - } - - private inner class StreamLineReader(stream: InputStream, isError: Boolean) : Thread("Command Line ${if (isError) "Error" else "Input"} Stream Line Reader") { - - private val reader = stream.bufferedReader() - private val completable = CompletableFuture() - - init { - isDaemon = true - } - - override fun run() { - try { - while (true) { - val line = reader.readLine() ?: break - - synchronized(listeners) { - listeners.forEach { it.onLineRead(line) } - } - } - } catch (e: InterruptedException) { - currentThread().interrupt() - LOG.dw("command line interrupted") - } catch (e: Throwable) { - LOG.dw("command line exited: {}", e.message) - } finally { - completable.complete(Unit) - reader.close() - } - } - - fun waitFor() { - return completable.join() - } - } - - class Builder : Supplier { - - private val builder = ProcessBuilder() - private val environment by lazy { builder.environment() } - private val arguments = mutableMapOf() - private var executable = "" - private val listeners = LinkedHashSet(1) - - fun executablePath(path: Path) = executable("$path") - - fun executable(executable: String) = run { this.executable = executable } - - fun env(key: String) = environment[key] - - fun putEnv(key: String, value: String) = environment.put(key, value) - - fun removeEnv(key: String) = environment.remove(key) - - fun hasEnv(key: String) = key in environment - - fun arg(name: String) = arguments[name] - - fun putArg(name: String, value: Any) = arguments.put(name, value) - - fun putArg(name: String) = arguments.put(name, null) - - fun removeArg(name: String) = arguments.remove(name) - - fun hasArg(name: String) = name in arguments - - fun workingDirectory(path: Path): Unit = run { builder.directory(path.toFile()) } - - fun registerCommandLineListener(listener: CommandLineListener) = listeners.add(listener) - - fun unregisterCommandLineListener(listener: CommandLineListener) = listeners.remove(listener) - - override fun get(): CommandLine { - val args = ArrayList(1 + arguments.size * 2) - - require(executable.isNotBlank()) { "executable must not be blank" } - - args.add(executable) - - for ((key, value) in arguments) { - args.add(key) - value?.toString()?.also(args::add) - } - - builder.command(args) - - return CommandLine(builder, listeners) - } - } - - companion object { - - @JvmStatic private val LOG = loggerFor() - } -} diff --git a/nebulosa-util/src/main/kotlin/nebulosa/util/exec/CommandLineListener.kt b/nebulosa-util/src/main/kotlin/nebulosa/util/exec/CommandLineListener.kt deleted file mode 100644 index aeccacc63..000000000 --- a/nebulosa-util/src/main/kotlin/nebulosa/util/exec/CommandLineListener.kt +++ /dev/null @@ -1,18 +0,0 @@ -package nebulosa.util.exec - -interface CommandLineListener { - - fun onLineRead(line: String) = Unit - - fun onExit(exitCode: Int, exception: Throwable?) = Unit - - fun interface OnLineRead : CommandLineListener { - - override fun onLineRead(line: String) - } - - fun interface OnExit : CommandLineListener { - - override fun onExit(exitCode: Int, exception: Throwable?) - } -} diff --git a/nebulosa-util/src/test/kotlin/CommandLineTest.kt b/nebulosa-util/src/test/kotlin/CommandLineTest.kt deleted file mode 100644 index 8f79ad192..000000000 --- a/nebulosa-util/src/test/kotlin/CommandLineTest.kt +++ /dev/null @@ -1,76 +0,0 @@ -import io.kotest.matchers.collections.shouldContain -import io.kotest.matchers.collections.shouldNotBeEmpty -import io.kotest.matchers.ints.shouldBeExactly -import io.kotest.matchers.ints.shouldNotBeExactly -import io.kotest.matchers.longs.shouldBeGreaterThanOrEqual -import io.kotest.matchers.longs.shouldBeLessThan -import nebulosa.test.LinuxOnly -import nebulosa.util.exec.CommandLineListener -import nebulosa.util.exec.commandLine -import org.junit.jupiter.api.Test -import java.nio.file.Path -import java.time.Duration -import kotlin.concurrent.thread -import kotlin.system.measureTimeMillis - -@LinuxOnly -class CommandLineTest { - - @Test - fun sleep() { - val cmd = commandLine { - executable("sleep") - putArg("2") - } - - measureTimeMillis { - cmd.start().get() shouldBeExactly 0 - } shouldBeGreaterThanOrEqual 2000 - } - - @Test - fun sleepWithTimeout() { - val cmd = commandLine { - executable("sleep") - putArg("10") - } - - measureTimeMillis { - cmd.start(Duration.ofSeconds(2)).get() shouldNotBeExactly 0 - } shouldBeGreaterThanOrEqual 2000 shouldBeLessThan 10000 - } - - @Test - fun killSleep() { - val cmd = commandLine { - executable("sleep") - putArg("10") - } - - thread { Thread.sleep(2000); cmd.stop() } - - measureTimeMillis { - cmd.start().get() shouldNotBeExactly 0 - } shouldBeGreaterThanOrEqual 2000 shouldBeLessThan 10000 - } - - @Test - fun ls() { - val lineReadListener = object : CommandLineListener.OnLineRead, ArrayList(64) { - - override fun onLineRead(line: String) { - add(line) - } - } - - val cmd = commandLine { - executable("ls") - workingDirectory(Path.of("../")) - registerCommandLineListener(lineReadListener) - } - - cmd.start().get() shouldBeExactly 0 - lineReadListener.shouldNotBeEmpty() - lineReadListener.shouldContain("nebulosa-image") - } -} diff --git a/nebulosa-watney/src/main/kotlin/nebulosa/watney/platesolver/WatneyPlateSolver.kt b/nebulosa-watney/src/main/kotlin/nebulosa/watney/platesolver/WatneyPlateSolver.kt index 79ab25cf9..ad77179f0 100644 --- a/nebulosa-watney/src/main/kotlin/nebulosa/watney/platesolver/WatneyPlateSolver.kt +++ b/nebulosa-watney/src/main/kotlin/nebulosa/watney/platesolver/WatneyPlateSolver.kt @@ -17,7 +17,6 @@ import nebulosa.platesolver.PlateSolution import nebulosa.platesolver.PlateSolver import nebulosa.stardetector.StarDetector import nebulosa.stardetector.StarPoint -import nebulosa.util.concurrency.cancellation.CancellationToken import nebulosa.watney.platesolver.math.equatorialToStandardCoordinates import nebulosa.watney.platesolver.math.lerp import nebulosa.watney.platesolver.math.solveLeastSquares @@ -46,7 +45,6 @@ data class WatneyPlateSolver( path: Path?, image: Image?, centerRA: Angle, centerDEC: Angle, radius: Angle, downsampleFactor: Int, timeout: Duration, - cancellationToken: CancellationToken, ): PlateSolution { val image = image ?: path!!.fits().use(Image::open) val stars = (starDetector ?: DEFAULT_STAR_DETECTOR).detect(image) diff --git a/settings.gradle.kts b/settings.gradle.kts index 27a4fafa5..c22e0a8be 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -32,6 +32,7 @@ dependencyResolutionManagement { library("apache-collections", "org.apache.commons:commons-collections4:4.4") library("apache-math", "org.apache.commons:commons-math3:3.6.1") library("apache-numbers-complex", "org.apache.commons:commons-numbers-complex:1.2") + library("apache-exec", "org.apache.commons:commons-exec:1.4.0") library("oshi", "com.github.oshi:oshi-core:6.6.5") library("jna", "net.java.dev.jna:jna:5.15.0") library("javalin", "io.javalin:javalin:6.3.0") @@ -57,6 +58,7 @@ include(":nebulosa-astrobin-api") include(":nebulosa-astrometrynet") include(":nebulosa-astrometrynet-jna") include(":nebulosa-autofocus") +include(":nebulosa-commandline") include(":nebulosa-constants") include(":nebulosa-curve-fitting") include(":nebulosa-erfa") From f4d035b888ab4a5e94f938a3183f56d129a40f4e Mon Sep 17 00:00:00 2001 From: tiagohm Date: Tue, 15 Oct 2024 19:03:20 -0300 Subject: [PATCH 07/15] [api][desktop]: Remove Stacker --- api/src/main/kotlin/nebulosa/api/Nebulosa.kt | 2 +- .../api/cameras/CameraLiveStackingManager.kt | 18 +- .../api/cameras/CameraStartCaptureRequest.kt | 4 +- .../converters/{modules => }/DeviceModule.kt | 2 +- .../ImageAnalyzed.kt} | 8 +- .../nebulosa/api/image/ImageController.kt | 6 + .../ImageFilterType.kt} | 4 +- .../kotlin/nebulosa/api/image/ImageService.kt | 16 ++ .../main/kotlin/nebulosa/api/inject/Inject.kt | 4 - .../nebulosa/api/stacker/StackerController.kt | 41 --- .../nebulosa/api/stacker/StackerEvent.kt | 18 -- .../nebulosa/api/stacker/StackerService.kt | 170 ------------- .../nebulosa/api/stacker/StackerState.kt | 8 - .../nebulosa/api/stacker/StackerType.kt | 5 - .../nebulosa/api/stacker/StackingRequest.kt | 52 ---- .../nebulosa/api/stacker/StackingTarget.kt | 18 -- api/src/test/kotlin/StackerServiceTest.kt | 69 ------ desktop/README.md | 4 - desktop/src/app/about/about.component.ts | 1 - desktop/src/app/app-routing.module.ts | 5 - desktop/src/app/app.module.ts | 2 - desktop/src/app/home/home.component.html | 9 - desktop/src/app/home/home.component.ts | 3 - .../app/sequencer/sequencer.component.html | 2 +- .../src/app/settings/settings.component.html | 45 ---- .../src/app/settings/settings.component.ts | 13 - .../src/app/stacker/stacker.component.html | 223 ----------------- desktop/src/app/stacker/stacker.component.ts | 233 ------------------ desktop/src/assets/icons/stack.png | Bin 2273 -> 0 bytes .../src/shared/pipes/dropdown-options.pipe.ts | 10 +- desktop/src/shared/pipes/enum.pipe.ts | 9 +- desktop/src/shared/services/api.service.ts | 29 +-- .../src/shared/services/electron.service.ts | 2 - .../src/shared/services/preference.service.ts | 3 - desktop/src/shared/types/camera.types.ts | 4 +- desktop/src/shared/types/home.types.ts | 2 +- desktop/src/shared/types/image.types.ts | 15 +- desktop/src/shared/types/settings.types.ts | 11 +- desktop/src/shared/types/stacker.types.ts | 117 --------- .../stacker/PixInsightAutoStacker.kt | 136 ---------- .../test/kotlin/PixInsightAutoStackerTest.kt | 43 ---- .../kotlin/nebulosa/stacker/AutoStacker.kt | 14 -- .../nebulosa/stacker/AutoStackerListener.kt | 18 -- 43 files changed, 71 insertions(+), 1327 deletions(-) rename api/src/main/kotlin/nebulosa/api/converters/{modules => }/DeviceModule.kt (98%) rename api/src/main/kotlin/nebulosa/api/{stacker/AnalyzedTarget.kt => image/ImageAnalyzed.kt} (76%) rename api/src/main/kotlin/nebulosa/api/{stacker/StackerGroupType.kt => image/ImageFilterType.kt} (93%) delete mode 100644 api/src/main/kotlin/nebulosa/api/stacker/StackerController.kt delete mode 100644 api/src/main/kotlin/nebulosa/api/stacker/StackerEvent.kt delete mode 100644 api/src/main/kotlin/nebulosa/api/stacker/StackerService.kt delete mode 100644 api/src/main/kotlin/nebulosa/api/stacker/StackerState.kt delete mode 100644 api/src/main/kotlin/nebulosa/api/stacker/StackerType.kt delete mode 100644 api/src/main/kotlin/nebulosa/api/stacker/StackingRequest.kt delete mode 100644 api/src/main/kotlin/nebulosa/api/stacker/StackingTarget.kt delete mode 100644 api/src/test/kotlin/StackerServiceTest.kt delete mode 100644 desktop/src/app/stacker/stacker.component.html delete mode 100644 desktop/src/app/stacker/stacker.component.ts delete mode 100644 desktop/src/assets/icons/stack.png delete mode 100644 desktop/src/shared/types/stacker.types.ts delete mode 100644 nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/stacker/PixInsightAutoStacker.kt delete mode 100644 nebulosa-pixinsight/src/test/kotlin/PixInsightAutoStackerTest.kt delete mode 100644 nebulosa-stacker/src/main/kotlin/nebulosa/stacker/AutoStacker.kt delete mode 100644 nebulosa-stacker/src/main/kotlin/nebulosa/stacker/AutoStackerListener.kt diff --git a/api/src/main/kotlin/nebulosa/api/Nebulosa.kt b/api/src/main/kotlin/nebulosa/api/Nebulosa.kt index a98359324..8744b76ac 100644 --- a/api/src/main/kotlin/nebulosa/api/Nebulosa.kt +++ b/api/src/main/kotlin/nebulosa/api/Nebulosa.kt @@ -11,7 +11,7 @@ import io.javalin.Javalin import io.javalin.http.Context import io.javalin.http.HttpStatus.BAD_REQUEST import io.javalin.json.JavalinJackson -import nebulosa.api.converters.modules.DeviceModule +import nebulosa.api.converters.DeviceModule import nebulosa.api.core.ErrorResponse import nebulosa.api.inject.* import nebulosa.json.PathModule diff --git a/api/src/main/kotlin/nebulosa/api/cameras/CameraLiveStackingManager.kt b/api/src/main/kotlin/nebulosa/api/cameras/CameraLiveStackingManager.kt index 763c7c557..02c311d37 100644 --- a/api/src/main/kotlin/nebulosa/api/cameras/CameraLiveStackingManager.kt +++ b/api/src/main/kotlin/nebulosa/api/cameras/CameraLiveStackingManager.kt @@ -1,8 +1,8 @@ package nebulosa.api.cameras import nebulosa.api.calibration.CalibrationFrameProvider +import nebulosa.api.image.ImageFilterType import nebulosa.api.livestacker.LiveStackingRequest -import nebulosa.api.stacker.StackerGroupType import nebulosa.fits.* import nebulosa.image.format.ImageHdu import nebulosa.livestacker.LiveStacker @@ -20,7 +20,7 @@ data class CameraLiveStackingManager( private val calibrationFrameProvider: CalibrationFrameProvider? = null, ) : AutoCloseable { - private val liveStackers = EnumMap(StackerGroupType::class.java) + private val liveStackers = EnumMap(ImageFilterType::class.java) private val workingDirectories = HashSet() @Volatile private var referencePath: Path? = null @@ -29,7 +29,7 @@ data class CameraLiveStackingManager( fun start(request: CameraStartCaptureRequest, path: Path): Boolean { if (request.stackerGroupType in liveStackers) { return true - } else if (request.stackerGroupType != StackerGroupType.NONE && request.liveStacking.enabled) { + } else if (request.stackerGroupType != ImageFilterType.NONE && request.liveStacking.enabled) { try { val workingDirectory = Files.createTempDirectory("ls-${request.stackerGroupType}-") workingDirectories.add(workingDirectory) @@ -50,7 +50,7 @@ data class CameraLiveStackingManager( @Synchronized fun stack(request: CameraStartCaptureRequest, path: Path?): Path? { - if (path == null || request.stackerGroupType == StackerGroupType.NONE) return null + if (path == null || request.stackerGroupType == ImageFilterType.NONE) return null val stackerGroupType = request.stackerGroupType val liveStacker = liveStackers[stackerGroupType] ?: return null @@ -65,10 +65,10 @@ data class CameraLiveStackingManager( } val combinedPath = Path.of("${path.parent}", "STACKED.fits") - val luminancePath = liveStackers[StackerGroupType.LUMINANCE]?.stackedPath - val redPath = liveStackers[StackerGroupType.RED]?.stackedPath - val greenPath = liveStackers[StackerGroupType.GREEN]?.stackedPath - val bluePath = liveStackers[StackerGroupType.BLUE]?.stackedPath + val luminancePath = liveStackers[ImageFilterType.LUMINANCE]?.stackedPath + val redPath = liveStackers[ImageFilterType.RED]?.stackedPath + val greenPath = liveStackers[ImageFilterType.GREEN]?.stackedPath + val bluePath = liveStackers[ImageFilterType.BLUE]?.stackedPath if (stackerGroupType.isLRGB && (luminancePath != null || redPath != null || greenPath != null || bluePath != null)) { if (stacker.combineLRGB(combinedPath, luminancePath, redPath, greenPath, bluePath)) { @@ -77,7 +77,7 @@ data class CameraLiveStackingManager( } else if (luminancePath != null) { stacker.align(luminancePath, stackedPath, stackedPath) - if (stacker.combineLuminance(combinedPath, luminancePath, stackedPath, stackerGroupType == StackerGroupType.MONO)) { + if (stacker.combineLuminance(combinedPath, luminancePath, stackedPath, stackerGroupType == ImageFilterType.MONO)) { stackedPath = combinedPath } } diff --git a/api/src/main/kotlin/nebulosa/api/cameras/CameraStartCaptureRequest.kt b/api/src/main/kotlin/nebulosa/api/cameras/CameraStartCaptureRequest.kt index 3577d8384..8ed13cb0c 100644 --- a/api/src/main/kotlin/nebulosa/api/cameras/CameraStartCaptureRequest.kt +++ b/api/src/main/kotlin/nebulosa/api/cameras/CameraStartCaptureRequest.kt @@ -2,8 +2,8 @@ package nebulosa.api.cameras import nebulosa.api.converters.time.DurationUnit import nebulosa.api.guiding.DitherAfterExposureRequest +import nebulosa.api.image.ImageFilterType import nebulosa.api.livestacker.LiveStackingRequest -import nebulosa.api.stacker.StackerGroupType import nebulosa.api.validators.* import nebulosa.indi.device.camera.FrameType import java.nio.file.Path @@ -35,7 +35,7 @@ data class CameraStartCaptureRequest( @JvmField val dither: DitherAfterExposureRequest = DitherAfterExposureRequest.DISABLED, // Stacking. @JvmField val liveStacking: LiveStackingRequest = LiveStackingRequest.DISABLED, - @JvmField val stackerGroupType: StackerGroupType = StackerGroupType.MONO, + @JvmField val stackerGroupType: ImageFilterType = ImageFilterType.MONO, // Filter Wheel. @JvmField val filterPosition: Int = 0, @JvmField val shutterPosition: Int = 0, diff --git a/api/src/main/kotlin/nebulosa/api/converters/modules/DeviceModule.kt b/api/src/main/kotlin/nebulosa/api/converters/DeviceModule.kt similarity index 98% rename from api/src/main/kotlin/nebulosa/api/converters/modules/DeviceModule.kt rename to api/src/main/kotlin/nebulosa/api/converters/DeviceModule.kt index 09d6d0dd9..ef43a157a 100644 --- a/api/src/main/kotlin/nebulosa/api/converters/modules/DeviceModule.kt +++ b/api/src/main/kotlin/nebulosa/api/converters/DeviceModule.kt @@ -1,4 +1,4 @@ -package nebulosa.api.converters.modules +package nebulosa.api.converters import com.fasterxml.jackson.databind.module.SimpleDeserializers import com.fasterxml.jackson.databind.module.SimpleModule diff --git a/api/src/main/kotlin/nebulosa/api/stacker/AnalyzedTarget.kt b/api/src/main/kotlin/nebulosa/api/image/ImageAnalyzed.kt similarity index 76% rename from api/src/main/kotlin/nebulosa/api/stacker/AnalyzedTarget.kt rename to api/src/main/kotlin/nebulosa/api/image/ImageAnalyzed.kt index 892ff518b..5c18dbd91 100644 --- a/api/src/main/kotlin/nebulosa/api/stacker/AnalyzedTarget.kt +++ b/api/src/main/kotlin/nebulosa/api/image/ImageAnalyzed.kt @@ -1,11 +1,11 @@ -package nebulosa.api.stacker +package nebulosa.api.image import nebulosa.fits.* import nebulosa.image.format.ReadableHeader import nebulosa.indi.device.camera.FrameType import nebulosa.indi.device.camera.FrameType.Companion.frameType -data class AnalyzedTarget( +data class ImageAnalyzed( @JvmField val width: Int, @JvmField val height: Int, @JvmField val binX: Int, @@ -13,11 +13,11 @@ data class AnalyzedTarget( @JvmField val gain: Double, @JvmField val exposureTime: Long, @JvmField val type: FrameType, - @JvmField val group: StackerGroupType, + @JvmField val filter: ImageFilterType, ) { constructor(header: ReadableHeader) : this( header.width, header.height, header.binX, header.binY, header.gain, header.exposureTimeInMicroseconds, - header.frameType ?: FrameType.LIGHT, StackerGroupType.from(header) + header.frameType ?: FrameType.LIGHT, ImageFilterType.from(header) ) } diff --git a/api/src/main/kotlin/nebulosa/api/image/ImageController.kt b/api/src/main/kotlin/nebulosa/api/image/ImageController.kt index 3df0e6e2b..7d7414e42 100644 --- a/api/src/main/kotlin/nebulosa/api/image/ImageController.kt +++ b/api/src/main/kotlin/nebulosa/api/image/ImageController.kt @@ -22,6 +22,7 @@ class ImageController( app.post("image", ::openImage) app.delete("image", ::closeImage) app.put("image/save-as", ::saveImageAs) + app.put("image/analyze", ::analyze) app.put("image/annotations", ::annotations) app.get("image/coordinate-interpolation", ::coordinateInterpolation) app.get("image/histogram", ::histogram) @@ -47,6 +48,11 @@ class ImageController( imageService.saveImageAs(path, save) } + private fun analyze(ctx: Context) { + val path = ctx.queryParam("path").notNull().path().exists() + imageService.analyze(path)?.also(ctx::json) + } + private fun annotations(ctx: Context) { val path = ctx.queryParam("path").notNull().path().exists() val request = ctx.bodyAsClass() diff --git a/api/src/main/kotlin/nebulosa/api/stacker/StackerGroupType.kt b/api/src/main/kotlin/nebulosa/api/image/ImageFilterType.kt similarity index 93% rename from api/src/main/kotlin/nebulosa/api/stacker/StackerGroupType.kt rename to api/src/main/kotlin/nebulosa/api/image/ImageFilterType.kt index f1065d1b9..8f9c4b891 100644 --- a/api/src/main/kotlin/nebulosa/api/stacker/StackerGroupType.kt +++ b/api/src/main/kotlin/nebulosa/api/image/ImageFilterType.kt @@ -1,10 +1,10 @@ -package nebulosa.api.stacker +package nebulosa.api.image import nebulosa.fits.filter import nebulosa.fits.naxis import nebulosa.image.format.ReadableHeader -enum class StackerGroupType { +enum class ImageFilterType { NONE, LUMINANCE, RED, diff --git a/api/src/main/kotlin/nebulosa/api/image/ImageService.kt b/api/src/main/kotlin/nebulosa/api/image/ImageService.kt index 06b11022e..20e5640f5 100644 --- a/api/src/main/kotlin/nebulosa/api/image/ImageService.kt +++ b/api/src/main/kotlin/nebulosa/api/image/ImageService.kt @@ -13,6 +13,7 @@ import nebulosa.image.Image import nebulosa.image.algorithms.computation.Histogram import nebulosa.image.algorithms.computation.Statistics import nebulosa.image.algorithms.transformation.* +import nebulosa.image.format.ImageHdu import nebulosa.image.format.ImageModifier import nebulosa.indi.device.camera.Camera import nebulosa.log.* @@ -31,6 +32,8 @@ import nebulosa.time.UTC import nebulosa.wcs.WCS import nebulosa.wcs.WCSException import nebulosa.xisf.XisfFormat +import nebulosa.xisf.isXisf +import nebulosa.xisf.xisf import okio.sink import java.net.URI import java.nio.file.Path @@ -39,6 +42,8 @@ import java.util.* import java.util.concurrent.CompletableFuture import java.util.concurrent.ExecutorService import javax.imageio.ImageIO +import kotlin.io.path.exists +import kotlin.io.path.isRegularFile import kotlin.io.path.outputStream import kotlin.math.roundToInt @@ -299,6 +304,17 @@ class ImageService( } } + fun analyze(path: Path): ImageAnalyzed? { + if (!path.exists() || !path.isRegularFile()) return null + + val image = if (path.isFits()) path.fits() + else if (path.isXisf()) path.xisf() + else return null + + return image.use { it.firstOrNull { hdu -> hdu is ImageHdu }?.header } + ?.let(::ImageAnalyzed) + } + fun frame( rightAscension: Angle, declination: Angle, width: Int, height: Int, fov: Angle, diff --git a/api/src/main/kotlin/nebulosa/api/inject/Inject.kt b/api/src/main/kotlin/nebulosa/api/inject/Inject.kt index 75c29dede..adee669fb 100644 --- a/api/src/main/kotlin/nebulosa/api/inject/Inject.kt +++ b/api/src/main/kotlin/nebulosa/api/inject/Inject.kt @@ -65,8 +65,6 @@ import nebulosa.api.rotators.RotatorService import nebulosa.api.sequencer.SequencerController import nebulosa.api.sequencer.SequencerExecutor import nebulosa.api.sequencer.SequencerService -import nebulosa.api.stacker.StackerController -import nebulosa.api.stacker.StackerService import nebulosa.api.stardetector.StarDetectionController import nebulosa.api.stardetector.StarDetectionService import nebulosa.api.wheels.WheelController @@ -303,7 +301,6 @@ fun servicesModule() = module { single { AutoFocusExecutor(get(), get(), get()) } single { AutoFocusService(get()) } single { LiveStackingService() } - single { StackerService(get()) } single { FramingService(get(), get()) } single { INDIService(get()) } single { DARVExecutor(get(), get(), get()) } @@ -341,7 +338,6 @@ fun controllersModule() = module(true) { single { StarDetectionController(get(), get()) } single { AutoFocusController(get(), get(), get()) } single { LiveStackingController(get(), get(), get()) } - single { StackerController(get(), get()) } single { FramingController(get(), get(), get()) } single { INDIController(get(), get(), get()) } single { PolarAlignmentController(get(), get(), get()) } diff --git a/api/src/main/kotlin/nebulosa/api/stacker/StackerController.kt b/api/src/main/kotlin/nebulosa/api/stacker/StackerController.kt deleted file mode 100644 index e1264f094..000000000 --- a/api/src/main/kotlin/nebulosa/api/stacker/StackerController.kt +++ /dev/null @@ -1,41 +0,0 @@ -package nebulosa.api.stacker - -import io.javalin.Javalin -import io.javalin.http.Context -import io.javalin.http.bodyAsClass -import nebulosa.api.core.Controller -import nebulosa.api.validators.* - -class StackerController( - override val app: Javalin, - private val stackerService: StackerService, -) : Controller { - - init { - app.put("stacker/start", ::start) - app.get("stacker/running", ::isRunning) - app.put("stacker/stop", ::stop) - app.put("stacker/analyze", ::analyze) - } - - private fun start(ctx: Context) { - val key = ctx.queryParam("key").notNullOrBlank() - val body = ctx.bodyAsClass().valid() - stackerService.stack(body, key)?.also(ctx::json) - } - - private fun isRunning(ctx: Context) { - val key = ctx.queryParam("key").notNullOrBlank() - ctx.json(stackerService.isRunning(key)) - } - - private fun stop(ctx: Context) { - val key = ctx.queryParam("key").notNullOrBlank() - stackerService.stop(key) - } - - private fun analyze(ctx: Context) { - val path = ctx.queryParam("path").notNull().path().exists() - stackerService.analyze(path)?.also(ctx::json) - } -} diff --git a/api/src/main/kotlin/nebulosa/api/stacker/StackerEvent.kt b/api/src/main/kotlin/nebulosa/api/stacker/StackerEvent.kt deleted file mode 100644 index 86322d501..000000000 --- a/api/src/main/kotlin/nebulosa/api/stacker/StackerEvent.kt +++ /dev/null @@ -1,18 +0,0 @@ -package nebulosa.api.stacker - -import nebulosa.api.message.MessageEvent - -data class StackerEvent( - @JvmField val state: StackerState = StackerState.IDLE, - @JvmField val type: StackerGroupType = StackerGroupType.MONO, - @JvmField val stackCount: Int = 0, - @JvmField val numberOfTargets: Int = 0, -) : MessageEvent { - - override val eventName = "STACKER.ELAPSED" - - companion object { - - @JvmStatic val IDLE = StackerEvent() - } -} diff --git a/api/src/main/kotlin/nebulosa/api/stacker/StackerService.kt b/api/src/main/kotlin/nebulosa/api/stacker/StackerService.kt deleted file mode 100644 index aaada6b06..000000000 --- a/api/src/main/kotlin/nebulosa/api/stacker/StackerService.kt +++ /dev/null @@ -1,170 +0,0 @@ -package nebulosa.api.stacker - -import nebulosa.api.message.MessageService -import nebulosa.fits.fits -import nebulosa.fits.isFits -import nebulosa.image.format.ImageHdu -import nebulosa.stacker.AutoStacker -import nebulosa.stacker.AutoStackerListener -import nebulosa.xisf.isXisf -import nebulosa.xisf.xisf -import java.nio.file.Path -import java.util.concurrent.ConcurrentHashMap -import kotlin.io.path.exists -import kotlin.io.path.isDirectory -import kotlin.io.path.isRegularFile - -class StackerService(private val messageService: MessageService?) { - - private val stackers = ConcurrentHashMap(1) - - fun stack(request: StackingRequest, key: String): Path? { - require(request.outputDirectory != null && request.outputDirectory.exists() && request.outputDirectory.isDirectory()) - require(!stackers.containsKey(key)) { "stacking is already in progress" } - - val luminance = request.targets.filter { it.enabled && it.group == StackerGroupType.LUMINANCE } - val red = request.targets.filter { it.enabled && it.group == StackerGroupType.RED } - val green = request.targets.filter { it.enabled && it.group == StackerGroupType.GREEN } - val blue = request.targets.filter { it.enabled && it.group == StackerGroupType.BLUE } - val mono = request.targets.filter { it.enabled && it.group == StackerGroupType.MONO } - val rgb = request.targets.filter { it.enabled && it.group == StackerGroupType.RGB } - - val name = "${System.currentTimeMillis()}" - - // Combined LRGB - return if (red.size + green.size + blue.size >= 1) { - val stacker = request.get() - stackers[key] = stacker - val autoStackerMessageHandler = AutoStackerMessageHandler(luminance, red, green, blue) - - try { - stacker.registerAutoStackerListener(autoStackerMessageHandler) - - val stackedLuminancePath = luminance.stack(request, stacker, name, StackerGroupType.LUMINANCE) - val stackedRedPath = red.stack(request, stacker, name, StackerGroupType.RED) - val stackedGreenPath = green.stack(request, stacker, name, StackerGroupType.GREEN) - val stackedBluePath = blue.stack(request, stacker, name, StackerGroupType.BLUE) - - val combinedPath = Path.of("${request.outputDirectory}", "$name.LRGB.fits") - stacker.combineLRGB(combinedPath, stackedLuminancePath, stackedRedPath, stackedGreenPath, stackedBluePath) - combinedPath - } finally { - messageService?.sendMessage(StackerEvent.IDLE) - stacker.unregisterAutoStackerListener(autoStackerMessageHandler) - } - } - // LRGB - else if (rgb.isNotEmpty()) { - val stacker = request.get() - stackers[key] = stacker - val autoStackerMessageHandler = AutoStackerMessageHandler(luminance, rgb = rgb) - - try { - stacker.registerAutoStackerListener(autoStackerMessageHandler) - - val stackedLuminancePath = luminance.stack(request, stacker, name, StackerGroupType.LUMINANCE) - val stackedRGBPath = rgb.stack(request, stacker, name, StackerGroupType.RGB) - - if (stackedLuminancePath != null && stackedRGBPath != null) { - val combinedPath = Path.of("${request.outputDirectory}", "$name.LRGB.fits") - stacker.combineLuminance(combinedPath, stackedLuminancePath, stackedRGBPath, false) - combinedPath - } else { - stackedLuminancePath ?: stackedRGBPath - } - } finally { - messageService?.sendMessage(StackerEvent.IDLE) - stacker.unregisterAutoStackerListener(autoStackerMessageHandler) - } - } - // MONO - else if (mono.isNotEmpty() || luminance.isNotEmpty()) { - val stacker = request.get() - stackers[key] = stacker - val autoStackerMessageHandler = AutoStackerMessageHandler(luminance, mono = mono) - - try { - stacker.registerAutoStackerListener(autoStackerMessageHandler) - - val stackedLuminancePath = luminance.stack(request, stacker, name, StackerGroupType.LUMINANCE) - val stackedMonoPath = mono.stack(request, stacker, name, StackerGroupType.MONO) - - if (stackedLuminancePath != null && stackedMonoPath != null) { - val combinedPath = Path.of("${request.outputDirectory}", "$name.LRGB.fits") - stacker.combineLuminance(combinedPath, stackedLuminancePath, stackedMonoPath, true) - combinedPath - } else { - stackedLuminancePath ?: stackedMonoPath - } - } finally { - messageService?.sendMessage(StackerEvent.IDLE) - stacker.unregisterAutoStackerListener(autoStackerMessageHandler) - } - } else { - null - } - } - - fun isRunning(key: String): Boolean { - return stackers.containsKey(key) - } - - fun stop(key: String) { - stackers.remove(key)?.stop() - } - - private fun List.stack( - request: StackingRequest, stacker: AutoStacker, - name: String, group: StackerGroupType - ): Path? { - return if (size > 1) { - val outputPath = Path.of("${request.outputDirectory}", "$name.$group.fits") - if (stacker.stack(map { it.path!! }, outputPath, request.referencePath!!)) outputPath else null - } else if (isNotEmpty()) { - val outputPath = Path.of("${request.outputDirectory}", "$name.$group.fits") - if (stacker.align(request.referencePath!!, this[0].path!!, outputPath)) outputPath else null - } else { - null - } - } - - fun analyze(path: Path): AnalyzedTarget? { - if (!path.exists() || !path.isRegularFile()) return null - - val image = if (path.isFits()) path.fits() - else if (path.isXisf()) path.xisf() - else return null - - return image.use { it.firstOrNull { hdu -> hdu is ImageHdu }?.header }?.let(::AnalyzedTarget) - } - - private inner class AutoStackerMessageHandler( - private val luminance: Collection = emptyList(), - private val red: Collection = emptyList(), - private val green: Collection = emptyList(), - private val blue: Collection = emptyList(), - private val mono: Collection = emptyList(), - private val rgb: Collection = emptyList(), - ) : AutoStackerListener { - - private val numberOfTargets = luminance.size + red.size + green.size + blue.size + mono.size + rgb.size - - override fun onCalibrationStarted(stackCount: Int, path: Path) = sendNotification(StackerState.CALIBRATING, path, stackCount) - - override fun onAlignStarted(stackCount: Int, path: Path) = sendNotification(StackerState.ALIGNING, path, stackCount) - - override fun onIntegrationStarted(stackCount: Int, path: Path) = sendNotification(StackerState.INTEGRATING, path, stackCount) - - private fun sendNotification(state: StackerState, path: Path, stackCount: Int) { - val type = if (luminance.any { it.path === path }) StackerGroupType.LUMINANCE - else if (red.any { it.path === path }) StackerGroupType.RED - else if (green.any { it.path === path }) StackerGroupType.GREEN - else if (blue.any { it.path === path }) StackerGroupType.BLUE - else if (mono.any { it.path === path }) StackerGroupType.MONO - else if (rgb.any { it.path === path }) StackerGroupType.RGB - else return - - messageService?.sendMessage(StackerEvent(state, type, stackCount + 1, numberOfTargets)) - } - } -} diff --git a/api/src/main/kotlin/nebulosa/api/stacker/StackerState.kt b/api/src/main/kotlin/nebulosa/api/stacker/StackerState.kt deleted file mode 100644 index 15d511c6f..000000000 --- a/api/src/main/kotlin/nebulosa/api/stacker/StackerState.kt +++ /dev/null @@ -1,8 +0,0 @@ -package nebulosa.api.stacker - -enum class StackerState { - IDLE, - CALIBRATING, - ALIGNING, - INTEGRATING, -} diff --git a/api/src/main/kotlin/nebulosa/api/stacker/StackerType.kt b/api/src/main/kotlin/nebulosa/api/stacker/StackerType.kt deleted file mode 100644 index 4aee6a289..000000000 --- a/api/src/main/kotlin/nebulosa/api/stacker/StackerType.kt +++ /dev/null @@ -1,5 +0,0 @@ -package nebulosa.api.stacker - -enum class StackerType { - PIXINSIGHT, -} diff --git a/api/src/main/kotlin/nebulosa/api/stacker/StackingRequest.kt b/api/src/main/kotlin/nebulosa/api/stacker/StackingRequest.kt deleted file mode 100644 index da756d7f8..000000000 --- a/api/src/main/kotlin/nebulosa/api/stacker/StackingRequest.kt +++ /dev/null @@ -1,52 +0,0 @@ -package nebulosa.api.stacker - -import nebulosa.api.validators.Validatable -import nebulosa.api.validators.minSize -import nebulosa.api.validators.notNull -import nebulosa.pixinsight.script.startPixInsight -import nebulosa.pixinsight.stacker.PixInsightAutoStacker -import nebulosa.stacker.AutoStacker -import java.nio.file.Files -import java.nio.file.Path -import java.util.function.Supplier -import kotlin.io.path.exists -import kotlin.io.path.isRegularFile - -data class StackingRequest( - @JvmField val outputDirectory: Path? = null, - @JvmField val type: StackerType = StackerType.PIXINSIGHT, - @JvmField val executablePath: Path? = null, - @JvmField val darkPath: Path? = null, - @JvmField val darkEnabled: Boolean = false, - @JvmField val flatPath: Path? = null, - @JvmField val flatEnabled: Boolean = false, - @JvmField val biasPath: Path? = null, - @JvmField val biasEnabled: Boolean = false, - @JvmField val use32Bits: Boolean = false, - @JvmField val slot: Int = 1, - @JvmField val referencePath: Path? = null, - @JvmField val targets: List = emptyList(), -) : Supplier, Validatable { - - override fun validate() { - outputDirectory.notNull().exists() - executablePath.notNull() - referencePath.notNull().exists() - targets.minSize(2).onEach { it.validate() } - } - - override fun get(): AutoStacker { - val workingDirectory = Files.createTempDirectory("as-") - - val darkPath = darkPath?.takeIf { darkEnabled && it.exists() && it.isRegularFile() } - val flatPath = flatPath?.takeIf { flatEnabled && it.exists() && it.isRegularFile() } - val biasPath = biasPath?.takeIf { biasEnabled && it.exists() && it.isRegularFile() } - - return when (type) { - StackerType.PIXINSIGHT -> { - val runner = startPixInsight(executablePath!!, slot) - PixInsightAutoStacker(runner, workingDirectory, darkPath, flatPath, biasPath, slot) - } - } - } -} diff --git a/api/src/main/kotlin/nebulosa/api/stacker/StackingTarget.kt b/api/src/main/kotlin/nebulosa/api/stacker/StackingTarget.kt deleted file mode 100644 index 9736d37b0..000000000 --- a/api/src/main/kotlin/nebulosa/api/stacker/StackingTarget.kt +++ /dev/null @@ -1,18 +0,0 @@ -package nebulosa.api.stacker - -import nebulosa.api.validators.Validatable -import nebulosa.api.validators.exists -import nebulosa.api.validators.notNull -import java.nio.file.Path - -data class StackingTarget( - @JvmField val enabled: Boolean = true, - @JvmField val path: Path? = null, - @JvmField val group: StackerGroupType = StackerGroupType.MONO, - @JvmField val debayer: Boolean = true, -) : Validatable { - - override fun validate() { - path.notNull().exists() - } -} diff --git a/api/src/test/kotlin/StackerServiceTest.kt b/api/src/test/kotlin/StackerServiceTest.kt deleted file mode 100644 index 1c884f99a..000000000 --- a/api/src/test/kotlin/StackerServiceTest.kt +++ /dev/null @@ -1,69 +0,0 @@ -import io.kotest.matchers.ints.shouldBeExactly -import io.kotest.matchers.nulls.shouldNotBeNull -import io.kotest.matchers.shouldBe -import nebulosa.api.stacker.* -import nebulosa.fits.fits -import nebulosa.image.Image -import nebulosa.image.algorithms.transformation.AutoScreenTransformFunction -import nebulosa.test.NonGitHubOnly -import nebulosa.test.save -import org.junit.jupiter.api.Test -import java.nio.file.Path -import kotlin.io.path.createDirectories - -@NonGitHubOnly -class StackerServiceTest { - - @Test - fun stackLRGB() { - val targets = PATHS.map { - val analyzed = SERVICE.analyze(it)!! - StackingTarget(true, it, analyzed.group, true) - } - - targets.count { it.group == StackerGroupType.LUMINANCE } shouldBeExactly 10 - targets.count { it.group == StackerGroupType.RED } shouldBeExactly 3 - targets.count { it.group == StackerGroupType.GREEN } shouldBeExactly 3 - targets.count { it.group == StackerGroupType.BLUE } shouldBeExactly 3 - - val request = StackingRequest( - Path.of(BASE_DIR, "stacker").createDirectories(), StackerType.PIXINSIGHT, - Path.of("PixInsight"), DARK_PATH, true, null, false, null, false, false, - 1, PATHS[0], targets - ) - - val image = SERVICE.stack(request).shouldNotBeNull().fits().use(Image::open) - image.transform(AutoScreenTransformFunction).save("stacker-lrgb").second shouldBe "465a296bb4582ab2f938757347500eb8" - } - - companion object { - - const val BASE_DIR = "/home/tiagohm/Imagens/Astrophotos/Light/Algieba/2024-05-13" - - @JvmStatic private val SERVICE = StackerService(null) - - @JvmStatic private val PATHS = listOf( - Path.of("$BASE_DIR/20240513.213424625-LIGHT.fits"), - Path.of("$BASE_DIR/20240513.213436506-LIGHT.fits"), - Path.of("$BASE_DIR/20240513.213448253-LIGHT.fits"), - Path.of("$BASE_DIR/20240513.213500627-LIGHT.fits"), - Path.of("$BASE_DIR/20240513.213512554-LIGHT.fits"), - Path.of("$BASE_DIR/20240513.213524278-LIGHT.fits"), - Path.of("$BASE_DIR/20240513.213535967-LIGHT.fits"), - Path.of("$BASE_DIR/20240513.213547683-LIGHT.fits"), - Path.of("$BASE_DIR/20240513.213559416-LIGHT.fits"), - Path.of("$BASE_DIR/20240513.213611421-LIGHT.fits"), - Path.of("$BASE_DIR/20240513.213624939-LIGHT.fits"), - Path.of("$BASE_DIR/20240513.213636654-LIGHT.fits"), - Path.of("$BASE_DIR/20240513.213648389-LIGHT.fits"), - Path.of("$BASE_DIR/20240513.213701880-LIGHT.fits"), - Path.of("$BASE_DIR/20240513.213713546-LIGHT.fits"), - Path.of("$BASE_DIR/20240513.213725316-LIGHT.fits"), - Path.of("$BASE_DIR/20240513.213738803-LIGHT.fits"), - Path.of("$BASE_DIR/20240513.213750501-LIGHT.fits"), - Path.of("$BASE_DIR/20240513.213802188-LIGHT.fits"), - ) - - @JvmStatic private val DARK_PATH = Path.of("/home/tiagohm/Imagens/Astrophotos/Dark/2024-06-08/ASI294_BIN4_G120_O80/10-DARK.fits") - } -} diff --git a/desktop/README.md b/desktop/README.md index 22dad9dd9..4f2e23a1f 100644 --- a/desktop/README.md +++ b/desktop/README.md @@ -66,10 +66,6 @@ The complete integrated solution for all of your astronomical imaging needs. ![](sequencer.png) -## Stacker - -![](stacker.png) - ## INDI ![](indi.png) diff --git a/desktop/src/app/about/about.component.ts b/desktop/src/app/about/about.component.ts index 035606541..2a8cd604f 100644 --- a/desktop/src/app/about/about.component.ts +++ b/desktop/src/app/about/about.component.ts @@ -44,7 +44,6 @@ export class AboutComponent { this.icons.push({ link: `${FLAT_ICON_URL}/picture_2659360`, name: 'Picture', author: 'Freepik - Flaticon' }) this.icons.push({ link: `${FLAT_ICON_URL}/calculator_7182540`, name: 'Calculator', author: 'Iconic Panda - Flaticon' }) this.icons.push({ link: `${FLAT_ICON_URL}/target_10542035`, name: 'Target', author: 'Arkinasi - Flaticon' }) - this.icons.push({ link: `${FLAT_ICON_URL}/stack_3342239`, name: 'Stack', author: 'Pixel perfect - Flaticon' }) this.icons.push({ link: `${FLAT_ICON_URL}/blackhole_6704410`, name: 'Blackhole', author: 'Freepik - Flaticon' }) this.icons.push({ link: `${FLAT_ICON_URL}/calibration_2364169`, name: 'Calibration', author: 'Freepik - Flaticon' }) this.icons.push({ link: `${FLAT_ICON_URL}/idea_3351801`, name: 'Bulb', author: 'Good Ware - Flaticon' }) diff --git a/desktop/src/app/app-routing.module.ts b/desktop/src/app/app-routing.module.ts index 86abfaab0..cb8058c3e 100644 --- a/desktop/src/app/app-routing.module.ts +++ b/desktop/src/app/app-routing.module.ts @@ -22,7 +22,6 @@ import { MountComponent } from './mount/mount.component' import { RotatorComponent } from './rotator/rotator.component' import { SequencerComponent } from './sequencer/sequencer.component' import { SettingsComponent } from './settings/settings.component' -import { StackerComponent } from './stacker/stacker.component' const routes: Routes = [ { @@ -102,10 +101,6 @@ const routes: Routes = [ path: 'auto-focus', component: AutoFocusComponent, }, - { - path: 'stacker', - component: StackerComponent, - }, { path: 'calculator', component: CalculatorComponent, diff --git a/desktop/src/app/app.module.ts b/desktop/src/app/app.module.ts index 5d2fee002..08a73bc71 100644 --- a/desktop/src/app/app.module.ts +++ b/desktop/src/app/app.module.ts @@ -103,7 +103,6 @@ import { MountComponent } from './mount/mount.component' import { RotatorComponent } from './rotator/rotator.component' import { SequencerComponent } from './sequencer/sequencer.component' import { SettingsComponent } from './settings/settings.component' -import { StackerComponent } from './stacker/stacker.component' @NgModule({ declarations: [ @@ -158,7 +157,6 @@ import { StackerComponent } from './stacker/stacker.component' SettingsComponent, SkyObjectPipe, SlideMenuComponent, - StackerComponent, StopPropagationDirective, WinPipe, ], diff --git a/desktop/src/app/home/home.component.html b/desktop/src/app/home/home.component.html index 18d3f164e..9469a67ad 100644 --- a/desktop/src/app/home/home.component.html +++ b/desktop/src/app/home/home.component.html @@ -270,15 +270,6 @@
Auto Focus
-
- - -
Stacker
-
-
-
-
-
- - - - -
-
- -
-
- - - - -
-
-
diff --git a/desktop/src/app/settings/settings.component.ts b/desktop/src/app/settings/settings.component.ts index a3f3d7ce2..3638dd7bb 100644 --- a/desktop/src/app/settings/settings.component.ts +++ b/desktop/src/app/settings/settings.component.ts @@ -7,7 +7,6 @@ import { DEFAULT_LOCATION, Location } from '../../shared/types/atlas.types' import { DEFAULT_CAMERA_CAPTURE_NAMING_FORMAT, FrameType, LiveStackerType } from '../../shared/types/camera.types' import { PlateSolverType } from '../../shared/types/platesolver.types' import { DEFAULT_SETTINGS_PREFERENCE, resetCameraCaptureNamingFormat, SettingsTab } from '../../shared/types/settings.types' -import { StackerType } from '../../shared/types/stacker.types' import { StarDetectorType } from '../../shared/types/stardetector.types' import { AppComponent } from '../app.component' @@ -23,7 +22,6 @@ export class SettingsComponent implements AfterViewInit, OnDestroy { protected plateSolverType: PlateSolverType = 'ASTAP' protected starDetectorType: StarDetectorType = 'ASTAP' protected liveStackerType: LiveStackerType = 'SIRIL' - protected stackerType: StackerType = 'PIXINSIGHT' private readonly locationChangePublisher = new Subject() private readonly locationChangeSubscription?: Subscription @@ -50,13 +48,6 @@ export class SettingsComponent implements AfterViewInit, OnDestroy { this.showTab('PLATE_SOLVER', e.item?.label) }, }, - { - icon: 'mdi mdi-image-multiple', - label: 'Stacker', - command: (e) => { - this.showTab('STACKER', e.item?.label) - }, - }, { icon: 'mdi mdi-image-multiple', label: 'Live Stacker', @@ -92,10 +83,6 @@ export class SettingsComponent implements AfterViewInit, OnDestroy { return this.preference.liveStacker[this.liveStackerType] } - get stacker() { - return this.preference.stacker[this.stackerType] - } - constructor( private readonly app: AppComponent, private readonly preferenceService: PreferenceService, diff --git a/desktop/src/app/stacker/stacker.component.html b/desktop/src/app/stacker/stacker.component.html deleted file mode 100644 index fc21b4a44..000000000 --- a/desktop/src/app/stacker/stacker.component.html +++ /dev/null @@ -1,223 +0,0 @@ -
-
- -
-
- -
- @if (running && event.state !== 'IDLE') { -
-
- - - {{ event.stackCount }} / {{ event.numberOfTargets }} - - - - {{ event.type }}: {{ event.state | enum | lowercase }} - -
-
- } -
- - - - -
-
-
- {{ item.type }} - -
- Reference - -
-
- Enabled - -
-
- @if (item.analyzed) { -
- EXP: {{ item.analyzed.exposureTime | exposureTime }} - WIDTH: {{ item.analyzed.width }} - HEIGHT: {{ item.analyzed.height }} - BIN: {{ item.analyzed.binX }}x{{ item.analyzed.binY }} - GAIN: {{ item.analyzed.gain }} -
- } -
- - {{ item.path }} - -
-
-
- - -
-
-
-
-
- -
- - -
-
- - -
-
- - -
-
- -
- - - - -
-
-
-
- - -
-
-
- - -
-
diff --git a/desktop/src/app/stacker/stacker.component.ts b/desktop/src/app/stacker/stacker.component.ts deleted file mode 100644 index 595c95a50..000000000 --- a/desktop/src/app/stacker/stacker.component.ts +++ /dev/null @@ -1,233 +0,0 @@ -import { AfterViewInit, Component, HostListener, NgZone, OnDestroy } from '@angular/core' -import { dirname } from 'path' -import { MenuItem } from '../../shared/components/menu-item/menu-item.component' -import { SEPARATOR_MENU_ITEM } from '../../shared/constants' -import { ApiService } from '../../shared/services/api.service' -import { BrowserWindowService } from '../../shared/services/browser-window.service' -import { ElectronService } from '../../shared/services/electron.service' -import { PreferenceService } from '../../shared/services/preference.service' -import { DEFAULT_STACKER_EVENT, DEFAULT_STACKER_PREFERENCE, StackingRequest, StackingTarget } from '../../shared/types/stacker.types' -import { AppComponent } from '../app.component' - -@Component({ - selector: 'neb-stacker', - templateUrl: './stacker.component.html', -}) -export class StackerComponent implements AfterViewInit, OnDestroy { - protected running = false - protected readonly preference = structuredClone(DEFAULT_STACKER_PREFERENCE) - protected request = this.preference.request - protected readonly event = structuredClone(DEFAULT_STACKER_EVENT) - protected selected: StackingTarget[] = [] - - private frameId = '' - - protected readonly menuModel: MenuItem[] = [ - { - icon: 'mdi mdi-checkbox-marked', - label: 'Select All', - command: () => { - this.selected = this.request.targets - }, - }, - { - icon: 'mdi mdi-checkbox-blank-outline', - label: 'Unselect All', - command: () => { - this.selected = [] - }, - }, - { - icon: 'mdi mdi-delete', - label: 'Delete', - iconClass: 'text-danger', - command: () => { - if (this.selected.length) { - for (let i = 0; i < this.selected.length; i++) { - const target = this.selected[i] - this.deleteTarget(target) - this.selected.splice(i--, 1) - } - - this.savePreference() - } - }, - }, - SEPARATOR_MENU_ITEM, - { - icon: 'mdi mdi-delete', - label: 'Delete All', - iconClass: 'text-danger', - command: () => { - this.request.targets = [] - this.savePreference() - this.selected = [] - }, - }, - ] - - get referenceTarget() { - return this.request.targets.find((e) => e.enabled && e.reference && e.type === 'LIGHT') - } - - get hasReference() { - return !!this.referenceTarget - } - - get canStart() { - return !!this.request.outputDirectory && this.hasReference - } - - private static readonly key = 'dE0qB9iGOm' - - constructor( - app: AppComponent, - ngZone: NgZone, - private readonly electronService: ElectronService, - private readonly api: ApiService, - private readonly preferenceService: PreferenceService, - private readonly browserWindowService: BrowserWindowService, - ) { - app.title = 'Stacker' - - electronService.on('STACKER.ELAPSED', (event) => { - ngZone.run(() => { - Object.assign(this.event, event) - }) - }) - } - - async ngAfterViewInit() { - this.loadPreference() - - this.running = await this.api.stackerIsRunning(StackerComponent.key) - - if (!this.running) { - await this.reanalyze() - } - } - - @HostListener('window:unload') - ngOnDestroy() { - void this.closeFrameWindow() - } - - protected async openImages() { - try { - this.running = true - - const images = await this.electronService.openImages({ defaultPath: this.preference.defaultPath }) - - if (images && images.length) { - const targets: StackingTarget[] = [...this.request.targets] - - for (const path of images) { - const analyzed = await this.api.stackerAnalyze(path) - - if (analyzed && analyzed.type === 'LIGHT') { - targets.push({ - enabled: true, - path, - analyzed, - type: analyzed.type, - group: analyzed.group, - reference: !targets.length && !this.referenceTarget, - }) - } - } - - this.request.targets = targets - - this.preference.defaultPath = dirname(images[0]) - this.savePreference() - } - } finally { - this.running = false - } - } - - protected referenceChanged(target: StackingTarget, enabled: boolean) { - if (enabled) { - for (const item of this.request.targets) { - if (item.reference && item !== target) { - item.reference = false - } - } - } - } - - protected async openTargetImage(target: StackingTarget) { - this.frameId = await this.browserWindowService.openImage({ path: target.path, id: 'stacker', source: 'PATH' }) - } - - protected deleteTarget(target: StackingTarget) { - const index = this.request.targets.findIndex((e) => e === target) - - if (index >= 0) { - this.request.targets.splice(index, 1) - } - } - - private async closeFrameWindow() { - if (this.frameId) { - await this.electronService.closeWindow(undefined, this.frameId) - } - } - - protected async startStacking() { - const settings = this.preferenceService.settings.get() - - const request: StackingRequest = { - ...this.request, - ...settings.stacker[this.request.type], - referencePath: this.referenceTarget!.path, - targets: this.request.targets.filter((e) => e.enabled), - } - - this.savePreference() - - try { - this.running = true - const path = await this.api.stackerStart(request, StackerComponent.key) - - if (path) { - await this.browserWindowService.openImage({ path, source: 'STACKER' }) - } - } finally { - this.running = false - } - } - - protected stopStacking() { - return this.api.stackerStop(StackerComponent.key) - } - - private async reanalyze() { - const targets: StackingTarget[] = [] - - for (const target of this.request.targets) { - const analyzed = await this.api.stackerAnalyze(target.path) - - if (analyzed && analyzed.type === 'LIGHT') { - targets.push({ - ...target, - analyzed, - type: analyzed.type, - group: analyzed.group, - }) - } - } - - this.request.targets = targets - this.savePreference() - } - - private loadPreference() { - Object.assign(this.preference, this.preferenceService.stacker.get()) - this.request = this.preference.request - } - - protected savePreference() { - this.preferenceService.stacker.set(this.preference) - } -} diff --git a/desktop/src/assets/icons/stack.png b/desktop/src/assets/icons/stack.png deleted file mode 100644 index 7cfd07d6bd9f55e0f17a1b1ccc7e1d832c49cdac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2273 zcmV<72p;!|P) z67t%xckk;Q|48q$$-eIH-n;4e`ET$2-QVN8=bn4+Ip-4Of%6yopVxHl2fATYnTFti z%-UB~RTUUxnO-&okTa5q*7>ES&4dumIzh6|#0KvV417W;k&~*b-RhLjECq<=f`Hhx zpuGGv08nSrisw}SSebD{Ip<{Y4zZMi_6&`UeGYI@0G~c`MA!8qxBTZw5e0oqO6he) zfPsO56-qd=!VTXJ^2w2jiAGlhPz>WoxpZ{-f=Ke1D*^~pEO*1N3*6&QA0SceieKlc zuKA`!JLahP!tfy!PVNCG23X162=SGe z@O&B4y+P^>xGBBQ16Y`sXfY5OLFM2#pojXQ1MvERO8@())LIc{%WyI9YiQK{*9Z!b z4X#H?fK_}V0_MMPXZZVIa?15QC?Km{k1~tG@S&u|TlmZ?SfIAJo(lyKc0I~00u!ya z?8=Rx%b85y0`IvVWp+?~*p{7747!{NKqYX*^(eEDtiYC?LEL|(pi)Tp+u1Di!Wvt4 zA@&8AGXbbEdi4Z=G1nu-Fj4j_xYwW3MiMWs`Mzn}r)FsA9XO6l=RsC8-Bg(`PBk`)v^Mt-#nF`@qXqfO|Y3ghD6tG3r@^4~lkS%=186ABJh* zTRR%jd%hP!+ZsUb94O7+FwMi1=4a>`i@|Qe@GY-Le_zhzTGPO{ zcQxWaqsgBX-4~BSNE2JW^a2=7Je5uXVB^!xW1Yb&d z&!;1i2vGsn*42#w$osI^4p#9)6GJ)snk~YtuAdyvIfBDDS-eH82s0){rIbNJ@PcEJ zl5jTk0Xi;sVpO?~MGF_9%6}VHm99dir*ej!kOD6UU&641#as0B_ka>MQ-Ct69h;Ee z2!Pb%)Ewv>>_GSBZdjA9L;4Vg^dY==qYoSBKZ?5w?*ef1pLe5pi|&g@XWjyJb#)O5 z{LQU^or4`XdiiLy_*e{K;H{xn^iB1-((IEFw=O^-gA(LErvq8Vrp{fy>hW$wfAjje@J`!ORfHLaN+-*VuB1))lay*o^)AoVA zr8yUG5fwo~8C4u`IFp2v0FOWNU@v1-&(`Vb;nM*}S5*m6v1Fbrw?ood5XQad9gmg^ zF7*k?1z`N3qmi7z1rd_ZgE8&5jzw`2heb##z~*)9`Z?z}9EsvI4vLU+CX{neU(ONS z!XXh-3P3sCYcCfvV{?UCYSjRNP(xUy;w2xsUm9Ytf?T(k}-wx-*@Lkb>~Z$WLOA zEiJwC=Y-~Y!zclYD~oN3P_tz9?kx}g@E>V2lv2_+{@AVyDN>5X7>`^zTUy-IR2NO<^Yy2#;y*G?&yKdv zHzSJDm_9H;VL{=RC+pX2n|-P&*i?OK`0Cb>8oF0Nc&+0B7RYF@6_M&VL4rxe>G-0e zWZpIFCnZuWqzf;2Jb{a)fs#KzSNq*_ahKQ$x^w@bmCDrQyLN0TrP5bWuyyl>hhDT} zKiAl9m~ZTES54FOrnj7S>-YPQ!xWpIYFK+U+vM_r?M(sP!_V4xD-nq(_iCp8;f}VB zBQ5(6Zp}>D+l@eC6Zq}4gK^YAbN~Zu4zb_^Y1Q3j}GvL^#P_yNGYu7q5 zM`rtrkwU??D}=n5tp7=ekb(+B6aldHDIEboxhKpxJ^IQ=H>0Wp*`h9;OT<-HOBY} z2GXXN`}Z`~)kU1}oe3EUAj%4!;hOfSVHg>I5lSgFIrm&;oc^7F?|yQ_18);T=6pj& z#^bZ!?+bobRi}E>i9i|CIp+h6(PNCVmM7M)iCb*>IFQu(`image/save-as?${query}`, save) } + imageAnalyze(path: string) { + const query = this.http.query({ path }) + return this.http.put(`image/analyze?${query}`) + } + coordinateInterpolation(path: string) { const query = this.http.query({ path }) return this.http.get(`image/coordinate-interpolation?${query}`) @@ -760,27 +764,6 @@ export class ApiService { return this.http.put(`auto-focus/${camera.id}/stop`) } - // STACKER - - stackerStart(request: StackingRequest, key: string) { - const query = this.http.query({ key }) - return this.http.put(`stacker/start?${query}`, request) - } - - stackerIsRunning( key: string) { - const query = this.http.query({ key }) - return this.http.get(`stacker/running?${query}`) - } - - stackerStop( key: string) { - const query = this.http.query({ key }) - return this.http.put(`stacker/stop?${query}`) - } - - stackerAnalyze(path: string) { - return this.http.put(`stacker/analyze?path=${path}`) - } - // CONFIRMATION confirm(idempotencyKey: string, accepted: boolean) { diff --git a/desktop/src/shared/services/electron.service.ts b/desktop/src/shared/services/electron.service.ts index b61d62260..437ad832d 100644 --- a/desktop/src/shared/services/electron.service.ts +++ b/desktop/src/shared/services/electron.service.ts @@ -24,7 +24,6 @@ import { LightBox } from '../types/lightbox.types' import { Mount } from '../types/mount.types' import { Rotator } from '../types/rotator.types' import { SequencerEvent } from '../types/sequencer.types' -import { StackerEvent } from '../types/stacker.types' import { Wheel, WheelRenamed } from '../types/wheel.types' export const OPEN_IMAGE_FILE_FILTER: Electron.FileFilter[] = [ @@ -99,7 +98,6 @@ export interface EventMap { 'WHEEL.RENAMED': WheelRenamed 'ROI.SELECTED': ROISelected 'AUTO_FOCUS.ELAPSED': AutoFocusEvent - 'STACKER.ELAPSED': StackerEvent } @Injectable({ providedIn: 'root' }) diff --git a/desktop/src/shared/services/preference.service.ts b/desktop/src/shared/services/preference.service.ts index 92397db96..c085a993a 100644 --- a/desktop/src/shared/services/preference.service.ts +++ b/desktop/src/shared/services/preference.service.ts @@ -14,7 +14,6 @@ import { DEFAULT_MOUNT_PREFERENCE, Mount, MountPreference, mountPreferenceWithDe import { DEFAULT_ROTATOR_PREFERENCE, Rotator, RotatorPreference, rotatorPreferenceWithDefault } from '../types/rotator.types' import { DEFAULT_SEQUENCER_PREFERENCE, SequencerPreference, sequencerPreferenceWithDefault } from '../types/sequencer.types' import { DEFAULT_SETTINGS_PREFERENCE, SettingsPreference, settingsPreferenceWithDefault } from '../types/settings.types' -import { DEFAULT_STACKER_PREFERENCE, StackerPreference, stackerPreferenceWithDefault } from '../types/stacker.types' import { DEFAULT_WHEEL_PREFERENCE, Wheel, WheelPreference, wheelPreferenceWithDefault } from '../types/wheel.types' import { Undefinable } from '../utils/types' import { LocalStorageService } from './local-storage.service' @@ -52,7 +51,6 @@ export class PreferenceService { readonly skyAtlas: PreferenceData readonly alignment: PreferenceData readonly calibration: PreferenceData - readonly stacker: PreferenceData readonly guider: PreferenceData readonly framing: PreferenceData readonly settings: PreferenceData @@ -64,7 +62,6 @@ export class PreferenceService { this.skyAtlas = this.create('atlas', () => structuredClone(DEFAULT_SKY_ATLAS_PREFERENCE), skyAtlasPreferenceWithDefault) this.alignment = this.create('alignment', () => structuredClone(DEFAULT_ALIGNMENT_PREFERENCE), alignmentPreferenceWithDefault) this.calibration = this.create('calibration', () => structuredClone(DEFAULT_CALIBRATION_PREFERENCE), calibrationPreferenceWithDefault) - this.stacker = this.create('stacker', () => structuredClone(DEFAULT_STACKER_PREFERENCE), stackerPreferenceWithDefault) this.guider = this.create('guider', () => structuredClone(DEFAULT_GUIDER_PREFERENCE), guiderPreferenceWithDefault) this.framing = this.create('framing', () => structuredClone(DEFAULT_FRAMING_PREFERENCE), framingPreferenceWithDefault) this.settings = this.create('settings', () => structuredClone(DEFAULT_SETTINGS_PREFERENCE), settingsPreferenceWithDefault) diff --git a/desktop/src/shared/types/camera.types.ts b/desktop/src/shared/types/camera.types.ts index 95c1ccb72..2aa3ef784 100644 --- a/desktop/src/shared/types/camera.types.ts +++ b/desktop/src/shared/types/camera.types.ts @@ -4,9 +4,9 @@ import type { CompanionDevice, Device, PropertyState } from './device.types' import { isCompanionDevice } from './device.types' import type { Focuser } from './focuser.types' import type { GuideOutput } from './guider.types' +import type { ImageFilterType } from './image.types' import type { Mount } from './mount.types' import type { Rotator } from './rotator.types' -import type { StackerGroupType } from './stacker.types' import type { Wheel } from './wheel.types' export type CameraMode = 'CAPTURE' | 'SEQUENCER' | 'FLAT_WIZARD' | 'TPPA' | 'DARV' | 'AUTO_FOCUS' @@ -114,7 +114,7 @@ export interface CameraStartCapture { focusOffset: number calibrationGroup?: string liveStacking: LiveStackingRequest - stackerGroupType: StackerGroupType + stackerGroupType: ImageFilterType namingFormat: CameraCaptureNamingFormat } diff --git a/desktop/src/shared/types/home.types.ts b/desktop/src/shared/types/home.types.ts index d63cd51e6..e8cf5c00d 100644 --- a/desktop/src/shared/types/home.types.ts +++ b/desktop/src/shared/types/home.types.ts @@ -1,6 +1,6 @@ import type { DeviceType } from './device.types' -export type HomeWindowType = DeviceType | 'GUIDER' | 'SKY_ATLAS' | 'ALIGNMENT' | 'SEQUENCER' | 'IMAGE' | 'FRAMING' | 'INDI' | 'SETTINGS' | 'CALCULATOR' | 'ABOUT' | 'FLAT_WIZARD' | 'AUTO_FOCUS' | 'STACKER' | 'CALIBRATION' +export type HomeWindowType = DeviceType | 'GUIDER' | 'SKY_ATLAS' | 'ALIGNMENT' | 'SEQUENCER' | 'IMAGE' | 'FRAMING' | 'INDI' | 'SETTINGS' | 'CALCULATOR' | 'ABOUT' | 'FLAT_WIZARD' | 'AUTO_FOCUS' | 'CALIBRATION' export type ConnectionType = 'INDI' | 'ALPACA' diff --git a/desktop/src/shared/types/image.types.ts b/desktop/src/shared/types/image.types.ts index b1fb79293..3e646972f 100644 --- a/desktop/src/shared/types/image.types.ts +++ b/desktop/src/shared/types/image.types.ts @@ -10,7 +10,7 @@ export type ImageChannel = 'RED' | 'GREEN' | 'BLUE' | 'GRAY' export type SCNRProtectionMethod = 'MAXIMUM_MASK' | 'ADDITIVE_MASK' | 'AVERAGE_NEUTRAL' | 'MAXIMUM_NEUTRAL' | 'MINIMUM_NEUTRAL' -export type ImageSource = 'FRAMING' | 'PATH' | 'CAMERA' | 'FLAT_WIZARD' | 'SEQUENCER' | 'ALIGNMENT' | 'AUTO_FOCUS' | 'STACKER' +export type ImageSource = 'FRAMING' | 'PATH' | 'CAMERA' | 'FLAT_WIZARD' | 'SEQUENCER' | 'ALIGNMENT' | 'AUTO_FOCUS' export type ImageFormat = 'FITS' | 'XISF' | 'PNG' | 'JPG' @@ -20,6 +20,8 @@ export type LiveStackingMode = 'NONE' | 'RAW' | 'STACKED' export type ImageCalibrationSource = 'CAMERA' | 'MENU' +export type ImageFilterType = 'LUMINANCE' | 'RED' | 'GREEN' | 'BLUE' | 'MONO' | 'RGB' | 'NONE' + export type ImageMousePosition = Point export interface Image { @@ -276,6 +278,17 @@ export interface ImageAnnotationDialog { filtered: ImageAnnotation[] } +export interface ImageAnalyzed { + width: number + height: number + binX: number + binY: number + gain: number + exposureTime: number + type: FrameType + filter: ImageFilterType +} + export interface ROISelected { camera: Camera x: number diff --git a/desktop/src/shared/types/settings.types.ts b/desktop/src/shared/types/settings.types.ts index 0d47c7f7b..fdc4d7a95 100644 --- a/desktop/src/shared/types/settings.types.ts +++ b/desktop/src/shared/types/settings.types.ts @@ -3,17 +3,15 @@ import { DEFAULT_LOCATION, locationWithDefault } from './atlas.types' import type { LiveStackerSettings, LiveStackerType } from './camera.types' import { cameraCaptureNamingFormatWithDefault, DEFAULT_CAMERA_CAPTURE_NAMING_FORMAT, DEFAULT_LIVE_STACKER_SETTINGS, liveStackerSettingsWithDefault, type CameraCaptureNamingFormat, type FrameType } from './camera.types' import { DEFAULT_PLATE_SOLVER_SETTINGS, plateSolverSettingsWithDefault, type PlateSolverSettings, type PlateSolverType } from './platesolver.types' -import { DEFAULT_STACKER_SETTINGS, stackerSettingsWithDefault, type StackerSettings, type StackerType } from './stacker.types' import { DEFAULT_STAR_DETECTOR_SETTINGS, starDetectorSettingsWithDefault, type StarDetectorSettings, type StarDetectorType } from './stardetector.types' -export type SettingsTab = 'GENERAL' | 'LOCATION' | 'PLATE_SOLVER' | 'STAR_DETECTOR' | 'LIVE_STACKER' | 'STACKER' | 'CAPTURE_NAMING_FORMAT' +export type SettingsTab = 'GENERAL' | 'LOCATION' | 'PLATE_SOLVER' | 'STAR_DETECTOR' | 'LIVE_STACKER' | 'CAPTURE_NAMING_FORMAT' export interface SettingsPreference { checkVersion: boolean plateSolver: Record starDetector: Record liveStacker: Record - stacker: Record namingFormat: CameraCaptureNamingFormat locations: Location[] location: Location @@ -37,9 +35,6 @@ export const DEFAULT_SETTINGS_PREFERENCE: SettingsPreference = { SIRIL: structuredClone(DEFAULT_LIVE_STACKER_SETTINGS), PIXINSIGHT: structuredClone(DEFAULT_LIVE_STACKER_SETTINGS), }, - stacker: { - PIXINSIGHT: structuredClone(DEFAULT_STACKER_SETTINGS), - }, namingFormat: DEFAULT_CAMERA_CAPTURE_NAMING_FORMAT, locations: [DEFAULT_LOCATION], location: DEFAULT_LOCATION, @@ -52,7 +47,6 @@ export function settingsPreferenceWithDefault(preference?: Partial, source: StackerSettings = DEFAULT_STACKER_SETTINGS) { - if (!preference) return structuredClone(source) - preference.executablePath ||= source.executablePath - preference.slot ??= source.slot - return preference as StackerSettings -} - -export function stackingRequestWithDefault(request?: Partial, source: StackingRequest = DEFAULT_STACKING_REQUEST) { - if (!request) return structuredClone(source) - stackerSettingsWithDefault(request, source) - request.outputDirectory ||= source.outputDirectory - request.type ||= source.type - request.darkPath ||= source.darkPath - request.darkEnabled ??= source.darkEnabled - request.flatPath ||= source.flatPath - request.flatEnabled ??= source.flatEnabled - request.biasPath ||= source.biasPath - request.biasEnabled ??= source.biasEnabled - request.use32Bits ??= source.use32Bits - request.referencePath ||= source.referencePath - request.targets ??= source.targets - return request as StackingRequest -} - -export function stackerPreferenceWithDefault(preference?: Partial, source: StackerPreference = DEFAULT_STACKER_PREFERENCE) { - if (!preference) return structuredClone(source) - preference.request = stackingRequestWithDefault(preference.request, source.request) - preference.defaultPath ??= source.defaultPath - return preference as StackerPreference -} diff --git a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/stacker/PixInsightAutoStacker.kt b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/stacker/PixInsightAutoStacker.kt deleted file mode 100644 index 8d5ec5bed..000000000 --- a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/stacker/PixInsightAutoStacker.kt +++ /dev/null @@ -1,136 +0,0 @@ -package nebulosa.pixinsight.stacker - -import nebulosa.pixinsight.script.PixInsightScript -import nebulosa.pixinsight.script.PixInsightScriptRunner -import nebulosa.stacker.AutoStacker -import nebulosa.stacker.AutoStackerListener -import java.nio.file.Path -import java.util.concurrent.CancellationException -import java.util.concurrent.atomic.AtomicBoolean -import kotlin.io.path.deleteIfExists - -data class PixInsightAutoStacker( - private val runner: PixInsightScriptRunner, - private val workingDirectory: Path, - private val darkPath: Path? = null, - private val flatPath: Path? = null, - private val biasPath: Path? = null, - private val slot: Int = PixInsightScript.UNSPECIFIED_SLOT, -) : AutoStacker { - - private val stacker = PixInsightStacker(runner, workingDirectory, slot) - private val listeners = HashSet() - private val stopped = AtomicBoolean() - - override fun registerAutoStackerListener(listener: AutoStackerListener) { - listeners.add(listener) - } - - override fun unregisterAutoStackerListener(listener: AutoStackerListener) { - listeners.remove(listener) - } - - override fun stack(targetPaths: Collection, outputPath: Path, referencePath: Path): Boolean { - if (targetPaths.isEmpty()) return false - - val calibratedPath = Path.of("$workingDirectory", "calibrated.xisf") - val alignedPath = Path.of("$workingDirectory", "aligned.xisf") - - stopped.set(false) - - try { - var stackCount = 0 - - for (path in targetPaths) { - var targetPath = path - - if (stopped.get()) return false - - listeners.forEach { it.onCalibrationStarted(stackCount, path) } - - if (calibrate(targetPath, calibratedPath, darkPath, flatPath, biasPath)) { - listeners.forEach { it.onCalibrationFinished(stackCount, path, calibratedPath) } - targetPath = calibratedPath - } - - if (stopped.get()) return false - - if (stackCount > 0) { - listeners.forEach { it.onAlignStarted(stackCount, path) } - - if (align(referencePath, targetPath, alignedPath)) { - listeners.forEach { it.onAlignFinished(stackCount, path, alignedPath) } - - if (stopped.get()) return false - - listeners.forEach { it.onIntegrationStarted(stackCount, path) } - integrate(stackCount, outputPath, alignedPath, outputPath) - listeners.forEach { it.onIntegrationFinished(stackCount, path, outputPath) } - stackCount++ - } - } else { - if (referencePath != path) { - listeners.forEach { it.onAlignStarted(stackCount, path) } - - if (align(referencePath, targetPath, alignedPath)) { - listeners.forEach { it.onAlignFinished(stackCount, path, alignedPath) } - - if (stopped.get()) return false - - saveAs(alignedPath, outputPath) - - if (stopped.get()) return false - - listeners.forEach { it.onIntegrationStarted(0, path) } - integrate(0, outputPath, alignedPath, outputPath) - listeners.forEach { it.onIntegrationFinished(0, path, outputPath) } - } else { - saveAs(targetPath, outputPath) - } - } else { - saveAs(targetPath, outputPath) - } - - stackCount = 1 - } - - if (stopped.get()) return false - } - } catch (_: CancellationException) { - return false - } finally { - calibratedPath.deleteIfExists() - alignedPath.deleteIfExists() - } - - return true - } - - override fun stop() { - stopped.set(true) - } - - override fun calibrate(targetPath: Path, outputPath: Path, darkPath: Path?, flatPath: Path?, biasPath: Path?): Boolean { - return stacker.calibrate(targetPath, outputPath, darkPath, flatPath, biasPath) - } - - override fun align(referencePath: Path, targetPath: Path, outputPath: Path): Boolean { - return stacker.align(referencePath, targetPath, outputPath) - } - - override fun integrate(stackCount: Int, stackedPath: Path, targetPath: Path, outputPath: Path): Boolean { - return stacker.integrate(stackCount, stackedPath, targetPath, outputPath) - } - - override fun combineLRGB(outputPath: Path, luminancePath: Path?, redPath: Path?, greenPath: Path?, bluePath: Path?): Boolean { - return stacker.combineLRGB(outputPath, luminancePath, redPath, greenPath, bluePath) - } - - override fun combineLuminance(outputPath: Path, luminancePath: Path, targetPath: Path, mono: Boolean): Boolean { - return stacker.combineLuminance(outputPath, luminancePath, targetPath, mono) - } - - override fun saveAs(inputPath: Path, outputPath: Path): Boolean { - return stacker.saveAs(inputPath, outputPath) - } -} diff --git a/nebulosa-pixinsight/src/test/kotlin/PixInsightAutoStackerTest.kt b/nebulosa-pixinsight/src/test/kotlin/PixInsightAutoStackerTest.kt deleted file mode 100644 index 69514950c..000000000 --- a/nebulosa-pixinsight/src/test/kotlin/PixInsightAutoStackerTest.kt +++ /dev/null @@ -1,43 +0,0 @@ -import PixInsightScriptTest.Companion.RUNNER -import PixInsightScriptTest.Companion.openAsImage -import io.kotest.matchers.booleans.shouldBeTrue -import io.kotest.matchers.shouldBe -import nebulosa.image.algorithms.transformation.AutoScreenTransformFunction -import nebulosa.pixinsight.stacker.PixInsightAutoStacker -import nebulosa.test.AbstractTest -import nebulosa.test.NonGitHubOnly -import nebulosa.test.fits.* -import nebulosa.test.save -import org.junit.jupiter.api.Disabled -import org.junit.jupiter.api.Test - -@NonGitHubOnly -class PixInsightAutoStackerTest : AbstractTest() { - - @Test - fun stack() { - val files = listOf(STACKING_LIGHT_MONO_01_FITS, STACKING_LIGHT_MONO_02_FITS, STACKING_LIGHT_MONO_03_FITS, STACKING_LIGHT_MONO_04_FITS, STACKING_LIGHT_MONO_05_FITS, STACKING_LIGHT_MONO_06_FITS, STACKING_LIGHT_MONO_07_FITS, STACKING_LIGHT_MONO_08_FITS) - val workingDirectory = tempDirectory("pi-") - val outputPath = tempPath("pi-", ".fits") - - val stacker = PixInsightAutoStacker(RUNNER, workingDirectory) - stacker.stack(files, outputPath).shouldBeTrue() - - outputPath.openAsImage().transform(AutoScreenTransformFunction) - .save("pi-auto-stacked").second shouldBe "a107143dff3d43c4b56c872da869f89b" - } - - @Test - @Disabled - fun calibratedStack() { - val files = listOf(STACKING_LIGHT_MONO_01_FITS, STACKING_LIGHT_MONO_02_FITS, STACKING_LIGHT_MONO_03_FITS, STACKING_LIGHT_MONO_04_FITS, STACKING_LIGHT_MONO_05_FITS, STACKING_LIGHT_MONO_06_FITS, STACKING_LIGHT_MONO_07_FITS, STACKING_LIGHT_MONO_08_FITS) - val workingDirectory = tempDirectory("pi-") - val outputPath = tempPath("pi-", ".fits") - - val stacker = PixInsightAutoStacker(RUNNER, workingDirectory, STACKING_DARK_MONO_FITS, STACKING_FLAT_MONO_FITS, STACKING_BIAS_MONO_FITS) - stacker.stack(files, outputPath).shouldBeTrue() - - outputPath.openAsImage().transform(AutoScreenTransformFunction) - .save("pi-calibrated-auto-stacked").second shouldBe "" - } -} diff --git a/nebulosa-stacker/src/main/kotlin/nebulosa/stacker/AutoStacker.kt b/nebulosa-stacker/src/main/kotlin/nebulosa/stacker/AutoStacker.kt deleted file mode 100644 index e48e8c355..000000000 --- a/nebulosa-stacker/src/main/kotlin/nebulosa/stacker/AutoStacker.kt +++ /dev/null @@ -1,14 +0,0 @@ -package nebulosa.stacker - -import java.nio.file.Path - -interface AutoStacker : Stacker { - - fun registerAutoStackerListener(listener: AutoStackerListener) - - fun unregisterAutoStackerListener(listener: AutoStackerListener) - - fun stack(targetPaths: Collection, outputPath: Path, referencePath: Path = targetPaths.first()): Boolean - - fun stop() -} diff --git a/nebulosa-stacker/src/main/kotlin/nebulosa/stacker/AutoStackerListener.kt b/nebulosa-stacker/src/main/kotlin/nebulosa/stacker/AutoStackerListener.kt deleted file mode 100644 index c82ed45d1..000000000 --- a/nebulosa-stacker/src/main/kotlin/nebulosa/stacker/AutoStackerListener.kt +++ /dev/null @@ -1,18 +0,0 @@ -package nebulosa.stacker - -import java.nio.file.Path - -interface AutoStackerListener { - - fun onCalibrationStarted(stackCount: Int, path: Path) = Unit - - fun onAlignStarted(stackCount: Int, path: Path) = Unit - - fun onIntegrationStarted(stackCount: Int, path: Path) = Unit - - fun onCalibrationFinished(stackCount: Int, path: Path, calibratedPath: Path) = Unit - - fun onAlignFinished(stackCount: Int, path: Path, alignedPath: Path) = Unit - - fun onIntegrationFinished(stackCount: Int, path: Path, alignedPath: Path) = Unit -} From f78e0984d1b546025003cbc3a2bd7c6ed3f2c8cf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 16 Oct 2024 03:39:49 +0000 Subject: [PATCH 08/15] [desktop]: Bump tslib from 2.7.0 to 2.8.0 in /desktop Bumps [tslib](https://github.com/Microsoft/tslib) from 2.7.0 to 2.8.0. - [Release notes](https://github.com/Microsoft/tslib/releases) - [Commits](https://github.com/Microsoft/tslib/compare/v2.7.0...v2.8.0) --- updated-dependencies: - dependency-name: tslib dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- desktop/package-lock.json | 9 ++++----- desktop/package.json | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/desktop/package-lock.json b/desktop/package-lock.json index 9644e1cda..710e29f10 100644 --- a/desktop/package-lock.json +++ b/desktop/package-lock.json @@ -32,7 +32,7 @@ "primeng": "17.18.11", "primeng-sass-theme": "github:primefaces/primeng-sass-theme#17.18.3", "rxjs": "7.8.1", - "tslib": "2.7.0", + "tslib": "2.8.0", "zone.js": "0.14.10" }, "devDependencies": { @@ -19589,10 +19589,9 @@ } }, "node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", - "license": "0BSD" + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz", + "integrity": "sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==" }, "node_modules/tty-browserify": { "version": "0.0.1", diff --git a/desktop/package.json b/desktop/package.json index a2ff9cc1b..d624cdb53 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -57,7 +57,7 @@ "primeng": "17.18.11", "primeng-sass-theme": "github:primefaces/primeng-sass-theme#17.18.3", "rxjs": "7.8.1", - "tslib": "2.7.0", + "tslib": "2.8.0", "zone.js": "0.14.10" }, "devDependencies": { From 2f6d1e769a365ae0ec564ebe3c81589477d5443e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 16 Oct 2024 03:39:56 +0000 Subject: [PATCH 09/15] [desktop]: Bump chart.js from 4.4.4 to 4.4.5 in /desktop Bumps [chart.js](https://github.com/chartjs/Chart.js) from 4.4.4 to 4.4.5. - [Release notes](https://github.com/chartjs/Chart.js/releases) - [Commits](https://github.com/chartjs/Chart.js/compare/v4.4.4...v4.4.5) --- updated-dependencies: - dependency-name: chart.js dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- desktop/package-lock.json | 9 ++++----- desktop/package.json | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/desktop/package-lock.json b/desktop/package-lock.json index 710e29f10..c2dbacc27 100644 --- a/desktop/package-lock.json +++ b/desktop/package-lock.json @@ -20,7 +20,7 @@ "@angular/platform-browser-dynamic": "18.2.8", "@angular/router": "18.2.8", "@mdi/font": "7.4.47", - "chart.js": "4.4.4", + "chart.js": "4.4.5", "chartjs-plugin-zoom": "2.0.1", "hotkeys-js": "3.13.7", "leaflet": "1.9.4", @@ -8363,10 +8363,9 @@ "license": "MIT" }, "node_modules/chart.js": { - "version": "4.4.4", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.4.tgz", - "integrity": "sha512-emICKGBABnxhMjUjlYRR12PmOXhJ2eJjEHL2/dZlWjxRAZT1D8xplLFq5M0tMQK8ja+wBS/tuVEJB5C6r7VxJA==", - "license": "MIT", + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.5.tgz", + "integrity": "sha512-CVVjg1RYTJV9OCC8WeJPMx8gsV8K6WIyIEQUE3ui4AR9Hfgls9URri6Ja3hyMVBbTF8Q2KFa19PE815gWcWhng==", "dependencies": { "@kurkle/color": "^0.3.0" }, diff --git a/desktop/package.json b/desktop/package.json index d624cdb53..d855fd70b 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -45,7 +45,7 @@ "@angular/platform-browser-dynamic": "18.2.8", "@angular/router": "18.2.8", "@mdi/font": "7.4.47", - "chart.js": "4.4.4", + "chart.js": "4.4.5", "chartjs-plugin-zoom": "2.0.1", "hotkeys-js": "3.13.7", "leaflet": "1.9.4", From a8d0137949bfc563299b8ab1957bc18ba2254fed Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 16 Oct 2024 03:59:09 +0000 Subject: [PATCH 10/15] [api]: Bump ch.qos.logback:logback-classic from 1.5.10 to 1.5.11 Bumps [ch.qos.logback:logback-classic](https://github.com/qos-ch/logback) from 1.5.10 to 1.5.11. - [Commits](https://github.com/qos-ch/logback/compare/v_1.5.10...v_1.5.11) --- updated-dependencies: - dependency-name: ch.qos.logback:logback-classic dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- settings.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index c22e0a8be..3f7837ecd 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -21,7 +21,7 @@ dependencyResolutionManagement { library("retrofit", "com.squareup.retrofit2:retrofit:2.11.0") library("retrofit-jackson", "com.squareup.retrofit2:converter-jackson:2.11.0") library("rx", "io.reactivex.rxjava3:rxjava:3.1.9") - library("logback", "ch.qos.logback:logback-classic:1.5.10") + library("logback", "ch.qos.logback:logback-classic:1.5.11") library("eventbus", "org.greenrobot:eventbus-java:3.3.1") library("netty-transport", "io.netty:netty-transport:4.1.114.Final") library("netty-codec", "io.netty:netty-codec:4.1.114.Final") From a40ee49b582935a853a605d6ca46eea743323598 Mon Sep 17 00:00:00 2001 From: tiagohm Date: Wed, 16 Oct 2024 10:32:45 -0300 Subject: [PATCH 11/15] [api]: Improve PixInsight Live Stacker --- .../livestacker/PixInsightLiveStacker.kt | 87 +++++++++---------- .../pixinsight/stacker/PixInsightStacker.kt | 20 +---- .../test/kotlin/PixInsightLiveStackerTest.kt | 7 +- .../main/kotlin/nebulosa/stacker/Stacker.kt | 2 - 4 files changed, 49 insertions(+), 67 deletions(-) diff --git a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/livestacker/PixInsightLiveStacker.kt b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/livestacker/PixInsightLiveStacker.kt index 737851740..ee6f07047 100644 --- a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/livestacker/PixInsightLiveStacker.kt +++ b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/livestacker/PixInsightLiveStacker.kt @@ -2,6 +2,7 @@ package nebulosa.pixinsight.livestacker import nebulosa.livestacker.LiveStacker import nebulosa.log.d +import nebulosa.log.dw import nebulosa.log.loggerFor import nebulosa.pixinsight.script.PixInsightIsRunning import nebulosa.pixinsight.script.PixInsightScript @@ -10,7 +11,10 @@ import nebulosa.pixinsight.script.PixInsightStartup import nebulosa.pixinsight.stacker.PixInsightStacker import java.nio.file.Path import java.util.concurrent.atomic.AtomicBoolean +import kotlin.io.path.Path +import kotlin.io.path.copyTo import kotlin.io.path.deleteIfExists +import kotlin.io.path.extension data class PixInsightLiveStacker( private val runner: PixInsightScriptRunner, @@ -24,10 +28,10 @@ data class PixInsightLiveStacker( private val running = AtomicBoolean() private val stacking = AtomicBoolean() - private val referencePath = Path.of("$workingDirectory", "reference.xisf") private val calibratedPath = Path.of("$workingDirectory", "calibrated.xisf") private val alignedPath = Path.of("$workingDirectory", "aligned.xisf") + @Volatile private var referencePath: Path? = null @Volatile private var stackCount = 0 override val stacker = PixInsightStacker(runner, workingDirectory, slot) @@ -38,12 +42,10 @@ data class PixInsightLiveStacker( override val isStacking get() = stacking.get() - override var stackedPath: Path? = null - private set + override val stackedPath: Path = Path.of("$workingDirectory", "stacked.xisf") - @Synchronized override fun start() { - if (!running.get()) { + if (running.compareAndSet(false, true)) { if (!PixInsightIsRunning(slot).use { it.runSync(runner).success }) { try { check(PixInsightStartup(slot).use { it.runSync(runner).success }) @@ -53,65 +55,58 @@ data class PixInsightLiveStacker( } stackCount = 0 - running.set(true) } } @Synchronized override fun add(path: Path, referencePath: Path?): Path? { - var targetPath = path + try { + if (running.get()) { + stacking.set(true) - return if (running.get()) { - stacking.set(true) + var targetPath = path - if (stacker.calibrate(targetPath, calibratedPath, darkPath, flatPath, biasPath)) { - LOG.d("live stacking calibrated. count={}, target={}, output={}", stackCount, targetPath, calibratedPath) - targetPath = calibratedPath - } + if (stacker.calibrate(targetPath, calibratedPath, darkPath, flatPath, biasPath)) { + LOG.d("live stacking calibrated. count={}, target={}, output={}", stackCount, targetPath, calibratedPath) + targetPath = calibratedPath + } - // TODO: Debayer, Resample? + // TODO: Debayer, Resample? - if (stackCount > 0) { - if (stacker.align(referencePath ?: this.referencePath, targetPath, alignedPath)) { - LOG.d("live stacking aligned. count={}, target={}, output={}", stackCount, targetPath, alignedPath) - targetPath = alignedPath + if (stackCount > 0) { + if (stacker.align(this.referencePath!!, targetPath, alignedPath)) { + LOG.d("live stacking aligned. count={}, target={}, output={}", stackCount, targetPath, alignedPath) + targetPath = alignedPath - if (stacker.integrate(stackCount, stackedPath!!, targetPath, stackedPath!!)) { - LOG.d("live stacking integrated. count={}, target={}, output={}", stackCount, targetPath, stackedPath) + if (stacker.integrate(stackCount, stackedPath, targetPath, stackedPath)) { + LOG.d("live stacking integrated. count={}, target={}, output={}", stackCount, targetPath, stackedPath) + stackCount++ + } } - - stackCount++ - } - } else { - var saved = if (referencePath != null) stacker.saveAs(referencePath, this.referencePath) - else stacker.saveAs(targetPath, this.referencePath) - - if (saved) { - with(Path.of("$workingDirectory", "stacked.fits")) { - saved = if (referencePath != null) { - stacker.align(referencePath, targetPath, alignedPath) - stacker.saveAs(alignedPath, this) - } else { - stacker.saveAs(targetPath, this) + } else { + if (this.referencePath == null) { + this.referencePath = with(referencePath ?: targetPath) { + Path("$workingDirectory", "reference.${extension}").also { copyTo(it, true) } } + } - if (saved) { - stackCount = 1 - stackedPath = this - } + if (!stacker.align(this.referencePath!!, targetPath, stackedPath)) { + LOG.dw("alignment failed. reference={}, target={}", this.referencePath, targetPath) + return null } + + stackCount = 1 } - } + return stackedPath + } + } finally { stacking.set(false) - - stackedPath - } else { - targetPath } + + return null } - @Synchronized override fun stop() { running.set(false) stackCount = 0 @@ -120,10 +115,10 @@ data class PixInsightLiveStacker( override fun close() { stop() - referencePath.deleteIfExists() + referencePath?.deleteIfExists() calibratedPath.deleteIfExists() alignedPath.deleteIfExists() - stackedPath?.deleteIfExists() + stackedPath.deleteIfExists() } companion object { diff --git a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/stacker/PixInsightStacker.kt b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/stacker/PixInsightStacker.kt index f338166ae..77a94272e 100644 --- a/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/stacker/PixInsightStacker.kt +++ b/nebulosa-pixinsight/src/main/kotlin/nebulosa/pixinsight/stacker/PixInsightStacker.kt @@ -3,6 +3,7 @@ package nebulosa.pixinsight.stacker import nebulosa.pixinsight.script.* import nebulosa.stacker.Stacker import java.nio.file.Path +import kotlin.io.path.copyTo import kotlin.io.path.deleteIfExists data class PixInsightStacker( @@ -16,14 +17,14 @@ data class PixInsightStacker( darkPath: Path?, flatPath: Path?, biasPath: Path?, ) = if (darkPath != null || flatPath != null || biasPath != null) { PixInsightCalibrate(slot, workingDirectory, targetPath, darkPath, flatPath, if (darkPath == null) biasPath else null) - .use { calibrate -> calibrate.runSync(runner).outputImage.saveAsAndDeleteIfExists(outputPath) } + .use { calibrate -> calibrate.runSync(runner).outputImage?.also { it.copyTo(outputPath, true); it.deleteIfExists() } != null } } else { false } override fun align(referencePath: Path, targetPath: Path, outputPath: Path): Boolean { return PixInsightAlign(slot, workingDirectory, referencePath, targetPath) - .use { align -> align.runSync(runner).outputImage.saveAsAndDeleteIfExists(outputPath) } + .use { align -> align.runSync(runner).outputImage?.also { it.copyTo(outputPath, true); it.deleteIfExists() } != null } } override fun integrate(stackCount: Int, stackedPath: Path, targetPath: Path, outputPath: Path): Boolean { @@ -48,19 +49,4 @@ data class PixInsightStacker( .use { it.runSync(runner).outputImage != null } } } - - override fun saveAs(inputPath: Path, outputPath: Path): Boolean { - return PixInsightFileFormatConversion(slot, inputPath, outputPath) - .use { it.runSync(runner).outputImage != null } - } - - private fun Path?.saveAsAndDeleteIfExists(outputPath: Path): Boolean { - return if (this == null) { - false - } else try { - saveAs(this, outputPath) - } finally { - deleteIfExists() - } - } } diff --git a/nebulosa-pixinsight/src/test/kotlin/PixInsightLiveStackerTest.kt b/nebulosa-pixinsight/src/test/kotlin/PixInsightLiveStackerTest.kt index 15d08e604..a6c5dd30d 100644 --- a/nebulosa-pixinsight/src/test/kotlin/PixInsightLiveStackerTest.kt +++ b/nebulosa-pixinsight/src/test/kotlin/PixInsightLiveStackerTest.kt @@ -16,7 +16,10 @@ import java.nio.file.Path @NonGitHubOnly class PixInsightLiveStackerTest : AbstractTest() { - private val files = listOf(STACKING_LIGHT_MONO_01_FITS, STACKING_LIGHT_MONO_02_FITS, STACKING_LIGHT_MONO_03_FITS, STACKING_LIGHT_MONO_04_FITS, STACKING_LIGHT_MONO_05_FITS, STACKING_LIGHT_MONO_06_FITS, STACKING_LIGHT_MONO_07_FITS, STACKING_LIGHT_MONO_08_FITS) + private val files = listOf( + STACKING_LIGHT_MONO_01_FITS, STACKING_LIGHT_MONO_02_FITS, STACKING_LIGHT_MONO_03_FITS, STACKING_LIGHT_MONO_04_FITS, + STACKING_LIGHT_MONO_05_FITS, STACKING_LIGHT_MONO_06_FITS, STACKING_LIGHT_MONO_07_FITS, STACKING_LIGHT_MONO_08_FITS, + ) @Test fun stack() { @@ -34,7 +37,7 @@ class PixInsightLiveStackerTest : AbstractTest() { } outputPath.shouldNotBeNull().openAsImage().transform(AutoScreenTransformFunction) - .save("pi-live-stacked").second shouldBe "a107143dff3d43c4b56c872da869f89b" + .save("pi-live-stacked").second shouldBe "31d4773fbe75e7f38307a4a62127ad49" } stacker.isRunning.shouldBeFalse() diff --git a/nebulosa-stacker/src/main/kotlin/nebulosa/stacker/Stacker.kt b/nebulosa-stacker/src/main/kotlin/nebulosa/stacker/Stacker.kt index 4a3b8fb4d..28b97db6c 100644 --- a/nebulosa-stacker/src/main/kotlin/nebulosa/stacker/Stacker.kt +++ b/nebulosa-stacker/src/main/kotlin/nebulosa/stacker/Stacker.kt @@ -13,6 +13,4 @@ interface Stacker { fun combineLRGB(outputPath: Path, luminancePath: Path? = null, redPath: Path? = null, greenPath: Path? = null, bluePath: Path? = null): Boolean fun combineLuminance(outputPath: Path, luminancePath: Path, targetPath: Path, mono: Boolean): Boolean - - fun saveAs(inputPath: Path, outputPath: Path): Boolean } From a0d5056dce5297965201302e1755758b1928864f Mon Sep 17 00:00:00 2001 From: tiagohm Date: Wed, 16 Oct 2024 10:59:28 -0300 Subject: [PATCH 12/15] [api]: Save live stacking image on app directory. Fix #573 --- .../nebulosa/api/cameras/CameraCaptureExecutor.kt | 7 +++++-- .../api/cameras/CameraLiveStackingManager.kt | 14 +++++++++++--- api/src/main/kotlin/nebulosa/api/inject/Inject.kt | 2 ++ .../nebulosa/api/sequencer/SequencerExecutor.kt | 10 ++++++++-- .../kotlin/nebulosa/api/sequencer/SequencerJob.kt | 7 +++---- 5 files changed, 29 insertions(+), 11 deletions(-) diff --git a/api/src/main/kotlin/nebulosa/api/cameras/CameraCaptureExecutor.kt b/api/src/main/kotlin/nebulosa/api/cameras/CameraCaptureExecutor.kt index d52ca6ff0..14ab6e4c6 100644 --- a/api/src/main/kotlin/nebulosa/api/cameras/CameraCaptureExecutor.kt +++ b/api/src/main/kotlin/nebulosa/api/cameras/CameraCaptureExecutor.kt @@ -1,6 +1,7 @@ package nebulosa.api.cameras import nebulosa.api.calibration.CalibrationFrameService +import nebulosa.api.inject.Named import nebulosa.api.message.MessageService import nebulosa.api.wheels.WheelEventAware import nebulosa.guiding.Guider @@ -14,6 +15,8 @@ import nebulosa.indi.device.rotator.Rotator import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode +import org.koin.core.component.KoinComponent +import org.koin.core.component.get import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.Executor import java.util.concurrent.ExecutorService @@ -25,7 +28,7 @@ class CameraCaptureExecutor( private val guider: Guider, private val executorService: ExecutorService, eventBus: EventBus, -) : Consumer, CameraEventAware, WheelEventAware, Executor by executorService { +) : Consumer, CameraEventAware, WheelEventAware, KoinComponent, Executor by executorService { private val jobs = ConcurrentHashMap.newKeySet(2) @@ -55,7 +58,7 @@ class CameraCaptureExecutor( check(camera.connected) { "${camera.name} Camera is not connected" } check(jobs.none { it.camera === camera }) { "${camera.name} Camera Capture is already in progress" } - val liveStackingManager = CameraLiveStackingManager(calibrationFrameService) + val liveStackingManager = CameraLiveStackingManager(get(Named.liveStackingDir), calibrationFrameService) with(CameraCaptureJob(this, camera, request, guider, liveStackingManager, mount, wheel, focuser, rotator)) { val completable = runAsync(executorService) diff --git a/api/src/main/kotlin/nebulosa/api/cameras/CameraLiveStackingManager.kt b/api/src/main/kotlin/nebulosa/api/cameras/CameraLiveStackingManager.kt index 02c311d37..b2191fe90 100644 --- a/api/src/main/kotlin/nebulosa/api/cameras/CameraLiveStackingManager.kt +++ b/api/src/main/kotlin/nebulosa/api/cameras/CameraLiveStackingManager.kt @@ -17,6 +17,7 @@ import java.util.* import kotlin.io.path.* data class CameraLiveStackingManager( + private val liveStackingDir: Path, private val calibrationFrameProvider: CalibrationFrameProvider? = null, ) : AutoCloseable { @@ -24,6 +25,7 @@ data class CameraLiveStackingManager( private val workingDirectories = HashSet() @Volatile private var referencePath: Path? = null + @Volatile private var stackedPath: Path? = null @Synchronized fun start(request: CameraStartCaptureRequest, path: Path): Boolean { @@ -83,7 +85,13 @@ data class CameraLiveStackingManager( } } - return stackedPath ?: liveStacker.stackedPath + if (stackedPath != null && this.stackedPath == null) { + this.stackedPath = Path("$liveStackingDir", "${System.currentTimeMillis()}.${stackedPath.extension}") + } + + this.stackedPath?.also { stackedPath?.copyTo(it, true) } + + return this.stackedPath } fun stop(request: CameraStartCaptureRequest) { @@ -147,7 +155,7 @@ data class CameraLiveStackingManager( @JvmStatic private val LOG = loggerFor() - private inline val Path?.isCalibrationFrame - get() = this != null && exists() && isRegularFile() && (isFits() || isXisf()) + private inline val Path.isCalibrationFrame + get() = exists() && isRegularFile() && (isFits() || isXisf()) } } diff --git a/api/src/main/kotlin/nebulosa/api/inject/Inject.kt b/api/src/main/kotlin/nebulosa/api/inject/Inject.kt index adee669fb..f66959c1b 100644 --- a/api/src/main/kotlin/nebulosa/api/inject/Inject.kt +++ b/api/src/main/kotlin/nebulosa/api/inject/Inject.kt @@ -116,6 +116,7 @@ object Named { val sequencesDir = named("sequencesDir") val cacheDir = named("cacheDir") val libsDir = named("libsDir") + val liveStackingDir = named("liveStackingDir") val defaultHttpClient = named("defaultHttpClient") val alpacaHttpClient = named("alpacaHttpClient") val mainBoxStore = named("mainBoxStore") @@ -154,6 +155,7 @@ fun pathModule(root: Path = Path(requireNotNull(System.getProperty(APP_DIR_KEY)) single(Named.sequencesDir) { Path("$root", "sequences").createDirectories() } single(Named.cacheDir) { Path("$root", "cache").createDirectories() } single(Named.libsDir) { Path("$root", "libs").createDirectories() } + single(Named.liveStackingDir) { Path("$root", "live-stacking").createDirectories() } } // CORE diff --git a/api/src/main/kotlin/nebulosa/api/sequencer/SequencerExecutor.kt b/api/src/main/kotlin/nebulosa/api/sequencer/SequencerExecutor.kt index c9ab39033..306ba49b8 100644 --- a/api/src/main/kotlin/nebulosa/api/sequencer/SequencerExecutor.kt +++ b/api/src/main/kotlin/nebulosa/api/sequencer/SequencerExecutor.kt @@ -2,7 +2,9 @@ package nebulosa.api.sequencer import nebulosa.api.calibration.CalibrationFrameService import nebulosa.api.cameras.CameraEventAware +import nebulosa.api.cameras.CameraLiveStackingManager import nebulosa.api.focusers.FocuserEventAware +import nebulosa.api.inject.Named import nebulosa.api.message.MessageEvent import nebulosa.api.message.MessageService import nebulosa.api.rotators.RotatorEventAware @@ -20,6 +22,8 @@ import nebulosa.indi.device.rotator.RotatorEvent import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode +import org.koin.core.component.KoinComponent +import org.koin.core.component.get import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.Executor import java.util.concurrent.ExecutorService @@ -31,7 +35,7 @@ class SequencerExecutor( private val executorService: ExecutorService, private val calibrationFrameService: CalibrationFrameService, eventBus: EventBus, -) : Consumer, CameraEventAware, WheelEventAware, FocuserEventAware, RotatorEventAware, Executor by executorService { +) : Consumer, CameraEventAware, WheelEventAware, FocuserEventAware, RotatorEventAware, KoinComponent, Executor by executorService { private val jobs = ConcurrentHashMap.newKeySet(1) @@ -82,7 +86,9 @@ class SequencerExecutor( check(jobs.none { it.rotator === rotator }) { "${camera.name} Sequencer Job is already in progress" } } - with(SequencerJob(this, camera, request, guider, mount, wheel, focuser, rotator, calibrationFrameService)) { + val liveStackingManager = CameraLiveStackingManager(get(Named.liveStackingDir), calibrationFrameService) + + with(SequencerJob(this, camera, request, guider, mount, wheel, focuser, rotator, liveStackingManager)) { val completable = runAsync(executorService) jobs.add(this) completable.whenComplete { _, _ -> jobs.remove(this) } diff --git a/api/src/main/kotlin/nebulosa/api/sequencer/SequencerJob.kt b/api/src/main/kotlin/nebulosa/api/sequencer/SequencerJob.kt index 249bd8bc5..452bb0ca9 100644 --- a/api/src/main/kotlin/nebulosa/api/sequencer/SequencerJob.kt +++ b/api/src/main/kotlin/nebulosa/api/sequencer/SequencerJob.kt @@ -39,13 +39,12 @@ data class SequencerJob( @JvmField val wheel: FilterWheel? = null, @JvmField val focuser: Focuser? = null, @JvmField val rotator: Rotator? = null, - private val calibrationFrameProvider: CalibrationFrameProvider? = null, + private val liveStackingManager: CameraLiveStackingManager? = null, ) : AbstractJob(), CameraEventAware, WheelEventAware, FocuserEventAware, RotatorEventAware { private val sequences = plan.sequences.filter { it.enabled } private val initialDelayTask = DelayTask(this, plan.initialDelay) private val waitForSettleTask = WaitForSettleTask(this, guider) - private val liveStackingManager = CameraLiveStackingManager(calibrationFrameProvider) private val cameraCaptureEvents = Array(plan.sequences.size + 1) { CameraCaptureEvent(camera, exposureAmount = plan.sequences.getOrNull(it - 1)?.exposureAmount ?: 0) } @@ -173,7 +172,7 @@ data class SequencerJob( } private fun addFrameToLiveStacker(request: CameraStartCaptureRequest, path: Path?): Path? { - return if (path != null && liveStackingManager.start(request, path)) { + return if (path != null && liveStackingManager != null && liveStackingManager.start(request, path)) { try { status.capture.state = CameraCaptureState.STACKING status.send() @@ -282,7 +281,7 @@ data class SequencerJob( } override fun afterFinish() { - liveStackingManager.close() + liveStackingManager?.close() status.state = SequencerState.IDLE status.send() From a74b894ffeacdf477995f316ed5ca8a2dac493fc Mon Sep 17 00:00:00 2001 From: tiagohm Date: Wed, 16 Oct 2024 11:15:58 -0300 Subject: [PATCH 13/15] [api][desktop]: Add option to debayer/undebayer an image. Fix #573 --- .../main/kotlin/nebulosa/api/image/ImageInfo.kt | 2 ++ .../kotlin/nebulosa/api/image/ImageService.kt | 2 +- desktop/src/app/image/image.component.ts | 15 +++++++++++++++ desktop/src/shared/types/image.types.ts | 3 +++ 4 files changed, 21 insertions(+), 1 deletion(-) diff --git a/api/src/main/kotlin/nebulosa/api/image/ImageInfo.kt b/api/src/main/kotlin/nebulosa/api/image/ImageInfo.kt index 576502787..b94dd882b 100644 --- a/api/src/main/kotlin/nebulosa/api/image/ImageInfo.kt +++ b/api/src/main/kotlin/nebulosa/api/image/ImageInfo.kt @@ -6,6 +6,7 @@ import nebulosa.api.converters.angle.DeclinationSerializer import nebulosa.api.converters.angle.RightAscensionSerializer import nebulosa.fits.Bitpix import nebulosa.image.algorithms.computation.Statistics +import nebulosa.image.algorithms.transformation.CfaPattern import nebulosa.indi.device.camera.Camera import java.nio.file.Path @@ -14,6 +15,7 @@ data class ImageInfo( @JvmField val width: Int, @JvmField val height: Int, @JvmField val mono: Boolean, + @JvmField val bayer: CfaPattern?, @JvmField val stretch: ImageTransformation.Stretch, @field:JsonSerialize(using = RightAscensionSerializer::class) @JvmField val rightAscension: Double? = null, @field:JsonSerialize(using = DeclinationSerializer::class) @JvmField val declination: Double? = null, diff --git a/api/src/main/kotlin/nebulosa/api/image/ImageService.kt b/api/src/main/kotlin/nebulosa/api/image/ImageService.kt index 20e5640f5..f74629365 100644 --- a/api/src/main/kotlin/nebulosa/api/image/ImageService.kt +++ b/api/src/main/kotlin/nebulosa/api/image/ImageService.kt @@ -91,7 +91,7 @@ class ImageService( val info = ImageInfo( path, - transformedImage.width, transformedImage.height, transformedImage.mono, + transformedImage.width, transformedImage.height, transformedImage.mono, CfaPattern.from(image.header), transformation.stretch.copy( shadow = (stretchParameters!!.shadow * 65536f).roundToInt(), highlight = (stretchParameters.highlight * 65536f).roundToInt(), diff --git a/desktop/src/app/image/image.component.ts b/desktop/src/app/image/image.component.ts index cd33d12fd..ee5544704 100644 --- a/desktop/src/app/image/image.component.ts +++ b/desktop/src/app/image/image.component.ts @@ -143,6 +143,18 @@ export class ImageComponent implements AfterViewInit, OnDestroy { }, } + private readonly debayerMenuItem: MenuItem = { + label: 'Debayer', + icon: 'mdi mdi-collage', + selected: false, + command: () => { + this.transformation.debayer = !this.transformation.debayer + this.debayerMenuItem.selected = this.transformation.debayer + this.savePreference() + return this.loadImage() + }, + } + private readonly horizontalMirrorMenuItem: MenuItem = { label: 'Horizontal mirror', icon: 'mdi mdi-flip-horizontal', @@ -300,6 +312,7 @@ export class ImageComponent implements AfterViewInit, OnDestroy { this.stretchMenuItem, this.autoStretchMenuItem, this.scnrMenuItem, + this.debayerMenuItem, this.horizontalMirrorMenuItem, this.verticalMirrorMenuItem, this.invertMenuItem, @@ -855,6 +868,7 @@ export class ImageComponent implements AfterViewInit, OnDestroy { this.imageInfo = info this.scnrMenuItem.disabled = info.mono + this.debayerMenuItem.disabled = !info.bayer if (info.rightAscension) this.solver.request.centerRA = info.rightAscension if (info.declination) this.solver.request.centerDEC = info.declination @@ -1351,6 +1365,7 @@ export class ImageComponent implements AfterViewInit, OnDestroy { this.fov.fovs = this.preference.fovs this.autoStretchMenuItem.selected = this.transformation.stretch.auto + this.debayerMenuItem.selected = this.preference.transformation.debayer this.invertMenuItem.selected = this.transformation.invert this.horizontalMirrorMenuItem.selected = this.transformation.mirrorHorizontal this.verticalMirrorMenuItem.selected = this.transformation.mirrorVertical diff --git a/desktop/src/shared/types/image.types.ts b/desktop/src/shared/types/image.types.ts index 3e646972f..c629b098f 100644 --- a/desktop/src/shared/types/image.types.ts +++ b/desktop/src/shared/types/image.types.ts @@ -22,6 +22,8 @@ export type ImageCalibrationSource = 'CAMERA' | 'MENU' export type ImageFilterType = 'LUMINANCE' | 'RED' | 'GREEN' | 'BLUE' | 'MONO' | 'RGB' | 'NONE' +export type BayerPattern = 'RGGB' | 'BGGR' | 'GBRG' | 'GRBG' | 'GRGB' | 'GBGR' | 'RGBG' | 'BGRG' + export type ImageMousePosition = Point export interface Image { @@ -58,6 +60,7 @@ export interface ImageInfo { width: number height: number mono: boolean + bayer?: BayerPattern stretch: ImageStretch rightAscension?: Angle declination?: Angle From ff620efb552dc0af1ae7d3c4b097b76278ad6394 Mon Sep 17 00:00:00 2001 From: tiagohm Date: Wed, 16 Oct 2024 11:39:34 -0300 Subject: [PATCH 14/15] [api]: Fix XISF test files --- nebulosa-test/src/main/kotlin/nebulosa/test/Files.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nebulosa-test/src/main/kotlin/nebulosa/test/Files.kt b/nebulosa-test/src/main/kotlin/nebulosa/test/Files.kt index 0d1f44248..f83f8bbbf 100644 --- a/nebulosa-test/src/main/kotlin/nebulosa/test/Files.kt +++ b/nebulosa-test/src/main/kotlin/nebulosa/test/Files.kt @@ -7,7 +7,7 @@ import nebulosa.math.hours const val ASTROPY_PHOTOMETRY_URL = "https://www.astropy.org/astropy-data/photometry" const val GITHUB_FITS_URL = "https://github.com/tiagohm/nebulosa.data/raw/main/fits" -const val GITHUB_XISF_URL = "https://github.com/tiagohm/nebulosa.data/raw/main/test/xisf" +const val GITHUB_XISF_URL = "https://github.com/tiagohm/nebulosa.data/raw/main/xisf" val M82_MONO_8_LZ4_XISF by lazy { download("$GITHUB_XISF_URL/M82.Mono.8.LZ4.xisf") } val M82_MONO_8_LZ4_HC_XISF by lazy { download("$GITHUB_XISF_URL/M82.Mono.8.LZ4-HC.xisf") } From dc14a18069d9942ad32798db08c0b224a89bc49b Mon Sep 17 00:00:00 2001 From: tiagohm Date: Wed, 16 Oct 2024 11:46:31 -0300 Subject: [PATCH 15/15] [api]: Fix WCS lib files --- .../main/kotlin/nebulosa/api/atlas/LibWCSDownloadTask.kt | 6 +++--- nebulosa-wcs/src/test/kotlin/LibWCSTest.kt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/api/src/main/kotlin/nebulosa/api/atlas/LibWCSDownloadTask.kt b/api/src/main/kotlin/nebulosa/api/atlas/LibWCSDownloadTask.kt index 59ad34da2..1086a6f24 100644 --- a/api/src/main/kotlin/nebulosa/api/atlas/LibWCSDownloadTask.kt +++ b/api/src/main/kotlin/nebulosa/api/atlas/LibWCSDownloadTask.kt @@ -57,14 +57,14 @@ class LibWCSDownloadTask( companion object { - const val VERSION_URL = "https://raw.githubusercontent.com/tiagohm/nebulosa.data/main/wcs/VERSION.txt" + const val VERSION_URL = "https://raw.githubusercontent.com/tiagohm/nebulosa.data/main/libs/wcs/VERSION.txt" const val VERSION_KEY = "LIBWCS.VERSION" @JvmStatic private val LOG = loggerFor() @JvmStatic private val LIBRARY_URLS = mapOf( - "linux-x86-64" to "https://raw.githubusercontent.com/tiagohm/nebulosa.data/main/wcs/linux-x86-64/libwcs.so", - "win32-x86-64" to "https://raw.githubusercontent.com/tiagohm/nebulosa.data/main/wcs/win32-x86-64/libwcs.dll", + "linux-x86-64" to "https://raw.githubusercontent.com/tiagohm/nebulosa.data/main/libs/wcs/linux-x86-64/libwcs.so", + "win32-x86-64" to "https://raw.githubusercontent.com/tiagohm/nebulosa.data/main/libs/wcs/win32-x86-64/libwcs.dll", ) } } diff --git a/nebulosa-wcs/src/test/kotlin/LibWCSTest.kt b/nebulosa-wcs/src/test/kotlin/LibWCSTest.kt index b71772deb..0aad1fb71 100644 --- a/nebulosa-wcs/src/test/kotlin/LibWCSTest.kt +++ b/nebulosa-wcs/src/test/kotlin/LibWCSTest.kt @@ -140,7 +140,7 @@ class LibWCSTest { @BeforeAll @JvmStatic fun loadLibWCS() { - val libPath = download("https://github.com/tiagohm/nebulosa.data/raw/main/wcs/linux-x86-64/libwcs.so") + val libPath = download("https://github.com/tiagohm/nebulosa.data/raw/main/libs/wcs/linux-x86-64/libwcs.so") System.setProperty(LibWCS.PATH, "$libPath") }