diff --git a/.DS_Store b/.DS_Store index fd6d5ac..989ca30 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..301f61d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +p.py \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..58d4582 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,3 @@ +{ + "printWidth":120 +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 6581d96..fc4aa24 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,20 +1,6 @@ { - "files.exclude": { - "**/.git": false, - "**/.svn": false, - "**/.hg": false, - "**/CVS": false, - "**/__pycache__": false, - "*.DS_Store": false, - "*.DS_Store.*": false, - "*.git": false, - "*.git.*": false, - "*.vscode": false, - "*.vscode.*": false, - "*server.*": false, - "*ui": false, - "*ui.*": false - }, - "cSpell.words": ["antd", "Collada", "Contolls", "fileupload", "lerp", "metadatas", "undici", "uuidv"], - "editor.rulers": [100] + "cSpell.words": [ + "Ведите", + "typedataset" + ] } diff --git a/README.md b/README.md index 421cd3c..13118e8 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,21 @@ -# Веб-сервис для отладки Robossembler Framework +# Веб-сервис Robossembler -Необходимость разработки сервиса хранения и просмотра пакетов обусловлена тем, что для корректной работы фреймворка «Робосборщик» необходима согласованная работа разнообразных программный модулей – результаты работы одних модулей должны передаваться через стандартизированные интерфейсы другим модулям. Как правило, результатами работы программных модулей являются исполняемые файлы программ, файлы 3D-моделей в форматах STL, FBX, Collada/DAE, OBJ, PLY и т.п., конфигурационные файлы в форматах yaml, json, ini, txt, веса нейронных сетей, описания роботов/сцен в форматах URDF, SDF, MJCF и т.д.. При этом необходимо соблюсти условие соответствия данных файлов/документов друг другу, иметь возможность формировать и отслеживать цепочки вычислений (конвейер, pipeline), которые их порождают. +Сервис для сопровождении процесса/жизненного цикла разработки программ сборки изделий роботами и интеграции программных модулей [Фреймворка Робосборщик](https://gitlab.com/robossembler/framework). -Данный веб-сервис выполняет следующие функции: +## Мотивация -- Создание процессов (process) – команд, запускающих определённые вычисления -- Создание триггеров (trigger) – событий, запускающихся по завершении процесса -- Создание конвейеров вычислений (pipeline) – цепочек из процессов -- Создание проектов (project) – набора конвейеров для выполнения прикладных задач -- Хранение и просмотр артефактов, порождаемых процессами, а также отслеживание их жизненного цикла -- Запуск процессов/конвейеров и отслеживание их состояния +Для корректной работы фреймворка необходима согласованная работа разнообразных программный модулей – результаты работы одних модулей должны передаваться через стандартизированные интерфейсы другим модулям. Результатами работы программных модулей являются исполняемые файлы программ, файлы 3D-моделей в форматах STL, FBX, Collada/DAE, OBJ, PLY и т.п., конфигурационные файлы в форматах yaml, json, ini, txt, веса нейронных сетей, описания роботов/сцен в форматах URDF, SDF, MJCF и т.д. + +## Состав модулей сервиса + +Каждая фаза жизненного цикла имеет своё представление в виде страницы в веб-сервисе: + +1. Создание проекта сборки, загрузка CAD-проекта изделия - "Проекты", вкладки "Детали", "Сборки" +2. Подготовка и генерация датасета для навыков машинного зрения - Вкладка "Датасеты" +3. Конфигурация сцены - Scene Builder - Вкладка "Сцена" +4. Создание дерева поведения из навыков - Вкладка "Поведение" +5. Просмотр результатов симуляции - Вкладка "Симуляция" +6. Оценка производительности навыков Вкладка "Анализ" Веб-сервис написан на языке TypeScript для среды исполнения NodeJS. Для хранения артефактов используется база данных MongoDB. Исходный код проекта разработан в соответствии с концепцией «Чистой архитектуры», описанной Робертом Мартином в одноимённой книге. Данный подход позволяет систематизировать код, отделить бизнес-логику от остальной части приложения. @@ -19,11 +25,35 @@ - Node.js - MongoDB +- BlenderProc (для генерации датасетов) -## Сборка UI +## Клонирование проекта -- `cd ui && npm i && npm run build && npm run deploy` +```bash +git clone https://gitlab.com/robossembler/webservice +``` -# Запуск сервиса +## Настройка переменных окружения -- `cd server && npm run dev` +Для работы Генератора Датасетов нужно задать следующие переменные в окружении `bash` + +```bash +export PYTHON_BLENDER="путь_к_директории_с_файлами_из_rcg_pipeline" +export PYTHON_BLENDER_PROC="путь_к_генератору_датасетов_renderBOPdataset.py" +``` + +## Запуск сервера + +Из директории `server` в корне репозитория + +```bash +npm run dev +``` + +## Сборка и запуск UI + +Из директории `ui` в корне репозитория + +```bash +npm i && npm run build && npm run deploy +``` diff --git a/server/package-lock.json b/server/package-lock.json index d408418..1c50223 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -20,9 +20,9 @@ "md5": "^2.3.0", "mongoose": "^7.6.2", "mongoose-autopopulate": "^1.1.0", - "node-watch": "^0.7.4", - "nodemon": "^3.0.1", + "pm2": "^5.3.1", "reflect-metadata": "^0.1.13", + "rimraf": "^5.0.5", "socket.io": "^4.7.2", "socket.io-client": "^4.7.2", "spark-md5": "^3.0.2", @@ -43,6 +43,8 @@ "chai": "latest", "eslint": "^8.47.0", "mocha": "latest", + "node-watch": "^0.7.4", + "nodemon": "^3.0.1", "nyc": "latest", "source-map-support": "latest", "ts-node": "^10.9.1", @@ -719,6 +721,95 @@ "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", "dev": true }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -925,6 +1016,351 @@ "node": ">= 8" } }, + "node_modules/@opencensus/core": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/@opencensus/core/-/core-0.0.9.tgz", + "integrity": "sha512-31Q4VWtbzXpVUd2m9JS6HEaPjlKvNMOiF7lWKNmXF84yUcgfAFL5re7/hjDmdyQbOp32oGc+RFV78jXIldVz6Q==", + "dependencies": { + "continuation-local-storage": "^3.2.1", + "log-driver": "^1.2.7", + "semver": "^5.5.0", + "shimmer": "^1.2.0", + "uuid": "^3.2.1" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/@opencensus/core/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/@opencensus/core/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/@opencensus/propagation-b3": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/@opencensus/propagation-b3/-/propagation-b3-0.0.8.tgz", + "integrity": "sha512-PffXX2AL8Sh0VHQ52jJC4u3T0H6wDK6N/4bg7xh4ngMYOIi13aR1kzVvX1sVDBgfGwDOkMbl4c54Xm3tlPx/+A==", + "dependencies": { + "@opencensus/core": "^0.0.8", + "uuid": "^3.2.1" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/@opencensus/propagation-b3/node_modules/@opencensus/core": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/@opencensus/core/-/core-0.0.8.tgz", + "integrity": "sha512-yUFT59SFhGMYQgX0PhoTR0LBff2BEhPrD9io1jWfF/VDbakRfs6Pq60rjv0Z7iaTav5gQlttJCX2+VPxFWCuoQ==", + "dependencies": { + "continuation-local-storage": "^3.2.1", + "log-driver": "^1.2.7", + "semver": "^5.5.0", + "shimmer": "^1.2.0", + "uuid": "^3.2.1" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/@opencensus/propagation-b3/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/@opencensus/propagation-b3/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@pm2/agent": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@pm2/agent/-/agent-2.0.3.tgz", + "integrity": "sha512-xkqqCoTf5VsciMqN0vb9jthW7olVAi4KRFNddCc7ZkeJZ3i8QwZANr4NSH2H5DvseRFHq7MiPspRY/EWAFWWTg==", + "dependencies": { + "async": "~3.2.0", + "chalk": "~3.0.0", + "dayjs": "~1.8.24", + "debug": "~4.3.1", + "eventemitter2": "~5.0.1", + "fast-json-patch": "^3.0.0-1", + "fclone": "~1.0.11", + "nssocket": "0.6.0", + "pm2-axon": "~4.0.1", + "pm2-axon-rpc": "~0.7.0", + "proxy-agent": "~6.3.0", + "semver": "~7.5.0", + "ws": "~7.4.0" + } + }, + "node_modules/@pm2/agent/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@pm2/agent/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@pm2/agent/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@pm2/agent/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/@pm2/agent/node_modules/dayjs": { + "version": "1.8.36", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.36.tgz", + "integrity": "sha512-3VmRXEtw7RZKAf+4Tv1Ym9AGeo8r8+CjDi26x+7SYQil1UqtqdaokhzoEJohqlzt0m5kacJSDhJQkG/LWhpRBw==" + }, + "node_modules/@pm2/agent/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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@pm2/agent/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@pm2/agent/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@pm2/agent/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@pm2/agent/node_modules/ws": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/@pm2/agent/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/@pm2/io": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@pm2/io/-/io-5.0.2.tgz", + "integrity": "sha512-XAvrNoQPKOyO/jJyCu8jPhLzlyp35MEf7w/carHXmWKddPzeNOFSEpSEqMzPDawsvpxbE+i918cNN+MwgVsStA==", + "dependencies": { + "@opencensus/core": "0.0.9", + "@opencensus/propagation-b3": "0.0.8", + "async": "~2.6.1", + "debug": "~4.3.1", + "eventemitter2": "^6.3.1", + "require-in-the-middle": "^5.0.0", + "semver": "~7.5.4", + "shimmer": "^1.2.0", + "signal-exit": "^3.0.3", + "tslib": "1.9.3" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/@pm2/io/node_modules/async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/@pm2/io/node_modules/eventemitter2": { + "version": "6.4.9", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.9.tgz", + "integrity": "sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==" + }, + "node_modules/@pm2/io/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@pm2/io/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@pm2/io/node_modules/tslib": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==" + }, + "node_modules/@pm2/io/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/@pm2/js-api": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@pm2/js-api/-/js-api-0.8.0.tgz", + "integrity": "sha512-nmWzrA/BQZik3VBz+npRcNIu01kdBhWL0mxKmP1ciF/gTcujPTQqt027N9fc1pK9ERM8RipFhymw7RcmCyOEYA==", + "dependencies": { + "async": "^2.6.3", + "debug": "~4.3.1", + "eventemitter2": "^6.3.1", + "extrareqp2": "^1.0.0", + "ws": "^7.0.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@pm2/js-api/node_modules/async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/@pm2/js-api/node_modules/eventemitter2": { + "version": "6.4.9", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.9.tgz", + "integrity": "sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==" + }, + "node_modules/@pm2/js-api/node_modules/ws": { + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/@pm2/pm2-version-check": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@pm2/pm2-version-check/-/pm2-version-check-1.0.4.tgz", + "integrity": "sha512-SXsM27SGH3yTWKc2fKR4SYNxsmnvuBQ9dd6QHtEWmiZ/VqaOYPAIlS8+vMcn27YLtAEBGvNRSh3TPNvtjZgfqA==", + "dependencies": { + "debug": "^4.3.1" + } + }, "node_modules/@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", @@ -1002,6 +1438,11 @@ "testdeck-watch": "bin/watch" } }, + "node_modules/@tootallnate/quickjs-emscripten": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", + "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==" + }, "node_modules/@tsconfig/node10": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", @@ -1383,7 +1824,8 @@ "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==" + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true }, "node_modules/accepts": { "version": "1.3.8", @@ -1427,6 +1869,17 @@ "node": ">=0.4.0" } }, + "node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/aggregate-error": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", @@ -1456,11 +1909,23 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/amp": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/amp/-/amp-0.3.1.tgz", + "integrity": "sha512-OwIuC4yZaRogHKiuU5WlMR5Xk/jAcpPtawWL05Gj8Lvm2F6mwoJt4O/bHI+DHwG79vWd+8OFYM4/BzYqyRd3qw==" + }, + "node_modules/amp-message": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/amp-message/-/amp-message-0.1.2.tgz", + "integrity": "sha512-JqutcFwoU1+jhv7ArgW38bqrE+LQdcRv4NxNw0mp0JHQyB6tXesWRjtYKlDgHRY2o3JE5UTaBGUK8kSWUdxWUg==", + "dependencies": { + "amp": "0.3.1" + } + }, "node_modules/ansi-colors": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true, "engines": { "node": ">=6" } @@ -1546,6 +2011,47 @@ "node": ">=12" } }, + "node_modules/ast-types": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", + "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ast-types/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" + }, + "node_modules/async-listener": { + "version": "0.6.10", + "resolved": "https://registry.npmjs.org/async-listener/-/async-listener-0.6.10.tgz", + "integrity": "sha512-gpuo6xOyF4D5DE5WvyqZdPA3NGhiT6Qf07l7DCB0wwDEsLvDIbCr6j9S5aj5Ch96dLace5tXVzWBZkxU/c5ohw==", + "dependencies": { + "semver": "^5.3.0", + "shimmer": "^1.1.0" + }, + "engines": { + "node": "<=0.11.8 || >0.11.10" + } + }, + "node_modules/async-listener/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "bin": { + "semver": "bin/semver" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -1766,6 +2272,14 @@ "node": "^4.5.0 || >= 5.9" } }, + "node_modules/basic-ftp": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", + "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -1774,6 +2288,22 @@ "node": ">=8" } }, + "node_modules/blessed": { + "version": "0.1.81", + "resolved": "https://registry.npmjs.org/blessed/-/blessed-0.1.81.tgz", + "integrity": "sha512-LoF5gae+hlmfORcG1M5+5XZi4LBmvlXTzwJWzUlPryN/SJdSflZvROM2TwkT0GMpq7oqT48NRd4GS7BiVBc5OQ==", + "bin": { + "blessed": "bin/tput.js" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/bodec": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/bodec/-/bodec-0.1.0.tgz", + "integrity": "sha512-Ylo+MAo5BDUq1KA3f3R/MFhh+g8cnHmo8bz3YPGhI1znrMaf77ol1sfvYJzsw3nTE+Y2GryfDxBaR+AqpAkEHQ==" + }, "node_modules/body-parser": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", @@ -1814,7 +2344,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, "dependencies": { "balanced-match": "^1.0.0" } @@ -1879,8 +2408,7 @@ "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, "node_modules/builtin-modules": { "version": "1.1.1", @@ -2020,6 +2548,11 @@ "node": "*" } }, + "node_modules/charm": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/charm/-/charm-0.1.2.tgz", + "integrity": "sha512-syedaZ9cPe7r3hoQA9twWYKu5AIyCswN5+szkmPBe9ccdLrj4bYaCnLVPTLd2kgVRc7+zoX4tyPgRnFKCj5YjQ==" + }, "node_modules/check-error": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.0.0.tgz", @@ -2090,6 +2623,78 @@ "node": ">=6" } }, + "node_modules/cli-tableau": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/cli-tableau/-/cli-tableau-2.0.1.tgz", + "integrity": "sha512-he+WTicka9cl0Fg/y+YyxcN6/bfQ/1O3QmgxRXDhABKqLzvoOSM4fMzp39uMyLBulAFuywD2N7UaoQE7WaADxQ==", + "dependencies": { + "chalk": "3.0.0" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/cli-tableau/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cli-tableau/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-tableau/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/cli-tableau/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/cli-tableau/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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-tableau/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -2184,6 +2789,15 @@ "node": ">= 0.6" } }, + "node_modules/continuation-local-storage": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/continuation-local-storage/-/continuation-local-storage-3.2.1.tgz", + "integrity": "sha512-jx44cconVqkCEEyLSKWwkvUXwO561jXMa3LPjTPsm5QR22PA0/mhe33FT4Xb5y74JDvt/Cq+5lm8S8rskLv9ZA==", + "dependencies": { + "async-listener": "^0.6.0", + "emitter-listener": "^1.1.1" + } + }, "node_modules/convert-source-map": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", @@ -2227,6 +2841,11 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true }, + "node_modules/croner": { + "version": "4.1.97", + "resolved": "https://registry.npmjs.org/croner/-/croner-4.1.97.tgz", + "integrity": "sha512-/f6gpQuxDaqXu+1kwQYSckUglPaOrHdbIlBAu0YuW8/Cdb45XwXYNUBXg3r/9Mo6n540Kn/smKcZWko5x99KrQ==" + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -2248,6 +2867,24 @@ "node": "*" } }, + "node_modules/culvert": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/culvert/-/culvert-0.1.2.tgz", + "integrity": "sha512-yi1x3EAWKjQTreYWeSd98431AV+IEE0qoDyOoaHJ7KJ21gv6HtBXHVLX74opVSGqcR8/AbjJBHAHpcOy2bj5Gg==" + }, + "node_modules/data-uri-to-buffer": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", + "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", + "engines": { + "node": ">= 14" + } + }, + "node_modules/dayjs": { + "version": "1.11.10", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", + "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==" + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -2320,6 +2957,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/degenerator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", + "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", + "dependencies": { + "ast-types": "^0.13.4", + "escodegen": "^2.1.0", + "esprima": "^4.0.1" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -2394,6 +3044,11 @@ "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -2405,6 +3060,14 @@ "integrity": "sha512-66L3pLlWhTNVUhnmSA5+qDM3fwnXsM6KAqE36e2w4KN0g6pkEtlT5bs41FQtQwVwKnfhNBXiWRLPs30HSxd7Kw==", "dev": true }, + "node_modules/emitter-listener": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/emitter-listener/-/emitter-listener-1.1.2.tgz", + "integrity": "sha512-Bt1sBAGFHY9DKY+4/2cV6izcKJUf5T7/gkdmkxzX/qv9CcGH8xSwVRW5mtX03SWJtRTWSOpzCuWN9rBFYZepZQ==", + "dependencies": { + "shimmer": "^1.2.0" + } + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -2466,6 +3129,17 @@ "node": ">= 0.6" } }, + "node_modules/enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dependencies": { + "ansi-colors": "^4.1.1" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/es-define-property": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", @@ -2512,6 +3186,35 @@ "node": ">=0.8.0" } }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/escodegen/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/eslint": { "version": "8.56.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", @@ -2768,7 +3471,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" @@ -2805,7 +3507,6 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, "engines": { "node": ">=4.0" } @@ -2840,6 +3541,11 @@ "through": "~2.3.1" } }, + "node_modules/eventemitter2": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-5.0.1.tgz", + "integrity": "sha512-5EM1GHXycJBS6mauYAbVKT1cVs7POKWb2NXD4Vyt8dDqeZa7LaDK1/sjtL+Zb0lzTpSNil4596Dyu97hz37QLg==" + }, "node_modules/express": { "version": "4.18.2", "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", @@ -2905,6 +3611,14 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/extrareqp2": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/extrareqp2/-/extrareqp2-1.0.0.tgz", + "integrity": "sha512-Gum0g1QYb6wpPJCVypWP3bbIuaibcFiJcpuPM10YSXp/tzqi84x9PJageob+eN4xVRIOto4wjSGNLyMD54D2xA==", + "dependencies": { + "follow-redirects": "^1.14.0" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -2939,6 +3653,11 @@ "node": ">= 6" } }, + "node_modules/fast-json-patch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-3.1.1.tgz", + "integrity": "sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ==" + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -2960,6 +3679,11 @@ "reusify": "^1.0.4" } }, + "node_modules/fclone": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/fclone/-/fclone-1.0.11.tgz", + "integrity": "sha512-GDqVQezKzRABdeqflsgMr7ktzgF9CyS+p2oe0jJqUY6izSSbhPIQJDpoU4PtGcD7VPM9xh/dVrTu6z1nwgmEGw==" + }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -3077,6 +3801,63 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/flat-cache/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/flat-cache/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/flat-cache/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/flat-cache/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/flatted": { "version": "3.2.9", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", @@ -3169,11 +3950,23 @@ } ] }, + "node_modules/fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/fsevents": { "version": "2.3.3", @@ -3249,6 +4042,30 @@ "node": ">=8.0.0" } }, + "node_modules/get-uri": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.3.tgz", + "integrity": "sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==", + "dependencies": { + "basic-ftp": "^5.0.2", + "data-uri-to-buffer": "^6.0.2", + "debug": "^4.3.4", + "fs-extra": "^11.2.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/git-node-fs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/git-node-fs/-/git-node-fs-1.0.0.tgz", + "integrity": "sha512-bLQypt14llVXBg0S0u8q8HmU7g9p3ysH+NvVlae5vILuUvs759665HvmR5+wb04KjHyjFcDRxdYb4kyNnluMUQ==" + }, + "node_modules/git-sha1": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/git-sha1/-/git-sha1-0.1.2.tgz", + "integrity": "sha512-2e/nZezdVlyCopOCYHeW0onkbZg7xP1Ad6pndPy1rCygeRykefUS6r7oA5cJRGEFvseiaz5a/qUHFVX1dd6Isg==" + }, "node_modules/glob": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", @@ -3343,8 +4160,7 @@ "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, "node_modules/graphemer": { "version": "1.4.0", @@ -3367,6 +4183,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, "engines": { "node": ">=4" } @@ -3473,6 +4290,30 @@ "node": ">= 0.8" } }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz", + "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -3496,7 +4337,8 @@ "node_modules/ignore-by-default": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", - "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==" + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true }, "node_modules/import-fresh": { "version": "3.3.0", @@ -3536,7 +4378,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -3547,6 +4388,11 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, "node_modules/invariant": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", @@ -3595,7 +4441,6 @@ "version": "2.13.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", - "dev": true, "dependencies": { "hasown": "^2.0.0" }, @@ -3773,6 +4618,63 @@ "node": ">=8" } }, + "node_modules/istanbul-lib-processinfo/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/istanbul-lib-processinfo/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/istanbul-lib-processinfo/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/istanbul-lib-processinfo/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/istanbul-lib-processinfo/node_modules/uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", @@ -3868,6 +4770,34 @@ "node": ">=8" } }, + "node_modules/jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/js-git": { + "version": "0.7.8", + "resolved": "https://registry.npmjs.org/js-git/-/js-git-0.7.8.tgz", + "integrity": "sha512-+E5ZH/HeRnoc/LW0AmAyhU+mNcWBzAKE+30+IDMLSLbbK+Tdt02AdkOKq9u15rlJsDEGFqtgckc8ZM59LhhiUA==", + "dependencies": { + "bodec": "^0.1.0", + "culvert": "^0.1.2", + "git-sha1": "^0.1.2", + "pako": "^0.2.5" + } + }, "node_modules/js-tokens": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", @@ -3916,6 +4846,12 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "optional": true + }, "node_modules/json5": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", @@ -3924,6 +4860,17 @@ "json5": "lib/cli.js" } }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, "node_modules/kareem": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.5.1.tgz", @@ -3941,6 +4888,14 @@ "json-buffer": "3.0.1" } }, + "node_modules/lazy": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/lazy/-/lazy-1.0.11.tgz", + "integrity": "sha512-Y+CjUfLmIpoUCCRl0ub4smrYtGGr5AOa2AKOaWelGHOGz33X/Y/KizefGqbkwfz44+cnq/+9habclf8vOmu2LA==", + "engines": { + "node": ">=0.2.0" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -3996,6 +4951,14 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/log-driver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.7.tgz", + "integrity": "sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg==", + "engines": { + "node": ">=0.8.6" + } + }, "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -4244,7 +5207,6 @@ "version": "9.0.3", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", - "dev": true, "dependencies": { "brace-expansion": "^2.0.1" }, @@ -4263,6 +5225,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minipass": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", @@ -4413,6 +5383,11 @@ "node": ">=10" } }, + "node_modules/module-details-from-path": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz", + "integrity": "sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==" + }, "node_modules/mongodb": { "version": "5.9.1", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-5.9.1.tgz", @@ -4520,12 +5495,41 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/needle": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.4.0.tgz", + "integrity": "sha512-4Hnwzr3mi5L97hMYeNl8wRW/Onhy4nUKR/lVemJ8gJedxxUyBLm9kkrDColJvoSfwi0jCNhD+xCdOtiGDQiRZg==", + "dependencies": { + "debug": "^3.2.6", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + }, + "bin": { + "needle": "bin/needle" + }, + "engines": { + "node": ">= 4.4.x" + } + }, + "node_modules/needle/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -4534,6 +5538,14 @@ "node": ">= 0.6" } }, + "node_modules/netmask": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", + "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/node-cleanup": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/node-cleanup/-/node-cleanup-2.1.2.tgz", @@ -4561,6 +5573,7 @@ "version": "0.7.4", "resolved": "https://registry.npmjs.org/node-watch/-/node-watch-0.7.4.tgz", "integrity": "sha512-RinNxoz4W1cep1b928fuFhvAQ5ag/+1UlMDV7rbyGthBIgsiEouS4kvRayvvboxii4m8eolKOIBo3OjDqbc+uQ==", + "dev": true, "engines": { "node": ">=6" } @@ -4569,6 +5582,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.3.tgz", "integrity": "sha512-7jH/NXbFPxVaMwmBCC2B9F/V6X1VkEdNgx3iu9jji8WxWcvhMWkmhNWhI5077zknOnZnBzba9hZP6bCPJLSReQ==", + "dev": true, "dependencies": { "chokidar": "^3.5.2", "debug": "^4", @@ -4596,6 +5610,7 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -4605,6 +5620,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -4616,6 +5632,7 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, "dependencies": { "has-flag": "^3.0.0" }, @@ -4627,6 +5644,7 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", + "dev": true, "dependencies": { "abbrev": "1" }, @@ -4645,6 +5663,23 @@ "node": ">=0.10.0" } }, + "node_modules/nssocket": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/nssocket/-/nssocket-0.6.0.tgz", + "integrity": "sha512-a9GSOIql5IqgWJR3F/JXG4KpJTA3Z53Cj0MeMvGpglytB1nxE4PdFNC0jINe27CS7cGivoynwc054EzCcT3M3w==", + "dependencies": { + "eventemitter2": "~0.4.14", + "lazy": "~1.0.11" + }, + "engines": { + "node": ">= 0.10.x" + } + }, + "node_modules/nssocket/node_modules/eventemitter2": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz", + "integrity": "sha512-K7J4xq5xAD5jHsGM5ReWXRTFa3JRGofHiMcVgQ8PRwgWxzjHpMWCIzsmyf60+mh8KLsqYPcjUMa0AC4hd6lPyQ==" + }, "node_modules/nyc": { "version": "15.1.0", "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", @@ -4842,6 +5877,21 @@ "node": ">=8" } }, + "node_modules/nyc/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/nyc/node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -4940,7 +5990,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, "dependencies": { "wrappy": "1" } @@ -5029,6 +6078,36 @@ "node": ">=6" } }, + "node_modules/pac-proxy-agent": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.1.tgz", + "integrity": "sha512-ASV8yU4LLKBAjqIPMbrgtaKIvxQri/yh2OpI+S6hVa9JRkUI3Y3NPFbfngDtY7oFtSMD3w31Xns89mDa3Feo5A==", + "dependencies": { + "@tootallnate/quickjs-emscripten": "^0.23.0", + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "get-uri": "^6.0.1", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.2", + "pac-resolver": "^7.0.0", + "socks-proxy-agent": "^8.0.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-resolver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", + "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", + "dependencies": { + "degenerator": "^5.0.0", + "netmask": "^2.0.2" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/package-hash": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", @@ -5044,6 +6123,11 @@ "node": ">=8" } }, + "node_modules/pako": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -5092,8 +6176,30 @@ "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/path-scurry": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.2.tgz", + "integrity": "sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", + "engines": { + "node": "14 || >=16.14" + } }, "node_modules/path-to-regexp": { "version": "0.1.7", @@ -5143,6 +6249,17 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pidusage": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pidusage/-/pidusage-3.0.2.tgz", + "integrity": "sha512-g0VU+y08pKw5M8EZ2rIGiEBaB8wrQMjYGFfW2QVIfyT8V+fq8YFLkvlz4bz5ljvFDJYNFCWT3PWqcRr2FKO81w==", + "dependencies": { + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/pkg-dir": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", @@ -5207,6 +6324,217 @@ "node": ">=8" } }, + "node_modules/pm2": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/pm2/-/pm2-5.3.1.tgz", + "integrity": "sha512-DLVQHpSR1EegaTaRH3KbRXxpPVaqYwAp3uHSCtCsS++LSErvk07WSxuUnntFblBRqNU/w2KQyqs12mSq5wurkg==", + "dependencies": { + "@pm2/agent": "~2.0.0", + "@pm2/io": "~5.0.0", + "@pm2/js-api": "~0.8.0", + "@pm2/pm2-version-check": "latest", + "async": "~3.2.0", + "blessed": "0.1.81", + "chalk": "3.0.0", + "chokidar": "^3.5.3", + "cli-tableau": "^2.0.0", + "commander": "2.15.1", + "croner": "~4.1.92", + "dayjs": "~1.11.5", + "debug": "^4.3.1", + "enquirer": "2.3.6", + "eventemitter2": "5.0.1", + "fclone": "1.0.11", + "mkdirp": "1.0.4", + "needle": "2.4.0", + "pidusage": "~3.0", + "pm2-axon": "~4.0.1", + "pm2-axon-rpc": "~0.7.1", + "pm2-deploy": "~1.0.2", + "pm2-multimeter": "^0.1.2", + "promptly": "^2", + "semver": "^7.2", + "source-map-support": "0.5.21", + "sprintf-js": "1.1.2", + "vizion": "~2.2.1", + "yamljs": "0.3.0" + }, + "bin": { + "pm2": "bin/pm2", + "pm2-dev": "bin/pm2-dev", + "pm2-docker": "bin/pm2-docker", + "pm2-runtime": "bin/pm2-runtime" + }, + "engines": { + "node": ">=10.0.0" + }, + "optionalDependencies": { + "pm2-sysmonit": "^1.2.8" + } + }, + "node_modules/pm2-axon": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pm2-axon/-/pm2-axon-4.0.1.tgz", + "integrity": "sha512-kES/PeSLS8orT8dR5jMlNl+Yu4Ty3nbvZRmaAtROuVm9nYYGiaoXqqKQqQYzWQzMYWUKHMQTvBlirjE5GIIxqg==", + "dependencies": { + "amp": "~0.3.1", + "amp-message": "~0.1.1", + "debug": "^4.3.1", + "escape-string-regexp": "^4.0.0" + }, + "engines": { + "node": ">=5" + } + }, + "node_modules/pm2-axon-rpc": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/pm2-axon-rpc/-/pm2-axon-rpc-0.7.1.tgz", + "integrity": "sha512-FbLvW60w+vEyvMjP/xom2UPhUN/2bVpdtLfKJeYM3gwzYhoTEEChCOICfFzxkxuoEleOlnpjie+n1nue91bDQw==", + "dependencies": { + "debug": "^4.3.1" + }, + "engines": { + "node": ">=5" + } + }, + "node_modules/pm2-axon/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pm2-deploy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pm2-deploy/-/pm2-deploy-1.0.2.tgz", + "integrity": "sha512-YJx6RXKrVrWaphEYf++EdOOx9EH18vM8RSZN/P1Y+NokTKqYAca/ejXwVLyiEpNju4HPZEk3Y2uZouwMqUlcgg==", + "dependencies": { + "run-series": "^1.1.8", + "tv4": "^1.3.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pm2-multimeter": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/pm2-multimeter/-/pm2-multimeter-0.1.2.tgz", + "integrity": "sha512-S+wT6XfyKfd7SJIBqRgOctGxaBzUOmVQzTAS+cg04TsEUObJVreha7lvCfX8zzGVr871XwCSnHUU7DQQ5xEsfA==", + "dependencies": { + "charm": "~0.1.1" + } + }, + "node_modules/pm2-sysmonit": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/pm2-sysmonit/-/pm2-sysmonit-1.2.8.tgz", + "integrity": "sha512-ACOhlONEXdCTVwKieBIQLSi2tQZ8eKinhcr9JpZSUAL8Qy0ajIgRtsLxG/lwPOW3JEKqPyw/UaHmTWhUzpP4kA==", + "optional": true, + "dependencies": { + "async": "^3.2.0", + "debug": "^4.3.1", + "pidusage": "^2.0.21", + "systeminformation": "^5.7", + "tx2": "~1.0.4" + } + }, + "node_modules/pm2-sysmonit/node_modules/pidusage": { + "version": "2.0.21", + "resolved": "https://registry.npmjs.org/pidusage/-/pidusage-2.0.21.tgz", + "integrity": "sha512-cv3xAQos+pugVX+BfXpHsbyz/dLzX+lr44zNMsYiGxUw+kV5sgQCIcLd1z+0vq+KyC7dJ+/ts2PsfgWfSC3WXA==", + "optional": true, + "dependencies": { + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pm2/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/pm2/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pm2/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/pm2/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/pm2/node_modules/commander": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==" + }, + "node_modules/pm2/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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/pm2/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/pm2/node_modules/sprintf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==" + }, + "node_modules/pm2/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -5236,6 +6564,14 @@ "node": ">=8" } }, + "node_modules/promptly": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/promptly/-/promptly-2.2.0.tgz", + "integrity": "sha512-aC9j+BZsRSSzEsXBNBwDnAxujdx19HycZoKgRgzWnS8eOHg1asuf9heuLprfbe739zY3IdUQx+Egv6Jn135WHA==", + "dependencies": { + "read": "^1.0.4" + } + }, "node_modules/protobufjs": { "version": "7.2.6", "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.6.tgz", @@ -5271,6 +6607,32 @@ "node": ">= 0.10" } }, + "node_modules/proxy-agent": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.3.1.tgz", + "integrity": "sha512-Rb5RVBy1iyqOtNl15Cw/llpeLH8bsb37gM1FUfKQ+Wck6xHlbAhWGUFiTRHtkjqGTA5pSHz6+0hrPW/oECihPQ==", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.2", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.0.1", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "engines": { + "node": ">=12" + } + }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -5293,7 +6655,8 @@ "node_modules/pstree.remy": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", - "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==" + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true }, "node_modules/punycode": { "version": "2.3.1", @@ -5368,6 +6731,17 @@ "node": ">= 0.8" } }, + "node_modules/read": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", + "integrity": "sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ==", + "dependencies": { + "mute-stream": "~0.0.4" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -5420,6 +6794,19 @@ "node": ">=0.10.0" } }, + "node_modules/require-in-the-middle": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-5.2.0.tgz", + "integrity": "sha512-efCx3b+0Z69/LGJmm9Yvi4cqEdxnoGnxYxGxBghkkTTFeXRtTCmmhO0AnAfHz59k957uTSuy8WaHqOs8wbYUWg==", + "dependencies": { + "debug": "^4.1.1", + "module-details-from-path": "^1.0.3", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/require-main-filename": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", @@ -5430,7 +6817,6 @@ "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", @@ -5463,60 +6849,67 @@ } }, "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.5.tgz", + "integrity": "sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==", "dependencies": { - "glob": "^7.1.3" + "glob": "^10.3.7" }, "bin": { - "rimraf": "bin.js" + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=14" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/rimraf/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, + "node_modules/rimraf/node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/rimraf/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, + "version": "10.3.12", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz", + "integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.6", + "minimatch": "^9.0.1", + "minipass": "^7.0.4", + "path-scurry": "^1.10.2" + }, + "bin": { + "glob": "dist/esm/bin.mjs" }, "engines": { - "node": "*" + "node": ">=16 || 14 >=14.17" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/rimraf/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, + "node_modules/rimraf/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "engines": { - "node": "*" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/run-parallel": { @@ -5542,6 +6935,25 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/run-series": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/run-series/-/run-series-1.1.9.tgz", + "integrity": "sha512-Arc4hUN896vjkqCYrUXquBFtRZdv1PfLbTYP71efP6butxyQ0kWpiNJyAgsxscmQg1cqvHY32/UCBzXedTpU2g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -5566,6 +6978,11 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "node_modules/sax": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", + "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==" + }, "node_modules/semver": { "version": "7.6.0", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", @@ -5706,6 +7123,11 @@ "node": ">=8" } }, + "node_modules/shimmer": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", + "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==" + }, "node_modules/side-channel": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.5.tgz", @@ -5731,13 +7153,13 @@ "node_modules/signal-exit": { "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 + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, "node_modules/simple-update-notifier": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, "dependencies": { "semver": "^7.5.3" }, @@ -5826,6 +7248,19 @@ "npm": ">= 3.0.0" } }, + "node_modules/socks-proxy-agent": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.3.tgz", + "integrity": "sha512-VNegTZKhuGq5vSD6XNKlbqWhyt/40CgoEw8XxD6dhnm8Jq9IEa3nIa4HwnM8XOqU0CdB0BwWVXusqiFXfHB3+A==", + "dependencies": { + "agent-base": "^7.1.1", + "debug": "^4.3.4", + "socks": "^2.7.1" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -5838,7 +7273,6 @@ "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -5848,7 +7282,6 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -5884,6 +7317,63 @@ "node": ">=8" } }, + "node_modules/spawn-wrap/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/spawn-wrap/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/spawn-wrap/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/spawn-wrap/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/split": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", @@ -5945,6 +7435,39 @@ "node": ">=8" } }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/string-width/node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -5975,6 +7498,26 @@ "node": ">=0.10.0" } }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, "node_modules/strip-bom": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", @@ -6008,7 +7551,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -6016,6 +7558,32 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/systeminformation": { + "version": "5.22.7", + "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.22.7.tgz", + "integrity": "sha512-AWxlP05KeHbpGdgvZkcudJpsmChc2Y5Eo/GvxG/iUA/Aws5LZKHAMSeAo+V+nD+nxWZaxrwpWcnx4SH3oxNL3A==", + "optional": true, + "os": [ + "darwin", + "linux", + "win32", + "freebsd", + "openbsd", + "netbsd", + "sunos", + "android" + ], + "bin": { + "systeminformation": "lib/cli.js" + }, + "engines": { + "node": ">=8.0.0" + }, + "funding": { + "type": "Buy me a coffee", + "url": "https://www.buymeacoffee.com/systeminfo" + } + }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -6114,6 +7682,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", + "dev": true, "dependencies": { "nopt": "~1.0.10" }, @@ -6407,6 +7976,23 @@ "typescript": ">=2.1.0 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >= 3.0.0-dev || >= 3.1.0-dev" } }, + "node_modules/tv4": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/tv4/-/tv4-1.3.0.tgz", + "integrity": "sha512-afizzfpJgvPr+eDkREK4MxJ/+r8nEEHcmitwgnPUqpaP+FpwQyadnxNoSACbgc/b1LsZYtODGoPiFxQrgJgjvw==", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/tx2": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tx2/-/tx2-1.0.5.tgz", + "integrity": "sha512-sJ24w0y03Md/bxzK4FU8J8JveYYUbSs2FViLJ2D/8bytSiyPRbuE3DyL/9UKYXTZlV3yXq0L8GLlhobTnekCVg==", + "optional": true, + "dependencies": { + "json-stringify-safe": "^5.0.1" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -6464,13 +8050,22 @@ "node_modules/undefsafe": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", - "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==" + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -6560,6 +8155,28 @@ "node": ">= 0.8" } }, + "node_modules/vizion": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/vizion/-/vizion-2.2.1.tgz", + "integrity": "sha512-sfAcO2yeSU0CSPFI/DmZp3FsFE9T+8913nv1xWBOyzODv13fwkn6Vl7HqxGpkr9F608M+8SuFId3s+BlZqfXww==", + "dependencies": { + "async": "^2.6.3", + "git-node-fs": "^1.0.0", + "ini": "^1.3.5", + "js-git": "^0.7.8" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/vizion/node_modules/async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dependencies": { + "lodash": "^4.17.14" + } + }, "node_modules/webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", @@ -6622,6 +8239,72 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/wrap-ansi/node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -6674,8 +8357,7 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/write-file-atomic": { "version": "3.0.3", @@ -6731,6 +8413,71 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, + "node_modules/yamljs": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/yamljs/-/yamljs-0.3.0.tgz", + "integrity": "sha512-C/FsVVhht4iPQYXOInoxUM/1ELSf9EsgKH34FofQOp6hwCPrW4vG4w5++TED3xRUo8gD7l0P1J1dLlDYzODsTQ==", + "dependencies": { + "argparse": "^1.0.7", + "glob": "^7.0.5" + }, + "bin": { + "json2yaml": "bin/json2yaml", + "yaml2json": "bin/yaml2json" + } + }, + "node_modules/yamljs/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/yamljs/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/yamljs/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/yamljs/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/yamljs/node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", diff --git a/server/package.json b/server/package.json index 3e5895a..45cb42d 100644 --- a/server/package.json +++ b/server/package.json @@ -45,13 +45,14 @@ "md5": "^2.3.0", "mongoose": "^7.6.2", "mongoose-autopopulate": "^1.1.0", + "pm2": "^5.3.1", "reflect-metadata": "^0.1.13", + "rimraf": "^5.0.5", "socket.io": "^4.7.2", "socket.io-client": "^4.7.2", "spark-md5": "^3.0.2", "ts-md5": "^1.3.1", "tsc-watch": "^6.0.4", - "uuid": "^9.0.1", - "pm2": "^5.3.1" + "uuid": "^9.0.1" } } diff --git a/server/src/core/controllers/app.ts b/server/src/core/controllers/app.ts index 00b8bc9..a65ba2f 100644 --- a/server/src/core/controllers/app.ts +++ b/server/src/core/controllers/app.ts @@ -58,6 +58,8 @@ export class App extends TypedEvent { io.on("connection", (socket) => { this.socketSubscribers.map((el) => { el.emitter.on((e) => { + console.log(el.event) + console.log(e) socket.emit(el.event, e); }); }); @@ -107,7 +109,7 @@ export class App extends TypedEvent { await result.fold( async (_s) => { await new CheckAndCreateStaticFilesFolderUseCase().call(); - await new SetLastActivePipelineToRealTimeServiceScenario().call(); + // await new SetLastActivePipelineToRealTimeServiceScenario().call(); }, async (_e) => { this.setServerStatus(ServerStatus.error); diff --git a/server/src/core/controllers/http_controller.ts b/server/src/core/controllers/http_controller.ts index 7d9d6b9..7fd5561 100644 --- a/server/src/core/controllers/http_controller.ts +++ b/server/src/core/controllers/http_controller.ts @@ -26,7 +26,7 @@ export abstract class CallBackStrategyWithQueryPage { export abstract class CallbackStrategyWithFileUpload { abstract checkingFileExpression: RegExp; abstract idValidationExpression: CoreValidation; - abstract call(file: File, id: string, description: string): ResponseBase; + abstract call(file: File, id: string): ResponseBase; } interface ISubSetFeatureRouter { @@ -122,12 +122,12 @@ export class CoreHttpController implements ICoreHttpController { res.status(400).json("need file to form data request"); return; } - if (req.query.description === undefined) { - res - .status(400) - .json("request query description is null, need query description &description={description:String}"); - return; - } + // if (req.query.description === undefined) { + // res + // .status(400) + // .json("request query description is null, need query description &description={description:String}"); + // return; + // } if (req.query.id === undefined) { res.status(400).json("request query id is null, need query id ?id={id:String}"); return; @@ -142,7 +142,7 @@ export class CoreHttpController implements ICoreHttpController { return; } } - await this.responseHelper(res, el.fn.call(req["files"]["file"], req.query.id, req.query.description)); + await this.responseHelper(res, el.fn.call(req["files"]["file"], req.query.id)); } }); }); @@ -197,12 +197,10 @@ export class CoreHttpController implements ICoreHttpController { } (await useCase(payload)).fold( (ok) => { - res.json(ok); - return; + return res.json(ok); }, (err) => { - res.status(400).json({ error: String(err) }); - return; + return res.status(400).json({ error: String(err) }); } ); } diff --git a/server/src/core/controllers/routes.ts b/server/src/core/controllers/routes.ts index b76963d..b63fc5b 100644 --- a/server/src/core/controllers/routes.ts +++ b/server/src/core/controllers/routes.ts @@ -1,30 +1,9 @@ -import { NixStoreManagerPresentation } from "../../features/nix_store_manager/nix_store_manager"; -import { PipelinePresentation } from "../../features/pipelines/pipeline_presentation"; -import { ProcessPresentation } from "../../features/process/process_presentation"; -import { - ProjectInstancePresentation, - RobossemblerAssetsPresentation, -} from "../../features/project_instance/project_instance_presentation"; +import { DatasetsPresentation } from "../../features/datasets/datasets_presentation"; import { ProjectsPresentation } from "../../features/projects/projects_presentation"; -import { RealTimePresentation } from "../../features/realtime/realtime_presentation"; -import { TriggerPresentation } from "../../features/triggers/triggers_presentation"; +// import { ProjectsPresentation } from "../../features/_projects/projects_presentation"; import { extensions } from "../extensions/extensions"; import { Routes } from "../interfaces/router"; extensions(); -export const routersImplementPureCrud = [ - new TriggerPresentation(), - new ProjectsPresentation(), - new ProcessPresentation(), - new PipelinePresentation(), -]; - -export const httpRoutes: Routes[] = [ - new RealTimePresentation(), - new ProjectInstancePresentation(), - new NixStoreManagerPresentation(), - new RobossemblerAssetsPresentation(), -] - .concat(routersImplementPureCrud) - .map((el) => el.call()); +export const httpRoutes: Routes[] = [new ProjectsPresentation(), new DatasetsPresentation()].map((el) => el.call()); diff --git a/server/src/core/extensions/string.ts b/server/src/core/extensions/string.ts index 055c615..a05eee1 100644 --- a/server/src/core/extensions/string.ts +++ b/server/src/core/extensions/string.ts @@ -32,6 +32,3 @@ export const StringExtensions = () => { }; } }; -// python3 /Users/idontsudo/framework/path.py --path /Users/idontsudo/webservice/server/build/public/0a3422cc-f2e3-4abc-87d8-ae13b8b6d26d/ --env /Users/idontsudo/framework/cad_generation/env.json -// python3 /Users/idontsudo/framework/path.py --path /Users/idontsudo/webservice/server/build/public/0a3422cc-f2e3-4abc-87d8-ae13b8b6d26d/ --env /Users/idontsudo/framework/cad_generation/env.json -// /Users/idontsudo/Desktop/FreeCAD.app/Contents/MacOS/FreeCAD /Users/idontsudo/framework/cad_generation/main.py diff --git a/server/src/core/helpers/worker_computed.ts b/server/src/core/helpers/worker_computed.ts index bc88fad..6a9b7a0 100644 --- a/server/src/core/helpers/worker_computed.ts +++ b/server/src/core/helpers/worker_computed.ts @@ -17,8 +17,6 @@ export interface WorkerDataExec { process.on("message", async (message) => { const workerData = message as WorkerDataExec; if (workerData.type == WorkerType.SPAWN) { - // Maybe error - // const subprocess = cp.spawn(workerData.command, workerData.cliArgs, { const subprocess = cp.spawn(workerData.command, { cwd: workerData.execPath, }); diff --git a/server/src/core/models/exec_error_model.ts b/server/src/core/models/exec_error_model.ts index 12cb3ac..9e28fb3 100644 --- a/server/src/core/models/exec_error_model.ts +++ b/server/src/core/models/exec_error_model.ts @@ -3,6 +3,19 @@ import { extensions } from "../extensions/extensions"; extensions(); export class ExecError extends Error { + id?:string; + script: string; + unixTime: number; + type = EXEC_TYPE.EXEC; + error: any; + + constructor(script: string, ...args: any) { + super(...args); + this.script = script; + this.unixTime = Date.now(); + this.error = args.firstElement(); + } + static isExecError(e: any): ExecError | void { try { if (e) { @@ -14,16 +27,6 @@ export class ExecError extends Error { console.log(error); } } - script: string; - unixTime: number; - type = EXEC_TYPE.EXEC; - error: any; - constructor(script: string, ...args: any) { - super(...args); - this.script = script; - this.unixTime = Date.now(); - this.error = args.firstElement(); - } } export class SpawnError extends Error { diff --git a/server/src/core/models/executor_result.ts b/server/src/core/models/executor_result.ts index fd4c32a..c5c5d87 100644 --- a/server/src/core/models/executor_result.ts +++ b/server/src/core/models/executor_result.ts @@ -4,6 +4,7 @@ export class ExecutorResult { type: EXEC_TYPE; event: EXEC_EVENT; data: any; + id?:string constructor(type: EXEC_TYPE, event: EXEC_EVENT, data: any) { this.type = type; this.event = event; diff --git a/server/src/core/models/process_model.ts b/server/src/core/models/process_model.ts index 9f601ed..85167f5 100644 --- a/server/src/core/models/process_model.ts +++ b/server/src/core/models/process_model.ts @@ -1,4 +1,4 @@ -import { Trigger } from "../../features/triggers/models/trigger_database_model"; +import { Trigger } from "../../features/_triggers/models/trigger_database_model"; import { EXEC_TYPE } from "./exec_error_model"; export interface IPipeline { diff --git a/server/src/core/models/robossembler_assets.ts b/server/src/core/models/robossembler_assets.ts index d0a99f2..8d4c9c3 100644 --- a/server/src/core/models/robossembler_assets.ts +++ b/server/src/core/models/robossembler_assets.ts @@ -1,160 +1,23 @@ -import { IsArray, IsEnum, IsNumber, IsOptional, IsString, ValidateNested } from "class-validator"; +import { IsArray, IsString } from "class-validator"; import { Type } from "class-transformer"; - -export class Gravity { - @IsNumber() - x: number; - @IsNumber() - y: number; - @IsNumber() - z: number; +class Asset { + @IsString() + public name: string; + @IsString() + public mesh: string; + @IsString() + public image: string; } - -export class Pose { - @IsNumber() - x: number; - @IsNumber() - y: number; - @IsNumber() - z: number; - @IsNumber() - roll: number; - @IsNumber() - pitch: number; - @IsNumber() - yaw: number; -} - -export class Position { - @IsNumber() - x: number; - @IsNumber() - y: number; - @IsNumber() - z: number; -} - -export enum InstanceType { - RGB_CAMERA = "rgb_camera", - SCENE_SIMPLE_OBJECT = "scene_simple_object", -} - -abstract class CoreInstances {} - -export class Instance extends CoreInstances { - @IsEnum(InstanceType) - instanceType: InstanceType; - @Type(() => Position) - position: Position; - @IsArray() - quaternion: number[]; - @IsOptional() - @IsString() - instanceAt: null | string = null; -} - -export class SceneSimpleObject extends Instance {} - -export class InstanceRgbCamera extends Instance { - @IsString() - cameraLink: string; - @IsString() - topicCameraInfo: string; - @IsOptional() - @IsString() - topicDepth: string | null; - @IsString() - topicImage: string; -} -export class Asset { - @IsString() - name: string; - @IsString() - ixx: string; - @IsString() - ixy: string; - @IsString() - ixz: string; - @IsString() - iyy: string; - @IsString() - izz: string; - @IsString() - mass: string; - @IsString() - posX: string; - @IsString() - posY: string; - @IsString() - posZ: string; - @IsString() - eulerX: string; - @IsString() - eulerY: string; - @IsString() - eulerZ: string; - @IsString() - iyz: string; - @IsString() - meshPath: string; - @IsString() - friction: string; - @IsString() - centerMassX: string; - @IsString() - centerMassY: string; - @IsString() - centerMassZ: string; -} - -export class Physics { - @IsString() - engine_name: string; - @Type(() => Gravity) - gravity: Gravity; -} - export class RobossemblerAssets { - @ValidateNested() + @IsArray() @Type(() => Asset) assets: Asset[]; - - @IsArray() - @Type(() => Instance, { - discriminator: { - property: "type", - subTypes: [ - { value: InstanceRgbCamera, name: InstanceType.RGB_CAMERA }, - { value: SceneSimpleObject, name: InstanceType.SCENE_SIMPLE_OBJECT }, - ], - }, - keepDiscriminatorProperty: true, - }) - instances: Instance[]; - - @IsOptional() - @ValidateNested() - @Type(() => Physics) - physics: Physics; - - convertLocalPathsToServerPaths(server_address: string): RobossemblerAssets { + convertLocalPathsToServerPaths(serverAddress: string): RobossemblerAssets { this.assets = this.assets.map((el) => { - el.meshPath = server_address + el.meshPath; + el.mesh = `${serverAddress}/${el.mesh.slice(2, el.mesh.length)}`; + el.image = `${serverAddress}/${el.image.slice(2, el.image.length)}`; return el; }); return this; } - - getAssetPath(assetName: string): string { - const findElement = this.assets.find((el) => el.name === assetName); - - if (findElement === undefined) { - throw new Error("RobossemblerAssets.getAssetPath not found asset by name:" + assetName); - } - return findElement.meshPath; - } - - getAssetAtInstance(instanceAt: string): Asset { - return this.assets.filter((el) => el.name === instanceAt)[0]; - } } diff --git a/server/src/core/models/static_files.ts b/server/src/core/models/static_files.ts index dfe36c4..aed291c 100644 --- a/server/src/core/models/static_files.ts +++ b/server/src/core/models/static_files.ts @@ -1,3 +1,4 @@ export enum StaticFiles { robossembler_assets = "robossembler_assets.json", + assets = "/assets/assets.json", } diff --git a/server/src/core/repository/file_system_repository.ts b/server/src/core/repository/file_system_repository.ts index 07d4422..7d89d4b 100644 --- a/server/src/core/repository/file_system_repository.ts +++ b/server/src/core/repository/file_system_repository.ts @@ -1,5 +1,6 @@ import * as fs from "fs"; import { promisify } from "node:util"; +import { rimraf } from 'rimraf' export class FileSystemRepository { public createDir = promisify(fs.mkdir); @@ -9,7 +10,7 @@ export class FileSystemRepository { public stat = promisify(fs.stat); public readFileAsync = promisify(fs.readFile); public readdir = promisify(fs.readdir); - + public deleteDirRecursive = rimraf; async readFileAtBuffer(path: string): Promise { if ((await this.lsStat(path)).isDirectory()) { return ( @@ -40,4 +41,5 @@ export class FileSystemRepository { }); return filesToDir; } + } diff --git a/server/src/core/scenarios/set_active_pipeline_to_realtime_service_scenario.ts b/server/src/core/scenarios/set_active_pipeline_to_realtime_service_scenario.ts index 5a8398a..fa4bf7d 100644 --- a/server/src/core/scenarios/set_active_pipeline_to_realtime_service_scenario.ts +++ b/server/src/core/scenarios/set_active_pipeline_to_realtime_service_scenario.ts @@ -1,8 +1,8 @@ import { IProjectInstanceModel, ProjectInstanceDbModel, -} from "../../features/project_instance/models/project_instance_database_model"; -import { pipelineRealTimeService } from "../../features/realtime/realtime_presentation"; +} from "../../features/projects/models/project_instance_database_model"; +import { pipelineRealTimeService } from "../../features/_realtime/realtime_presentation"; import { App } from "../controllers/app"; import { CreateFolderUseCase } from "../usecases/create_folder_usecase"; import { SearchDataBaseModelUseCase } from "../usecases/search_database_model_usecase"; @@ -15,6 +15,10 @@ export class SetLastActivePipelineToRealTimeServiceScenario { }) ).fold( async (projectModel) => { + if (projectModel.project === null) { + return; + } + const projectPath = App.staticFilesStoreDir() + projectModel.rootDir + "/"; await new CreateFolderUseCase().call(projectPath); pipelineRealTimeService.setPipelineDependency( diff --git a/server/src/core/services/executor_program_service.ts b/server/src/core/services/executor_program_service.ts index f6ed534..bee83c7 100644 --- a/server/src/core/services/executor_program_service.ts +++ b/server/src/core/services/executor_program_service.ts @@ -25,7 +25,12 @@ export class ExecutorProgramService this.maxTime = maxTime; } - private async workerExecuted(command: string, workerType: WorkerType, args: Array | undefined = undefined) { + private async workerExecuted( + command: string, + workerType: WorkerType, + args: Array | undefined = undefined, + id: string | undefined = undefined + ) { try { cluster.setupPrimary({ exec: __dirname + "/../helpers/worker_computed.js", @@ -49,18 +54,23 @@ export class ExecutorProgramService if (spawnError instanceof SpawnError) { this.emit(Result.error(spawnError)); + this.worker = undefined; return; } const execError = ExecError.isExecError(e); if (execError instanceof ExecError) { + execError.id = id this.emit(Result.error(execError)); + this.worker = undefined; return; } const executorResult = ExecutorResult.isExecutorResult(e); if (executorResult instanceof ExecutorResult) { + executorResult.id = id this.emit(Result.ok(executorResult)); + this.worker = undefined; return; } }); @@ -78,14 +88,22 @@ export class ExecutorProgramService console.log(error); } } - - public async call(type: EXEC_TYPE, command: string, args: Array | undefined = undefined): Promise { + public deleteWorker() { + if (this.worker) this.worker.kill(); + this.worker = undefined; + } + public async call( + type: EXEC_TYPE, + command: string, + args: Array | undefined = undefined, + id: string | undefined = undefined + ): Promise { if (type == EXEC_TYPE.EXEC) { - this.workerExecuted(command, WorkerType.EXEC); + this.workerExecuted(command, WorkerType.EXEC, undefined, id); return; } - this.workerExecuted(command, WorkerType.SPAWN, args); + this.workerExecuted(command, WorkerType.SPAWN, args, id); return; } diff --git a/server/src/core/services/stack_service.ts b/server/src/core/services/stack_service.ts index 336b758..16f105b 100644 --- a/server/src/core/services/stack_service.ts +++ b/server/src/core/services/stack_service.ts @@ -7,7 +7,7 @@ import { Result } from "../helpers/result"; import { ExecutorResult } from "../models/executor_result"; import { delay } from "../helpers/delay"; import { TriggerService } from "./trigger_service"; -import { Trigger } from "../../features/triggers/models/trigger_database_model"; +import { Trigger } from "../../features/_triggers/models/trigger_database_model"; export interface Iteration { hashes: IHashesCache | null; diff --git a/server/src/core/services/trigger_service.ts b/server/src/core/services/trigger_service.ts index e4979f1..940b814 100644 --- a/server/src/core/services/trigger_service.ts +++ b/server/src/core/services/trigger_service.ts @@ -3,7 +3,7 @@ import { IHashesCache } from "./files_change_notifier_service"; import { EventsFileChanger } from "../models/meta_data_file_manager_model"; import { Result } from "../helpers/result"; import { TypedEvent } from "../helpers/typed_event"; -import { Trigger, TriggerType } from "../../features/triggers/models/trigger_database_model"; +import { Trigger, TriggerType } from "../../features/_triggers/models/trigger_database_model"; export class TriggerCallResult { results: Array; diff --git a/server/src/core/usecases/delete_recursive_folder_usecase.ts b/server/src/core/usecases/delete_recursive_folder_usecase.ts new file mode 100644 index 0000000..070045a --- /dev/null +++ b/server/src/core/usecases/delete_recursive_folder_usecase.ts @@ -0,0 +1,12 @@ +import { Result } from "../helpers/result"; +import { FileSystemRepository } from "../repository/file_system_repository"; + +export class DeleteRecursiveFolderUseCase{ + repository:FileSystemRepository = new FileSystemRepository() + call = async (path:string):Promise> =>{ + console.log(path) + await this.repository.deleteDirRecursive(path) + return Result.ok() + } + +} \ No newline at end of file diff --git a/server/src/core/usecases/exec_process_usecase.ts b/server/src/core/usecases/exec_process_usecase.ts new file mode 100644 index 0000000..12b7d16 --- /dev/null +++ b/server/src/core/usecases/exec_process_usecase.ts @@ -0,0 +1,44 @@ +import { CallbackStrategyWithEmpty } from "../controllers/http_controller"; +import { Result } from "../helpers/result"; +import { TypedEvent } from "../helpers/typed_event"; +import { EXEC_TYPE, ExecError, SpawnError } from "../models/exec_error_model"; +import { ExecutorResult } from "../models/executor_result"; +import { ExecutorProgramService } from "../services/executor_program_service"; + +export const executorProgramService = new ExecutorProgramService(""); +export class KillLastProcessUseCase extends CallbackStrategyWithEmpty { + call = async (): Promise> => { + executorProgramService.deleteWorker(); + return Result.ok("ok"); + }; +} + +export class IsHaveActiveProcessUseCase extends CallbackStrategyWithEmpty { + call = async (): Promise> => { + if (executorProgramService.worker === undefined) { + return Result.ok("process not work"); + } + return Result.error("process is exists"); + }; +} + +export class ExecProcessUseCase { + call = async ( + path: string, + command: string, + id:string, + watcher?: TypedEvent> + ): Promise> => { + try { + executorProgramService.execPath = path; + executorProgramService.on((event) => { + if (watcher) watcher.emit(event); + }); + executorProgramService.call(EXEC_TYPE.EXEC, command, undefined ,id); + + return Result.ok("ok"); + } catch (error) { + return Result.error(error); + } + }; +} diff --git a/server/src/core/usecases/read_by_id_database_model_usecase.ts b/server/src/core/usecases/read_by_id_database_model_usecase.ts index 8d14c0f..63f294e 100644 --- a/server/src/core/usecases/read_by_id_database_model_usecase.ts +++ b/server/src/core/usecases/read_by_id_database_model_usecase.ts @@ -9,7 +9,15 @@ export class ReadByIdDataBaseModelUseCase { call = async (id: string): Promise> => { try { const dbModel = this.databaseModel as any; - return Result.ok(await dbModel.findById(id)); + const model = await dbModel.findById(id); + if (model === null) { + return Result.error( + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + Error(`the database table ${this.databaseModel.modelName} does not contain an object with this ID`) + ); + } + return Result.ok(model); } catch (error) { return Result.error(error); } diff --git a/server/src/core/usecases/search_database_model_usecase.ts b/server/src/core/usecases/search_database_model_usecase.ts index 44ad0d2..c8e6fd4 100644 --- a/server/src/core/usecases/search_database_model_usecase.ts +++ b/server/src/core/usecases/search_database_model_usecase.ts @@ -7,10 +7,10 @@ export class SearchDataBaseModelUseCase { this.model = model; } - call = async (findFilter: Partial): Promise> => { + call = async (findFilter: Partial, error: string = "not found database"): Promise> => { const result = await this.model.findOne(findFilter); if (result === null) { - return Result.error(null); + return Result.error(error); } else { return Result.ok(result); } diff --git a/server/src/features/nix_store_manager/nix_store_manager.ts b/server/src/features/_nix_store_manager/nix_store_manager.ts similarity index 96% rename from server/src/features/nix_store_manager/nix_store_manager.ts rename to server/src/features/_nix_store_manager/nix_store_manager.ts index 145bfd5..d9b56c2 100644 --- a/server/src/features/nix_store_manager/nix_store_manager.ts +++ b/server/src/features/_nix_store_manager/nix_store_manager.ts @@ -4,7 +4,7 @@ import { EXEC_TYPE } from "../../core/models/exec_error_model"; import { ExecutorResult } from "../../core/models/executor_result"; import { IPipeline, IssueType, StackGenerateType } from "../../core/models/process_model"; import { StackService } from "../../core/services/stack_service"; -import { TriggerType } from "../triggers/models/trigger_database_model"; +import { TriggerType } from "../_triggers/models/trigger_database_model"; class NixStoreModel {} diff --git a/server/src/features/pipelines/models/pipeline_database_model.ts b/server/src/features/_pipelines/models/pipeline_database_model.ts similarity index 81% rename from server/src/features/pipelines/models/pipeline_database_model.ts rename to server/src/features/_pipelines/models/pipeline_database_model.ts index 1d39bd7..dac4f36 100644 --- a/server/src/features/pipelines/models/pipeline_database_model.ts +++ b/server/src/features/_pipelines/models/pipeline_database_model.ts @@ -1,7 +1,7 @@ import { Schema, model } from "mongoose"; import { IPipeline } from "../../../core/models/process_model"; -import { schemaProcess } from "../../process/models/process_database_model"; -import { triggerSchema } from "../../triggers/models/trigger_database_model"; +import { schemaProcess } from "../../_process/models/process_database_model"; +import { triggerSchema } from "../../_triggers/models/trigger_database_model"; export const PipelineSchema = new Schema({ process: { diff --git a/server/src/features/pipelines/models/pipeline_model.ts b/server/src/features/_pipelines/models/pipeline_model.ts similarity index 75% rename from server/src/features/pipelines/models/pipeline_model.ts rename to server/src/features/_pipelines/models/pipeline_model.ts index 69cd0fc..fca67ba 100644 --- a/server/src/features/pipelines/models/pipeline_model.ts +++ b/server/src/features/_pipelines/models/pipeline_model.ts @@ -1,8 +1,8 @@ import { IsOptional, ValidateNested } from "class-validator"; import { IPipeline, IProcess, StackGenerateType } from "../../../core/models/process_model"; import { Type } from "class-transformer"; -import { ProcessModel } from "../../process/models/process_validation_model"; -import { TriggerModelValidationModel } from "../../triggers/models/trigger_validation_model"; +import { ProcessModel } from "../../_process/models/process_validation_model"; +import { TriggerModelValidationModel } from "../../_triggers/models/trigger_validation_model"; export class PipelineModel implements IPipeline { @ValidateNested() diff --git a/server/src/features/pipelines/models/pipeline_validation_model.ts b/server/src/features/_pipelines/models/pipeline_validation_model.ts similarity index 80% rename from server/src/features/pipelines/models/pipeline_validation_model.ts rename to server/src/features/_pipelines/models/pipeline_validation_model.ts index d16382e..59fc6b3 100644 --- a/server/src/features/pipelines/models/pipeline_validation_model.ts +++ b/server/src/features/_pipelines/models/pipeline_validation_model.ts @@ -1,6 +1,6 @@ import { IsMongoId, IsOptional } from "class-validator"; import { IProcess, StackGenerateType } from "../../../core/models/process_model"; -import { TriggerModelValidationModel } from "../../triggers/models/trigger_validation_model"; +import { TriggerModelValidationModel } from "../../_triggers/models/trigger_validation_model"; export class PipelineValidationModel { @IsMongoId() diff --git a/server/src/features/pipelines/pipeline_presentation.ts b/server/src/features/_pipelines/pipeline_presentation.ts similarity index 100% rename from server/src/features/pipelines/pipeline_presentation.ts rename to server/src/features/_pipelines/pipeline_presentation.ts diff --git a/server/src/features/process/models/process_database_model.ts b/server/src/features/_process/models/process_database_model.ts similarity index 100% rename from server/src/features/process/models/process_database_model.ts rename to server/src/features/_process/models/process_database_model.ts diff --git a/server/src/features/process/models/process_validation_model.ts b/server/src/features/_process/models/process_validation_model.ts similarity index 100% rename from server/src/features/process/models/process_validation_model.ts rename to server/src/features/_process/models/process_validation_model.ts diff --git a/server/src/features/process/process_presentation.ts b/server/src/features/_process/process_presentation.ts similarity index 100% rename from server/src/features/process/process_presentation.ts rename to server/src/features/_process/process_presentation.ts diff --git a/server/src/features/projects/models/project_database_model.ts b/server/src/features/_projects/models/project_database_model.ts similarity index 63% rename from server/src/features/projects/models/project_database_model.ts rename to server/src/features/_projects/models/project_database_model.ts index 2948f94..e45b146 100644 --- a/server/src/features/projects/models/project_database_model.ts +++ b/server/src/features/_projects/models/project_database_model.ts @@ -1,6 +1,5 @@ import { Schema, model } from "mongoose"; -import { schemaPipeline } from "../../pipelines/models/pipeline_database_model"; -import { PipelineValidationModel } from "../../pipelines/models/pipeline_validation_model"; +import { PipelineValidationModel } from "../../_pipelines/models/pipeline_validation_model"; export interface IProjectModel { _id?: string; @@ -11,15 +10,12 @@ export interface IProjectModel { } export const ProjectSchema = new Schema({ - pipelines: { - type: Array, - ref: schemaPipeline, - autopopulate: true, - default: null, - }, description: { type: String, }, + rootDir: { + type: String, + }, isActive: { type: Boolean, default: false, diff --git a/server/src/features/projects/models/project_validation_model.ts b/server/src/features/_projects/models/project_validation_model.ts similarity index 100% rename from server/src/features/projects/models/project_validation_model.ts rename to server/src/features/_projects/models/project_validation_model.ts diff --git a/server/src/features/_projects/projects_presentation.ts b/server/src/features/_projects/projects_presentation.ts new file mode 100644 index 0000000..253bf2c --- /dev/null +++ b/server/src/features/_projects/projects_presentation.ts @@ -0,0 +1,13 @@ +import { CrudController } from "../../core/controllers/crud_controller"; +import { ProjectDBModel } from "./models/project_database_model"; +import { ProjectValidationModel } from "./models/project_validation_model"; + +export class ProjectsPresentation extends CrudController { + constructor() { + super({ + url: "project", + validationModel: ProjectValidationModel, + databaseModel: ProjectDBModel, + }); + } +} diff --git a/server/src/features/realtime/domain/pipeline_status_usecase.ts b/server/src/features/_realtime/domain/pipeline_status_usecase.ts similarity index 100% rename from server/src/features/realtime/domain/pipeline_status_usecase.ts rename to server/src/features/_realtime/domain/pipeline_status_usecase.ts diff --git a/server/src/features/realtime/domain/run_instance_pipeline_usecase.ts b/server/src/features/_realtime/domain/run_instance_pipeline_usecase.ts similarity index 90% rename from server/src/features/realtime/domain/run_instance_pipeline_usecase.ts rename to server/src/features/_realtime/domain/run_instance_pipeline_usecase.ts index 37c1c1a..b47b873 100644 --- a/server/src/features/realtime/domain/run_instance_pipeline_usecase.ts +++ b/server/src/features/_realtime/domain/run_instance_pipeline_usecase.ts @@ -3,11 +3,8 @@ import { CallbackStrategyWithEmpty } from "../../../core/controllers/http_contro import { Result } from "../../../core/helpers/result"; import { IPipeline } from "../../../core/models/process_model"; import { ReadByIdDataBaseModelUseCase } from "../../../core/usecases/read_by_id_database_model_usecase"; -import { PipelineValidationModel } from "../../pipelines/models/pipeline_validation_model"; -import { - IProjectInstanceModel, - ProjectInstanceDbModel, -} from "../../project_instance/models/project_instance_database_model"; +import { PipelineValidationModel } from "../../_pipelines/models/pipeline_validation_model"; +import { IProjectInstanceModel, ProjectInstanceDbModel } from "../../projects/models/project_instance_database_model"; import { pipelineRealTimeService } from "../realtime_presentation"; import { PipelineStatusUseCase } from "./pipeline_status_usecase"; diff --git a/server/src/features/realtime/realtime_presentation.ts b/server/src/features/_realtime/realtime_presentation.ts similarity index 100% rename from server/src/features/realtime/realtime_presentation.ts rename to server/src/features/_realtime/realtime_presentation.ts diff --git a/server/src/features/triggers/models/trigger_database_model.ts b/server/src/features/_triggers/models/trigger_database_model.ts similarity index 100% rename from server/src/features/triggers/models/trigger_database_model.ts rename to server/src/features/_triggers/models/trigger_database_model.ts diff --git a/server/src/features/triggers/models/trigger_validation_model.ts b/server/src/features/_triggers/models/trigger_validation_model.ts similarity index 100% rename from server/src/features/triggers/models/trigger_validation_model.ts rename to server/src/features/_triggers/models/trigger_validation_model.ts diff --git a/server/src/features/triggers/triggers_presentation.ts b/server/src/features/_triggers/triggers_presentation.ts similarity index 97% rename from server/src/features/triggers/triggers_presentation.ts rename to server/src/features/_triggers/triggers_presentation.ts index 0300f15..b401373 100644 --- a/server/src/features/triggers/triggers_presentation.ts +++ b/server/src/features/_triggers/triggers_presentation.ts @@ -11,4 +11,3 @@ export class TriggerPresentation extends CrudController { + constructor() { + super({ + url: "datasets", + validationModel: DatasetValidationModel, + databaseModel: DatasetDBModel, + }); + super.post(new CreateDataSetScenario().call); + super.get(new GetDatasetActiveProjectScenario().call); + super.delete(null) + this.subRoutes.push({ + method: "POST", + subUrl: "exec", + fn: new ExecDatasetProcessScenario(), + }); + this.subRoutes.push({ + method: "GET", + subUrl: "is/running", + fn: new IsHaveActiveProcessUseCase(), + }); + this.subRoutes.push({ + method: "GET", + subUrl: "delete/process", + fn: new KillLastProcessUseCase(), + }); + this.subRoutes.push({ + method: "DELETE", + subUrl: "dataset", + fn: new DeleteDatasetUseCase() + }) + + } +} diff --git a/server/src/features/datasets/domain/create_dataset_scenario.ts b/server/src/features/datasets/domain/create_dataset_scenario.ts new file mode 100644 index 0000000..859c80c --- /dev/null +++ b/server/src/features/datasets/domain/create_dataset_scenario.ts @@ -0,0 +1,60 @@ +import { ObjectId } from "mongoose"; +import { CallbackStrategyWithValidationModel, ResponseBase } from "../../../core/controllers/http_controller"; +import { Result } from "../../../core/helpers/result"; +import { TypedEvent } from "../../../core/helpers/typed_event"; +import { EXEC_EVENT, ExecError, SpawnError } from "../../../core/models/exec_error_model"; +import { ExecutorResult } from "../../../core/models/executor_result"; +import { SearchDataBaseModelUseCase } from "../../../core/usecases/search_database_model_usecase"; +import { IProjectModel, ProjectDBModel } from "../../_projects/models/project_database_model"; +import { DatasetDBModel } from "../models/dataset_database_model"; +import { DatasetValidationModel, ProcessStatus } from "../models/dataset_validation_model"; + +export class ProcessWatcherAndDatabaseUpdateService extends TypedEvent> { + databaseId: ObjectId; + constructor(databaseId: ObjectId) { + super(); + this.databaseId = databaseId; + this.on((event) => this.lister(event)); + } + + lister(event: Result) { + event.fold( + async (success) => { + if (success.event == EXEC_EVENT.END) { + const dbModel = await DatasetDBModel.findById(this.databaseId); + if (dbModel !== null) { + dbModel.local_path; + dbModel.processStatus = ProcessStatus.END; + dbModel.processLogs = success.data; + await dbModel.save(); + } + } + }, + async (error) => { + const dbModel = await DatasetDBModel.findById(this.databaseId); + if (dbModel !== null) { + dbModel.processStatus = ProcessStatus.ERROR; + dbModel.processLogs = error.message; + await dbModel.save(); + } + } + ); + } +} +export class CreateDataSetScenario extends CallbackStrategyWithValidationModel { + validationModel: DatasetValidationModel; + call = async (model: DatasetValidationModel): ResponseBase => { + return ( + await new SearchDataBaseModelUseCase(ProjectDBModel).call({ isActive: true }, "no active projects") + ).map(async (project) => { + model.processStatus = ProcessStatus.NEW; + model.local_path = project.rootDir; + model.projectId = project._id; + const d = new DatasetDBModel(); + Object.assign(d, model); + await d.save(); + + return Result.ok("create dataset ok"); + }); + }; +} diff --git a/server/src/features/datasets/domain/delete_dataset_use_case.ts b/server/src/features/datasets/domain/delete_dataset_use_case.ts new file mode 100644 index 0000000..bf2a3c6 --- /dev/null +++ b/server/src/features/datasets/domain/delete_dataset_use_case.ts @@ -0,0 +1,19 @@ +import { CallbackStrategyWithIdQuery, ResponseBase } from "../../../core/controllers/http_controller"; +import { Result } from "../../../core/helpers/result"; +import { DeleteDataBaseModelUseCase } from "../../../core/usecases/delete_database_model_usecase"; +import { DeleteRecursiveFolderUseCase } from "../../../core/usecases/delete_recursive_folder_usecase"; +import { ReadByIdDataBaseModelUseCase } from "../../../core/usecases/read_by_id_database_model_usecase"; +import { MongoIdValidation } from "../../../core/validations/mongo_id_validation"; +import { DatasetDBModel } from "../models/dataset_database_model"; +import { IDatasetModel } from "../models/dataset_validation_model"; + +export class DeleteDatasetUseCase extends CallbackStrategyWithIdQuery { + idValidationExpression = new MongoIdValidation(); + call = async (id: string): ResponseBase => + (await new ReadByIdDataBaseModelUseCase(DatasetDBModel).call(id)).map(async (model) => + (await new DeleteRecursiveFolderUseCase().call(`${model.local_path}/${model.name}/`)).map(async () => + (await new DeleteDataBaseModelUseCase(model).call(model._id)).map(() => Result.ok({ status: "delete dataset" })) + ) + ); +} + \ No newline at end of file diff --git a/server/src/features/datasets/domain/exec_process_scenario.ts b/server/src/features/datasets/domain/exec_process_scenario.ts new file mode 100644 index 0000000..20062cf --- /dev/null +++ b/server/src/features/datasets/domain/exec_process_scenario.ts @@ -0,0 +1,27 @@ +import { ObjectId } from "mongoose"; +import { CallbackStrategyWithIdQuery, ResponseBase } from "../../../core/controllers/http_controller"; +import { ExecProcessUseCase, IsHaveActiveProcessUseCase } from "../../../core/usecases/exec_process_usecase"; +import { ReadByIdDataBaseModelUseCase } from "../../../core/usecases/read_by_id_database_model_usecase"; +import { MongoIdValidation } from "../../../core/validations/mongo_id_validation"; +import { DatasetDBModel } from "../models/dataset_database_model"; +import { IDatasetModel } from "../models/dataset_validation_model"; +import { ProcessWatcherAndDatabaseUpdateService } from "./create_dataset_scenario"; + +export class ExecDatasetProcessScenario extends CallbackStrategyWithIdQuery { + idValidationExpression = new MongoIdValidation(); + + call = async (id: string): ResponseBase => { + return (await new ReadByIdDataBaseModelUseCase(DatasetDBModel).call(id)).map(async (model) => { + return (await new IsHaveActiveProcessUseCase().call()).map(async () => { + await DatasetDBModel.findById(id).updateOne({ processStatus: "RUN" }); + console.log(`blenderproc run $PYTHON_BLENDER_PROC --cfg '${JSON.stringify(model)}'`); + return new ExecProcessUseCase().call( + `${model.project.rootDir}/`, + `blenderproc run $PYTHON_BLENDER_PROC --cfg '${JSON.stringify(model)}'`, + id, + new ProcessWatcherAndDatabaseUpdateService(id as unknown as ObjectId) + ); + }); + }); + }; +} diff --git a/server/src/features/datasets/domain/get_dataset_active_project_scenario.ts b/server/src/features/datasets/domain/get_dataset_active_project_scenario.ts new file mode 100644 index 0000000..d5cd6c7 --- /dev/null +++ b/server/src/features/datasets/domain/get_dataset_active_project_scenario.ts @@ -0,0 +1,15 @@ +import { CallbackStrategyWithEmpty, ResponseBase } from "../../../core/controllers/http_controller"; +import { Result } from "../../../core/helpers/result"; +import { SearchDataBaseModelUseCase } from "../../../core/usecases/search_database_model_usecase"; +import { IProjectModel, ProjectDBModel } from "../../_projects/models/project_database_model"; +import { DatasetDBModel } from "../models/dataset_database_model"; + +export class GetDatasetActiveProjectScenario extends CallbackStrategyWithEmpty { + call = async (): ResponseBase => { + return ( + await new SearchDataBaseModelUseCase(ProjectDBModel).call({ isActive: true }, "no active projects") + ).map(async (project) => { + return Result.ok(await DatasetDBModel.find({ project: project._id })); + }); + }; +} diff --git a/server/src/features/datasets/models/dataset_database_model.ts b/server/src/features/datasets/models/dataset_database_model.ts new file mode 100644 index 0000000..a169a3d --- /dev/null +++ b/server/src/features/datasets/models/dataset_database_model.ts @@ -0,0 +1,50 @@ +import { Mongoose, Schema, model } from "mongoose"; +import { IDatasetModel } from "./dataset_validation_model"; +import { projectSchema } from "../../_projects/models/project_database_model"; + +export const DatasetSchema = new Schema({ + name: { + type: String, + }, + local_path: { + type: String, + }, + dataSetObjects: { + type: Array, + of: String, + }, + formBuilder: { + type: Schema.Types.Mixed, + of: String, + }, + unixTime: { + type: Number, + default: Date.now(), + }, + neuralNetworkAction: { + type: String, + }, + neuralNetworkName: { + type: String, + }, + processStatus: { + type: String, + default: "none", + }, + project: { + type: Schema.Types.ObjectId, + ref: projectSchema, + autopopulate: true, + require: true, + }, + processLogs: { + type: String, + }, + datasetType: { + type: String, + }, +}).plugin(require("mongoose-autopopulate")); + +export const datasetSchema = "Dataset"; + +export const DatasetDBModel = model(datasetSchema, DatasetSchema); diff --git a/server/src/features/datasets/models/dataset_validation_model.ts b/server/src/features/datasets/models/dataset_validation_model.ts new file mode 100644 index 0000000..fc5aaf1 --- /dev/null +++ b/server/src/features/datasets/models/dataset_validation_model.ts @@ -0,0 +1,43 @@ +import { Type } from "class-transformer"; +import { IsArray, IsOptional, IsString, ValidateNested } from "class-validator"; +import { IProjectModel } from "../../_projects/models/project_database_model"; + +export class FormBuilderValidationModel { + @IsString() + public result: string; + @IsString() + public context: string; + @IsArray() + public form: []; +} +export enum ProcessStatus { + END = "END", + ERROR = "ERROR", + NEW = "NEW", +} +export interface IDatasetModel { + _id?:string; + name: string; + local_path: string; + dataSetObjects: string[]; + formBuilder: FormBuilderValidationModel; + processLogs: string; + processStatus: ProcessStatus; + project?: IProjectModel; +} + +export class DatasetValidationModel implements IDatasetModel { + @IsString() + public name: string; + @IsArray() + public dataSetObjects: string[]; + @ValidateNested() + @Type(() => FormBuilderValidationModel) + public formBuilder: FormBuilderValidationModel; + public local_path: string; + @IsOptional() + @IsString() + public processStatus: ProcessStatus; + public projectId: string; + public processLogs: string; +} diff --git a/server/src/features/project_instance/domain/create_new_project_scenario.ts b/server/src/features/project_instance/domain/create_new_project_scenario.ts deleted file mode 100644 index 76023b1..0000000 --- a/server/src/features/project_instance/domain/create_new_project_scenario.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { App } from "../../../core/controllers/app"; -import { Result } from "../../../core/helpers/result"; -import { CreateDataBaseModelUseCase } from "../../../core/usecases/create_database_model_usecase"; -import { CreateFolderUseCase } from "../../../core/usecases/create_folder_usecase"; -import { pipelineRealTimeService } from "../../realtime/realtime_presentation"; -import { ProjectInstanceDbModel } from "../models/project_instance_database_model"; -import { ProjectInstanceValidationModel } from "../models/project_instance_validation_model"; -import { v4 as uuidv4 } from "uuid"; -import { SetActiveProjectScenario } from "./set_active_project_use_scenario"; - -export class CreateNewProjectInstanceScenario { - call = async (): Promise> => { - try { - // (await new SetActiveProjectScenario().call(id)).map(() => { - // return Result.ok({ status: "ok" }); - // }); - } catch (error) { - console.log(error); - return Result.error(error as Error); - } - }; -} diff --git a/server/src/features/project_instance/domain/robossembler_assets_network_mapper_scenario.ts b/server/src/features/project_instance/domain/robossembler_assets_network_mapper_scenario.ts deleted file mode 100644 index ead9008..0000000 --- a/server/src/features/project_instance/domain/robossembler_assets_network_mapper_scenario.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { CallbackStrategyWithEmpty, ResponseBase } from "../../../core/controllers/http_controller"; -import { Result } from "../../../core/helpers/result"; -import { RobossemblerAssets } from "../../../core/models/robossembler_assets"; -import { StaticFiles } from "../../../core/models/static_files"; -import { ReadingJsonFileAndConvertingToInstanceClassScenario } from "../../../core/scenarios/read_file_and_json_to_plain_instance_class_scenario"; -import { GetServerAddressUseCase } from "../../../core/usecases/get_server_address_usecase"; -import { PipelineStatusUseCase } from "../../realtime/domain/pipeline_status_usecase"; - -export class RobossemblerAssetsNetworkMapperScenario extends CallbackStrategyWithEmpty { - async call(): ResponseBase { - try { - const result = await new PipelineStatusUseCase().call(); - - return await result.map(async (activeInstanceModel) => { - return ( - await new ReadingJsonFileAndConvertingToInstanceClassScenario(RobossemblerAssets).call( - `${activeInstanceModel.path}${StaticFiles.robossembler_assets}` - ) - ).map((robossemblerAssets) => { - return new GetServerAddressUseCase().call().map((address) => { - return Result.ok( - robossemblerAssets.convertLocalPathsToServerPaths( - `${address}/${activeInstanceModel.rootDir.pathNormalize()}` - ) - ); - }); - }); - }); - } catch (error) { - return Result.error(error); - } - } -} diff --git a/server/src/features/project_instance/domain/upload_file_to_to_project_scenario.ts b/server/src/features/project_instance/domain/upload_file_to_to_project_scenario.ts deleted file mode 100644 index deb06e8..0000000 --- a/server/src/features/project_instance/domain/upload_file_to_to_project_scenario.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { App } from "../../../core/controllers/app"; -import { CallbackStrategyWithFileUpload, ResponseBase } from "../../../core/controllers/http_controller"; -import { Result } from "../../../core/helpers/result"; -import { IFile } from "../../../core/interfaces/file"; -import { CreateDataBaseModelUseCase } from "../../../core/usecases/create_database_model_usecase"; -import { CreateFileUseCase } from "../../../core/usecases/create_file_usecase"; -import { CreateFolderUseCase } from "../../../core/usecases/create_folder_usecase"; -import { MongoIdValidation } from "../../../core/validations/mongo_id_validation"; -import { ProjectInstanceDbModel } from "../models/project_instance_database_model"; -import { ProjectInstanceValidationModel } from "../models/project_instance_validation_model"; -import { v4 as uuidv4 } from "uuid"; -import { SetActiveProjectScenario } from "./set_active_project_use_scenario"; - -export class UploadCadFileToProjectScenario extends CallbackStrategyWithFileUpload { - checkingFileExpression: RegExp = RegExp(".FCStd"); - idValidationExpression = new MongoIdValidation(); - - async call(file: IFile, id: string, description: string): ResponseBase { - const folderName = uuidv4() + "/"; - const model = new ProjectInstanceValidationModel(); - model["project"] = id; - model["description"] = description; - model["rootDir"] = folderName; - model["isActive"] = true; - return (await new CreateFolderUseCase().call(App.staticFilesStoreDir() + folderName)).map(async () => - (await new CreateDataBaseModelUseCase(ProjectInstanceDbModel).call(model)).map(async (databaseModel) => - (await new SetActiveProjectScenario().call(databaseModel.id)).map(async () => - (await new CreateFileUseCase().call(App.staticFilesStoreDir() + folderName + file.name, file.data)).map( - () => { - return Result.ok("ok"); - } - ) - ) - ) - ); - } -} diff --git a/server/src/features/project_instance/project_instance_presentation.ts b/server/src/features/project_instance/project_instance_presentation.ts deleted file mode 100644 index 5732113..0000000 --- a/server/src/features/project_instance/project_instance_presentation.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { CrudController } from "../../core/controllers/crud_controller"; -import { CoreHttpController } from "../../core/controllers/http_controller"; -import { RobossemblerAssets } from "../../core/models/robossembler_assets"; - -import { CreateNewProjectInstanceScenario } from "./domain/create_new_project_scenario"; -import { RobossemblerAssetsNetworkMapperScenario } from "./domain/robossembler_assets_network_mapper_scenario"; -import { SaveActiveSceneScenario } from "./domain/save_active_scene_scenario"; -import { SetActiveProjectScenario } from "./domain/set_active_project_use_scenario"; -import { UploadCadFileToProjectScenario } from "./domain/upload_file_to_to_project_scenario"; -import { ProjectInstanceDbModel } from "./models/project_instance_database_model"; -import { ProjectInstanceValidationModel } from "./models/project_instance_validation_model"; - -export class ProjectInstancePresentation extends CrudController< - ProjectInstanceValidationModel, - typeof ProjectInstanceDbModel -> { - constructor() { - super({ - validationModel: ProjectInstanceValidationModel, - url: "project_instance", - databaseModel: ProjectInstanceDbModel, - }); - - super.post(new CreateNewProjectInstanceScenario().call); - - this.subRoutes.push({ - method: "POST", - subUrl: "set/active/project", - fn: new SetActiveProjectScenario(), - }); - - this.subRoutes.push({ - method: "POST", - subUrl: "upload", - fn: new UploadCadFileToProjectScenario(), - }); - } -} - -export class RobossemblerAssetsPresentation extends CoreHttpController { - constructor() { - super({ - url: "robossembler_assets", - validationModel: RobossemblerAssets, - }); - super.get(new RobossemblerAssetsNetworkMapperScenario().call); - super.post(new SaveActiveSceneScenario().call); - } -} diff --git a/server/src/features/projects/domain/create_new_project_scenario.ts b/server/src/features/projects/domain/create_new_project_scenario.ts new file mode 100644 index 0000000..b2ee76c --- /dev/null +++ b/server/src/features/projects/domain/create_new_project_scenario.ts @@ -0,0 +1,34 @@ +import { App } from "../../../core/controllers/app"; +import { Result } from "../../../core/helpers/result"; +import { v4 as uuidv4 } from "uuid"; +import { IsString } from "class-validator"; +import { ProjectDBModel } from "../../_projects/models/project_database_model"; +import { CreateDataBaseModelUseCase } from "../../../core/usecases/create_database_model_usecase"; +import { CreateFolderUseCase } from "../../../core/usecases/create_folder_usecase"; + +export class ProjectValidationModel { + @IsString() + description: string; +} +export class CreateNewProjectInstanceScenario { + call = async (model: ProjectValidationModel): Promise> => { + try { + const projectFolder = uuidv4(); + return (await new CreateFolderUseCase().call(App.staticFilesStoreDir() + projectFolder)).map(async (_) => { + for await (const el of await ProjectDBModel.find({ isActive: true })) { + el.isActive = false; + await el.save(); + } + + const projectDbModel = await new ProjectDBModel({ + isActive: true, + rootDir: App.staticFilesStoreDir() + projectFolder, + description: model.description, + }).save(); + return Result.ok({ id: projectDbModel._id }); + }); + } catch (error) { + return Result.error(error as Error); + } + }; +} diff --git a/server/src/features/projects/domain/get_active_project_scenario.ts b/server/src/features/projects/domain/get_active_project_scenario.ts new file mode 100644 index 0000000..ecf4b6b --- /dev/null +++ b/server/src/features/projects/domain/get_active_project_scenario.ts @@ -0,0 +1,12 @@ +import { CallbackStrategyWithEmpty, ResponseBase } from "../../../core/controllers/http_controller"; +import { Result } from "../../../core/helpers/result"; +import { SearchDataBaseModelUseCase } from "../../../core/usecases/search_database_model_usecase"; +import { IProjectModel, ProjectDBModel } from "../../_projects/models/project_database_model"; + +export class GetActiveProjectScenario extends CallbackStrategyWithEmpty { + async call(): ResponseBase { + return ( + await new SearchDataBaseModelUseCase(ProjectDBModel).call({ isActive: true }, "no active projects") + ).map((model) => Result.ok({ id: model._id })); + } +} diff --git a/server/src/features/projects/domain/robossembler_assets_network_mapper_scenario.ts b/server/src/features/projects/domain/robossembler_assets_network_mapper_scenario.ts new file mode 100644 index 0000000..c788900 --- /dev/null +++ b/server/src/features/projects/domain/robossembler_assets_network_mapper_scenario.ts @@ -0,0 +1,33 @@ +import { CallbackStrategyWithEmpty, ResponseBase } from "../../../core/controllers/http_controller"; +import { Result } from "../../../core/helpers/result"; +import { RobossemblerAssets } from "../../../core/models/robossembler_assets"; +import { StaticFiles } from "../../../core/models/static_files"; +import { ReadingJsonFileAndConvertingToInstanceClassScenario } from "../../../core/scenarios/read_file_and_json_to_plain_instance_class_scenario"; +import { GetServerAddressUseCase } from "../../../core/usecases/get_server_address_usecase"; +import { ProjectDBModel } from "../../_projects/models/project_database_model"; + +export class RobossemblerAssetsNetworkMapperScenario extends CallbackStrategyWithEmpty { + async call(): ResponseBase { + const projectDbModel = await ProjectDBModel.findOne({ isActive: true }); + if (projectDbModel === null) { + return Result.error("is dont active projects"); + } + const { rootDir } = projectDbModel; + + return new GetServerAddressUseCase().call().map(async (address) => + ( + await new ReadingJsonFileAndConvertingToInstanceClassScenario(RobossemblerAssets).call( + rootDir + StaticFiles.assets + ) + ).map((model) => { + return Result.ok( + model.convertLocalPathsToServerPaths( + `${address}/${ + rootDir.match(new RegExp(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gm))[0] + }/assets` + ) + ); + }) + ); + } +} diff --git a/server/src/features/project_instance/domain/save_active_scene_scenario.ts b/server/src/features/projects/domain/save_active_scene_scenario.ts similarity index 94% rename from server/src/features/project_instance/domain/save_active_scene_scenario.ts rename to server/src/features/projects/domain/save_active_scene_scenario.ts index 6ab753a..2f7b2fa 100644 --- a/server/src/features/project_instance/domain/save_active_scene_scenario.ts +++ b/server/src/features/projects/domain/save_active_scene_scenario.ts @@ -4,7 +4,7 @@ import { RobossemblerAssets } from "../../../core/models/robossembler_assets"; import { StaticFiles } from "../../../core/models/static_files"; import { ReadingJsonFileAndConvertingToInstanceClassScenario } from "../../../core/scenarios/read_file_and_json_to_plain_instance_class_scenario"; import { WriteFileSystemFileUseCase } from "../../../core/usecases/write_file_system_file_usecase"; -import { PipelineStatusUseCase } from "../../realtime/domain/pipeline_status_usecase"; +import { PipelineStatusUseCase } from "../../_realtime/domain/pipeline_status_usecase"; export class SaveActiveSceneScenario extends CallbackStrategyWithValidationModel { validationModel: RobossemblerAssets = new RobossemblerAssets(); diff --git a/server/src/features/project_instance/domain/set_active_project_use_scenario.ts b/server/src/features/projects/domain/set_active_project_use_scenario.ts similarity index 50% rename from server/src/features/project_instance/domain/set_active_project_use_scenario.ts rename to server/src/features/projects/domain/set_active_project_use_scenario.ts index 2d18dc8..5bc82d8 100644 --- a/server/src/features/project_instance/domain/set_active_project_use_scenario.ts +++ b/server/src/features/projects/domain/set_active_project_use_scenario.ts @@ -12,24 +12,31 @@ export class SetActiveProjectScenario extends CallbackStrategyWithIdQuery { idValidationExpression = new MongoIdValidation(); async call(id: string): ResponseBase { - const result = await new ReadByIdDataBaseModelUseCase(ProjectInstanceDbModel).call(id); - // id + try { + const result = await new ReadByIdDataBaseModelUseCase(ProjectInstanceDbModel).call(id); + // id - if (result.isFailure()) { - return result.forward(); - } - const model = result.value; + if (result.isFailure()) { + return result.forward(); + } + const model = result.value; - return await ( - await new CreateFolderUseCase().call(App.staticFilesStoreDir() + model.rootDir) - ).map(async () => { - model.isActive = true; - return (await new UpdateDataBaseModelUseCase(ProjectInstanceDbModel).call(model)).map(async (el) => { - // TODO(IDONTSUDO): move it to a separate UseCase - await ProjectInstanceDbModel.updateMany({ _id: { $ne: el._id }, isActive: { $eq: true } }, { isActive: false }); - await new SetLastActivePipelineToRealTimeServiceScenario().call(); - return Result.ok(`project ${id} is active`); + return await ( + await new CreateFolderUseCase().call(App.staticFilesStoreDir() + model.rootDir) + ).map(async () => { + model.isActive = true; + return (await new UpdateDataBaseModelUseCase(ProjectInstanceDbModel).call(model)).map(async (el) => { + // TODO(IDONTSUDO): move it to a separate UseCase + await ProjectInstanceDbModel.updateMany( + { _id: { $ne: el._id }, isActive: { $eq: true } }, + { isActive: false } + ); + await new SetLastActivePipelineToRealTimeServiceScenario().call(); + return Result.ok(`project ${id} is active`); + }); }); - }); + } catch (error) { + return Result.error("SetActiveProjectScenario error:" + String(error)); + } } } diff --git a/server/src/features/projects/domain/upload_file_to_to_project_scenario.ts b/server/src/features/projects/domain/upload_file_to_to_project_scenario.ts new file mode 100644 index 0000000..e24a7bf --- /dev/null +++ b/server/src/features/projects/domain/upload_file_to_to_project_scenario.ts @@ -0,0 +1,27 @@ +import { CallbackStrategyWithFileUpload, ResponseBase } from "../../../core/controllers/http_controller"; +import { IFile } from "../../../core/interfaces/file"; +import { CreateFileUseCase } from "../../../core/usecases/create_file_usecase"; +import { MongoIdValidation } from "../../../core/validations/mongo_id_validation"; +import { IProjectInstanceModel } from "../models/project_instance_database_model"; +import { ReadByIdDataBaseModelUseCase } from "../../../core/usecases/read_by_id_database_model_usecase"; +import { ProjectDBModel } from "../../_projects/models/project_database_model"; +import { ExecProcessUseCase } from "../../../core/usecases/exec_process_usecase"; + +export class UploadCadFileToProjectScenario extends CallbackStrategyWithFileUpload { + checkingFileExpression: RegExp = new RegExp(".FCStd"); + idValidationExpression = new MongoIdValidation(); + + async call(file: IFile, id: string): ResponseBase { + return (await new ReadByIdDataBaseModelUseCase(ProjectDBModel).call(id)).map( + async (databaseModel) => + (await new CreateFileUseCase().call(`${databaseModel.rootDir}/${file.name}`, file.data)).map( + async () => + await new ExecProcessUseCase().call( + `${databaseModel.rootDir}/`, + '', + `python3 $PYTHON_BLENDER --path '${databaseModel.rootDir}/assets/'` + ) + ) + ); + } +} diff --git a/server/src/features/project_instance/models/project_instance_database_model.ts b/server/src/features/projects/models/project_instance_database_model.ts similarity index 73% rename from server/src/features/project_instance/models/project_instance_database_model.ts rename to server/src/features/projects/models/project_instance_database_model.ts index b0111bc..604f8b9 100644 --- a/server/src/features/project_instance/models/project_instance_database_model.ts +++ b/server/src/features/projects/models/project_instance_database_model.ts @@ -1,5 +1,5 @@ import { Schema, model } from "mongoose"; -import { IProjectModel, projectSchema } from "../../projects/models/project_database_model"; +import { IProjectModel, projectSchema } from "../../_projects/models/project_database_model"; export interface IProjectInstanceModel { _id: string; @@ -10,12 +10,6 @@ export interface IProjectInstanceModel { } export const ProjectInstanceSchema = new Schema({ - project: { - type: Schema.Types.ObjectId, - ref: projectSchema, - autopopulate: true, - default: null, - }, description: { type: String, }, diff --git a/server/src/features/project_instance/models/project_instance_validation_model.ts b/server/src/features/projects/models/project_instance_validation_model.ts similarity index 85% rename from server/src/features/project_instance/models/project_instance_validation_model.ts rename to server/src/features/projects/models/project_instance_validation_model.ts index cb3149a..ef7b6b0 100644 --- a/server/src/features/project_instance/models/project_instance_validation_model.ts +++ b/server/src/features/projects/models/project_instance_validation_model.ts @@ -1,9 +1,6 @@ import { IsMongoId, IsOptional, IsString } from "class-validator"; export class ProjectInstanceValidationModel { - @IsMongoId() - public project: string; - @IsString() public description: string; diff --git a/server/src/features/projects/projects_presentation.ts b/server/src/features/projects/projects_presentation.ts index 253bf2c..1fa0714 100644 --- a/server/src/features/projects/projects_presentation.ts +++ b/server/src/features/projects/projects_presentation.ts @@ -1,13 +1,41 @@ import { CrudController } from "../../core/controllers/crud_controller"; -import { ProjectDBModel } from "./models/project_database_model"; -import { ProjectValidationModel } from "./models/project_validation_model"; +import { ProjectDBModel } from "../_projects/models/project_database_model"; +import { CreateNewProjectInstanceScenario } from "./domain/create_new_project_scenario"; +import { GetActiveProjectScenario } from "./domain/get_active_project_scenario"; +import { RobossemblerAssetsNetworkMapperScenario } from "./domain/robossembler_assets_network_mapper_scenario"; +import { SetActiveProjectScenario } from "./domain/set_active_project_use_scenario"; +import { UploadCadFileToProjectScenario } from "./domain/upload_file_to_to_project_scenario"; +import { ProjectInstanceValidationModel as ProjectsValidationModel } from "./models/project_instance_validation_model"; -export class ProjectsPresentation extends CrudController { +export class ProjectsPresentation extends CrudController { constructor() { super({ - url: "project", - validationModel: ProjectValidationModel, + validationModel: ProjectsValidationModel, + url: "projects", databaseModel: ProjectDBModel, }); + + super.post(new CreateNewProjectInstanceScenario().call); + + this.subRoutes.push({ + method: "POST", + subUrl: "set/active/project", + fn: new SetActiveProjectScenario(), + }); + this.subRoutes.push({ + method: "GET", + subUrl: "get/active/project/id", + fn: new GetActiveProjectScenario(), + }); + this.subRoutes.push({ + method: "POST", + subUrl: "upload", + fn: new UploadCadFileToProjectScenario(), + }); + this.subRoutes.push({ + method: "GET", + subUrl: "assets", + fn: new RobossemblerAssetsNetworkMapperScenario(), + }); } } diff --git a/server/src/main.ts b/server/src/main.ts index f2682ca..1398356 100644 --- a/server/src/main.ts +++ b/server/src/main.ts @@ -3,12 +3,10 @@ import { App } from "./core/controllers/app"; import { SocketSubscriber } from "./core/controllers/socket_controller"; import { extensions } from "./core/extensions/extensions"; import { httpRoutes } from "./core/controllers/routes"; -import { pipelineRealTimeService } from "./features/realtime/realtime_presentation"; -import { main } from "./p"; +import { executorProgramService } from "./core/usecases/exec_process_usecase"; extensions(); -const socketSubscribers = [new SocketSubscriber(pipelineRealTimeService, "realtime")]; +const socketSubscribers = [new SocketSubscriber(executorProgramService, "realtime")]; new App(httpRoutes, socketSubscribers).listen(); -main(); diff --git a/server/test/model/mock_pipelines.ts b/server/test/model/mock_pipelines.ts index 3dd865d..34e73c2 100644 --- a/server/test/model/mock_pipelines.ts +++ b/server/test/model/mock_pipelines.ts @@ -1,6 +1,6 @@ import { EXEC_TYPE } from "../../src/core/models/exec_error_model"; import { IPipeline, IssueType, StackGenerateType } from "../../src/core/models/process_model"; -import { TriggerType } from "../../src/features/triggers/models/trigger_database_model"; +import { TriggerType } from "../../src/features/_triggers/models/trigger_database_model"; export const mockSimplePipeline: IPipeline[] = [ { diff --git a/server/test/services/trigger_service_test.ts b/server/test/services/trigger_service_test.ts index 970168c..1013d44 100644 --- a/server/test/services/trigger_service_test.ts +++ b/server/test/services/trigger_service_test.ts @@ -1,7 +1,7 @@ import { EventsFileChanger, MetaDataFileManagerModel } from "../../src/core/models/meta_data_file_manager_model"; import { TriggerService } from "../../src/core/services/trigger_service"; -import { TriggerType } from "../../src/features/triggers/models/trigger_database_model"; +import { TriggerType } from "../../src/features/_triggers/models/trigger_database_model"; import { assert } from "../test"; abstract class TriggerTest { abstract test(): Promise; diff --git a/server/test/test.ts b/server/test/test.ts index d0487c5..977850e 100644 --- a/server/test/test.ts +++ b/server/test/test.ts @@ -14,7 +14,7 @@ import { UpdateDataBaseModelUseCaseTest } from "./usecases/update_database_model import { PaginationDataBaseModelUseCaseTest } from "./usecases/pagination_database_model_usecase_test"; import { extensions } from "../src/core/extensions/extensions"; import { CrudControllerTest } from "./controllers/crud_controller_test"; -import { TriggerPresentation } from "../src/features/triggers/triggers_presentation"; +import { TriggerPresentation } from "../src/features/_triggers/triggers_presentation"; import { App, Environment, ServerStatus } from "../src/core/controllers/app"; import { httpRoutes } from "../src/core/controllers/routes"; import { DataBaseConnectUseCase } from "../src/core/usecases/database_connect_usecase"; diff --git a/ui/package-lock.json b/ui/package-lock.json index 19b2bf7..656fe65 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -26,15 +26,22 @@ "mobx-react-lite": "^4.0.4", "mobx-store-inheritance": "^1.0.6", "react": "^18.2.0", + "react-accessible-treeview": "^2.8.3", + "react-dnd": "^16.0.1", + "react-dnd-html5-backend": "^16.0.1", "react-dom": "^18.2.0", "react-i18next": "^13.3.1", "react-infinite-scroll-component": "^6.1.0", "react-router-dom": "^6.18.0", "react-scripts": "5.0.1", "reflect-metadata": "^0.1.13", + "rete-connection-plugin": "^2.0.0", + "rete-react-plugin": "^2.0.4", + "rete-render-utils": "^2.0.1", "sass": "^1.66.1", "serve": "^14.2.1", "socket.io-client": "^4.7.2", + "styled-components": "^6.1.8", "three": "^0.159.0", "three-stdlib": "^2.28.9", "three-transform-controls": "^1.0.4", @@ -42,7 +49,8 @@ "typescript": "^4.9.5", "urdf-loader": "^0.12.1", "uuid": "^9.0.1", - "web-vitals": "^2.1.4" + "web-vitals": "^2.1.4", + "xml-formatter": "^3.6.2" }, "devDependencies": { "@types/three": "^0.158.3" @@ -2503,6 +2511,24 @@ "node": ">=10" } }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz", + "integrity": "sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==", + "dependencies": { + "@emotion/memoize": "^0.8.1" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" + }, + "node_modules/@emotion/unitless": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.0.tgz", + "integrity": "sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw==" + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -3232,6 +3258,21 @@ "react-dom": ">=16.9.0" } }, + "node_modules/@react-dnd/asap": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-5.0.2.tgz", + "integrity": "sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A==" + }, + "node_modules/@react-dnd/invariant": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@react-dnd/invariant/-/invariant-4.0.2.tgz", + "integrity": "sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw==" + }, + "node_modules/@react-dnd/shallowequal": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-4.0.2.tgz", + "integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==" + }, "node_modules/@remix-run/router": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.0.tgz", @@ -4048,6 +4089,11 @@ "integrity": "sha512-pXNfAD3KHOdif9EQXZ9deK82HVNaXP5ZIF5RP2QG6OQFNTaY2YIetfrE9t528vEreGQvEPRDDc8muaoYeK0SxQ==", "dev": true }, + "node_modules/@types/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-n4sx2bqL0mW1tvDf/loQ+aMX7GQD3lc3fkCMC55VFNDu/vBOabO+LTIeXKM14xK0ppk5TUGcWRjiSpIlUpghKw==" + }, "node_modules/@types/testing-library__jest-dom": { "version": "5.14.9", "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.9.tgz", @@ -5635,6 +5681,14 @@ "node": ">= 6" } }, + "node_modules/camelize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", + "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/caniuse-api": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", @@ -6244,6 +6298,14 @@ "postcss": "^8.4" } }, + "node_modules/css-color-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", + "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==", + "engines": { + "node": ">=4" + } + }, "node_modules/css-declaration-sorter": { "version": "6.4.1", "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz", @@ -6434,6 +6496,16 @@ "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==" }, + "node_modules/css-to-react-native": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", + "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", + "dependencies": { + "camelize": "^1.0.0", + "css-color-keywords": "^1.0.0", + "postcss-value-parser": "^4.0.2" + } + }, "node_modules/css-tree": { "version": "1.0.0-alpha.37", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", @@ -6882,9 +6954,9 @@ "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" }, - "node_modules/diff-sequences": { + "node_modules/diff-Sequence": { "version": "27.5.1", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", + "resolved": "https://registry.npmjs.org/diff-Sequence/-/diff-Sequence-27.5.1.tgz", "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", "engines": { "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" @@ -6906,6 +6978,16 @@ "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" }, + "node_modules/dnd-core": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-16.0.1.tgz", + "integrity": "sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng==", + "dependencies": { + "@react-dnd/asap": "^5.0.1", + "@react-dnd/invariant": "^4.0.1", + "redux": "^4.2.0" + } + }, "node_modules/dns-packet": { "version": "5.6.1", "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", @@ -8980,7 +9062,6 @@ "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "peer": true, "dependencies": { "react-is": "^16.7.0" } @@ -10154,7 +10235,7 @@ "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", "dependencies": { "chalk": "^4.0.0", - "diff-sequences": "^27.5.1", + "diff-Sequence": "^27.5.1", "jest-get-type": "^27.5.1", "pretty-format": "^27.5.1" }, @@ -14375,6 +14456,17 @@ "node": ">=0.10.0" } }, + "node_modules/react-accessible-treeview": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/react-accessible-treeview/-/react-accessible-treeview-2.8.3.tgz", + "integrity": "sha512-taDTIYZ6p96/zIhJBUKvyGTXcInudatP/9fwKG0BW+VRf1PmU5hOT2FkDovDKzSwj2VSOj1PRx+E6ojhOA+2xA==", + "peerDependencies": { + "classnames": "^2.2.6", + "prop-types": "^15.7.2", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-app-polyfill": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/react-app-polyfill/-/react-app-polyfill-3.0.0.tgz", @@ -14438,6 +14530,43 @@ "node": ">= 12.13.0" } }, + "node_modules/react-dnd": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-16.0.1.tgz", + "integrity": "sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==", + "dependencies": { + "@react-dnd/invariant": "^4.0.1", + "@react-dnd/shallowequal": "^4.0.1", + "dnd-core": "^16.0.1", + "fast-deep-equal": "^3.1.3", + "hoist-non-react-statics": "^3.3.2" + }, + "peerDependencies": { + "@types/hoist-non-react-statics": ">= 3.3.1", + "@types/node": ">= 12", + "@types/react": ">= 16", + "react": ">= 16.14" + }, + "peerDependenciesMeta": { + "@types/hoist-non-react-statics": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-dnd-html5-backend": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-16.0.1.tgz", + "integrity": "sha512-Wu3dw5aDJmOGw8WjH1I1/yTH+vlXEL4vmjk5p+MHxP8HuHJS1lAGeIdG/hze1AvNeXWo/JgULV87LyQOr+r5jw==", + "dependencies": { + "dnd-core": "^16.0.1" + } + }, "node_modules/react-dom": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", @@ -14671,6 +14800,14 @@ "node": ">=8" } }, + "node_modules/redux": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", + "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", + "dependencies": { + "@babel/runtime": "^7.9.2" + } + }, "node_modules/reflect-metadata": { "version": "0.1.14", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz", @@ -14953,6 +15090,69 @@ "node": ">=10" } }, + "node_modules/rete": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/rete/-/rete-2.0.3.tgz", + "integrity": "sha512-/xzcyEBhVXhMZVZHElnYaLKOmTEuwlnul9Wfjvxw5sdl/+6Nqn2nyqIaW4koefrFpIWZy9aitnjnP3zeCMVDuw==", + "hasInstallScript": true, + "peer": true, + "dependencies": { + "@babel/runtime": "^7.21.0" + } + }, + "node_modules/rete-area-plugin": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/rete-area-plugin/-/rete-area-plugin-2.0.2.tgz", + "integrity": "sha512-pMRNRl5jNFwsEcaIWvaHgB9QV4CrqfGipEPpxPrbS1oQXezDJ18sbELU68WceJ534OMAcu/9XiN2VLpGbRWoog==", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "peerDependencies": { + "rete": "^2.0.0" + } + }, + "node_modules/rete-connection-plugin": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/rete-connection-plugin/-/rete-connection-plugin-2.0.1.tgz", + "integrity": "sha512-KE1IcjeOQtHgkByODtWS5hgRJDGhR3Z9sZyJAEd7YMgI6o+KUIflcNjbkvhJvPeIAv6WlEAh7ZkwdLhF9bkr4w==", + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "peerDependencies": { + "rete": "^2.0.1", + "rete-area-plugin": "^2.0.0" + } + }, + "node_modules/rete-react-plugin": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/rete-react-plugin/-/rete-react-plugin-2.0.5.tgz", + "integrity": "sha512-xoui2+Mv6iqpRTxccAu3MZv3+l5LYk4AmtqGWEqlCIwZjplrsAoVeOLYq235spwf+vd3ujzapnycEzYF9aj3cA==", + "dependencies": { + "@babel/runtime": "^7.21.0", + "usehooks-ts": "^2.9.1" + }, + "peerDependencies": { + "react": "^16.8.6 || ^17 || ^18", + "react-dom": "^16.8.6 || ^17 || ^18", + "rete": "^2.0.1", + "rete-area-plugin": "^2.0.0", + "rete-render-utils": "^2.0.0", + "styled-components": "^5.3.6 || ^6" + } + }, + "node_modules/rete-render-utils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/rete-render-utils/-/rete-render-utils-2.0.2.tgz", + "integrity": "sha512-f4kj+dFL5QrebOkjCdwi8htHteDFbKyqrVdFDToEUvGuGod1sdLeKxOPBOhwyYDB4Zxd3Cq84I93vD2etrTL9g==", + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "peerDependencies": { + "rete": "^2.0.0", + "rete-area-plugin": "^2.0.0" + } + }, "node_modules/retry": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", @@ -15535,8 +15735,7 @@ "node_modules/shallowequal": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", - "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==", - "peer": true + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" }, "node_modules/shebang-command": { "version": "2.0.0", @@ -16137,6 +16336,70 @@ "webpack": "^5.0.0" } }, + "node_modules/styled-components": { + "version": "6.1.8", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.8.tgz", + "integrity": "sha512-PQ6Dn+QxlWyEGCKDS71NGsXoVLKfE1c3vApkvDYS5KAK+V8fNWGhbSUEo9Gg2iaID2tjLXegEW3bZDUGpofRWw==", + "dependencies": { + "@emotion/is-prop-valid": "1.2.1", + "@emotion/unitless": "0.8.0", + "@types/stylis": "4.2.0", + "css-to-react-native": "3.2.0", + "csstype": "3.1.2", + "postcss": "8.4.31", + "shallowequal": "1.1.0", + "stylis": "4.3.1", + "tslib": "2.5.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/styled-components" + }, + "peerDependencies": { + "react": ">= 16.8.0", + "react-dom": ">= 16.8.0" + } + }, + "node_modules/styled-components/node_modules/csstype": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" + }, + "node_modules/styled-components/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/styled-components/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + }, "node_modules/stylehacks": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", @@ -16152,6 +16415,11 @@ "postcss": "^8.2.15" } }, + "node_modules/stylis": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.1.tgz", + "integrity": "sha512-EQepAV+wMsIaGVGX1RECzgrcqRRU/0sYOHkeLsZ3fzHaHXZy4DaOOX0vOlGQdlsjkh3mFHAIlVimpwAs4dslyQ==" + }, "node_modules/sucrase": { "version": "3.35.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", @@ -17104,6 +17372,20 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/usehooks-ts": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/usehooks-ts/-/usehooks-ts-2.16.0.tgz", + "integrity": "sha512-bez95WqYujxp6hFdM/CpRDiVPirZPxlMzOH2QB8yopoKQMXpscyZoxOjpEdaxvV+CAWUDSM62cWnqHE0E/MZ7w==", + "dependencies": { + "lodash.debounce": "^4.0.8" + }, + "engines": { + "node": ">=16.15.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -18164,11 +18446,30 @@ } } }, + "node_modules/xml-formatter": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/xml-formatter/-/xml-formatter-3.6.2.tgz", + "integrity": "sha512-enWhevZNOwffZFUhzl1WMcha8lFLZUgJ7NzFs5Ug4ZOFCoNheGYXz1J9Iz/e+cTn9rCkuT1GwTacz+YlmFHOGw==", + "dependencies": { + "xml-parser-xo": "^4.1.0" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/xml-name-validator": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==" }, + "node_modules/xml-parser-xo": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/xml-parser-xo/-/xml-parser-xo-4.1.1.tgz", + "integrity": "sha512-Ggf2y90+Y6e9IK5hoPuembVHJ03PhDSdhldEmgzbihzu9k0XBo0sfcFxaSi4W1PlUSSI1ok+MJ0JCXUn+U4Ilw==", + "engines": { + "node": ">= 14" + } + }, "node_modules/xmlchars": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", diff --git a/ui/package.json b/ui/package.json index 70da4b2..8a23e6b 100644 --- a/ui/package.json +++ b/ui/package.json @@ -44,7 +44,8 @@ "typescript": "^4.9.5", "urdf-loader": "^0.12.1", "uuid": "^9.0.1", - "web-vitals": "^2.1.4" + "web-vitals": "^2.1.4", + "xml-formatter": "^3.6.2" }, "scripts": { "dev": "react-scripts start", diff --git a/ui/public/index.html b/ui/public/index.html index 2371e57..1407629 100644 --- a/ui/public/index.html +++ b/ui/public/index.html @@ -17,5 +17,13 @@
+ + \ No newline at end of file diff --git a/ui/src/core/assets/images/logo.svg b/ui/src/core/assets/images/logo.svg deleted file mode 100644 index 58b91d4..0000000 --- a/ui/src/core/assets/images/logo.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/ui/src/core/assets/images/pose_estemation.jpg b/ui/src/core/assets/images/pose_estemation.jpg new file mode 100644 index 0000000..85ed898 Binary files /dev/null and b/ui/src/core/assets/images/pose_estemation.jpg differ diff --git a/ui/src/core/extensions/array.ts b/ui/src/core/extensions/array.ts index 4431efc..8454e31 100644 --- a/ui/src/core/extensions/array.ts +++ b/ui/src/core/extensions/array.ts @@ -51,4 +51,10 @@ export const ArrayExtensions = () => { return this.indexOf(element) !== -1; }; } + if ([].repeat === undefined) { + // eslint-disable-next-line no-extend-native + Array.prototype.repeat = function (quantity) { + return Array(quantity).fill(this[0]); + }; + } }; diff --git a/ui/src/core/extensions/extensions.ts b/ui/src/core/extensions/extensions.ts index 2b7998c..b77c8b0 100644 --- a/ui/src/core/extensions/extensions.ts +++ b/ui/src/core/extensions/extensions.ts @@ -4,8 +4,12 @@ import { NumberExtensions } from "./number"; import { StringExtensions } from "./string"; export type CallBackVoidFunction = (value: T) => void; + export type CallBackStringVoidFunction = (value: string) => void; export type CallBackEventTarget = (value: EventTarget) => void; +export type OptionalProperties = { + [P in keyof T]?: T[P]; +}; declare global { interface Array { @@ -15,22 +19,36 @@ declare global { isEmpty(): boolean; isNotEmpty(): boolean; hasIncludeElement(element: T): boolean; + repeat(quantity: number): Array; } interface Number { fromArray(): number[]; + toPx(): string; + unixFromDate(): string; + isValid(str: string): boolean; + randRange(min:number,max:number):number } + interface String { isEmpty(): boolean; isNotEmpty(): boolean; + replaceMany(searchValues: string[], replaceValue: string): string; + isEqual(str: string): boolean; + isEqualMany(str: string[]): boolean; } interface Map { addValueOrMakeCallback(key: K, value: V, callBack: CallBackVoidFunction): void; + getKeyFromValueIsExists(value: V): K | undefined; + overrideValue(key: K, value: OptionalProperties): void; + keysToJson(): string; + toArray(): V[]; + getPredicateValue(callBack: (value: V) => boolean): K[]; } interface Vector3 {} } export const extensions = () => { - ArrayExtensions(); StringExtensions(); + ArrayExtensions(); NumberExtensions(); MapExtensions(); }; diff --git a/ui/src/core/extensions/map.ts b/ui/src/core/extensions/map.ts index 22fd3e7..8b349ff 100644 --- a/ui/src/core/extensions/map.ts +++ b/ui/src/core/extensions/map.ts @@ -11,5 +11,51 @@ export const MapExtensions = () => { } }; } + if (Map.prototype.getKeyFromValueIsExists === undefined) { + // eslint-disable-next-line no-extend-native + Map.prototype.getKeyFromValueIsExists = function (value) { + let result; + this.forEach((el, key) => { + if (el === value) { + result = key; + } + }); + return result; + }; + } + if (Map.prototype.overrideValue === undefined) { + // eslint-disable-next-line no-extend-native + Map.prototype.overrideValue = function (key, value) { + const result = this.get(key); + + this.set(key, Object.assign(result, value)); + }; + } + if (Map.prototype.keysToJson === undefined) { + // eslint-disable-next-line no-extend-native + Map.prototype.keysToJson = function () { + const result: any[] = []; + this.forEach((el) => result.push(el)); + return JSON.stringify(result); + }; + } + if (Map.prototype.toArray === undefined) { + // eslint-disable-next-line no-extend-native + Map.prototype.toArray = function () { + return Array.from(this.values()); + }; + } + if (Map.prototype.getPredicateValue === undefined) { + // eslint-disable-next-line no-extend-native + Map.prototype.getPredicateValue = function (callBack) { + const result: any[] = []; + this.forEach((el, key) => { + const callBackExecute = callBack(el); + if (callBackExecute) { + result.push(key); + } + }); + return result; + }; + } }; -Object(); diff --git a/ui/src/core/extensions/number.ts b/ui/src/core/extensions/number.ts index 565ced6..2a33423 100644 --- a/ui/src/core/extensions/number.ts +++ b/ui/src/core/extensions/number.ts @@ -5,4 +5,29 @@ export const NumberExtensions = () => { return Array.from(this.toString()).map((el) => Number(el)); }; } + if (Number().toPx === undefined) { + // eslint-disable-next-line no-extend-native + Number.prototype.toPx = function () { + return String(this) + "px"; + }; + } + if (Number().unixFromDate === undefined) { + // eslint-disable-next-line no-extend-native + Number.prototype.unixFromDate = function () { + const date = new Date(Number(this) * 1000); + return `${date.getUTCFullYear()}.${date.getMonth()}.${date.getDay()} ${date.getHours()}:${date.getMinutes()}`; + }; + } + if (Number().isValid === undefined) { + // eslint-disable-next-line no-extend-native + Number.prototype.isValid = function (str: string) { + return !isNaN(Number(str)); + }; + } + if(Number().randRange === undefined){ + // eslint-disable-next-line no-extend-native + Number.prototype.randRange = function (min,max) { + return Math.random() * (max - min) + min; + } + } }; diff --git a/ui/src/core/extensions/string.ts b/ui/src/core/extensions/string.ts index 86b5246..79709c1 100644 --- a/ui/src/core/extensions/string.ts +++ b/ui/src/core/extensions/string.ts @@ -1,3 +1,4 @@ +/* eslint-disable no-extend-native */ export const StringExtensions = () => { if ("".isEmpty === undefined) { // eslint-disable-next-line no-extend-native @@ -11,4 +12,28 @@ export const StringExtensions = () => { return this.length !== 0; }; } + if ("".replaceMany === undefined) { + String.prototype.replaceMany = function (searchValues: string[], replaceValue: string) { + let result = this as string; + searchValues.forEach((el) => { + result = result.replaceAll(el, replaceValue); + }); + return result; + }; + } + if ("".isEqual === undefined) { + String.prototype.isEqual = function (str: string) { + return this === str; + }; + } + if ("".isEqualMany === undefined) { + String.prototype.isEqualMany = function (str: string[]) { + for (const el of str) { + if (el === this) { + return true; + } + } + return false; + }; + } }; diff --git a/ui/src/features/scene_manager/model/robossembler_assets.ts b/ui/src/core/model/robossembler_assets.ts similarity index 89% rename from ui/src/features/scene_manager/model/robossembler_assets.ts rename to ui/src/core/model/robossembler_assets.ts index 7c98cd8..8e26306 100644 --- a/ui/src/features/scene_manager/model/robossembler_assets.ts +++ b/ui/src/core/model/robossembler_assets.ts @@ -105,6 +105,9 @@ export class Asset { centerMassY: string; @IsString() centerMassZ: string; + @IsArray() + @IsOptional() + actions: string[]; } export class Physics { @@ -116,13 +119,22 @@ export class Physics { export class RobossemblerAssets { @ValidateNested() - @Type(() => Asset) + @Type(() => Asset, { + discriminator: { + property: "type", + subTypes: [ + { value: InstanceRgbCamera, name: InstanceType.RGB_CAMERA }, + { value: SceneSimpleObject, name: InstanceType.SCENE_SIMPLE_OBJECT }, + ], + }, + keepDiscriminatorProperty: true, + }) assets: Asset[]; @IsArray() @Type(() => Instance, { discriminator: { - property: "instanceType", + property: "type", subTypes: [ { value: InstanceRgbCamera, name: InstanceType.RGB_CAMERA }, { value: SceneSimpleObject, name: InstanceType.SCENE_SIMPLE_OBJECT }, diff --git a/ui/src/core/repository/core_three_repository.ts b/ui/src/core/repository/core_three_repository.ts index 1d82d30..531c0c2 100644 --- a/ui/src/core/repository/core_three_repository.ts +++ b/ui/src/core/repository/core_three_repository.ts @@ -21,9 +21,7 @@ import { Quaternion, MeshBasicMaterial, PlaneGeometry, - BoxGeometry, - BufferGeometry, - Line, + BoxGeometry } from "three"; import { TypedEvent } from "../helper/typed_event"; import { Result } from "../helper/result"; @@ -35,13 +33,8 @@ import { } from "../../features/scene_manager/model/scene_assets"; import { SceneMode } from "../../features/scene_manager/model/scene_view"; import { throttle } from "../helper/throttle"; -import { - InstanceRgbCamera, - RobossemblerAssets, - SceneSimpleObject, -} from "../../features/scene_manager/model/robossembler_assets"; -import { CoreVector3 } from "../model/core_vector3"; - +import { Asset, InstanceRgbCamera, RobossemblerAssets, SceneSimpleObject } from "../model/robossembler_assets"; + export enum UserData { selectedObject = "selected_object", cameraInitialization = "camera_initialization", @@ -157,6 +150,15 @@ export class CoreThreeRepository extends TypedEvent { } }); } + loadInstance(asset: Asset, loadCallback?: Function) { + this.loader( + asset.meshPath, + loadCallback ? loadCallback : () => {}, + asset.name, + new Vector3(Number(asset.posX), Number(asset.posY), Number(asset.posZ)), + new Quaternion(0, 0, 0, 0) + ); + } setTransformMode(mode?: SceneMode) { switch (mode) { diff --git a/ui/src/core/repository/http_repository.ts b/ui/src/core/repository/http_repository.ts index dc6b2ef..d11463b 100644 --- a/ui/src/core/repository/http_repository.ts +++ b/ui/src/core/repository/http_repository.ts @@ -4,6 +4,8 @@ import { Result } from "../helper/result"; export enum HttpMethod { GET = "GET", POST = "POST", + DELETE = "DELETE", + PUT = "PUT" } export class HttpError extends Error { status: number; diff --git a/ui/src/core/repository/socket_repository.ts b/ui/src/core/repository/socket_repository.ts index 6eda6e7..697c3ea 100644 --- a/ui/src/core/repository/socket_repository.ts +++ b/ui/src/core/repository/socket_repository.ts @@ -1,14 +1,30 @@ import { Socket, io } from "socket.io-client"; +import { Result } from "../helper/result"; +import { TypedEvent } from "../helper/typed_event"; -export class SocketRepository { +export class SocketRepository extends TypedEvent { serverURL = "ws://localhost:4001"; socket: Socket | undefined; - async connect() { + + async connect():Promise> { const socket = io(this.serverURL); this.socket = socket; socket.connect(); socket.on('realtime', (d) =>{ + console.log("D") console.log(d) + + this.emit({ + event:"realtime", + payload:d + }) }) + if(socket.connected){ + return Result.ok(true) + } + return Result.error(false) } + } + +export const socketRepository = new SocketRepository() \ No newline at end of file diff --git a/ui/src/core/routers/routers.tsx b/ui/src/core/routers/routers.tsx index 75731d4..ccc7d24 100644 --- a/ui/src/core/routers/routers.tsx +++ b/ui/src/core/routers/routers.tsx @@ -4,28 +4,23 @@ import { PipelineInstanceScreen, PipelineInstanceScreenPath, } from "../../features/pipeline_instance_main_screen/pipeline_instance_screen"; -import { - SelectProjectScreen, - SelectProjectScreenPath, -} from "../../features/select_project/presentation/select_project"; -import { - CreatePipelineScreen, - CreatePipelineScreenPath, -} from "../../features/create_pipeline/presentation/create_pipeline_screen"; + import { CreateProjectScreen, CreateProjectScreenPath } from "../../features/create_project/create_project_screen"; -import { - CreateTriggerScreenPath, - TriggerScreen, -} from "../../features/create_trigger/presentation/create_trigger_screen"; -import { - CreateProcessScreen, - CreateProcessScreenPath, -} from "../../features/create_process/presentation/create_process_screen"; -import { - CreateProjectInstancePath, - CreateProjectInstanceScreen, -} from "../../features/create_project_instance/create_project_instance"; + import { SceneManger, SceneManagerPath } from "../../features/scene_manager/presentation/scene_manager"; +import { + BehaviorTreeBuilderPath, + BehaviorTreeBuilderScreen, +} from "../../features/behavior_tree_builder/presentation/behavior_tree_builder_screen"; +import { + StickObjectsMarkingScreen, + StickObjectsMarkingScreenPath, +} from "../../features/_stick_objects_marking/stick_objects_marking_screen"; +import { DataSetScreen, DatasetsScreenPath } from "../../features/dataset/dataset_screen"; +import DetailsScreen, { DetailsScreenPath } from "../../features/details/details_screen"; +import { AssemblesScreen, AssemblesScreenPath } from "../../features/assembles/assembles_screen"; +import SimulationScreen, { SimulationScreenPath } from "../../features/simulations/simulations_screen"; +import { EstimateScreen, EstimateScreenPath } from "../../features/estimate/estimate_screen"; const idURL = ":id"; @@ -38,32 +33,43 @@ export const router = createBrowserRouter([ path: PipelineInstanceScreenPath + idURL, element: , }, - { - path: SelectProjectScreenPath, - element: , - }, - { - path: CreatePipelineScreenPath, - element: , - }, + { path: CreateProjectScreenPath, element: , }, + { - path: CreateTriggerScreenPath, - element: , - }, - { - path: CreateProcessScreenPath, - element: , - }, - { - path: CreateProjectInstancePath + idURL, - element: , - }, - { - path: SceneManagerPath + idURL, + path: SceneManagerPath, element: , }, + { + path: BehaviorTreeBuilderPath, + element: , + }, + { + path: StickObjectsMarkingScreenPath, + element: , + }, + { + path: DatasetsScreenPath, + element: , + }, + { + path: DetailsScreenPath, + element: , + }, + { + path: AssemblesScreenPath, + element: , + }, + { + path: SimulationScreenPath, + element: , + }, + + { + path: EstimateScreenPath, + element: , + }, ]); diff --git a/ui/src/core/ui/button/button.tsx b/ui/src/core/ui/button/button.tsx new file mode 100644 index 0000000..e38256a --- /dev/null +++ b/ui/src/core/ui/button/button.tsx @@ -0,0 +1,33 @@ +import * as React from "react"; +import { CoreText, CoreTextType } from "../text/text"; + +export interface IButtonProps { + block?: boolean; + filled?: boolean; + text?: string; + onClick?: any; + style?:React.CSSProperties +} + +export function CoreButton(props: IButtonProps) { + return ( +
props.onClick?.call()} + style={Object.assign({ + backgroundColor: props.filled ? "rgba(103, 80, 164, 1)" : "", + paddingRight: 20, + paddingLeft: 20, + paddingTop: 10, + paddingBottom: 10, + borderRadius: 24, + border: props.block ? "1px solid rgba(29, 27, 32, 0.12)" : props.filled ? "" : "1px solid black", + },props.style)} + > + +
+ ); +} diff --git a/ui/src/core/ui/form_builder/form_builder.tsx b/ui/src/core/ui/form_builder/form_builder.tsx new file mode 100644 index 0000000..fe30759 --- /dev/null +++ b/ui/src/core/ui/form_builder/form_builder.tsx @@ -0,0 +1,155 @@ +import * as React from "react"; +import { FormViewModel, InputBuilderViewModel, InputType } from "./form_view_model"; +import { observer } from "mobx-react-lite"; +import { FormBuilderStore } from "./form_builder_store"; +import { FormBuilderValidationModel } from "../../../features/dataset/dataset_model"; +import { SelectCore } from "../select/select"; +import { CoreInput } from "../input/input"; +import { Icon } from "../icons/icons"; +import { CoreText, CoreTextType } from "../text/text"; + +export interface IFormBuilder { + formBuilder: FormBuilderValidationModel; + onChange: (change: FormBuilderValidationModel) => void; +} + +export const FormBuilder = observer((props: IFormBuilder) => { + const [store] = React.useState(() => new FormBuilderStore()); + + React.useEffect(() => { + store.init(props.formBuilder.context, props.formBuilder.result); + if (props.formBuilder.form.isNotEmpty()) { + store.formViewModel = new FormViewModel( + props.formBuilder.form.map((el) => InputBuilderViewModel.fromJSON(el)), + props.formBuilder.result, + props.formBuilder.context + ); + props.formBuilder.form.map((el) => InputBuilderViewModel.fromJSON(el)); + } + store.changerForm.on((event) => { + if (event) props.onChange(event); + }); + }, []); + + return ( +
+ {store.isError ? ( + <>Error + ) : ( +
+ {store.formViewModel?.inputs.map((element) => { + if (element.type.isEqual(InputType.ENUM)) { + const values = element.values as string[]; + return ( + store.changeTotalValue(element.id, value)} + label={element.name} + style={{ margin: 20 }} + /> + ); + } + if (element.type.isEqual(InputType.ARRAY)) { + return ( +
+
{ + store.open(element.id); + }} + > + + +
+ + {element.isOpen ? ( +
+ {element.totalValue instanceof Array + ? element.totalValue?.map((subArray, index) => { + return ( +
+
+ + store.deleteTotalValueSubItem(element.id, index)} + /> +
+ + {subArray.map((subSubArrayItem: InputBuilderViewModel, subIndex: number) => { + if (subSubArrayItem.type.isEqual(InputType.ENUM)) { + return ( + <> + String(el)) ?? []} + value={subSubArrayItem.totalValue ?? subSubArrayItem.defaultValue} + onChange={(value) => store.changeTotalSubValue(element.id, subIndex, value)} + label={element.name} + style={{ margin: 5 }} + /> + + ); + } + if (subSubArrayItem.type.isEqualMany([InputType.NUMBER, InputType.STRING])) + return ( +
+ { + store.changeTotalSubValue(element.id, subIndex, e); + }} + validation={ + subSubArrayItem.type.isEqual(InputType.NUMBER) + ? (el) => Number().isValid(el) + : undefined + } + error="только числа" + value={subSubArrayItem.totalValue ?? subSubArrayItem.defaultValue} + label={subSubArrayItem.name} + /> +
+ ); + + return <>Error; + })} +
+ ); + }) + : null} +
+ ) : null} +
+ ); + } + + if (element.type.isEqualMany([InputType.NUMBER, InputType.STRING])) + return ( +
+ Number().isValid(el) : undefined} + onChange={(e) => { + store.changeTotalValue(element.id, e); + }} + value={element.totalValue ?? element.defaultValue} + error="только числа" + label={element.name} + style={{ margin: 20 }} + /> +
+ ); + + return <>Error; + })} +
+ )} +
+ ); +}); diff --git a/ui/src/core/ui/form_builder/form_builder_store.ts b/ui/src/core/ui/form_builder/form_builder_store.ts new file mode 100644 index 0000000..cbf1157 --- /dev/null +++ b/ui/src/core/ui/form_builder/form_builder_store.ts @@ -0,0 +1,101 @@ +import { makeAutoObservable } from "mobx"; +import { FormViewModel } from "./form_view_model"; +import { TypedEvent } from "../../helper/typed_event"; +import { FormBuilderValidationModel } from "../../../features/dataset/dataset_model"; + +export class ChangerForm extends TypedEvent {} + +export class FormBuilderStore { + isError = false; + formViewModel?: FormViewModel; + changerForm: ChangerForm; + numberValidation: RegExp = new RegExp(/^\s*[+-]?(\d+|\d*\.\d+|\d+\.\d*)([Ee][+-]?\d+)?\s*$/); + constructor() { + makeAutoObservable(this); + this.changerForm = new ChangerForm(); + } + + changeTotalSubValue(id: string, subIndex: number, value: string) { + if (this.formViewModel?.inputs) { + this.formViewModel.inputs = this.formViewModel?.inputs.map((el) => { + if (!el.id.isEqual(id)) { + return el; + } else { + if (el.totalValue instanceof Array) { + el.totalValue = el.totalValue.map((subElement) => { + if (subElement instanceof Array) { + subElement.map((subSubElement, i) => { + if (subIndex !== i) { + return subSubElement; + } + subSubElement.totalValue = value; + return subSubElement; + }); + } + return subElement; + }); + return el; + } + return el; + } + }); + + this.changerForm.emit(this.formViewModel?.fromFormBuilderValidationModel()); + } + } + + changeTotalValue(id: string, value: string) { + if (this.formViewModel?.inputs) + this.formViewModel.inputs = this.formViewModel?.inputs.map((el) => { + if (!el.id.isEqual(id)) { + return el; + } + el.totalValue = value; + return el; + }); + this.changerForm.emit(this.formViewModel?.fromFormBuilderValidationModel()); + } + + deleteTotalValueSubItem(id: string, index: number): void { + if (this.formViewModel?.inputs) + this.formViewModel.inputs = this.formViewModel?.inputs.map((el) => { + if (!el.id.isEqual(id)) { + return el; + } else { + if (el.totalValue instanceof Array) { + el.totalValue = el.totalValue.filter((_, i) => index !== i); + return el; + } + return el; + } + }); + this.changerForm.emit(this.formViewModel?.fromFormBuilderValidationModel()); + } + + open = (id: string) => { + if (this.formViewModel) + this.formViewModel.inputs = this.formViewModel?.inputs.map((el) => { + if (!el.id.isEqual(id)) { + return el; + } + + el.isOpen = true; + + if (!(el.totalValue instanceof Array)) { + el.totalValue = []; + } + el.totalValue.push(el.values); + return el; + }); + this.changerForm.emit(this.formViewModel?.fromFormBuilderValidationModel()); + }; + + init(context: string, result: string) { + FormViewModel.fromString(result, context).fold( + (model) => { + this.formViewModel = model; + }, + () => (this.isError = true) + ); + } +} diff --git a/ui/src/core/ui/form_builder/form_view_model.ts b/ui/src/core/ui/form_builder/form_view_model.ts new file mode 100644 index 0000000..581c14c --- /dev/null +++ b/ui/src/core/ui/form_builder/form_view_model.ts @@ -0,0 +1,241 @@ +import { makeAutoObservable, observable } from "mobx"; +import { Result } from "../../helper/result"; +import { v4 as uuidv4 } from "uuid"; +import { FormBuilderValidationModel } from "../../../features/dataset/dataset_model"; + +export enum InputType { + NUMBER = "number", + STRING = "string", + ARRAY = "Array", + ENUM = "Enum", +} + +interface IOperation { + regExp: RegExp; + result: any; +} + +export class InputBuilderViewModel { + id: string; + constructor( + public name: string, + public type: InputType, + public defaultValue: string, + public values: string[] | undefined | InputBuilderViewModel[] = undefined, + public totalValue: any | undefined = undefined, + public isOpen: boolean = false, + public subType: string | undefined = undefined, + id: string | undefined = undefined + ) { + this.id = id ?? uuidv4(); + } + static fromJSON(json: any) { + try { + const value = JSON.parse(json); + return new InputBuilderViewModel( + value.name, + value.type, + value.defaultValue, + value.values, + value.totalValue, + value.isOpen, + value.subType, + value.id + ); + } catch (error) { + console.log("InputBuilderViewModel.fromJSON(): " + json); + throw new Error("InputBuilderViewModel.fromJSON"); + } + } + public toJson(): string { + try { + return JSON.stringify(this); + } catch (error) { + console.log("InputBuilderViewModel.toJson(): " + this.id); + console.log(error); + return ""; + } + } +} + +export const tagParse = new RegExp(/\${.*?}/gm); +export const bracketParser = new RegExp(/<.*?>/gm); +export const enumParser = new RegExp(/ENUM.*=..*?;/gm); +export const enumBodyParse = new RegExp(/".*?;/gm); +export const enumNameParse = new RegExp(/^(.*?)=/gm); +export const typeBodyParse = new RegExp(/{.*?};/gms); +export const typeNameParse = new RegExp(/type .*?{/gms); +export const typeParse = new RegExp(/type.*?};/gms); + +export class FormViewModel { + @observable + inputs: InputBuilderViewModel[]; + + constructor(inputs: InputBuilderViewModel[], public result: string, public context: string) { + this.inputs = inputs; + makeAutoObservable(this); + } + public json() { + const result = this.toResult(); + if (result.isEmpty()) { + console.log("result is Empty error"); + return; + } + try { + return JSON.parse(result.replaceAll("\n", "").replaceAll("\\", "").replaceAll("/", "")); + } catch (error) { + console.log("ERROR: FormViewModel json() " + result); + } + } + public fromFormBuilderValidationModel() { + return new FormBuilderValidationModel( + this.context, + this.result, + this.inputs.map((el) => el.toJson()), + this.json() as any + ); + } + public toResult(): string { + const operations: IOperation[] = []; + + this.inputs.forEach((element) => { + let inputResult = element.totalValue ?? element.defaultValue; + if (element.type.isEqualMany([InputType.STRING, InputType.ENUM])) { + inputResult = `"${String(inputResult)}"`; + } + if (element.type.isEqual(InputType.NUMBER)) { + inputResult = Number(inputResult); + } + if (element.type.isEqual(InputType.ARRAY)) { + if (element.totalValue === undefined) { + inputResult = "[]"; + } else { + inputResult = []; + if (element.totalValue instanceof Array) { + element.totalValue.forEach((el) => { + const objectUnion = {}; + let objectMapperResult = ""; + if (el instanceof Array) + el.forEach((subElement) => { + let subResult = subElement.totalValue ?? subElement.defaultValue; + console.log(subResult); + if (subElement.type.isEqualMany([InputType.STRING, InputType.ENUM])) { + subResult = `"${String(subResult)}"`; + } + if (subElement.type.isEqual(InputType.NUMBER)) { + subResult = Number(subResult); + } + + // @ts-ignore + objectUnion[subElement.name] = subResult; + }); + if (Object.keys(objectUnion).length !== 0) { + if (element.subType) { + objectMapperResult = this.getTypeBody(element.subType); + if (objectMapperResult !== undefined) { + Object.entries(objectUnion).forEach(([key, value]) => { + objectMapperResult = objectMapperResult.replace(new RegExp("\\${" + key + ".*?}"), value as any); + }); + } + } + if (objectMapperResult) + inputResult.push( + objectMapperResult.replaceAll("\n", "").replaceAll("\\", "").replaceAll("/", "").replaceAll(";", "") + ); + } + }); + } + } + } + if (inputResult instanceof Array) inputResult = JSON.stringify(inputResult.map((el) => JSON.parse(el))); + operations.push({ regExp: new RegExp("\\${" + element.name + ".*?}"), result: inputResult }); + }); + + let result = this.result; + + operations.forEach((el) => { + result = result.replace(el.regExp, el.result); + }); + + return result; + } + getTypeBody(subType: string): string { + let result; + this.context.match(typeParse)?.forEach((type) => { + const matchTypeName = type.match(typeNameParse)?.at(0)?.split(" ").at(1); + if (matchTypeName?.isEqual(subType)) { + result = type.match(typeBodyParse)?.at(0); + } + }); + return result as unknown as string; + } + static fromString(result: string, context: string): Result { + try { + const enums = new Map(); + const types = new Map(); + + context.match(enumParser)?.forEach((el) => { + const enumMatch = el.match(enumBodyParse)?.at(0); + if (enumMatch !== undefined) { + const EnumValue = enumMatch.slice(0, enumMatch.length - 2).split(","); + const enumBody = EnumValue.map((el) => el.replaceAll('"', "")); + const enumName = el.match(enumNameParse)?.at(0)?.split(" ").at(1); + if (enumBody.isNotEmpty() && enumName) enums.set(enumName, enumBody); + } + }); + + context.match(typeParse)?.forEach((type) => { + const matchTypeName = type.match(typeNameParse)?.at(0)?.split(" ").at(1); + const typeBody = type.match(typeBodyParse)?.at(0); + + if (typeBody && matchTypeName) types.set(matchTypeName, this.typeParse(typeBody, enums)); + }); + + return Result.ok(new FormViewModel(this.typeParse(result, enums, types), result, context)); + } catch (error) { + console.log(error); + return Result.error(undefined); + } + } + static typeParse( + result: string, + enums: Map, + types: Map | undefined = undefined + ) { + return result + .match(tagParse) + ?.map((el) => { + const inputArray = el.replaceMany(["$", "{", "}"], "").split(":"); + if (el.includes(InputType.ENUM)) { + const enumName = inputArray.at(1)?.replaceMany([InputType.ENUM, "<", ">"], ""); + + if (enumName && enums.get(enumName)) { + return new InputBuilderViewModel(inputArray[0], InputType.ENUM, inputArray[2], enums.get(enumName)); + } + } + if (el.includes(InputType.ARRAY) && types) { + const name = inputArray.at(1)?.replaceMany(["Array<", ">"], ""); + + if (name) { + return new InputBuilderViewModel( + inputArray[0], + InputType.ARRAY, + inputArray[2], + types.get(name), + undefined, + false, + name + ); + } + } + if (el.includes(InputType.NUMBER)) { + return new InputBuilderViewModel(inputArray[0], InputType.NUMBER, inputArray[2]); + } + if (el.includes(InputType.STRING)) { + return new InputBuilderViewModel(inputArray[0], InputType.STRING, inputArray[2]); + } + return el; + }) + .filter((el) => el !== undefined) as InputBuilderViewModel[]; + } +} diff --git a/ui/src/core/ui/form_builder/readme.md b/ui/src/core/ui/form_builder/readme.md new file mode 100644 index 0000000..5673db6 --- /dev/null +++ b/ui/src/core/ui/form_builder/readme.md @@ -0,0 +1,33 @@ +# RESULT + +``` +{ + "scene":{ + "objects": \${OBJECTS_SCENE:Array:[]}, + }, + "camera_position":{ + "center_shell": [\${CENTER_SHELL_1:string:0}, \${COLISION_SHAPE:Enum:"BOX"}] + } +} +``` + +# CONTEXT + +### хранит в себе type и enum для result + +``` +ENUM T = "ObjectDetection","PoseEstimation"; +ENUM L = "POINT","SUN"; + +type MODELS = { +"id": \${IMAGE_FORMAT:Enum:"jpg"},, +"name": \${NAME:string:""}, +"model": \${MODEL:string:"models/1.fbx"} +}; + +type OBJECTS_SCENE = { +"name": \${NAME:string:""}, +"collision_shape": \${COLISION_SHAPE:Enum:"BOX"}, +"loc_xyz": [\${TEST123:string:"test123"}, \${LOC_XYZ_2:number:0}, \${LOC_XYZ_3:number:0}], +}; +``` diff --git a/ui/src/core/ui/icons/icons.tsx b/ui/src/core/ui/icons/icons.tsx new file mode 100644 index 0000000..9952268 --- /dev/null +++ b/ui/src/core/ui/icons/icons.tsx @@ -0,0 +1,334 @@ +import * as React from "react"; +import { Result } from "../../helper/result"; + +export interface IIconsProps { + type: string; + style?: React.CSSProperties; + onClick?: Function; +} + +export function Icon(props: IIconsProps) { + const icon = getIconSvg(props.type); + return icon.fold( + (node) => { + return ( +
{ + if (props.onClick) props.onClick(); + }} + style={props.style} + > + {node} +
+ ); + }, + () => ( +
{ + if (props.onClick) props.onClick(); + }} + style={props.style} + > + + + +
+ ) + ); +} +const getIconSvg = (type: string): Result => { + switch (type) { + case "": + return Result.ok(); + case "Setting": + return Result.ok( + + + + + + + + + + + ); + case "Assembly": + return Result.ok( + + + + + + + + ); + case "Datasets": + return Result.ok( + + + + + + + + + + ); + case "Layers": + return Result.ok( + + + + + + ); + + case "Rocket": + return Result.ok( + + + + + + + + + ); + case "Simulation": + return Result.ok( + + + + + + + + + ); + case "Grade": + return Result.ok( + + + + + + + + + + + + ); + case "DeleteCircle": + return Result.ok( + + + + ); + case "Check": + return Result.ok( + + + + ); + case "PlusCircle": + return Result.ok( + + + + ); + case "Pencil": + return Result.ok( + + + + ); + case "MenuFab": + return Result.ok( + + + + + + + + + + + ); + case "Settings": + return Result.ok( + + + + + + + + + + + ); + } + return Result.error(undefined); +}; diff --git a/ui/src/core/ui/input/input.tsx b/ui/src/core/ui/input/input.tsx new file mode 100644 index 0000000..2aa86de --- /dev/null +++ b/ui/src/core/ui/input/input.tsx @@ -0,0 +1,78 @@ +import * as React from "react"; +import { CoreText, CoreTextType } from "../text/text"; + +interface IInputProps { + label: string; + value?: string; + onChange?: (value: string) => void; + style?: React.CSSProperties; + validation?: (value: string) => boolean; + error?: string; +} + +export const CoreInput = (props: IInputProps) => { + const [value, setValue] = React.useState(() => props.value ?? ""); + const ref = React.useRef(null); + const [isAppendInnerText, setAppendInnerText] = React.useState(true); + React.useEffect(() => { + if (ref.current && isAppendInnerText) { + ref.current.innerText = value; + setAppendInnerText(false); + } + }, [ref, value, isAppendInnerText, setAppendInnerText]); + + + return ( +
+ + + { + const val = e.target.value; + setValue(val) + if (val) { + if (props.validation !== undefined && props.validation(val) && props.onChange) { + props.onChange(val); + return; + } + + if (props.onChange && props.validation === undefined) { + props.onChange(val); + return; + } + } + }} + /> + {value ? ( + props.validation ? ( + props.validation(value) ? null : ( +
{props.error ? props.error : "error"}
+ ) + ) : null + ) : null} +
+ ); +}; diff --git a/ui/src/core/ui/pages/main_page.tsx b/ui/src/core/ui/pages/main_page.tsx new file mode 100644 index 0000000..07efdef --- /dev/null +++ b/ui/src/core/ui/pages/main_page.tsx @@ -0,0 +1,127 @@ +import { DatasetsScreenPath } from "../../../features/dataset/dataset_screen"; +import { Icon } from "../icons/icons"; +import { useNavigate } from "react-router-dom"; +import { Spin } from "antd"; +import { LoadingOutlined } from "@ant-design/icons"; +import React from "react"; +import { SceneManagerPath } from "../../../features/scene_manager/presentation/scene_manager"; +import { AssemblesScreenPath } from "../../../features/assembles/assembles_screen"; +import { DetailsScreenPath } from "../../../features/details/details_screen"; +import { SimulationScreenPath } from "../../../features/simulations/simulations_screen"; +import { EstimateScreenPath } from "../../../features/estimate/estimate_screen"; +import { BehaviorTreeBuilderPath } from "../../../features/behavior_tree_builder/presentation/behavior_tree_builder_screen"; +export interface IBlockProps { + name: string; + isActive: boolean; + path: string; + icon?: string; +} +const Block = (props: IBlockProps) => { + const navigate = useNavigate(); + + return ( +
navigate(props.path)} style={{ height: 56, cursor: "pointer" }}> +
+ +
+
{props.name}
+
+ ); +}; +export interface IMainPageProps { + page: string; + bodyChildren?: JSX.Element; + isLoading?: boolean; +} +export const MainPage = (props: IMainPageProps) => { + const blocksNames = [ + { name: "Детали", path: DetailsScreenPath, icon: "Setting" }, + { name: "Сборки", path: AssemblesScreenPath, icon: "Assembly" }, + { name: "Датасеты", path: DatasetsScreenPath, icon: "Datasets" }, + { name: "Сцена", path: SceneManagerPath, icon: "Layers" }, + { name: "Навыки", path: BehaviorTreeBuilderPath, icon: "Rocket" }, + { name: "Симуляция", path: SimulationScreenPath, icon: "Simulation" }, + { name: "Оценка", path: EstimateScreenPath, icon: "Grade" }, + ]; + const blocks: IBlockProps[] = blocksNames + .map((el) => { + return Object.assign({ isActive: false }, el); + }) + .map((el) => { + if (el.name.isEqual(props.page)) { + el.isActive = true; + return el; + } + return el; + }); + React.useEffect(() => { + document.body.style.overflow = "hidden"; + return () => { + document.body.style.overflow = "scroll"; + }; + }); + return ( +
+
+
+
+ +
+
+ +
+
+
+ {blocks.map((el) => ( + + ))} +
+
+ +
+
+ {props.isLoading ? ( +
+ } /> +
+ ) : ( + <> +
+ {props.bodyChildren} + + )} +
+ ); +}; diff --git a/ui/src/core/ui/select/select.tsx b/ui/src/core/ui/select/select.tsx new file mode 100644 index 0000000..b4cf558 --- /dev/null +++ b/ui/src/core/ui/select/select.tsx @@ -0,0 +1,67 @@ +import React from "react"; +import { CoreText, CoreTextType } from "../text/text"; + +interface ISelectCoreProps { + items: string[]; + value: string; + label: string; + onChange: (value: string) => void; + style?: React.CSSProperties; +} +export const SelectCore = (props: ISelectCoreProps) => { + const ref = React.useRef(null); + const [cursorIsCorses, setCursorIsCorses] = React.useState(false); + const [value, setValue] = React.useState(props.value); + React.useEffect(() => { + ref.current?.addEventListener("mousemove", () => { + setCursorIsCorses(true); + }); + ref.current?.addEventListener("mouseleave", () => { + setCursorIsCorses(false); + }); + }, [ref, setCursorIsCorses]); + + return ( +
+
+ +
{value}
+
+
+ {cursorIsCorses + ? props.items.map((el) => ( +
{ + setValue(el); + props.onChange(el); + }} + style={{ + height: 48, + textAlign: "center", + alignContent: "center", + cursor: "pointer", + borderBottom: "1px solid", + }} + > + {el} +
+ )) + : null} +
+
+ ); +}; diff --git a/ui/src/core/ui/switch/switch.tsx b/ui/src/core/ui/switch/switch.tsx new file mode 100644 index 0000000..d7ed645 --- /dev/null +++ b/ui/src/core/ui/switch/switch.tsx @@ -0,0 +1,19 @@ +import { Icon } from "../icons/icons"; + +interface ISwitchProps { + isSelected: boolean; + id: string; + onChange: (status: boolean, id: string) => void; +} +export const CoreSwitch = (props: ISwitchProps) => { + return ( +
props.onChange(props.isSelected, props.id)} + > +
+ {props.isSelected ? : null} +
+
+ ); +}; diff --git a/ui/src/core/ui/text/text.tsx b/ui/src/core/ui/text/text.tsx index 3b455e6..faa8847 100644 --- a/ui/src/core/ui/text/text.tsx +++ b/ui/src/core/ui/text/text.tsx @@ -2,14 +2,80 @@ import * as React from "react"; export enum CoreTextType { header, + medium, + large, + small, } export interface ITextProps { text: string; type: CoreTextType; + color?: string; } export function CoreText(props: ITextProps) { - if (props.type === CoreTextType.header) return
{props.text}
; + if (props.type === CoreTextType.small) { + return ( +
+ {props.text} +
+ ); + } + if (props.type === CoreTextType.large) { + return ( +
+ {props.text} +
+ ); + } + if (props.type === CoreTextType.medium) + return ( +
+ {props.text} +
+ ); + if (props.type === CoreTextType.header) + return ( +
+ {props.text} +
+ ); return
{props.text}
; } diff --git a/ui/src/features/_stick_objects_marking/model/stick_objects_marking_store_mode.ts b/ui/src/features/_stick_objects_marking/model/stick_objects_marking_store_mode.ts new file mode 100644 index 0000000..ef1699e --- /dev/null +++ b/ui/src/features/_stick_objects_marking/model/stick_objects_marking_store_mode.ts @@ -0,0 +1,7 @@ +export enum StickObjectsMarkingStoreMode { + objectsToWhichItSticks = "objectsToWhichItSticks", + objectThatSticks = "objectThatSticks", + addPointsObjectsToWhichItSticks = "addPointsObjectsToWhichItSticks", + addPointsObjectThatSticks = "addPointsObjectThatSticks", + move = "move", +} diff --git a/ui/src/features/_stick_objects_marking/model/sticky_helper.ts b/ui/src/features/_stick_objects_marking/model/sticky_helper.ts new file mode 100644 index 0000000..df82c3c --- /dev/null +++ b/ui/src/features/_stick_objects_marking/model/sticky_helper.ts @@ -0,0 +1,20 @@ +import { Vector3 } from "three"; + +export class StickyHelper { + objectThatSticksName: string; + objectThatSticksNamePoints: Vector3[] = []; + objectsToWhichItSticksName: string; + objectsToWhichItSticksPoints: Vector3[] = []; + + constructor( + objectThatSticksName: string, + objectThatSticksNamePoints: Vector3[], + objectsToWhichItSticksName: string, + objectsToWhichItSticksPoints: Vector3[] + ) { + this.objectThatSticksName = objectThatSticksName; + this.objectThatSticksNamePoints = objectThatSticksNamePoints; + this.objectsToWhichItSticksName = objectsToWhichItSticksName; + this.objectsToWhichItSticksPoints = objectsToWhichItSticksPoints; + } +} diff --git a/ui/src/features/_stick_objects_marking/model/sticky_loader_mode.ts b/ui/src/features/_stick_objects_marking/model/sticky_loader_mode.ts new file mode 100644 index 0000000..3c21147 --- /dev/null +++ b/ui/src/features/_stick_objects_marking/model/sticky_loader_mode.ts @@ -0,0 +1,4 @@ +export enum StickyLoaderMode { + IN = "IN", + OUT = "OUT", +} diff --git a/ui/src/features/_stick_objects_marking/stick_objects_marking_screen.tsx b/ui/src/features/_stick_objects_marking/stick_objects_marking_screen.tsx new file mode 100644 index 0000000..dc16830 --- /dev/null +++ b/ui/src/features/_stick_objects_marking/stick_objects_marking_screen.tsx @@ -0,0 +1,148 @@ +import * as React from "react"; +import { observer } from "mobx-react-lite"; +import { useParams } from "react-router-dom"; +import { Button } from "antd"; +import { StickObjectsMarkingStore } from "./stick_objects_marking_store"; +import { Vector3 } from "three"; +import { StickObjectsMarkingStoreMode } from "./model/stick_objects_marking_store_mode"; +import { StickyLoaderMode } from "./model/sticky_loader_mode"; + +export const StickObjectsMarking = "/stick/objects/marking"; +interface StickButtonsProps { + isVisible: boolean; + name: string; + groupMode: StickObjectsMarkingStoreMode; + storeMode: StickObjectsMarkingStoreMode; + storeModePoints: StickObjectsMarkingStoreMode; + setMode: Function; + setPointMode: Function; + points: Vector3[]; + body: string; +} + +export const StickButtons: React.FunctionComponent = observer((props) => { + return ( + <> + {props.isVisible ? ( + <> + {props.name} + + {props.points.map((el) => { + return ( + <> + {el.x} {el.y} {el.z} + + ); + })} + + ) : ( + + )} + + ); +}); +export const StickObjectsMarkingScreenPath = "/sticky/objects/mark"; +export const StickObjectsMarkingScreen = observer(() => { + const canvasRef = React.useRef(null); + const [store] = React.useState(() => new StickObjectsMarkingStore()); + const id = useParams().id as string; + + React.useEffect(() => { + store.init(); + store.loadScene(canvasRef.current!); + document.body.style.overflow = "hidden"; + return () => { + document.body.style.overflow = "scroll"; + store.dispose(); + }; + }, [id, store]); + + return ( +
+ +
+
+ {store.stickyObjects?.map((el) => { + return ( +
+
+ + +
+
{el.name}
+
+ ); + })} +
+ {/*
+ {Object.keys(store.points).map((el) => { + // @ts-expect-error + const v = store.points[el]; + return ( + <> +
+ {el as string}:{v} +
+ + ); + })} +
Marking objects for sticking
+
+ store.setMode(StickObjectsMarkingStoreMode.objectThatSticks)} + groupMode={StickObjectsMarkingStoreMode.objectThatSticks} + name={store.objectThatSticksName} + storeModePoints={StickObjectsMarkingStoreMode.addPointsObjectThatSticks} + isVisible={store.objectThatSticksName !== undefined} + setPointMode={() => store.setMode(StickObjectsMarkingStoreMode.addPointsObjectThatSticks)} + points={store.objectThatSticksNamePoints} + body="objectThatSticksName" + /> + store.setMode(StickObjectsMarkingStoreMode.objectsToWhichItSticks)} + groupMode={StickObjectsMarkingStoreMode.objectsToWhichItSticks} + name={store.objectsToWhichItSticksName} + storeModePoints={StickObjectsMarkingStoreMode.addPointsObjectsToWhichItSticks} + isVisible={store.objectsToWhichItSticksName !== undefined} + setPointMode={() => store.setMode(StickObjectsMarkingStoreMode.addPointsObjectsToWhichItSticks)} + /> + + +
+
*/} +
+
+ ); +}); diff --git a/ui/src/features/_stick_objects_marking/stick_objects_marking_store.tsx b/ui/src/features/_stick_objects_marking/stick_objects_marking_store.tsx new file mode 100644 index 0000000..dc68b23 --- /dev/null +++ b/ui/src/features/_stick_objects_marking/stick_objects_marking_store.tsx @@ -0,0 +1,120 @@ +import makeAutoObservable from "mobx-store-inheritance"; +import { Box3, Vector2, Vector3 } from "three"; +import { UiErrorState } from "../../core/store/base_store"; +import { HttpError } from "../../core/repository/http_repository"; +import { UiBaseError } from "../../core/model/ui_base_error"; +import { RobossemblerFiles } from "../scene_manager/model/scene_assets"; +import { StickObjectsMarkingThreeRepository } from "./stick_objects_marking_three_repository"; +import { UserData } from "../../core/repository/core_three_repository"; +import { SceneHttpRepository } from "../scene_manager/data/scene_repository"; +import { Asset, RobossemblerAssets } from "../../core/model/robossembler_assets"; +import { StickObjectsMarkingStoreMode } from "./model/stick_objects_marking_store_mode"; +import { StickyHelper } from "./model/sticky_helper"; +import { StickyLoaderMode } from "./model/sticky_loader_mode"; + +export class StickObjectsMarkingStore extends UiErrorState { + mode: StickObjectsMarkingStoreMode; + stickObjectsMarkingThreeRepository: null | StickObjectsMarkingThreeRepository = null; + sceneHttpRepository: SceneHttpRepository; + objectThatSticksName: string; + objectThatSticksNamePoints: Vector3[] = []; + objectsToWhichItSticksName: string; + objectsToWhichItSticksPoints: Vector3[] = []; + sceneAssets?: RobossemblerAssets; + points = {}; + + get stickyObjects() { + return this.sceneAssets?.assets.filter((el) => el.actions.includes("Sticking")); + } + + constructor() { + super(); + makeAutoObservable(this); + this.points = {}; + this.mode = StickObjectsMarkingStoreMode.move; + this.sceneHttpRepository = new SceneHttpRepository(); + } + updatePoint = (key: string, value: any) => { + // @ts-expect-error + this.points[key] = value; + }; + onSaveSticky(): void {} + setMode(stickObjectsMarkingStoreMode: StickObjectsMarkingStoreMode): void { + this.mode = stickObjectsMarkingStoreMode; + } + + loaderWatcher() {} + + async init(): Promise { + this.mapOk("sceneAssets", this.sceneHttpRepository.getRobossemblerAssets()); + } + + errorHandingStrategy = (error: HttpError) => { + if (error.status === 404) { + this.errors.push(new UiBaseError(`${RobossemblerFiles.robossemblerAssets} not found to project`)); + } + }; + + async loadScene(canvasRef: HTMLCanvasElement) { + this.loadWebGl(canvasRef); + } + watcherSceneEditorObject() {} + loadWebGl(canvasRef: HTMLCanvasElement): void { + this.stickObjectsMarkingThreeRepository = new StickObjectsMarkingThreeRepository( + canvasRef as HTMLCanvasElement, + this.watcherSceneEditorObject, + this.updatePoint + ); + this.stickObjectsMarkingThreeRepository.stickyHelperLoader([ + new StickyHelper( + this.objectThatSticksName, + this.objectThatSticksNamePoints, + this.objectsToWhichItSticksName, + this.objectsToWhichItSticksPoints + ), + ]); + this.stickObjectsMarkingThreeRepository.render(); + window.addEventListener("click", (event) => this.clickLister(event)); + } + + clickLister(event: MouseEvent) { + const vector = new Vector2(); + vector.x = (event.clientX / window.innerWidth) * 2 - 1; + vector.y = -(event.clientY / window.innerHeight) * 2 + 1; + if (this.mode) { + if (this.mode === StickObjectsMarkingStoreMode.move) { + this.stickObjectsMarkingThreeRepository?.setRayCastAndGetFirstObject(vector).fold( + (success) => this.stickObjectsMarkingThreeRepository?.setTransformControlsAttach(success), + (_error) => this.stickObjectsMarkingThreeRepository?.disposeTransformControlsMode() + ); + } + this.stickObjectsMarkingThreeRepository?.setRayCast(vector).map((touch) => { + const objectMagnetism = touch.filter((el) => el.object.userData[UserData.objectForMagnetism] === true); + const BoundBoxVector = new Box3().setFromObject(objectMagnetism[0].object).getCenter(new Vector3()); + const centerRelativeVector = new Vector3().subVectors(BoundBoxVector, objectMagnetism[0].point); + if (objectMagnetism.isNotEmpty()) { + if (this.mode === StickObjectsMarkingStoreMode.objectsToWhichItSticks) { + this.objectsToWhichItSticksName = objectMagnetism[0].object.name; + } + if (this.mode === StickObjectsMarkingStoreMode.objectThatSticks) { + this.objectThatSticksName = objectMagnetism[0].object.name; + } + if (this.mode === StickObjectsMarkingStoreMode.addPointsObjectThatSticks) { + this.objectThatSticksNamePoints.push(centerRelativeVector); + } + if (this.mode === StickObjectsMarkingStoreMode.addPointsObjectsToWhichItSticks) { + this.objectsToWhichItSticksPoints.push(centerRelativeVector); + } + } + }); + } + } + loadAsset(asset: Asset, mode: StickyLoaderMode): void { + this.stickObjectsMarkingThreeRepository?.loadInstance(asset,() =>{ + + }); + } + dispose() { + window.removeEventListener("click", this.clickLister); + } +} diff --git a/ui/src/features/_stick_objects_marking/stick_objects_marking_three_repository.ts b/ui/src/features/_stick_objects_marking/stick_objects_marking_three_repository.ts new file mode 100644 index 0000000..67f3846 --- /dev/null +++ b/ui/src/features/_stick_objects_marking/stick_objects_marking_three_repository.ts @@ -0,0 +1,79 @@ +import { Box3, BoxGeometry, Mesh, MeshBasicMaterial, Object3D, Vector3 } from "three"; +import { CoreThreeRepository } from "../../core/repository/core_three_repository"; +import { CoreVector3 } from "../../core/model/core_vector3"; +import { StickyHelper } from "./model/sticky_helper"; + +export class StickObjectsMarkingThreeRepository extends CoreThreeRepository { + stickyObjects: StickyHelper[]; + drawUiPoint: Function; + constructor(htmlCanvasRef: HTMLCanvasElement, watcherSceneEditorObject: Function, updatePoint: Function) { + super(htmlCanvasRef, watcherSceneEditorObject); + this.drawUiPoint = updatePoint; + this.sceneWatcher(); + } + + getStickyObject(name: string, pointNameHelper: string, index: number) { + const objectThatSticksNameMesh = this.scene.getObjectByName(name); + const pointName = objectThatSticksNameMesh!.name + ":point:" + index + pointNameHelper; + return this.scene.getObjectByName(pointName); + } + + mapperStickyObject(point: Vector3, index: number, name: string, pointNameHelper: string) { + const objectThatSticksNameMesh = this.scene.getObjectByName(name); + const pointName = objectThatSticksNameMesh!.name + ":point:" + index + pointNameHelper; + const sceneObject = this.scene.getObjectByName(pointName); + let pointMesh: Object3D; + if (sceneObject) { + pointMesh = sceneObject; + } else { + pointMesh = new Mesh(new BoxGeometry(1.1, 1.1, 1.1), new MeshBasicMaterial({ color: "#8BC34A" })); + } + pointMesh.position.copy(new Box3().setFromObject(objectThatSticksNameMesh!).getCenter(new Vector3()).add(point)); + pointMesh.name = pointName; + if (sceneObject === undefined) this.scene.add(pointMesh); + } + + stickyHelperLoader(stickyObjects: StickyHelper[]) { + this.stickyObjects = stickyObjects; + + stickyObjects.forEach((el) => { + el.objectThatSticksNamePoints.forEach((point, index) => + this.mapperStickyObject(point, index, el.objectThatSticksName, "objectThatSticksNamePoints") + ); + el.objectsToWhichItSticksPoints.forEach((point, index) => + this.mapperStickyObject(point, index, el.objectsToWhichItSticksName, "objectsToWhichItSticksPoints") + ); + }); + } + getCenter = (obj: Object3D) => new Box3().setFromObject(obj).getCenter(new Vector3()); + + sceneWatcher() { + this.transformControls.addEventListener("objectChange", (event) => { + //@ts-expect-error + const sceneActiveObject = event.target.object as Mesh; + + this.stickyObjects.forEach((stickyHelper) => { + if (sceneActiveObject.name === stickyHelper.objectThatSticksName) { + const objectsToWhichItSticksPointLocalVector = stickyHelper.objectsToWhichItSticksPoints[0]; + const globalVectorObjStickyName = this.scene.getObjectByName(stickyHelper.objectThatSticksName); + + const globalVectorObjToWhichSticks = this.scene.getObjectByName(stickyHelper.objectsToWhichItSticksName); + + const objectsToWhichItSticksNamePosition = new CoreVector3(globalVectorObjToWhichSticks!.position).add( + objectsToWhichItSticksPointLocalVector + ).vector; + + this.scene + .getObjectByName("cube2:point:0objectThatSticksNamePoints") + ?.position.copy(objectsToWhichItSticksNamePosition); + + globalVectorObjStickyName?.position.copy( + this.scene + .getObjectByName("cube2:point:0objectThatSticksNamePoints")! + .position.add(objectsToWhichItSticksPointLocalVector) + ); + } + }); + }); + } +} diff --git a/ui/src/features/all_projects/data/project_repository.ts b/ui/src/features/all_projects/data/project_repository.ts index 4cc8038..237d4f4 100644 --- a/ui/src/features/all_projects/data/project_repository.ts +++ b/ui/src/features/all_projects/data/project_repository.ts @@ -1,14 +1,16 @@ -import { ActivePipeline } from "../../../core/model/active_pipeline"; import { HttpMethod, HttpRepository } from "../../../core/repository/http_repository"; import { IProjectModel } from "../model/project_model"; +export interface UUID { + id: string; +} export class ProjectRepository extends HttpRepository { async getAllProject() { - return this._jsonRequest(HttpMethod.GET, "/project_instance"); + return this._jsonRequest(HttpMethod.GET, "/projects"); } async getActivePipeline() { - return this._jsonRequest(HttpMethod.GET, "/realtime"); + return this._jsonRequest(HttpMethod.GET, "/projects/get/active/project/id"); } async setActivePipeline(id: string) { return this._jsonRequest(HttpMethod.POST, `/project_instance/set/active/project?id=${id}`); diff --git a/ui/src/features/all_projects/model/project_model.ts b/ui/src/features/all_projects/model/project_model.ts index e74166b..4d39434 100644 --- a/ui/src/features/all_projects/model/project_model.ts +++ b/ui/src/features/all_projects/model/project_model.ts @@ -2,7 +2,7 @@ import { PipelineModel } from "../../create_project/create_project_repository"; export interface IProjectModel { _id?: string; - pipelines: [PipelineModel]; rootDir: string; description: string; + isActive?: boolean; } diff --git a/ui/src/features/all_projects/presentation/all_projects_screen.tsx b/ui/src/features/all_projects/presentation/all_projects_screen.tsx index 92bcea5..c7d1041 100644 --- a/ui/src/features/all_projects/presentation/all_projects_screen.tsx +++ b/ui/src/features/all_projects/presentation/all_projects_screen.tsx @@ -3,19 +3,19 @@ import { AllProjectStore } from "./all_projects_store"; import { ProjectRepository } from "../data/project_repository"; import { LoadPage } from "../../../core/ui/pages/load_page"; import { observer } from "mobx-react-lite"; -import { SelectProjectScreenPath } from "../../select_project/presentation/select_project"; import { useNavigate } from "react-router-dom"; import { Button } from "antd"; import { PipelineInstanceScreenPath } from "../../pipeline_instance_main_screen/pipeline_instance_screen"; +import { CreateProjectScreenPath } from "../../create_project/create_project_screen"; export const AllProjectScreenPath = "/"; export const AllProjectScreen: React.FunctionComponent = observer(() => { - const [allProjectStore] = React.useState(() => new AllProjectStore(new ProjectRepository())); + const [store] = React.useState(() => new AllProjectStore(new ProjectRepository())); const navigate = useNavigate(); React.useEffect(() => { - allProjectStore.init(); - }, [allProjectStore]); + store.init(); + }, [store]); return ( <> @@ -23,35 +23,23 @@ export const AllProjectScreen: React.FunctionComponent = observer(() => { largeText={"Projects"} needBackButton={false} minText="create project?" - path={SelectProjectScreenPath} - isError={allProjectStore.isError} - isLoading={allProjectStore.isLoading} + path={CreateProjectScreenPath} + isError={store.isError} + isLoading={store.isLoading} children={
-

Projects

-
- - {allProjectStore.activePipeline?.projectId ?? "loading"} -
- {allProjectStore.projectsModels?.map((el) => { + {store.projectsModels?.map((el) => { return (
- + {el.isActive ? ( + + ) : null}
{el.description}
); diff --git a/ui/src/features/all_projects/presentation/all_projects_store.ts b/ui/src/features/all_projects/presentation/all_projects_store.ts index 7e512dc..b73e6d9 100644 --- a/ui/src/features/all_projects/presentation/all_projects_store.ts +++ b/ui/src/features/all_projects/presentation/all_projects_store.ts @@ -1,5 +1,5 @@ import makeAutoObservable from "mobx-store-inheritance"; -import { ProjectRepository } from "../data/project_repository"; +import { ProjectRepository, UUID } from "../data/project_repository"; import { IProjectModel } from "../model/project_model"; import { SimpleErrorState } from "../../../core/store/base_store"; import { ActivePipeline } from "../../../core/model/active_pipeline"; @@ -21,8 +21,8 @@ export class ProjectView { } export class AllProjectStore extends SimpleErrorState { projectsModels?: IProjectModel[]; + activeProjectId?: UUID; repository: ProjectRepository; - activePipeline?: ActivePipeline; constructor(repository: ProjectRepository) { super(); this.repository = repository; @@ -31,18 +31,14 @@ export class AllProjectStore extends SimpleErrorState { async getProjects(): Promise { await this.mapOk("projectsModels", this.repository.getAllProject()); } - - async getActiveProject(): Promise { - await this.mapOk("activePipeline", this.repository.getActivePipeline()); + async getActiveProjectId(): Promise { + await this.mapOk("activeProjectId", this.repository.getActivePipeline()); } - async init() { - await Promise.all([this.getProjects(), this.getActiveProject()]); - await this.projectViewGenerate(); - } - projectViewGenerate() { - this.projectsModels = this.projectsModels?.filter((el) => el._id !== this.activePipeline?.projectId); + await Promise.all([this.getProjects(), this.getActiveProjectId()]); + this.projectsModels?.map((el) => (el._id === this.activeProjectId ? ((el.isActive = true), el) : el)); } + async setPipelineActive(id: string) { await this.httpHelper(this.repository.setActivePipeline(id)); } diff --git a/ui/src/features/assembles/assembles_screen.tsx b/ui/src/features/assembles/assembles_screen.tsx new file mode 100644 index 0000000..f0ff83d --- /dev/null +++ b/ui/src/features/assembles/assembles_screen.tsx @@ -0,0 +1,8 @@ +import * as React from "react"; +import { MainPage } from "../../core/ui/pages/main_page"; + +export interface IAssemblesScreenProps {} +export const AssemblesScreenPath = "/assembles"; +export function AssemblesScreen(props: IAssemblesScreenProps) { + return ; +} diff --git a/ui/src/features/behavior_tree_builder/model/editor_view.ts b/ui/src/features/behavior_tree_builder/model/editor_view.ts new file mode 100644 index 0000000..800d994 --- /dev/null +++ b/ui/src/features/behavior_tree_builder/model/editor_view.ts @@ -0,0 +1,89 @@ +import { TypedEvent } from "../../../core/helper/typed_event"; +import { NodeEditor } from "rete"; +import { AreaExtra, Schemes } from "../presentation/ui/editor/editor"; +import { Result } from "../../../core/helper/result"; +import { AreaPlugin } from "rete-area-plugin"; + +export interface BtDrawDragAndDropView { + x: number; + y: number; + name: string; +} + +export class BtNodeView extends TypedEvent {} + +export class ReteObserver extends TypedEvent {} +export class BtBuilderModel { + public static result = ""; + static fromReteScene(editor: NodeEditor, area: AreaPlugin): Result { + try { + this.getFirstSequence(editor).map((sortedSequence) => { + const firstNodeId = sortedSequence.getKeyFromValueIsExists(1) as string; + this.findSequence(firstNodeId, editor, sortedSequence, 2); + this.result += `<${this.getNodeLabelAtId(editor, firstNodeId)} id="${firstNodeId}">`; + this.toXML(sortedSequence as Map, editor, area, firstNodeId); + this.result += ``; + }); + return Result.ok(this.result); + } catch (error) { + return Result.error("BtBuilderModel fromReteScene error"); + } + } + public static getNodeLabelAtId(editor: NodeEditor, id: string) { + return editor.getNode(id).label; + } + + static toXML( + sortedSequence: Map, + editor: NodeEditor, + area: AreaPlugin, + activeElementId = "" + ) { + editor.getConnections().forEach((el) => { + if (el.source === activeElementId) { + this.result += `<${this.getNodeLabelAtId(editor, el.target)} id="${el.target}">`; + this.toXML(sortedSequence, editor, area, el.target); + this.result += ``; + } + }); + } + + public static getBtPriorities(ids: string[]) {} + public static findSequence( + nodeId: string, + editor: NodeEditor, + result: Map, + lvl: number + ): Map | undefined { + const connections: string[] = []; + + editor.getConnections().forEach((el) => { + if (el.source === nodeId) { + connections.push(el.target); + } + }); + + if (connections.isEmpty()) return result as Map; + let lv = lvl; + connections.forEach((el) => { + result.set(el, lvl); + this.findSequence(el, editor, result, (lv += 1)); + }); + } + public static getFirstSequence(editor: NodeEditor): Result> { + const node = new Map(); + editor.getNodes().forEach((el) => { + node.set(el.id, 1); + }); + + editor.getConnections().forEach((el) => { + node.set(el.target, false); + }); + + const key = node.getKeyFromValueIsExists(1); + if (key === undefined) { + return Result.error(undefined); + } + return Result.ok(node); + } +} diff --git a/ui/src/features/behavior_tree_builder/model/node_behavior_tree.ts b/ui/src/features/behavior_tree_builder/model/node_behavior_tree.ts new file mode 100644 index 0000000..00b0040 --- /dev/null +++ b/ui/src/features/behavior_tree_builder/model/node_behavior_tree.ts @@ -0,0 +1,48 @@ +import { AreaExtra, Schemes } from "../presentation/ui/editor/editor"; +import { NodeEditor } from "rete"; +import { AreaPlugin } from "rete-area-plugin"; + +export interface Position2D { + x: number; + y: number; +} + +export class NodeBehaviorTree { + label: string; + id: string; + outputs: string[]; + inputs: string[]; + connectTo?: string; + position: Position2D; + + constructor(label: string, id: string, outputs: string[], inputs: string[], position: Position2D) { + this.label = label; + this.id = id; + this.outputs = outputs; + this.inputs = inputs; + this.connectTo = undefined; + this.position = position; + } + + static fromReteScene(editor: NodeEditor, area: AreaPlugin): NodeBehaviorTree[] { + const nodes = new Map(); + editor + .getNodes() + .forEach((el) => + nodes.set( + el.id, + new NodeBehaviorTree( + el.label, + el.id, + Object.keys(el.outputs), + Object.keys(el.inputs), + area.nodeViews.get(el.id)!.position + ) + ) + ); + editor.getConnections().forEach((el) => console.log(el)); + editor.getConnections().forEach((el) => nodes.overrideValue(el.target, { connectTo: el.source })); + + return nodes.toArray(); + } +} diff --git a/ui/src/features/behavior_tree_builder/presentation/behavior_tree_builder_screen.tsx b/ui/src/features/behavior_tree_builder/presentation/behavior_tree_builder_screen.tsx index a85c764..249d3a9 100644 --- a/ui/src/features/behavior_tree_builder/presentation/behavior_tree_builder_screen.tsx +++ b/ui/src/features/behavior_tree_builder/presentation/behavior_tree_builder_screen.tsx @@ -1,10 +1,12 @@ import * as React from "react"; import { CoreText, CoreTextType } from "../../../core/ui/text/text"; +import { Button, Input } from "antd"; import { useRete } from "rete-react-plugin"; import { createEditor } from "./ui/editor/editor"; import { SkillTree } from "./ui/skill_tree/skill_tree"; import { BehaviorTreeBuilderStore } from "./behavior_tree_builder_store"; +import { MainPage } from "../../../core/ui/pages/main_page"; export const behaviorTreeBuilderScreenPath = "behavior/tree/screen/path"; @@ -12,27 +14,24 @@ const skills = { name: "", children: [ { - name: "arm", - children: [{ name: "move to", interface: "Vector3", out: "vo" }], + name: "Actions", + children: [{ name: "MoveToPose", interface: "Vector3", out: "vo" }], }, { - name: "image", + name: "Control", children: [ - { - name: "object detection", - interface: "image", - out: "BoundBox", - }, - { name: "people detection", interface: "image", out: "BoundBox" }, + { name: "Fallback", interface: "Vector3", out: "vo" }, + { name: "Sequence", interface: "Vector3", out: "vo" }, ], }, ], }; export const behaviorTreeBuilderStore = new BehaviorTreeBuilderStore(); - +export const BehaviorTreeBuilderPath = "/behavior/tree/"; export function BehaviorTreeBuilderScreen() { - const [store] = React.useState(behaviorTreeBuilderStore); + const store = behaviorTreeBuilderStore; const [ref] = useRete(createEditor); + React.useEffect(() => { store.init(); @@ -42,9 +41,9 @@ export function BehaviorTreeBuilderScreen() { // @ts-expect-error ref.current.offsetLeft, // @ts-expect-error - Number(String(ref.current.style.width).replaceAll("px", "")), + ref.current.clientWidth, // @ts-expect-error - Number(String(ref.current.style.height).replaceAll("px", "")) + ref.current.clientHeight ); return () => { @@ -53,54 +52,24 @@ export function BehaviorTreeBuilderScreen() { }, [store, ref]); return ( -
-
- -
-
-
-
-
-
-
-
- -
-
+ +
+ {/* */} -
-
-
+
+
+ + } + /> ); } diff --git a/ui/src/features/behavior_tree_builder/presentation/behavior_tree_builder_store.tsx b/ui/src/features/behavior_tree_builder/presentation/behavior_tree_builder_store.tsx index 8a2439b..a67032a 100644 --- a/ui/src/features/behavior_tree_builder/presentation/behavior_tree_builder_store.tsx +++ b/ui/src/features/behavior_tree_builder/presentation/behavior_tree_builder_store.tsx @@ -1,24 +1,133 @@ import makeAutoObservable from "mobx-store-inheritance"; - +import xmlFormat from "xml-formatter"; import { HttpError } from "../../../core/repository/http_repository"; import { UiErrorState } from "../../../core/store/base_store"; -import { BtNodeView } from "./ui/editor/editor_view"; +import { BtBuilderModel, BtNodeView, ReteObserver } from "../model/editor_view"; +import { NodeEditor } from "rete"; +import { AreaExtra, Schemes } from "./ui/editor/editor"; +import { AreaPlugin } from "rete-area-plugin"; +import { NodeBehaviorTree } from "../model/node_behavior_tree"; + interface I2DArea { x: number; y: number; w: number; h: number; } + export class BehaviorTreeBuilderStore extends UiErrorState { + saveBt(): void { + this.reteNode?.emit("200"); + } + validateBt() {} area?: I2DArea; - errorHandingStrategy: (error: HttpError) => void; btNodeView: BtNodeView = new BtNodeView(); + reteNode?: ReteObserver; + canRun = true; + nodes: NodeBehaviorTree[] = [ + { + label: "Sequence", + id: "1", + outputs: ["a"], + inputs: ["a"], + position: { x: -488.1303402823156, y: -227.90861570911895 }, + }, + { + label: "Sequence", + id: "2", + outputs: ["a"], + inputs: ["a"], + connectTo: "1", + position: { x: 119.95735514023244, y: -182.24902817216878 }, + }, + { + label: "Sequence", + id: "6", + outputs: ["a"], + inputs: ["a"], + connectTo: "2", + position: { x: 402.06111561958505, y: -100.97814086372247 }, + }, + { + label: "MoveToPose", + id: "7", + outputs: ["a"], + inputs: ["a"], + connectTo: "6", + position: { x: 932.0688448287292, y: 90.10780461923443 }, + }, + { + label: "MoveToPose", + id: "8", + outputs: ["a"], + inputs: ["a"], + connectTo: "6", + position: { x: 898.1684213039546, y: -119.80545806921208 }, + }, + { + label: "MoveToPose", + id: "3", + outputs: [], + inputs: ["a"], + connectTo: "1", + position: { x: 208.65749743442188, y: 16.256489050033785 }, + }, + { + label: "MoveToPose", + id: "4", + outputs: [], + inputs: ["a"], + connectTo: "1", + position: { x: -50.46426323140673, y: 99.22642447650014 }, + }, + { + label: "Sequence", + id: "5", + outputs: ["a"], + inputs: ["a"], + connectTo: "1", + position: { x: -234.54554662642457, y: 245.4012710608967 }, + }, + ]; + constructor() { super(); makeAutoObservable(this); } - canRun = true; - draw() {} + + setBt(value: string): void { + JSON.parse(value) as BtNodeView[]; + } + bt = async (editor: NodeEditor, area: AreaPlugin) => { + (await BtBuilderModel.fromReteScene(editor, area)).fold( + (s) => { + console.log( + xmlFormat(` + + + ${s} + + + + + + + + + + + + `) + ); + }, + (_) => _ + ); + }; + copyBt = () => { + this.reteNode?.emit("200"); + }; + errorHandingStrategy: (error: HttpError) => void; + dragEnd = (e: EventTarget) => { if (this.canRun) { //@ts-expect-error @@ -26,7 +135,7 @@ export class BehaviorTreeBuilderStore extends UiErrorState { this.canRun = false; setTimeout(() => { this.canRun = true; - }, 1000); + }, 100); } }; drawSkillCheck(x: number, y: number, name: string) { @@ -38,16 +147,14 @@ export class BehaviorTreeBuilderStore extends UiErrorState { drawPoint.y < this.area!.y + this.area!.h && drawPoint.y + drawPoint.h > this.area!.y ) { - this.drawSkill(x, y - (this.area!.y + this.area!.h / 2), name); + this.btNodeView.emit({ + x: x, + y: y - (this.area!.y + this.area!.h / 2), + name: name, + }); } } - drawSkill(x: number, y: number, name: string) { - this.btNodeView.emit({ - x: x, - y: y, - name: name, - }); - } + async init(): Promise {} dragZoneSetOffset(offsetTop: number, offsetWidth: number, width: number, height: number) { this.area = { diff --git a/ui/src/features/behavior_tree_builder/presentation/ui/editor/custom_connection.tsx b/ui/src/features/behavior_tree_builder/presentation/ui/editor/custom_connection.tsx index a5696af..d426ce5 100644 --- a/ui/src/features/behavior_tree_builder/presentation/ui/editor/custom_connection.tsx +++ b/ui/src/features/behavior_tree_builder/presentation/ui/editor/custom_connection.tsx @@ -14,7 +14,7 @@ const Svg = styled.svg` const Path = styled.path<{ styles?: (props: any) => any }>` fill: none; - stroke-width: 5px; + stroke-width: 3px; stroke: black; pointer-events: auto; ${(props) => props.styles && props.styles(props)} diff --git a/ui/src/features/behavior_tree_builder/presentation/ui/editor/custom_socket.tsx b/ui/src/features/behavior_tree_builder/presentation/ui/editor/custom_socket.tsx index fbbc456..4cdc4c4 100644 --- a/ui/src/features/behavior_tree_builder/presentation/ui/editor/custom_socket.tsx +++ b/ui/src/features/behavior_tree_builder/presentation/ui/editor/custom_socket.tsx @@ -1,21 +1,21 @@ import * as React from "react"; import { ClassicPreset } from "rete"; import styled from "styled-components"; -import { $socketsize } from "./vars"; const Styles = styled.div` display: inline-block; cursor: pointer; - border: 1px solid grey; - width: ${$socketsize}px; - height: ${$socketsize * 2}px; + border: 1px solid black; + width: 20px; + height: 20px; vertical-align: middle; - background: #fff; + background: black; z-index: 2; box-sizing: border-box; &:hover { background: #ddd; } + border-radius: 50%; `; export function CustomSocket(props: { data: T }) { diff --git a/ui/src/features/behavior_tree_builder/presentation/ui/editor/editor.tsx b/ui/src/features/behavior_tree_builder/presentation/ui/editor/editor.tsx index 44dbe3c..3b5d810 100644 --- a/ui/src/features/behavior_tree_builder/presentation/ui/editor/editor.tsx +++ b/ui/src/features/behavior_tree_builder/presentation/ui/editor/editor.tsx @@ -3,28 +3,39 @@ import { NodeEditor, GetSchemes, ClassicPreset } from "rete"; import { AreaPlugin, AreaExtensions } from "rete-area-plugin"; import { ConnectionPlugin, Presets as ConnectionPresets } from "rete-connection-plugin"; import { ReactPlugin, Presets, ReactArea2D } from "rete-react-plugin"; -import { CustomNode } from "./custom_node"; import { CustomSocket } from "./custom_socket"; import { CustomConnection } from "./custom_connection"; import { addCustomBackground } from "./custom_background"; -import { StyledNode } from "./style_node"; import { behaviorTreeBuilderStore } from "../../behavior_tree_builder_screen"; +import { SequenceNode } from "./nodes/controls_node"; +import { ReteObserver } from "../../../model/editor_view"; -type Schemes = GetSchemes>; -type AreaExtra = ReactArea2D; +export type Schemes = GetSchemes>; +export type AreaExtra = ReactArea2D; export async function createEditor(container: HTMLElement) { const socket = new ClassicPreset.Socket("socket"); + const observer = new ReteObserver(); + + behaviorTreeBuilderStore.reteNode = observer; behaviorTreeBuilderStore.btNodeView.on(async (event) => { setTimeout(async () => { const node = new ClassicPreset.Node(event.name); + const { x, y } = areaContainer.area.pointer; - console.log(x, y); + + node.addOutput("a", new ClassicPreset.Output(socket)); + node.addInput("a", new ClassicPreset.Input(socket)); await editor.addNode(node); await areaContainer.translate(node.id, { x, y }); - }, 50); + }, 100); }); + + observer.on(() => { + behaviorTreeBuilderStore.bt(editor, areaContainer); + }); + const editor = new NodeEditor(); const areaContainer = new AreaPlugin(container); const connection = new ConnectionPlugin(); @@ -38,13 +49,7 @@ export async function createEditor(container: HTMLElement) { Presets.classic.setup({ customize: { node(context) { - if (context.payload.label === "Fully customized") { - return CustomNode; - } - if (context.payload.label === "Override styles") { - return StyledNode; - } - return Presets.classic.Node; + return SequenceNode; }, socket(_context) { return CustomSocket; @@ -66,18 +71,27 @@ export async function createEditor(container: HTMLElement) { AreaExtensions.simpleNodesOrder(areaContainer); - const a = new ClassicPreset.Node("Override styles"); + for await (const el of behaviorTreeBuilderStore.nodes) { + const node = new ClassicPreset.Node(el.label); + node.id = el.id; + el.outputs.forEach((outputName) => { + node.addOutput(outputName, new ClassicPreset.Output(socket)); + }); + el.inputs.forEach((inputName) => { + node.addInput(inputName, new ClassicPreset.Input(socket)); + }); - a.addOutput("a", new ClassicPreset.Output(socket)); - a.addInput("a", new ClassicPreset.Input(socket)); - await editor.addNode(a); + await editor.addNode(node); + await areaContainer.translate(node.id, el.position); + } - const b = new ClassicPreset.Node("Fully customized"); - b.addOutput("a", new ClassicPreset.Output(socket)); - b.addInput("a", new ClassicPreset.Input(socket)); - await editor.addNode(b); - - await editor.addConnection(new ClassicPreset.Connection(a, "a", b, "a")); + behaviorTreeBuilderStore.nodes.forEach(async (el) => { + if (el.connectTo) { + const nodeConnectTo = editor.getNode(el.connectTo!); + const nodeConnect = editor.getNode(el.id); + await editor.addConnection(new ClassicPreset.Connection(nodeConnectTo, "a", nodeConnect, "a")); + } + }); setTimeout(() => { AreaExtensions.zoomAt(areaContainer, editor.getNodes()); diff --git a/ui/src/features/behavior_tree_builder/presentation/ui/editor/nodes/background.css b/ui/src/features/behavior_tree_builder/presentation/ui/editor/nodes/background.css new file mode 100644 index 0000000..6db054b --- /dev/null +++ b/ui/src/features/behavior_tree_builder/presentation/ui/editor/nodes/background.css @@ -0,0 +1,19 @@ +.fill-area { + display: table; + z-index: -1; + position: absolute; + top: -320000px; + left: -320000px; + width: 640000px; + height: 640000px; +} + +.background { + background-color: #ffffff; + opacity: 1; + background-image: linear-gradient(#f1f1f1 3.2px, transparent 3.2px), + linear-gradient(90deg, #f1f1f1 3.2px, transparent 3.2px), linear-gradient(#f1f1f1 1.6px, transparent 1.6px), + linear-gradient(90deg, #f1f1f1 1.6px, #ffffff 1.6px); + background-size: 80px 80px, 80px 80px, 16px 16px, 16px 16px; + background-position: -3.2px -3.2px, -3.2px -3.2px, -1.6px -1.6px, -1.6px -1.6px; +} diff --git a/ui/src/features/behavior_tree_builder/presentation/ui/editor/nodes/controls_node.tsx b/ui/src/features/behavior_tree_builder/presentation/ui/editor/nodes/controls_node.tsx new file mode 100644 index 0000000..a098d48 --- /dev/null +++ b/ui/src/features/behavior_tree_builder/presentation/ui/editor/nodes/controls_node.tsx @@ -0,0 +1,161 @@ +import * as React from "react"; +import { ClassicScheme, RenderEmit, Presets } from "rete-react-plugin"; +import styled, { css } from "styled-components"; +import { $nodewidth, $socketmargin, $socketsize } from "./vars"; + +const { RefSocket, RefControl } = Presets.classic; + +type NodeExtraData = { width?: number; height?: number }; + +export const NodeStyles = styled.div any }>` + background: blue; + border: 2px solid grey; + + cursor: pointer; + box-sizing: border-box; + width: ${(props) => (Number.isFinite(props.width) ? `${props.width}px` : `${$nodewidth}px`)}; + height: ${(props) => (Number.isFinite(props.height) ? `${props.height}px` : "auto")}; + padding-bottom: 6px; + + user-select: none; + &:hover { + background: #333; + } + ${(props) => + props.selected && + css` + border-color: red; + `} + .title { + color: white; + font-family: sans-serif; + font-size: 18px; + padding: 8px; + position: relative; + top: 12px; + text-align-last: center; + } + .output { + text-align: right; + } + .input { + text-align: left; + } + .output-socket { + text-align: right; + margin-right: -1px; + display: inline-block; + } + .input-socket { + text-align: left; + margin-left: -1px; + display: inline-block; + } + .input-title, + .output-title { + vertical-align: middle; + color: white; + display: inline-block; + font-family: sans-serif; + font-size: 14px; + margin: ${$socketmargin}px; + line-height: ${$socketsize}px; + } + .input-control { + z-index: 1; + width: calc(100% - ${$socketsize + 2 * $socketmargin}px); + vertical-align: middle; + display: inline-block; + } + .control { + display: block; + padding: ${$socketmargin}px ${$socketsize / 2 + $socketmargin}px; + } + ${(props) => props.styles && props.styles(props)} +`; + +function sortByIndex(entries: T) { + entries.sort((a, b) => { + const ai = a[1]?.index || 0; + const bi = b[1]?.index || 0; + + return ai - bi; + }); +} + +type Props = { + data: S["Node"] & NodeExtraData; + styles?: () => any; + emit: RenderEmit; +}; +export type NodeComponent = (props: Props) => JSX.Element; + +export function SequenceNode(props: Props) { + const inputs = Object.entries(props.data.inputs); + const outputs = Object.entries(props.data.outputs); + const controls = Object.entries(props.data.controls); + const selected = props.data.selected || false; + + const { id, label, width, height } = props.data; + + sortByIndex(inputs); + sortByIndex(outputs); + sortByIndex(controls); + + return ( + +
{}} className="title" data-testid="title"> + {label} +
{id}
+
+ + {outputs.map( + ([key, output]) => + output && ( +
+
+ {output?.label} +
+ +
+ ) + )} + + {controls.map(([key, control]) => { + return control ? : null; + })} + {inputs.map( + ([key, input]) => + input && ( +
+ + {input && (!input.control || !input.showControl) && ( +
+ {input?.label} +
+ )} + {input?.control && input?.showControl && ( + + + + )} +
+ ) + )} +
+ ); +} diff --git a/ui/src/features/behavior_tree_builder/presentation/ui/editor/nodes/custom_node.tsx b/ui/src/features/behavior_tree_builder/presentation/ui/editor/nodes/custom_node.tsx new file mode 100644 index 0000000..30f0d86 --- /dev/null +++ b/ui/src/features/behavior_tree_builder/presentation/ui/editor/nodes/custom_node.tsx @@ -0,0 +1,164 @@ +import * as React from "react"; +import { ClassicScheme, RenderEmit, Presets } from "rete-react-plugin"; +import styled, { css } from "styled-components"; +import { $nodewidth, $socketmargin, $socketsize } from "./vars"; + +const { RefSocket, RefControl } = Presets.classic; + +type NodeExtraData = { width?: number; height?: number }; + +export const NodeStyles = styled.div any }>` + background: black; + border: 2px solid grey; + border-radius: 10px; + cursor: pointer; + box-sizing: border-box; + width: ${(props) => (Number.isFinite(props.width) ? `${props.width}px` : `${$nodewidth}px`)}; + height: ${(props) => (Number.isFinite(props.height) ? `${props.height}px` : "auto")}; + padding-bottom: 6px; + position: relative; + user-select: none; + &:hover { + background: #333; + } + ${(props) => + props.selected && + css` + border-color: red; + `} + .title { + color: white; + font-family: sans-serif; + font-size: 18px; + padding: 8px; + height: 100%; + } + .output { + text-align: right; + } + .input { + text-align: left; + } + .output-socket { + text-align: right; + margin-right: -1px; + display: inline-block; + } + .input-socket { + text-align: left; + margin-left: -1px; + display: inline-block; + } + .input-title, + .output-title { + vertical-align: middle; + color: white; + display: inline-block; + font-family: sans-serif; + font-size: 14px; + margin: ${$socketmargin}px; + line-height: ${$socketsize}px; + } + .input-control { + z-index: 1; + width: calc(100% - ${$socketsize + 2 * $socketmargin}px); + vertical-align: middle; + display: inline-block; + } + .control { + display: block; + padding: ${$socketmargin}px ${$socketsize / 2 + $socketmargin}px; + } + ${(props) => props.styles && props.styles(props)} +`; + +function sortByIndex(entries: T) { + entries.sort((a, b) => { + const ai = a[1]?.index || 0; + const bi = b[1]?.index || 0; + + return ai - bi; + }); +} + +type Props = { + data: S["Node"] & NodeExtraData; + styles?: () => any; + emit: RenderEmit; +}; +export type NodeComponent = (props: Props) => JSX.Element; + +export function CustomNode(props: Props) { + const inputs = Object.entries(props.data.inputs); + const outputs = Object.entries(props.data.outputs); + const controls = Object.entries(props.data.controls); + const selected = props.data.selected || false; + const { id, label, width, height } = props.data; + + sortByIndex(inputs); + sortByIndex(outputs); + sortByIndex(controls); + + return ( + +
{ + e.stopPropagation(); + }} + className="title" + data-testid="title" + > + {label} +
+ + {outputs.map( + ([key, output]) => + output && ( +
+
BODY
+
+ {output?.label} +
+ +
+ ) + )} + + {controls.map(([key, control]) => { + return control ? : null; + })} + {inputs.map( + ([key, input]) => + input && ( +
+ + {input && (!input.control || !input.showControl) && ( +
+ {input?.label} +
+ )} + {input?.control && input?.showControl && ( + + + + )} +
+ ) + )} +
+ ); +} diff --git a/ui/src/features/behavior_tree_builder/presentation/ui/editor/nodes/style_node.tsx b/ui/src/features/behavior_tree_builder/presentation/ui/editor/nodes/style_node.tsx new file mode 100644 index 0000000..f0cb56b --- /dev/null +++ b/ui/src/features/behavior_tree_builder/presentation/ui/editor/nodes/style_node.tsx @@ -0,0 +1,30 @@ +import { Presets } from "rete-react-plugin"; +import { css } from "styled-components"; +import "./background.css"; + +const styles = css<{ selected?: boolean }>` + background: #ebebeb; + border-color: #646464; + .title { + color: #646464; + } + &:hover { + background: #f2f2f2; + } + .output-socket { + margin-right: -1px; + } + .input-socket { + margin-left: -1px; + } + ${(props) => + props.selected && + css` + border-color: red; + `} +`; + +export function StyledNode(props: any) { + // eslint-disable-next-line react/jsx-pascal-case + return styles} {...props} />; +} diff --git a/ui/src/features/behavior_tree_builder/presentation/ui/editor/nodes/vars.tsx b/ui/src/features/behavior_tree_builder/presentation/ui/editor/nodes/vars.tsx new file mode 100644 index 0000000..c79b882 --- /dev/null +++ b/ui/src/features/behavior_tree_builder/presentation/ui/editor/nodes/vars.tsx @@ -0,0 +1,3 @@ +export const $nodewidth = 200; +export const $socketmargin = 6; +export const $socketsize = 16; diff --git a/ui/src/features/behavior_tree_builder/presentation/ui/skill_tree/skill_tree.tsx b/ui/src/features/behavior_tree_builder/presentation/ui/skill_tree/skill_tree.tsx index 0acb353..b314948 100644 --- a/ui/src/features/behavior_tree_builder/presentation/ui/skill_tree/skill_tree.tsx +++ b/ui/src/features/behavior_tree_builder/presentation/ui/skill_tree/skill_tree.tsx @@ -16,10 +16,10 @@ interface IRefListerProps { } export const RefListener = (props: IRefListerProps) => { - const canvasRef = React.useRef(null); + const ref = React.useRef(null); React.useEffect(() => { - if (canvasRef.current) { - canvasRef.current.addEventListener("dragend", (e) => { + if (ref.current) { + ref.current.addEventListener("dragend", (e) => { // @ts-expect-error if (e.target.innerHTML) { // @ts-expect-error @@ -27,7 +27,7 @@ export const RefListener = (props: IRefListerProps) => { } }); } - }, [canvasRef, props]); + }, [ref, props]); return (
@@ -36,7 +36,7 @@ export const RefListener = (props: IRefListerProps) => { props.handleSelect(e); }} /> - + {props.element.name}
diff --git a/ui/src/features/create_pipeline/data/create_pipeline_repository.ts b/ui/src/features/create_pipeline/data/create_pipeline_repository.ts deleted file mode 100644 index 7532737..0000000 --- a/ui/src/features/create_pipeline/data/create_pipeline_repository.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { HttpMethod, HttpRepository } from "../../../core/repository/http_repository"; -import { ITriggerModel } from "../../../core/model/trigger_model"; -import { Result } from "../../../core/helper/result"; -import { IProcess } from "../../create_process/model/process_model"; -import { PipelineModelDataBase } from "../model/pipeline_model"; - -export class CreatePipelineRepository extends HttpRepository { - async savePipeline(model: PipelineModelDataBase): Promise> { - return await this._jsonRequest(HttpMethod.POST, `/pipeline`, model); - } - async getTriggers(page = 1): Promise> { - return await this._jsonRequest(HttpMethod.GET, `/trigger?${page}`); - } - - async getProcessed(page = 1): Promise> { - return await this._jsonRequest(HttpMethod.GET, `/process?${page}`); - } -} diff --git a/ui/src/features/create_pipeline/model/pipeline_model.ts b/ui/src/features/create_pipeline/model/pipeline_model.ts deleted file mode 100644 index 74352a1..0000000 --- a/ui/src/features/create_pipeline/model/pipeline_model.ts +++ /dev/null @@ -1,8 +0,0 @@ -export interface IColor { - color: string; -} - -export interface PipelineModelDataBase { - process: string; - trigger: string; -} \ No newline at end of file diff --git a/ui/src/features/create_pipeline/presentation/create_pipeline_screen.tsx b/ui/src/features/create_pipeline/presentation/create_pipeline_screen.tsx deleted file mode 100644 index aac631a..0000000 --- a/ui/src/features/create_pipeline/presentation/create_pipeline_screen.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import * as React from "react"; -import { Row, Button } from "antd"; -import { LoadPage } from "../../../core/ui/pages/load_page"; - -import { observer } from "mobx-react-lite"; -import { Icon, List } from "../../../core/ui/list/list"; -import { CreateTriggerScreenPath } from "../../create_trigger/presentation/create_trigger_screen"; -import { CreateProcessScreenPath } from "../../create_process/presentation/create_process_screen"; -import { CreatePipelineStore } from "./create_pipeline_store"; -import { CreatePipelineRepository } from "../data/create_pipeline_repository"; - -export const CreatePipelineScreenPath = "/create_pipeline"; - -export const CreatePipelineScreen: React.FunctionComponent = observer(() => { - const [createPipelineStore] = React.useState(() => new CreatePipelineStore(new CreatePipelineRepository())); - - React.useEffect(() => {}, [createPipelineStore]); - return ( - <> - - - { - return { text: el.description, id: el._id }; - })} - onClick={(e) => createPipelineStore.addProcess(e.text, e.id!)} - icon={Icon.add} - /> -
- - { - createPipelineStore.filterPipelineViewModel(index); - }} - /> -
- - { - return { text: el.description, id: el._id }; - })} - onClick={(e) => createPipelineStore.addTrigger(e.text, e.id!)} - icon={Icon.add} - /> -
- - } - /> - - ); -}); diff --git a/ui/src/features/create_pipeline/presentation/create_pipeline_store.ts b/ui/src/features/create_pipeline/presentation/create_pipeline_store.ts deleted file mode 100644 index e3b076a..0000000 --- a/ui/src/features/create_pipeline/presentation/create_pipeline_store.ts +++ /dev/null @@ -1,99 +0,0 @@ -import makeAutoObservable from "mobx-store-inheritance"; -import { CreatePipelineRepository } from "../data/create_pipeline_repository"; -import { ITriggerModel } from "../../../core/model/trigger_model"; -import { IProcess } from "../../create_process/model/process_model"; -import { message } from "antd"; -import { SimpleErrorState } from "../../../core/store/base_store"; - -enum Type { - PROCESS, - TRIGGER, -} -export interface UnionView { - text: string; - color: string; - type: Type; - uuid?: string; -} - -export class CreatePipelineStore extends SimpleErrorState { - repository: CreatePipelineRepository; - triggersModels: ITriggerModel[] = []; - processModels: IProcess[] = []; - pipelineViewModels: UnionView[] = []; - - constructor(repository: CreatePipelineRepository) { - super(); - this.repository = repository; - makeAutoObservable(this); - this.init(); - } - private init() { - this.loadTriggers(); - this.loadProcess(); - } - - filterPipelineViewModel(index: number): void { - this.pipelineViewModels = this.pipelineViewModels.filter((_el, i) => i !== index); - } - addTrigger(e: string, id: string): void { - const lastElement = this.pipelineViewModels.lastElement(); - if (this.pipelineViewModels.length === 2) { - return; - } - if (lastElement !== undefined) { - if (lastElement.type !== Type.PROCESS) { - message.error("Need process"); - - return; - } - } - this.pipelineViewModels.push({ - uuid: id, - text: e, - color: "blanchedalmond", - type: Type.TRIGGER, - }); - } - addProcess(e: string, id: string): void { - const lastElement = this.pipelineViewModels.lastElement(); - if (this.pipelineViewModels.length === 2) { - return; - } - if (lastElement !== undefined) { - if (lastElement.type !== Type.TRIGGER) { - message.error("Need trigger"); - return; - } - } - - this.pipelineViewModels.push({ - uuid: id, - text: e, - color: "activeborder", - type: Type.PROCESS, - }); - } - - async createPipeline(): Promise { - if (this.pipelineViewModels.isEmpty()) { - message.error("not found pipelines process"); - return; - } - const triggerId = this.pipelineViewModels.find((el) => el.type === Type.TRIGGER)!.uuid as string; - const processId = this.pipelineViewModels.find((el) => el.type === Type.PROCESS)!.uuid as string; - - this.repository.savePipeline({ - process: processId, - trigger: triggerId, - }); - } - - async loadProcess() { - this.mapOk("processModels", this.repository.getProcessed()); - } - - async loadTriggers() { - this.mapOk("triggersModels", this.repository.getTriggers()); - } -} diff --git a/ui/src/features/create_process/data/process_repostiory.ts b/ui/src/features/create_process/data/process_repostiory.ts deleted file mode 100644 index 5b8f64b..0000000 --- a/ui/src/features/create_process/data/process_repostiory.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { HttpMethod, HttpRepository } from "../../../core/repository/http_repository"; -import { IProcess } from "../model/process_model"; - -export class ProcessRepository extends HttpRepository { - async save(model: IProcess): Promise { - await this._jsonRequest(HttpMethod.POST, "/process", model); - } -} diff --git a/ui/src/features/create_process/model/process_model.ts b/ui/src/features/create_process/model/process_model.ts deleted file mode 100644 index ac128f4..0000000 --- a/ui/src/features/create_process/model/process_model.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { DatabaseModel } from "../../../core/model/database_model"; - -export interface IProcess extends DatabaseModel { - description: string; - type: EXEC_TYPE | string; - command: string; - isGenerating: boolean; - isLocaleCode: boolean; - issueType: IssueType | string; - timeout?: number; - commit?: string | undefined; -} - -export enum EXEC_TYPE { - SPAWN = "SPAWN", - EXEC = "EXEC", -} -export enum IssueType { - WARNING = "WARNING", - ERROR = "ERROR", -} -export const processModelMock: IProcess = { - description: "", - type: EXEC_TYPE.SPAWN, - command: "", - isGenerating: true, - isLocaleCode: true, - issueType: IssueType.WARNING, -}; diff --git a/ui/src/features/create_process/presentation/create_process_screen.tsx b/ui/src/features/create_process/presentation/create_process_screen.tsx deleted file mode 100644 index 0bf13d7..0000000 --- a/ui/src/features/create_process/presentation/create_process_screen.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import * as React from "react"; -import { processStore } from "./logic/process_store"; -import { observer } from "mobx-react-lite"; -import { SubmitButton, Input, ResetButton, Form, Radio, Switch } from "formik-antd"; -import { Formik } from "formik"; -import { Row, Col } from "antd"; -import { EXEC_TYPE, IssueType, processModelMock } from "../model/process_model"; -export const CreateProcessScreenPath = "/create/process"; -export const CreateProcessScreen = observer(() => { - return ( -
-
- { - await processStore.saveResult(values); - actions.setSubmitting(false); - actions.resetForm(); - }} - validate={(values) => { - if (!values.command) { - return { command: "required" }; - } - if (!values.description) { - return { description: "required" }; - } - return {}; - }} - render={() => ( -
-
- - - - - - - - - - - - - - - - Reset - Submit - - -
-
- )} - /> -
-
- ); -}); diff --git a/ui/src/features/create_process/presentation/logic/process_store.ts b/ui/src/features/create_process/presentation/logic/process_store.ts deleted file mode 100644 index d9e5afc..0000000 --- a/ui/src/features/create_process/presentation/logic/process_store.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { makeAutoObservable } from "mobx"; -import { ProcessRepository } from "../../data/process_repostiory"; -import { IProcess } from "../../model/process_model"; - -class ProcessStore { - repository: ProcessRepository; - constructor(repository: ProcessRepository) { - this.repository = repository; - makeAutoObservable(this); - } - async saveResult(model: IProcess) { - await this.repository.save(model); - } -} - -export const processStore = new ProcessStore(new ProcessRepository()); diff --git a/ui/src/features/create_project/create_project_repository.ts b/ui/src/features/create_project/create_project_repository.ts index 68082f3..750aad6 100644 --- a/ui/src/features/create_project/create_project_repository.ts +++ b/ui/src/features/create_project/create_project_repository.ts @@ -2,19 +2,19 @@ import { Result } from "../../core/helper/result"; import { DatabaseModel } from "../../core/model/database_model"; import { ITriggerModel } from "../../core/model/trigger_model"; import { HttpMethod, HttpRepository } from "../../core/repository/http_repository"; -import { IProcess } from "../create_process/model/process_model"; +import { UUID } from "../all_projects/data/project_repository"; import { ICreateProjectViewModel } from "./project_model"; export interface PipelineModel extends DatabaseModel { - process: IProcess; + process: any; trigger: ITriggerModel; } export class CreateProjectRepository extends HttpRepository { - async getAllPipelines(page = 1): Promise> { - return await this._jsonRequest(HttpMethod.GET, "/pipeline"); + async saveProject(model: ICreateProjectViewModel): Promise> { + return await this._jsonRequest(HttpMethod.POST, "/projects", model); } - async saveProject(model: ICreateProjectViewModel): Promise> { - return await this._jsonRequest(HttpMethod.POST, "/project", model); + async setProjectRootFile(file: File, projectId: string) { + return await this._formDataRequest(HttpMethod.POST, `/projects/upload?id=${projectId}`, file); } } diff --git a/ui/src/features/create_project/create_project_screen.tsx b/ui/src/features/create_project/create_project_screen.tsx index 273193e..ff07100 100644 --- a/ui/src/features/create_project/create_project_screen.tsx +++ b/ui/src/features/create_project/create_project_screen.tsx @@ -1,85 +1,46 @@ import * as React from "react"; import { LoadPage as MainPage } from "../../core/ui/pages/load_page"; import { observer } from "mobx-react-lite"; -import { Col, Row, Input, Button } from "antd"; -import { ReactComponent as AddIcon } from "../../core/assets/icons/add.svg"; -import { CreatePipelineScreenPath } from "../create_pipeline/presentation/create_pipeline_screen"; +import { Col, Row, Input, Button, Upload } from "antd"; +import type { UploadProps } from "antd"; import { CreateProjectStore } from "./create_project_store"; import { CreateProjectRepository } from "./create_project_repository"; export const CreateProjectScreenPath = "/create_project"; - export const CreateProjectScreen: React.FunctionComponent = observer(() => { - const [createProjectStore] = React.useState(() => new CreateProjectStore(new CreateProjectRepository())); + const [store] = React.useState(() => new CreateProjectStore(new CreateProjectRepository())); React.useEffect(() => { - createProjectStore.init(); - }, [createProjectStore]); + store.init(); + }, [store]); return ( <> - - <>Pipelines - {createProjectStore.pipelineModels?.map((el) => { - return ( -
+ + + store.setDescriptionToNewProject(e.target.value)} + placeholder="project description" + /> + { + store.file = e.file.originFileObj; }} > -
{el.process.description}
-
{el.trigger.description}
- { - createProjectStore.addPipeline(el); - }} - /> -
- ); - })} - - - - createProjectStore.setDescriptionToNewProject(e.target.value)} - placeholder="project description" - /> - - - - - {createProjectStore.newProjectViews.map((el, index) => { - return ( -
-
{el.process.description}
-
{el.trigger.description}
-
{index + 1}
-
- ); - })} - + + + + + + } /> diff --git a/ui/src/features/create_project/create_project_store.ts b/ui/src/features/create_project/create_project_store.ts index c330d02..33d74ed 100644 --- a/ui/src/features/create_project/create_project_store.ts +++ b/ui/src/features/create_project/create_project_store.ts @@ -1,30 +1,20 @@ import makeAutoObservable from "mobx-store-inheritance"; -import { CreateProjectRepository, PipelineModel } from "./create_project_repository"; +import { CreateProjectRepository } from "./create_project_repository"; import { message } from "antd"; import { SimpleErrorState } from "../../core/store/base_store"; export class CreateProjectStore extends SimpleErrorState { repository: CreateProjectRepository; - pipelineModels?: PipelineModel[]; newProjectDescription: string = ""; - newProjectViews: PipelineModel[] = []; + file?: File; constructor(repository: CreateProjectRepository) { super(); this.repository = repository; makeAutoObservable(this); } - async init() { - await this.loadPipelines(); - } - async addPipeline(model: PipelineModel) { - this.newProjectViews.push(model); - } - - async loadPipelines() { - this.mapOk("pipelineModels", this.repository.getAllPipelines()); - } + async init() {} setDescriptionToNewProject(value: string): void { this.newProjectDescription = value; @@ -35,27 +25,27 @@ export class CreateProjectStore extends SimpleErrorState { message.error("project description is not empty"); return; } - if (this.newProjectViews.isEmpty()) { - message.error("project view is not empty"); + if (this.file === undefined) { + message.error("upload file"); return; } + this.isLoading = true; - const result = await this.repository.saveProject({ - description: this.newProjectDescription, - pipelines: this.newProjectViews.map((el) => el._id ?? ""), - }); - - this.newProjectDescription = ""; - this.newProjectViews = []; - this.isLoading = false; - - result.fold( - (_s) => { - message.success("save"); + ( + await this.repository.saveProject({ + description: this.newProjectDescription, + }) + ).fold( + (uuid) => { + this.newProjectDescription = ""; + this.isLoading = false; + this.repository.setProjectRootFile(this.file as File, uuid.id); }, - (_e) => { + (_) => { this.isError = true; } ); + + // } } diff --git a/ui/src/features/create_project/project_model.ts b/ui/src/features/create_project/project_model.ts index a124f49..ee9c740 100644 --- a/ui/src/features/create_project/project_model.ts +++ b/ui/src/features/create_project/project_model.ts @@ -1,5 +1,3 @@ export interface ICreateProjectViewModel { - pipelines: string[]; - description: string; } diff --git a/ui/src/features/create_project_instance/create_project_instance.tsx b/ui/src/features/create_project_instance/create_project_instance.tsx deleted file mode 100644 index 3927516..0000000 --- a/ui/src/features/create_project_instance/create_project_instance.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import * as React from "react"; -import { CreateProjectInstanceStore } from "./create_project_instance_store"; -import { CreateProjectInstanceRepository } from "./create_project_instance_repository"; -import { observer } from "mobx-react-lite"; -import { Upload, Button } from "antd"; -import { useNavigate, useParams } from "react-router-dom"; -import { Input } from "antd"; - -export const CreateProjectInstancePath = "/create/project/instance/"; - -export const CreateProjectInstanceScreen = observer(() => { - const [createProjectInstanceStore] = React.useState( - () => new CreateProjectInstanceStore(new CreateProjectInstanceRepository()) - ); - const id = useParams().id; - const navigate = useNavigate(); - - React.useEffect(() => { - createProjectInstanceStore.init(navigate, id as string); - }, [id, createProjectInstanceStore, navigate]); - - return ( - <> -

project description

- createProjectInstanceStore.setProjectDescription(e.target.value)}> -

root entity

- { - createProjectInstanceStore.file = e.file.originFileObj; - }} - > - - - - - ); -}); diff --git a/ui/src/features/create_project_instance/create_project_instance_repository.ts b/ui/src/features/create_project_instance/create_project_instance_repository.ts deleted file mode 100644 index b835961..0000000 --- a/ui/src/features/create_project_instance/create_project_instance_repository.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { HttpMethod, HttpRepository } from "../../core/repository/http_repository"; -import { NewProjectModel } from "./new_project_model"; - -export class CreateProjectInstanceRepository extends HttpRepository { - async setProjectRootFile(file: File) { - return await this._formDataRequest(HttpMethod.POST, "/project_instance/upload", file); - } - - async createNewProject(project: NewProjectModel) { - return await this._jsonRequest(HttpMethod.POST, "/project_instance", project); - } - - async setActiveProject(id: string) { - return await this._jsonRequest(HttpMethod.POST, `/project_instance/set/active/project?id=${id}`); - } -} diff --git a/ui/src/features/create_project_instance/create_project_instance_store.ts b/ui/src/features/create_project_instance/create_project_instance_store.ts deleted file mode 100644 index 1730a75..0000000 --- a/ui/src/features/create_project_instance/create_project_instance_store.ts +++ /dev/null @@ -1,39 +0,0 @@ -import makeAutoObservable from "mobx-store-inheritance"; -import { SimpleErrorState } from "../../core/store/base_store"; -import { CreateProjectInstanceRepository } from "./create_project_instance_repository"; -import { message } from "antd"; -import { HttpMethod } from "../../core/repository/http_repository"; -import { NavigateFunction } from "react-router-dom"; -import { NewProjectModel } from "./new_project_model"; - -export class CreateProjectInstanceStore extends SimpleErrorState { - newProjectModel: NewProjectModel; - repository: CreateProjectInstanceRepository; - file?: File; - navigate?: NavigateFunction; - constructor(repository: CreateProjectInstanceRepository) { - super(); - this.repository = repository; - makeAutoObservable(this); - this.newProjectModel = NewProjectModel.empty(); - } - - setProjectDescription(value: string): void { - this.newProjectModel.description = value; - } - init(navigate: NavigateFunction, projectId: string) { - this.navigate = navigate; - this.newProjectModel.project = projectId; - } - async saveInstance(): Promise { - if (this.file === undefined) { - message.error("Need upload file"); - } else { - if (this.newProjectModel.isValid()) { - await this.repository.createNewProject(this.newProjectModel); - await this.repository.setProjectRootFile(this.file); - if (this.navigate !== undefined) this.navigate("/"); - } - } - } -} diff --git a/ui/src/features/create_project_instance/new_project_model.ts b/ui/src/features/create_project_instance/new_project_model.ts deleted file mode 100644 index 805ec64..0000000 --- a/ui/src/features/create_project_instance/new_project_model.ts +++ /dev/null @@ -1,17 +0,0 @@ -export class NewProjectModel { - project: string; - description: string; - constructor(project: string, description: string) { - this.project = project; - this.description = description; - } - static empty() { - return new NewProjectModel("", ""); - } - isValid(): boolean { - return this.project.isNotEmpty() && this.description.isNotEmpty(); - } - messages() { - return ""; - } -} diff --git a/ui/src/features/create_trigger/data/trigger_repository.ts b/ui/src/features/create_trigger/data/trigger_repository.ts deleted file mode 100644 index 9cd2b25..0000000 --- a/ui/src/features/create_trigger/data/trigger_repository.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { HttpMethod, HttpRepository } from "../../../core/repository/http_repository"; -import { ITriggerModel } from "../../../core/model/trigger_model"; - -export class TriggerRepository extends HttpRepository { - public async save(model: ITriggerModel) { - return await this._jsonRequest(HttpMethod.POST, "/trigger", model); - } -} diff --git a/ui/src/features/create_trigger/model/trigger_form_view_model.ts b/ui/src/features/create_trigger/model/trigger_form_view_model.ts deleted file mode 100644 index 9ffe677..0000000 --- a/ui/src/features/create_trigger/model/trigger_form_view_model.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { TriggerType } from "../../../core/model/trigger_model"; - -export interface ITriggerViewValueModel { - value: string; -} - -export interface TriggerViewModel extends ITriggerViewValueModel { - id: string; - type: TriggerType; -} diff --git a/ui/src/features/create_trigger/presentation/components/code_trigger_form.tsx b/ui/src/features/create_trigger/presentation/components/code_trigger_form.tsx deleted file mode 100644 index d399667..0000000 --- a/ui/src/features/create_trigger/presentation/components/code_trigger_form.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import * as React from "react"; -import Editor from "@monaco-editor/react"; -import { Button } from "antd"; -import { observer } from "mobx-react-lite"; -import { CallBackStringVoidFunction } from "../../../../core/extensions/extensions"; - -interface ICodeTriggerFormProps { - codeTriggerValue: string; - clearTriggerCode: VoidFunction; - saveCode: VoidFunction; - writeNewTrigger: CallBackStringVoidFunction; -} - -export const CodeTriggerForm: React.FunctionComponent = observer( - (props: ICodeTriggerFormProps) => { - return ( - <> -
- - { - props.writeNewTrigger(v ?? ""); - }} - onValidate={(_m) => {}} - /> - -
-
- - - - - - ); - } -); diff --git a/ui/src/features/create_trigger/presentation/components/file_trigger_form.tsx b/ui/src/features/create_trigger/presentation/components/file_trigger_form.tsx deleted file mode 100644 index 2f6936d..0000000 --- a/ui/src/features/create_trigger/presentation/components/file_trigger_form.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import * as React from "react"; -import { Formik } from "formik"; -import { SubmitButton, Input, ResetButton, Form, FormItem } from "formik-antd"; -import { Row, Col } from "antd"; -import { observer } from "mobx-react-lite"; -import { TriggerType } from "../../../../core/model/trigger_model"; -import { validateRequired } from "../../../../core/helper/validate"; -export interface IFileTriggerFormProps { - pushTrigger: (value: string, type: TriggerType) => void; -} -export const FileTriggerForm: React.FunctionComponent = observer( - (props: IFileTriggerFormProps) => { - return ( - <> -
- { - props.pushTrigger(values.value, TriggerType.FILE); - actions.setSubmitting(false); - actions.resetForm(); - }} - validate={(values) => { - if (values.value.length === 0) { - return false; - } - return {}; - }} - render={() => ( -
-
- - - - - - Reset - Submit - - -
-
- )} - /> -
- - ); - } -); diff --git a/ui/src/features/create_trigger/presentation/create_trigger_screen.tsx b/ui/src/features/create_trigger/presentation/create_trigger_screen.tsx deleted file mode 100644 index 243f62e..0000000 --- a/ui/src/features/create_trigger/presentation/create_trigger_screen.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import * as React from "react"; -import { Button, Col, Row, Switch, Typography, Input } from "antd"; -import { CodeTriggerForm } from "./components/code_trigger_form"; -import { observer } from "mobx-react-lite"; -import { FileTriggerForm } from "./components/file_trigger_form"; -import { ReactComponent as DeleteIcon } from "../../../core/assets/icons/delete.svg"; -import { Loader } from "../../../core/ui/loader/loader"; -import { TriggerRepository } from "../data/trigger_repository"; -import { TriggerStore } from "./trigger_store"; -import { TriggerViewModel } from "../model/trigger_form_view_model"; -import { CallBackStringVoidFunction } from "../../../core/extensions/extensions"; - -const { Title } = Typography; - -const Bottom = observer((props: { triggers: TriggerViewModel[]; callBack: CallBackStringVoidFunction }) => { - return ( - - {props.triggers.map((el) => { - return ( - - {el.value} - props.callBack(el.id)} /> - - ); - })} - - ); -}); -export const CreateTriggerScreenPath = "/create/trigger"; - -export const TriggerScreen: React.FunctionComponent = observer(() => { - const [triggerStore] = React.useState(() => new TriggerStore(new TriggerRepository())); - return ( - <> -
- {!triggerStore.isLoading ? ( - <> - -
- triggerStore.setTriggerType()} /> -
- Trigger editor: {triggerStore.getTriggerDescription()} -
- -
- triggerStore.changeTriggerDescription} /> - {triggerStore.getTriggerType() ? ( - <> - - - ) : ( - <> - - - )} - - - ) : ( - <> - - - )} -
- - ); -}); diff --git a/ui/src/features/create_trigger/presentation/trigger_store.ts b/ui/src/features/create_trigger/presentation/trigger_store.ts deleted file mode 100644 index bc251d7..0000000 --- a/ui/src/features/create_trigger/presentation/trigger_store.ts +++ /dev/null @@ -1,84 +0,0 @@ -import makeAutoObservable from "mobx-store-inheritance"; -import { v4 as uuidv4 } from "uuid"; -import { TriggerType } from "../../../core/model/trigger_model"; -import { TriggerRepository } from "../data/trigger_repository"; -import { TriggerViewModel } from "../model/trigger_form_view_model"; -import { SimpleErrorState } from "../../../core/store/base_store"; - -export class TriggerStore extends SimpleErrorState { - constructor(repository: TriggerRepository) { - super(); - this.triggerType = TriggerType.FILE; - this.repository = repository; - makeAutoObservable(this); - } - - triggerDescription: string = ""; - triggerType: TriggerType; - codeTriggerValue = ""; - triggers: TriggerViewModel[] = []; - repository: TriggerRepository; - - changeTriggerDescription(value: string): void { - this.triggerDescription = value; - } - - deleteItem(id: string): void { - this.triggers = this.triggers.filter((el) => el.id !== id); - } - - getTriggerType = (): boolean => { - return this.triggerType === TriggerType.FILE; - }; - setTriggerType = (): void => { - this.triggers = []; - if (this.triggerType === TriggerType.FILE) { - this.triggerType = TriggerType.PROCESS; - return; - } - this.triggerType = TriggerType.FILE; - }; - getTriggerDescription = (): string => { - return this.triggerType === TriggerType.FILE ? TriggerType.FILE : TriggerType.PROCESS; - }; - pushTrigger = (value: string, type: TriggerType): void => { - this.triggers.push({ - value: value, - id: uuidv4(), - type: type, - }); - }; - - writeNewTrigger(v: string | undefined): void { - if (v === undefined) { - throw new Error("Editor Value is undefined"); - } - this.codeTriggerValue = v; - } - clearTriggerCode(): void { - this.codeTriggerValue = ""; - } - saveCode(): void { - if (this.codeTriggerValue !== "") { - this.triggers.push({ - id: uuidv4(), - value: this.codeTriggerValue, - type: TriggerType.PROCESS, - }); - - this.codeTriggerValue = ""; - } - } - async saveResult(): Promise { - await this.httpHelper( - this.repository.save({ - type: this.getTriggerDescription(), - description: this.triggerDescription, - value: this.triggers.map((el) => { - return el.value; - }), - }) - ); - } -} -export const triggerStore = new TriggerStore(new TriggerRepository()); diff --git a/ui/src/features/dataset/card_dataset.tsx b/ui/src/features/dataset/card_dataset.tsx new file mode 100644 index 0000000..3b54c31 --- /dev/null +++ b/ui/src/features/dataset/card_dataset.tsx @@ -0,0 +1,166 @@ +import { Spin } from "antd"; +import { LoadingOutlined } from "@ant-design/icons"; +import poseIMG from "../../core/assets/images/pose_estemation.jpg"; +import { Icon } from "../../core/ui/icons/icons"; +import { CoreText, CoreTextType } from "../../core/ui/text/text"; +import { CoreButton } from "../../core/ui/button/button"; +import type { MenuProps } from "antd"; +import { Dropdown } from "antd"; +import { ProcessStatus } from "./dataset_model"; + +interface IMenuItem { + onClick: Function; + name: string; +} + +export const enum CardDataSetType { + EMPTY = "EMPTY", + COMPLETED = "COMPLETED", +} + +interface ICardDataSetProps { + type: CardDataSetType; + neuralNetworkAction?: string; + neuralNetworkName?: string; + objects?: string[]; + unixDate?: number; + processStatus?: string; + onClickButton?: (id: string) => void; + onClickEmptyCard?: Function; + onEdit?: Function; + onDelete?: Function; + id?: string; +} + +export const CardDataSet = (props: ICardDataSetProps) => { + const menu: IMenuItem[] = [ + { + onClick: () => { + if (props.onEdit) props.onEdit(props.id); + }, + name: "Редактировать", + }, + { + onClick: () => { + if (props.onDelete) props.onDelete(props.id); + }, + name: "Удалить", + }, + ]; + const items: MenuProps["items"] = menu.map((el, index) => { + return { + key: String(index), + label: , + onClick: () => el.onClick(props.id), + }; + }); + return ( +
{ + if (props.type.isEqual(CardDataSetType.EMPTY) && props.onClickEmptyCard) { + props.onClickEmptyCard(); + } + }} + > +
+ {props.type.isEqual(CardDataSetType.EMPTY) ? null : ( +
+
+
+ + +
+
+ +
+ +
+
+
+
+ )} +
+ pose +
+ {props.type === CardDataSetType.EMPTY ? ( + + ) : ( +
+ + +
+
+ +
+
+ {props.processStatus === ProcessStatus.RUN ? ( + } /> + ) : null} +
+ {props.processStatus === ProcessStatus.NEW ? ( + { + if (props.type.isEqual(CardDataSetType.COMPLETED) && props.onClickButton && props.id) { + props.onClickButton(props.id); + } + }} + filled={true} + text="Старт" + /> + ) : null} + {props.processStatus === ProcessStatus.END ? ( { + if (props.type.isEqual(CardDataSetType.COMPLETED) && props.onClickButton && props.id) { + props.onClickButton(props.id); + } + }} + + text="Завершен" + />):null} + {props.processStatus === ProcessStatus.RUN ? (<> + { + if (props.type.isEqual(CardDataSetType.COMPLETED) && props.onClickButton && props.id) { + props.onClickButton(props.id); + } + }} + block={true} + text="Стоп" + /> + ):null} + {props.processStatus === ProcessStatus.ERROR ? ( + { + if (props.type.isEqual(CardDataSetType.COMPLETED) && props.onClickButton && props.id) { + props.onClickButton(props.id); + } + }} + filled={true} + text="Ошибка" + /> + ) : null} +
+
+ )} +
+
+ ); +}; diff --git a/ui/src/features/dataset/dataset_model.ts b/ui/src/features/dataset/dataset_model.ts new file mode 100644 index 0000000..f9faecf --- /dev/null +++ b/ui/src/features/dataset/dataset_model.ts @@ -0,0 +1,181 @@ +import { Result } from "../../core/helper/result"; +import makeAutoObservable from "mobx-store-inheritance"; + +export enum ProcessStatus { + END = "END", + ERROR = "ERROR", + NEW = "NEW", + RUN = "RUN", +} + +export interface IDatasetModel { + _id: string; + dataSetObjects: string[]; + processStatus: ProcessStatus; + projectId: string; + name: string; + formBuilder: FormBuilderValidationModel; + unixTime: number; + datasetType: string; + local_path: string; + __v: number; + processLogs: string; +} + +export interface Dataset { + name: string; + local_path: string; + dataSetObjects: string[]; + unixDate: number; + formBuilder: FormBuilderValidationModel; +} + +export interface Assets { + assets: Asset[]; +} + +export interface Asset { + name: string; + mesh: string; + image: string; +} +export class FormBuilderValidationModel { + public result: string; + public context: string; + public form: string[]; + public output: any; + constructor(context: string, result: string, form: string[], output: string) { + this.context = context; + this.result = result; + this.form = form; + this.output = output; + } + static empty() { + return new FormBuilderValidationModel(datasetFormMockContext, datasetFormMockResult, [], defaultFormValue); + } +} + +export class DataSetModel { + dataSetObjects: string[]; + datasetType: string; + name: string; + formBuilder: FormBuilderValidationModel = FormBuilderValidationModel.empty(); + project?: string; + processStatus?: string; + isNew: boolean; + _id?: string; + constructor( + dataSetObjects: string[], + datasetType = datasetTypes[0], + datasetName: string, + isNew = true, + id: string | undefined = undefined + ) { + this.dataSetObjects = dataSetObjects; + this.datasetType = datasetType; + this.name = datasetName; + this.isNew = isNew; + this._id = id; + makeAutoObservable(this); + } + + static empty() { + return new DataSetModel([], "", "", true); + } + + isValid(): Result { + if (this.project === undefined) { + return Result.error("project is unknow"); + } + if (this.dataSetObjects.isEmpty()) { + return Result.error("Не выделены детали"); + } + if (this.name.isEmpty()) { + return Result.error("ВВедите имя датасета"); + } + return Result.ok(); + } + + static fromIDatasetModel(model: IDatasetModel) { + const dataSetModel = new DataSetModel(model.dataSetObjects, model.datasetType, model.name, false, model._id); + dataSetModel.formBuilder = model.formBuilder; + return dataSetModel; + } +} + +export const datasetTypes = ["Object Detection - YOLOv8", "Pose Estimation - DOPE"]; + +export const datasetFormMockResult = `{ + "typedataset": \${typedataset:Enum:ObjectDetection}, + "models_randomization":{ + "loc_range_low": [\${LOC_RANGE_LOW_1:number:-1}, \${LOC_RANGE_LOW_2:number:-1},/\${LOC_RANGE_LOW_3:number:0}], + "loc_range_high": [\${LOC_RANGE_HIGH_1:number:1}, \${LOC_RANGE_HIGH_2:number:1},/\${LOC_RANGE_HIGH_3:number:2}] + }, + "scene":{ + "objects": \${OBJECTS_SCENE:Array:[]}, + "lights": \${LIGHTS:Array:[]} + }, + "camera_position":{ + "center_shell": [\${CENTER_SHELL_1:number:0}, \${CENTER_SHELL_2:number:0}, \${CENTER_SHELL_3:number:0}], + "radius_range": [\${RADIUS_RANGE_1:number:1}, \${RADIUS_RANGE_2:number:1.4}], + "elevation_range": [\${ELEVATION_RANGE_1:number:10}, \${ELEVATION_RANGE_2:number:90}] + }, + "generation":{ + "n_cam_pose": \${N_CAM_POSE:number:5}, + "n_sample_on_pose": \${N_SAMPLE_ON_POSE:number:3}, + "n_series": \${N_SERIES:number:100}, + "image_format": \${image_format:Enum:JPEG}, + "image_size_wh": [\${IMAGE_SIZE_WH_1:number:640}, \${IMAGE_SIZE_WH_2:number:480}] + } +} +`; + +export const datasetFormMockContext = ` +ENUM T = "ObjectDetection","PoseEstimation"; +ENUM L = "POINT","SUN"; +ENUM F = "JPEG","PNG"; +ENUM COLLISION_SHAPE = "SHAPE","COLLISION"; + +type OBJECTS_SCENE = { +"name": \${NAME:string:default}, +"collision_shape": \${collision_shape:Enum:BOX}, +"loc_xyz": [\${LOC_XYZ_1:number:0}, \${LOC_XYZ_2:number:0}, \${LOC_XYZ_3:number:0}], +"rot_euler": [\${ROT_EULER_1:number:0},\${ROT_EULER_2:number:0}, \${ROT_EULER_3:number:0}], +"material_randomization": { + "specular": [\${SPECULAR_1:number:0}, \${SPECULAR_2:number:1}], + "roughness": [\${ROUGHNESS_1:number:0}, \${ROUGHNESS_2:number:1}], + "metallic": [\${METALLIC_1:number:0}, \${METALLIC_2:number:1}], + "base_color": [ + [ + \${BASE_COLOR_1:number:0}, + \${BASE_COLOR_2:number:0}, + \${BASE_COLOR_3:number:0}, + \${BASE_COLOR_4:number:1} + ], + [ + \${BASE_COLOR_5:number:1}, + \${BASE_COLOR_6:number:1}, + \${BASE_COLOR_7:number:1}, + \${BASE_COLOR_8:number:1} + ] + ] +} +}; +type LIGHTS = { +"id": \${ID:number:1}, +"type": \${type:Enum:POINT}, +"loc_xyz": [\${LOC_XYZ_1:number:5}, \${LOC_XYZ_2:number:5}, \${LOC_XYZ_3:number:5}], +"rot_euler": [\${ROT_EULER_1:number:-0.06}, \${ROT_EULER_2:number:0.61}, \${ROT_EULER_3:number:-0.19}], +"color_range_low": [\${COLOR_RANGE_LOW_1:number:0.5}, \${COLOR_RANGE_LOW_2:number:0.5}, \${COLOR_RANGE_LOW_3:number:0.5}], +"color_range_high":[\${COLOR_RANGE_HIGH_1:number:1}, \${COLOR_RANGE_HIGH_2:number:1}, $\{COLOR_RANGE_HIGH_3:number:1}], +"energy_range":[\${ENERGY_RANGE_1:number:400},\${ENERGY_RANGE_2:number:900}] +}; +`; + +export const defaultFormValue: any = { + typedataset: "PoseEstimation", + models_randomization: { loc_range_low: [-1, -1, 0], loc_range_high: [1, 1, 2] }, + scene: { objects: [], lights: [] }, + camera_position: { center_shell: [0, 0, 0], radius_range: [1, 1.4], elevation_range: [10, 90] }, + generation: { n_cam_pose: 5, n_sample_on_pose: 3, n_series: 100, image_format: "JPEG", image_size_wh: [640, 480] }, +}; diff --git a/ui/src/features/dataset/dataset_repository.ts b/ui/src/features/dataset/dataset_repository.ts new file mode 100644 index 0000000..796a30f --- /dev/null +++ b/ui/src/features/dataset/dataset_repository.ts @@ -0,0 +1,35 @@ +import { Result } from "../../core/helper/result"; +import { HttpError, HttpMethod, HttpRepository } from "../../core/repository/http_repository"; +import { UUID } from "../all_projects/data/project_repository"; +import { Assets, DataSetModel, Dataset, IDatasetModel, ProcessStatus } from "./dataset_model"; + +export class DataSetRepository extends HttpRepository { + editDataset(dataSetModel: DataSetModel) { + dataSetModel.processStatus = ProcessStatus.NEW; + return this._jsonRequest(HttpMethod.PUT, `/datasets`, dataSetModel); + } + deleteDataset(id: string) { + return this._jsonRequest(HttpMethod.DELETE, `/datasets/dataset?id=${id}`); + } + getActiveProjectId(): Promise> { + return this._jsonRequest(HttpMethod.GET, "/projects/get/active/project/id"); + } + getAssetsActiveProject = async (): Promise> => { + return this._jsonRequest(HttpMethod.GET, "/projects/assets"); + }; + saveDataSet = async (model: DataSetModel) => { + return this._jsonRequest(HttpMethod.POST, "/datasets", model); + }; + getDatasetsActiveProject = async (): Promise> => { + return this._jsonRequest(HttpMethod.GET, "/datasets"); + }; + execDatasetProcess = async (id: string) => { + return this._jsonRequest(HttpMethod.POST, `/datasets/exec?id=${id}`); + }; + isRunningProcess = async () => { + return this._jsonRequest(HttpMethod.GET, "/datasets/is/running"); + }; + deleteProcess = async () => { + return this._jsonRequest(HttpMethod.GET, "/datasets/delete/process"); + }; +} diff --git a/ui/src/features/dataset/dataset_screen.tsx b/ui/src/features/dataset/dataset_screen.tsx new file mode 100644 index 0000000..57027a8 --- /dev/null +++ b/ui/src/features/dataset/dataset_screen.tsx @@ -0,0 +1,130 @@ +import * as React from "react"; +import { Drawer } from "antd"; +import { FormBuilder } from "../../core/ui/form_builder/form_builder"; +import { DataSetStore, DrawersDataset } from "./dataset_store"; +import { observer } from "mobx-react-lite"; +import { MainPage } from "../../core/ui/pages/main_page"; +import { CardDataSet, CardDataSetType } from "./card_dataset"; +import { CoreInput } from "../../core/ui/input/input"; +import { CoreButton } from "../../core/ui/button/button"; +import { ListItem } from "./list_item"; + +export const DatasetsScreenPath = "/dataset"; + +export const DataSetScreen: React.FunctionComponent = observer(() => { + const [store] = React.useState(() => new DataSetStore()); + + React.useEffect(() => { + store.init(); + }, [store]); + return ( + <> + + {store.datasets?.map((el) => { + return ( + store.runProcess(id)} + onDelete={(id: string) => store.deleteDataset(id)} + onEdit={(id: string) => store.editDataset(id)} + /> + ); + })} + { + store.openEmptyCard(); + }} + /> + store.editDrawer(DrawersDataset.FormBuilderDrawer, false)} + open={store.drawers.find((el) => el.name === DrawersDataset.FormBuilderDrawer)?.status ?? false} + > + { + store.dataSetModel.formBuilder = el; + }} + /> +
+ store.editDrawer(DrawersDataset.FormBuilderDrawer, false)} + /> +
+ store.editDrawer(DrawersDataset.FormBuilderDrawer, false)} /> +
+ + store.editDrawer(DrawersDataset.NewDataset, false)} + open={store.drawers.find((el) => el.name === DrawersDataset.NewDataset)?.status} + > +
+
+ store.setNewDatasetName(e)} + /> + +
+ store.editDrawer(DrawersDataset.FormBuilderDrawer, true)} + text="Настройки датасета" + filled={true} + /> +
+ {store.assets?.assets?.map((el) => { + return ( + { + store.datasetCheckBox(el); + }} + /> + ); + })} +
+ +
+ store.saveDataset()} /> +
+ store.editDrawer(DrawersDataset.NewDataset, false)} /> +
+
+ +
+ } + /> + + ); +}); diff --git a/ui/src/features/dataset/dataset_store.ts b/ui/src/features/dataset/dataset_store.ts new file mode 100644 index 0000000..f9efa23 --- /dev/null +++ b/ui/src/features/dataset/dataset_store.ts @@ -0,0 +1,167 @@ +import makeAutoObservable from "mobx-store-inheritance"; +import { DataSetRepository } from "./dataset_repository"; +import { UiErrorState } from "../../core/store/base_store"; +import { HttpError } from "../../core/repository/http_repository"; +import { Asset, Assets, DataSetModel, IDatasetModel, ProcessStatus } from "./dataset_model"; +import { message } from "antd"; +import { UUID } from "../all_projects/data/project_repository"; +import { SocketRepository, socketRepository } from "../../core/repository/socket_repository"; + +export enum DrawersDataset { + NewDataset = "Новый датасет", + FormBuilderDrawer = "Редактировать датасет", +} +export interface Drawer { + name: string; + status: boolean; +} +export class DataSetStore extends UiErrorState { + dataSetRepository: DataSetRepository; + assets?: Assets; + datasets?: IDatasetModel[]; + activeProject: UUID; + dataSetModel = DataSetModel.empty(); + drawers: Drawer[]; + socketRepository: SocketRepository; + titleDrawer: string = DrawersDataset.NewDataset; + + constructor() { + super(); + this.socketRepository = socketRepository; + this.dataSetRepository = new DataSetRepository(); + this.drawers = Object.entries(DrawersDataset).map((k, v) => { + return { + name: k.at(1) ?? "", + status: false, + }; + }); + makeAutoObservable(this); + socketRepository.on((e) => { + if (e.event === "realtime") { + if (e.payload !== undefined && e.payload.value !== undefined && e.payload.value.id !== undefined) { + this.updateDatasetStatus(String(e.payload.value.id), ProcessStatus.END); + } + if (e.payload !== undefined && e.payload.error !== undefined && e.payload.error.id !== undefined) { + this.updateDatasetStatus(String(e.payload.error.id), ProcessStatus.ERROR); + } + } + }); + } + updateDatasetStatus(id: string, status: ProcessStatus) { + this.datasets = this.datasets?.map((el) => { + if (el._id.isEqual(id)) { + el.processStatus = status; + } + return el; + }); + } + openEmptyCard() { + this.dataSetModel = DataSetModel.empty(); + this.titleDrawer = DrawersDataset.NewDataset; + this.editDrawer(DrawersDataset.NewDataset, true); + } + + setNewDatasetName(e: string): void { + this.dataSetModel.name = e; + } + + setSkillSelected(el: string): void { + this.dataSetModel.datasetType = el; + } + + skillIsSelected(el: string): boolean { + return el === this.dataSetModel.datasetType; + } + + assetStatus(name: string): boolean { + return this.dataSetModel.dataSetObjects.includes(name); + } + + datasetCheckBox(asset: Asset): void { + if (this.assetStatus(asset.name)) { + this.dataSetModel.dataSetObjects = this.dataSetModel.dataSetObjects.filter((el) => !el.isEqual(asset.name)); + } else { + this.dataSetModel.dataSetObjects.push(asset.name); + } + } + + editDrawer(drawerName: DrawersDataset, status: boolean): void { + this.drawers = this.drawers.map((el) => { + if (el.name === drawerName) { + el.status = status; + } + return el; + }); + } + + editDataset(id: string) { + this.titleDrawer = DrawersDataset.FormBuilderDrawer; + this.dataSetModel = DataSetModel.fromIDatasetModel(this.datasets?.find((el) => el._id === id) as IDatasetModel); + this.editDrawer(DrawersDataset.NewDataset, true); + } + + deleteDataset = async (id: string) => { + (await this.dataSetRepository.deleteDataset(id)).fold( + async () => { + message.success("датасет удален"); + await this.getDatasets(); + }, + async (e) => message.error(e.message) + ); + }; + + runProcess = async (id: string): Promise => { + (await this.dataSetRepository.isRunningProcess()).fold( + async (s) => { + (await this.dataSetRepository.execDatasetProcess(id)).fold( + () => { + this.updateDatasetStatus(id, ProcessStatus.RUN); + message.success("Процесс запущен"); + }, + (e) => message.error(e.message) + ); + }, + async (e) => message.error(e.message) + ); + }; + errorHandingStrategy = (error: HttpError) => { + message.error(error.message); + }; + + saveDataset(): void { + this.dataSetModel.project = this.activeProject.id; + this.dataSetModel.isValid().fold( + async () => { + if (this.dataSetModel.isNew) { + (await this.dataSetRepository.saveDataSet(this.dataSetModel)).fold( + async () => { + message.success("Датасет сохранен"); + await this.getDatasets(); + this.editDrawer(DrawersDataset.NewDataset, false); + }, + async (error) => message.error(error.message) + ); + } else { + (await this.dataSetRepository.editDataset(this.dataSetModel)).fold( + async () => { + message.success("Настройки датасета измнены"); + await this.getDatasets(); + this.editDrawer(DrawersDataset.NewDataset, false); + }, + async (error) => message.error(error.message) + ); + } + }, + async (error) => message.error(error) + ); + } + + init = async () => { + await this.mapOk("assets", this.dataSetRepository.getAssetsActiveProject()); + await this.getDatasets(); + await this.mapOk("activeProject", this.dataSetRepository.getActiveProjectId()); + }; + getDatasets = async () => { + await this.mapOk("datasets", this.dataSetRepository.getDatasetsActiveProject()); + }; +} diff --git a/ui/src/features/dataset/list_item.tsx b/ui/src/features/dataset/list_item.tsx new file mode 100644 index 0000000..cd4c82f --- /dev/null +++ b/ui/src/features/dataset/list_item.tsx @@ -0,0 +1,31 @@ +import { CoreSwitch } from "../../core/ui/switch/switch"; +import { CoreText, CoreTextType } from "../../core/ui/text/text"; + +export interface IListItemProps { + name: string; + imgURL: string; + status: boolean; + onChange: (status: boolean, id: string) => void; +} +export const ListItem = (props: IListItemProps) => { + return ( +
+ + + +
+ ); +}; diff --git a/ui/src/features/dataset/p.json b/ui/src/features/dataset/p.json new file mode 100644 index 0000000..0519ecb --- /dev/null +++ b/ui/src/features/dataset/p.json @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/src/features/details/details_screen.tsx b/ui/src/features/details/details_screen.tsx new file mode 100644 index 0000000..2f9d9e2 --- /dev/null +++ b/ui/src/features/details/details_screen.tsx @@ -0,0 +1,8 @@ +import * as React from "react"; +import { MainPage } from "../../core/ui/pages/main_page"; + +export interface IDetailsScreenProps {} +export const DetailsScreenPath = "/detail"; +export default function DetailsScreen(props: IDetailsScreenProps) { + return ; +} diff --git a/ui/src/features/estimate/estimate_screen.tsx b/ui/src/features/estimate/estimate_screen.tsx new file mode 100644 index 0000000..842e683 --- /dev/null +++ b/ui/src/features/estimate/estimate_screen.tsx @@ -0,0 +1,8 @@ +import * as React from "react"; +import { MainPage } from "../../core/ui/pages/main_page"; + +export interface IEstimateScreenProps {} +export const EstimateScreenPath = "/estimate"; +export function EstimateScreen(props: IEstimateScreenProps) { + return ; +} diff --git a/ui/src/features/p.tsx b/ui/src/features/p.tsx deleted file mode 100644 index 81853f1..0000000 --- a/ui/src/features/p.tsx +++ /dev/null @@ -1,68 +0,0 @@ -export {}; -// import React from "react"; -// import { CoreError, UiErrorState } from "../core/store/base_store"; -// import { SelectProjectStore } from "./select_project/presentation/select_project_store"; - -// export declare type ClassConstructor = { -// new (...args: any[]): T; -// }; -// interface MobxReactComponentProps, ClassConstructor> { -// store: ClassConstructor; -// children: (element: T) => React.ReactElement; -// } - -// class UiStateErrorComponent, K> extends React.Component< -// MobxReactComponentProps, -// { store: T | undefined } -// > { -// async componentDidMount(): Promise { -// const store = this.props.store as ClassConstructor; -// console.log(store); -// const s = new store(); -// this.setState({ store: s }); -// if (this.state !== null) { -// await this.state.store?.init(); -// } -// } -// componentWillUnmount(): void { -// if (this.state.store !== undefined) { -// this.state.store.dispose(); -// } -// } - -// render() { -// if (this.state !== null) { -// if (this.state.store?.isLoading) { -// return <>Loading; -// } -// if (this.state.store !== undefined) { -// return this.props.children(this.state.store); -// } -// } - -// return ( -//
-// <>{this.props.children} -//
-// ); -// } -// } - -// export const ExampleScreen: React.FC = () => { -// return ( -//
-// store={SelectProjectStore}> -// {(store) => { -// console.log(store); -// return ( -//
-// {store.projects.map((el) => { -// return <>{el}; -// })} -//
-// ); -// }} -// -//
-// ); -// }; diff --git a/ui/src/features/pipeline_instance_main_screen/pipeline_instance_screen.tsx b/ui/src/features/pipeline_instance_main_screen/pipeline_instance_screen.tsx index 5476534..655a6b0 100644 --- a/ui/src/features/pipeline_instance_main_screen/pipeline_instance_screen.tsx +++ b/ui/src/features/pipeline_instance_main_screen/pipeline_instance_screen.tsx @@ -1,18 +1,27 @@ import * as React from "react"; -import { LoadPage } from "../../core/ui/pages/load_page"; import { PipelineInstanceStore } from "./pipeline_instance_store"; +import { useNavigate } from "react-router-dom"; +import { Icon } from "../../core/ui/icons/icons"; +import { CardDataSet, CardDataSetType } from "../dataset/card_dataset"; +import { MainPage } from "../../core/ui/pages/main_page"; export const PipelineInstanceScreenPath = "/pipeline_instance/"; + export const PipelineInstanceScreen: React.FunctionComponent = () => { - const [pipelineInstanceStore] = React.useState(() => new PipelineInstanceStore()); - React.useEffect(() => {}, [pipelineInstanceStore]); + const [store] = React.useState(() => { + new PipelineInstanceStore(); + }); + React.useEffect(() => { + document.body.style.overflow = "hidden"; + return () => { + document.body.style.overflow = "scroll"; + }; + }, [store]); + const navigate = useNavigate(); + return ( -
} - /> + <> + + ); }; diff --git a/ui/src/features/scene_manager/data/scene_repository.ts b/ui/src/features/scene_manager/data/scene_repository.ts index 30e8575..810e576 100644 --- a/ui/src/features/scene_manager/data/scene_repository.ts +++ b/ui/src/features/scene_manager/data/scene_repository.ts @@ -1,7 +1,7 @@ import { Result } from "../../../core/helper/result"; import { HttpMethod, HttpRepository } from "../../../core/repository/http_repository"; import { CoreError } from "../../../core/store/base_store"; -import { RobossemblerAssets } from "../model/robossembler_assets"; +import { RobossemblerAssets } from "../../../core/model/robossembler_assets"; export class SceneHttpRepository extends HttpRepository { async getRobossemblerAssets() { diff --git a/ui/src/features/scene_manager/model/scene_assets.ts b/ui/src/features/scene_manager/model/scene_assets.ts index 8efc8de..4e1e913 100644 --- a/ui/src/features/scene_manager/model/scene_assets.ts +++ b/ui/src/features/scene_manager/model/scene_assets.ts @@ -1,7 +1,13 @@ import { CameraHelper, Object3D, PerspectiveCamera, Quaternion, Scene, Vector3 } from "three"; import { v4 as uuidv4 } from "uuid"; import { UserData } from "../../../core/repository/core_three_repository"; -import { Asset, Instance, InstanceRgbCamera, InstanceType, SceneSimpleObject } from "./robossembler_assets"; +import { + Asset, + Instance, + InstanceRgbCamera, + InstanceType, + SceneSimpleObject, +} from "../../../core/model/robossembler_assets"; export enum RobossemblerFiles { robossemblerAssets = "robossembler_assets.json", diff --git a/ui/src/features/scene_manager/presentation/components/scene_widget.tsx b/ui/src/features/scene_manager/presentation/components/scene_widget.tsx index 9cf137b..65d23b1 100644 --- a/ui/src/features/scene_manager/presentation/components/scene_widget.tsx +++ b/ui/src/features/scene_manager/presentation/components/scene_widget.tsx @@ -57,7 +57,6 @@ export const SceneWidget = () => {

{ event.stopPropagation(); - console.log(201); }} > HYO diff --git a/ui/src/features/scene_manager/presentation/scene_manager.tsx b/ui/src/features/scene_manager/presentation/scene_manager.tsx index 47e2884..c08ebcb 100644 --- a/ui/src/features/scene_manager/presentation/scene_manager.tsx +++ b/ui/src/features/scene_manager/presentation/scene_manager.tsx @@ -1,13 +1,9 @@ import * as React from "react"; import { SceneMangerStore } from "./scene_manager_store"; import { observer } from "mobx-react-lite"; -import { StaticAssetModelView } from "./components/static_asset_item_view"; import { useParams } from "react-router-dom"; import { SceneManagerView, SceneMode } from "../model/scene_view"; -import { Button } from "antd"; -import { Form, Input, ResetButton, SubmitButton } from "formik-antd"; -import { Formik } from "formik"; -import { CameraViewModel } from "../model/scene_assets"; +import { MainPage } from "../../../core/ui/pages/main_page"; export const SceneManagerPath = "/scene/manager/"; @@ -18,7 +14,7 @@ export const SceneManger = observer(() => { React.useEffect(() => { store.init(); - store.loadScene(id, canvasRef.current!); + store.loadScene(canvasRef.current!); document.body.style.overflow = "hidden"; return () => { document.body.style.overflow = "scroll"; @@ -33,141 +29,150 @@ export const SceneManger = observer(() => { }); return ( -
- -
-
- {sceneIcons.map((el) => { - return ( -
{ - el.clickHandel(); - }} - > - {el.name} -
- ); - })} + +
-
Scene manager
- {store.isVisibleSaveButton ? ( - <> - - - ) : ( - <> - )} - {store.isLoading ? <>Loading... : <>} - {store.sceneMode === SceneMode.ADD_CAMERA ? ( -
- { - store.addNewCamera(model); - actions.setSubmitting(false); - actions.resetForm(); - }} - validate={(model) => { - return model.validate(store.getCameraLinkNames()); - }} - render={() => ( -
-
- - - - - - Reset - Submit -
-
- )} - /> -
- ) : ( - <> - )} - {store.sceneMode === SceneMode.MOVING || SceneMode.ROTATE ? ( - <> - {store.robossemblerAssets?.assets.map((el) => { - return ( -
-
- {el.name} - {store.isRenderedAsset(el.name) ? ( - <> - ) : ( - + + ) : ( + <> + )} + {store.isLoading ? <>Loading... : <>} + {store.sceneMode === SceneMode.ADD_CAMERA ? ( +
+ { + store.addNewCamera(model); + actions.setSubmitting(false); + actions.resetForm(); + }} + validate={(model) => { + return model.validate(store.getCameraLinkNames()); + }} + render={() => ( +
+
- add scene - - )} -
-
- ); - })} + + + + + + Reset + Submit +
+ + )} + /> +
+ ) : ( + <> + )} + {store.sceneMode === SceneMode.MOVING || SceneMode.ROTATE ? ( + <> + {store.robossemblerAssets?.assets.map((el) => { + return ( +
+
+ {el.name} + {store.isRenderedAsset(el.name) ? ( + <> + ) : ( + + )} +
+
+ ); + })} + + ) : ( + <> + )} +
+
+
+ {store.sceneModels.map((el) => { + return store.deleteSceneItem(el)} model={el} />; + })} +
+ + {store.sceneMode === SceneMode.MAGNETISM_MARKING ? ( + <> +
+
completion of objects
+
+ {store.objectMagnetism ? ( + <>{store.objectMagnetism} + ) : ( + + )} + {store.objectForMagnetism ? ( + <>{store.objectForMagnetism} + ) : ( + + )} +
+
) : ( <> - )} -
-
-
- {store.sceneModels.map((el) => { - return store.deleteSceneItem(el)} model={el} />; - })} -
- - {store.sceneMode === SceneMode.MAGNETISM_MARKING ? ( - <> -
-
completion of objects
-
- {store.objectMagnetism ? ( - <>{store.objectMagnetism} - ) : ( - - )} - {store.objectForMagnetism ? <>{store.objectForMagnetism} : } -
-
- - ) : ( - <> - )} -

-
+ )} */} + {/*
*/} + + } + /> ); }); diff --git a/ui/src/features/scene_manager/presentation/scene_manager_store.ts b/ui/src/features/scene_manager/presentation/scene_manager_store.ts index 2093419..a2f0be2 100644 --- a/ui/src/features/scene_manager/presentation/scene_manager_store.ts +++ b/ui/src/features/scene_manager/presentation/scene_manager_store.ts @@ -8,7 +8,7 @@ import { SceneMenu, SceneMode } from "../model/scene_view"; import { BaseSceneItemModel, CameraViewModel, RobossemblerFiles, StaticAssetItemModel } from "../model/scene_assets"; import { SceneHttpRepository } from "../data/scene_repository"; import { message } from "antd"; -import { RobossemblerAssets } from "../model/robossembler_assets"; +import { RobossemblerAssets } from "../../../core/model/robossembler_assets"; export class SceneMangerStore extends UiErrorState { sceneMode: SceneMode; @@ -112,7 +112,7 @@ export class SceneMangerStore extends UiErrorState { } }; - async loadScene(sceneId: string, canvasRef: HTMLCanvasElement) { + async loadScene(canvasRef: HTMLCanvasElement) { this.loadWebGl(canvasRef); await this.mapOk("robossemblerAssets", this.sceneHttpRepository.getRobossemblerAssets()); if (this.robossemblerAssets) { diff --git a/ui/src/features/select_project/data/select_project_repository.ts b/ui/src/features/select_project/data/select_project_repository.ts deleted file mode 100644 index 16607a5..0000000 --- a/ui/src/features/select_project/data/select_project_repository.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Result } from "../../../core/helper/result"; -import { HttpMethod, HttpRepository } from "../../../core/repository/http_repository"; -import { IProjectModel } from "../model/project_model"; - -export class SelectProjectRepository extends HttpRepository { - async setActiveProject(id: string) { - return await this._jsonRequest(HttpMethod.POST, `/project?${id}`); - } - async getAllProjects(page = 1): Promise> { - return await this._jsonRequest(HttpMethod.GET, `/project?${page}`); - } -} diff --git a/ui/src/features/select_project/model/project_model.ts b/ui/src/features/select_project/model/project_model.ts deleted file mode 100644 index 6b98740..0000000 --- a/ui/src/features/select_project/model/project_model.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { DatabaseModel } from "../../../core/model/database_model"; -import { IProcess } from "../../create_process/model/process_model"; - -export interface IProjectModel extends DatabaseModel { - pipelines: [IProcess]; - rootDir: string; - description: string; -} - \ No newline at end of file diff --git a/ui/src/features/select_project/presentation/select_project.tsx b/ui/src/features/select_project/presentation/select_project.tsx deleted file mode 100644 index c4e29cc..0000000 --- a/ui/src/features/select_project/presentation/select_project.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import * as React from "react"; -import { observer } from "mobx-react-lite"; -import { LoadPage } from "../../../core/ui/pages/load_page"; -import { CreateProjectScreenPath } from "../../create_project/create_project_screen"; -import { SelectProjectStore } from "./select_project_store"; -import { useNavigate } from "react-router-dom"; -import { CreateProjectInstancePath } from "../../create_project_instance/create_project_instance"; -import { Button } from "antd"; -export const SelectProjectScreenPath = "/select_project"; - -export const SelectProjectScreen: React.FunctionComponent = observer(() => { - const [selectProjectStore] = React.useState(() => new SelectProjectStore()); - const navigate = useNavigate(); - - React.useEffect(() => { - selectProjectStore.init(); - }, [selectProjectStore, navigate]); - - return ( - <> - { - return ( - <> -
{el.description}
-
- -
- - ); - })} - /> - - ); -}); diff --git a/ui/src/features/select_project/presentation/select_project_store.ts b/ui/src/features/select_project/presentation/select_project_store.ts deleted file mode 100644 index dd210dd..0000000 --- a/ui/src/features/select_project/presentation/select_project_store.ts +++ /dev/null @@ -1,28 +0,0 @@ -import makeAutoObservable from "mobx-store-inheritance"; -import { SelectProjectRepository } from "../data/select_project_repository"; -import { IProjectModel } from "../model/project_model"; -import { CoreError, UiErrorState } from "../../../core/store/base_store"; - -export class SelectProjectStore extends UiErrorState { - errorHandingStrategy = (error: CoreError) => {}; - repository: SelectProjectRepository; - errors = []; - page = 1; - projects: IProjectModel[] = []; - - constructor() { - super(); - - this.repository = new SelectProjectRepository(); - makeAutoObservable(this); - } - async setActiveProject(id: string): Promise { - this.httpHelper(this.repository.setActiveProject(id)); - } - async getPipelines(): Promise { - await this.mapOk("projects", this.repository.getAllProjects(this.page)); - } - async init() { - await this.getPipelines(); - } -} diff --git a/ui/src/features/simulations/simulations_screen.tsx b/ui/src/features/simulations/simulations_screen.tsx new file mode 100644 index 0000000..b0fd134 --- /dev/null +++ b/ui/src/features/simulations/simulations_screen.tsx @@ -0,0 +1,11 @@ +import * as React from "react"; +import { MainPage } from "../../core/ui/pages/main_page"; + +export interface ISimulationScreenProps {} +export const SimulationScreenPath = "/simulation"; + +export default class SimulationScreen extends React.Component { + public render() { + return ; + } +} diff --git a/ui/src/features/socket_lister/socket_lister.tsx b/ui/src/features/socket_lister/socket_lister.tsx index abc7004..3e00adf 100644 --- a/ui/src/features/socket_lister/socket_lister.tsx +++ b/ui/src/features/socket_lister/socket_lister.tsx @@ -9,7 +9,7 @@ export interface ISocketListerProps { export const SocketLister = observer((props: ISocketListerProps) => { return ( - <> +
{socketListerStore.socketHasDisconnect ? ( { @@ -28,6 +28,6 @@ export const SocketLister = observer((props: ISocketListerProps) => { )} {props.children} - +
); }); diff --git a/ui/src/features/socket_lister/socket_lister_store.ts b/ui/src/features/socket_lister/socket_lister_store.ts index d744af9..0b7d24c 100644 --- a/ui/src/features/socket_lister/socket_lister_store.ts +++ b/ui/src/features/socket_lister/socket_lister_store.ts @@ -1,20 +1,30 @@ import { makeAutoObservable } from "mobx"; -import { SocketRepository } from "../../core/repository/socket_repository"; +import { SocketRepository, socketRepository } from "../../core/repository/socket_repository"; +import { setTimeout } from "timers/promises"; class SocketListerStore { repository: SocketRepository; - socketHasDisconnect = false; + socketHasDisconnect = true; constructor(repository: SocketRepository) { this.repository = repository; makeAutoObservable(this); - repository.connect() + this.init(); + } + async init() { + (await this.repository.connect()).fold( + () => { + this.socketHasDisconnect = false + }, + () => { + this.socketHasDisconnect = true + } + ); } - async reconnect() { - await this.repository.connect() - this.socketHasDisconnect = false + await this.repository.connect(); + this.socketHasDisconnect = false; } } -export const socketListerStore = new SocketListerStore(new SocketRepository()); +export const socketListerStore = new SocketListerStore(socketRepository); diff --git a/ui/src/index.tsx b/ui/src/index.tsx index e93ad76..d952371 100644 --- a/ui/src/index.tsx +++ b/ui/src/index.tsx @@ -1,31 +1,19 @@ import React from "react"; import ReactDOM from "react-dom/client"; import "reflect-metadata"; - import "antd/dist/antd.min.css"; - import { extensions } from "./core/extensions/extensions"; import { SocketLister } from "./features/socket_lister/socket_lister"; import { RouterProvider } from "react-router-dom"; import { router } from "./core/routers/routers"; -import { SceneManger } from "./features/scene_manager/presentation/scene_manager"; -import { BehaviorTreeBuilderScreen } from "./features/behavior_tree_builder/presentation/behavior_tree_builder_screen"; -import { StickObjectsMarkingScreen } from "./features/stick_objects_marking/stick_objects_marking_screen"; extensions(); const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement); root.render( <> - {/* */} - {/* */} - - {/* */} - <> - {/* */} - {/* */} - - - + + + ); diff --git a/ui/tsconfig.json b/ui/tsconfig.json index f13dfaf..ebca409 100644 --- a/ui/tsconfig.json +++ b/ui/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "es5", + "target": "es6", "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, diff --git a/web_p/blender.py b/web_p/blender.py new file mode 100644 index 0000000..923555d --- /dev/null +++ b/web_p/blender.py @@ -0,0 +1,20 @@ +import shutil +import argparse +import os.path + +parser = argparse.ArgumentParser() +parser.add_argument("--path") +args = parser.parse_args() + +def copy_and_move_folder(src, dst): + try: + if os.path.exists(dst): + shutil.rmtree(dst) + shutil.copytree(src, dst) + print(f"Folder {src} successfully copied") + except shutil.Error as e: + print(f"Error: {e}") + +source_folder = os.path.dirname(os.path.abspath(__file__)) + "/blender" #"/home/shalenikol/web_p/blender/" + +copy_and_move_folder(source_folder, args.path) \ No newline at end of file diff --git a/web_p/blender/assets/assets.json b/web_p/blender/assets/assets.json new file mode 100644 index 0000000..f661cbb --- /dev/null +++ b/web_p/blender/assets/assets.json @@ -0,0 +1,6 @@ +{ + "assets": [ + { "name": "bear_holder", "mesh": "./mesh/fork.stl", "image": "./images/bear_holder_220425.png" }, + { "name": "bear_holder1", "mesh": "./mesh/fork.stl", "image": "./images/bear_holder_220425.png" } + ] +} diff --git a/web_p/blender/assets/images/bear_holder_220425.png b/web_p/blender/assets/images/bear_holder_220425.png new file mode 100644 index 0000000..a366b36 Binary files /dev/null and b/web_p/blender/assets/images/bear_holder_220425.png differ diff --git a/web_p/blender/assets/mesh/floor.fbx b/web_p/blender/assets/mesh/floor.fbx new file mode 100644 index 0000000..0c904fd Binary files /dev/null and b/web_p/blender/assets/mesh/floor.fbx differ diff --git a/web_p/blender/assets/mesh/fork.fbx b/web_p/blender/assets/mesh/fork.fbx new file mode 100644 index 0000000..891e3a2 Binary files /dev/null and b/web_p/blender/assets/mesh/fork.fbx differ diff --git a/web_p/blender/assets/mesh/fork.obj b/web_p/blender/assets/mesh/fork.obj new file mode 100644 index 0000000..e69de29 diff --git a/web_p/blender/assets/mesh/fork.stl b/web_p/blender/assets/mesh/fork.stl new file mode 100644 index 0000000..e69de29 diff --git a/web_p/impassable object.FCStd b/web_p/impassable object.FCStd new file mode 100644 index 0000000..d2a87a5 Binary files /dev/null and b/web_p/impassable object.FCStd differ diff --git a/web_p/renderBOPdataset.py b/web_p/renderBOPdataset.py new file mode 100755 index 0000000..f3d8b15 --- /dev/null +++ b/web_p/renderBOPdataset.py @@ -0,0 +1,370 @@ +import blenderproc as bproc +""" + renderBOPdataset + Общая задача: common pipeline + Реализуемая функция: создание датасета в формате BOP с заданными параметрами рандомизации + Используется модуль blenderproc + + 19.04.2024 @shalenikol release 0.1 +""" +import numpy as np +import argparse +import random +import os +import shutil +import json + +VHACD_PATH = "blenderproc_resources/vhacd" +# DIR_BOP = "bop_data" +DIR_MODELS = "models" +FILE_LOG_SCENE = "res.txt" +FILE_RBS_INFO = "rbs_info.json" +FILE_GT_COCO = "scene_gt_coco.json" + +Not_Categories_Name = True # наименование категории в COCO-аннотации отсутствует + +def _get_path_model(name_model: str) -> str: + # TODO on name_model find path for mesh (model.fbx) + # local_path/assets/mesh/ + return os.path.join(rnd_par.output_dir, "assets/mesh/"+name_model+".fbx") + # , d: dict + # return d["model"] + +def _get_path_object(name_obj: str) -> str: + # TODO on name_obj find path for scene object (object.fbx) + return os.path.join(rnd_par.output_dir, "assets/mesh/"+name_obj+".fbx") + # , d: dict + # return d["path"] + +def convert2relative(height, width, bbox): + """ + YOLO format use relative coordinates for annotation + """ + x, y, w, h = bbox + x += w/2 + y += h/2 + return x/width, y/height, w/width, h/height + +def render() -> int: + for obj in all_meshs: + # Make the object actively participate in the physics simulation + obj.enable_rigidbody(active=True, collision_shape="COMPOUND") + # Also use convex decomposition as collision shapes + obj.build_convex_decomposition_collision_shape(VHACD_PATH) + + objs = all_meshs + rnd_par.scene.objs + + log_txt = os.path.join(rnd_par.output_dir, FILE_LOG_SCENE) + with open(log_txt, "w") as fh: + for i,o in enumerate(objs): + loc = o.get_location() + euler = o.get_rotation_euler() + fh.write(f"{i} : {o.get_name()} {loc} {euler} category_id = {o.get_cp('category_id')}\n") + + # define a light and set its location and energy level + ls = [] + for l in rnd_par.scene.light_data: + light = bproc.types.Light(name=f"l{l['id']}") + light.set_type(l["type"]) + light.set_location(l["loc_xyz"]) #[5, -5, 5]) + light.set_rotation_euler(l["rot_euler"]) #[-0.063, 0.6177, -0.1985]) + ls += [light] + + # define the camera intrinsics + bproc.camera.set_intrinsics_from_blender_params(1, + rnd_par.image_size_wh[0], + rnd_par.image_size_wh[1], + lens_unit="FOV") + + # add segmentation masks (per class and per instance) + bproc.renderer.enable_segmentation_output(map_by=["category_id", "instance", "name"]) + + # activate depth rendering + bproc.renderer.enable_depth_output(activate_antialiasing=False) + + res_dir = os.path.join(rnd_par.output_dir, rnd_par.ds_name) + if os.path.isdir(res_dir): + shutil.rmtree(res_dir) + # Цикл рендеринга + # Do multiple times: Position the shapenet objects using the physics simulator and render X images with random camera poses + for r in range(rnd_par.n_series): + # один случайный объект в кадре / все заданные объекты + random_obj = random.choice(range(rnd_par.scene.n_obj)) + meshs = [] + for i,o in enumerate(all_meshs): #objs + if rnd_par.single_object and i != random_obj: + continue + meshs += [o] + rnd_mat = rnd_par.scene.obj_data[i]["material_randomization"] + mats = o.get_materials() #[0] + for mat in mats: + val = rnd_mat["specular"] + mat.set_principled_shader_value("Specular", random.uniform(val[0], val[1])) + val = rnd_mat["roughness"] + mat.set_principled_shader_value("Roughness", random.uniform(val[0], val[1])) + val = rnd_mat["base_color"] + mat.set_principled_shader_value("Base Color", np.random.uniform(val[0], val[1])) + val = rnd_mat["metallic"] + mat.set_principled_shader_value("Metallic", random.uniform(val[0], val[1])) + + # Randomly set the color and energy + for i,l in enumerate(ls): + current = rnd_par.scene.light_data[i] + l.set_color(np.random.uniform(current["color_range_low"], current["color_range_high"])) + energy = current["energy_range"] + l.set_energy(random.uniform(energy[0], energy[1])) + + # Clear all key frames from the previous run + bproc.utility.reset_keyframes() + + # Define a function that samples 6-DoF poses + def sample_pose(obj: bproc.types.MeshObject): + obj.set_location(np.random.uniform(rnd_par.loc_range_low, rnd_par.loc_range_high)) #[-1, -1, 0], [1, 1, 2])) + obj.set_rotation_euler(bproc.sampler.uniformSO3()) + + # Sample the poses of all shapenet objects above the ground without any collisions in-between + bproc.object.sample_poses(meshs, + objects_to_check_collisions = meshs + rnd_par.scene.collision_objects, + sample_pose_func = sample_pose) + + # Run the simulation and fix the poses of the shapenet objects at the end + bproc.object.simulate_physics_and_fix_final_poses(min_simulation_time=4, max_simulation_time=20, check_object_interval=1) + + # Find point of interest, all cam poses should look towards it + poi = bproc.object.compute_poi(meshs) + + coord_max = [0.1, 0.1, 0.1] + coord_min = [0., 0., 0.] + + with open(log_txt, "a") as fh: + fh.write("*****************\n") + fh.write(f"{r}) poi = {poi}\n") + i = 0 + for o in meshs: + i += 1 + loc = o.get_location() + euler = o.get_rotation_euler() + fh.write(f" {i} : {o.get_name()} {loc} {euler}\n") + for j in range(3): + if loc[j] < coord_min[j]: + coord_min[j] = loc[j] + if loc[j] > coord_max[j]: + coord_max[j] = loc[j] + + # Sample up to X camera poses + #an = np.random.uniform(0.78, 1.2) #1. #0.35 + for i in range(rnd_par.n_cam_pose): + # Sample location + location = bproc.sampler.shell(center=rnd_par.center_shell, + radius_min=rnd_par.radius_range[0], + radius_max=rnd_par.radius_range[1], + elevation_min=rnd_par.elevation_range[0], + elevation_max=rnd_par.elevation_range[1]) + # координата, по которой будем сэмплировать положение камеры + j = random.randint(0, 2) + # разовый сдвиг по случайной координате + d = (coord_max[j] - coord_min[j]) / rnd_par.n_sample_on_pose + if location[j] < 0: + d = -d + for _ in range(rnd_par.n_sample_on_pose): + # Compute rotation based on vector going from location towards poi + rotation_matrix = bproc.camera.rotation_from_forward_vec(poi - location, inplane_rot=np.random.uniform(-0.7854, 0.7854)) + # Add homog cam pose based on location an rotation + cam2world_matrix = bproc.math.build_transformation_mat(location, rotation_matrix) + bproc.camera.add_camera_pose(cam2world_matrix) + location[j] -= d + # render the whole pipeline + data = bproc.renderer.render() + # Write data to bop format + bproc.writer.write_bop(res_dir, + target_objects = all_meshs, # Optional[List[MeshObject]] = None + depths = data["depth"], + depth_scale = 1.0, + colors = data["colors"], + color_file_format=rnd_par.image_format, + append_to_existing_output = (r>0), + save_world2cam = False) # world coords are arbitrary in most real BOP datasets + # dataset="robo_ds", + + models_dir = os.path.join(res_dir, DIR_MODELS) + os.mkdir(models_dir) + + data = [] + for i,objn in enumerate(rnd_par.models.names): + rec = {} + rec["id"] = i+1 + rec["name"] = objn + rec["model"] = os.path.join(DIR_MODELS, os.path.split(rnd_par.models.filenames[i])[1]) # путь относительный + t = [obj.get_bound_box(local_coords=True).tolist() for obj in all_meshs if obj.get_name() == objn] + rec["cuboid"] = t[0] + data.append(rec) + # ff = os.path.join(args.obj_path, rnd_par.models.filenames[i]) # путь к исходному файлу + # shutil.copy2(ff, models_dir) + shutil.copy2(rnd_par.models.filenames[i], models_dir) + f = (os.path.splitext(rnd_par.models.filenames[i]))[0] + ".mtl" # файл материала + if os.path.isfile(f): + shutil.copy2(f, models_dir) + + with open(os.path.join(res_dir, FILE_RBS_INFO), "w") as fh: + json.dump(data, fh, indent=2) + + """ + !!! categories -> name берётся из category_id !!! + см.ниже + blenderproc.python.writer : BopWriterUtility.py + class _BopWriterUtility + def calc_gt_coco + ... + CATEGORIES = [{'id': obj.get_cp('category_id'), 'name': str(obj.get_cp('category_id')), 'supercategory': + dataset_name} for obj in dataset_objects] + + поэтому заменим наименование категории в аннотации + """ + def change_categories_name(dir: str): + coco_file = os.path.join(dir,FILE_GT_COCO) + with open(coco_file, "r") as fh: + data = json.load(fh) + cats = data["categories"] + + for i,cat in enumerate(cats): + cat["name"] = rnd_par.models.names[i] #obj_names[i] + + with open(coco_file, "w") as fh: + json.dump(data, fh, indent=0) + + def explore(path: str): + if not os.path.isdir(path): + return + folders = [ + os.path.join(path, o) + for o in os.listdir(path) + if os.path.isdir(os.path.join(path, o)) + ] + for path_entry in folders: + print(path_entry) + if os.path.isfile(os.path.join(path_entry,FILE_GT_COCO)): + change_categories_name(path_entry) + else: + explore(path_entry) + + if Not_Categories_Name: + explore(res_dir) + return 0 # success + +def _get_models(par, data) -> int: + global all_meshs + + par.models = lambda: None + par.models.n_item = len(data) + if par.models.n_item == 0: + return 0 # no models + + # загрузим объекты + par.models.names = [] #list(map(lambda x: x["name"], data)) # obj_names + par.models.filenames = [] #list(map(lambda x: x["model"], data)) #obj_filenames + i = 1 + for f in data: + nam = f + par.models.names.append(nam) + ff = _get_path_model(nam) + # ff = f["model"] # путь к файлу объекта + par.models.filenames.append(ff) + if not os.path.isfile(ff): + print(f"Error: no such file '{ff}'") + return -1 + obj = bproc.loader.load_obj(ff) + all_meshs += obj + obj[0].set_cp("category_id", i) #f["id"]) # начиная с 1 + i += 1 + return par.models.n_item + +def _get_scene(par, data) -> int: + # load scene + par.scene = lambda: None + objs = data["objects"] + par.scene.n_obj = len(objs) + if par.scene.n_obj == 0: + return 0 # empty scene + lights = data["lights"] + par.scene.n_light = len(lights) + if par.scene.n_light == 0: + return 0 # no lighting + + par.scene.objs = [] + par.scene.collision_objects = [] + for f in objs: + ff = _get_path_object(f["name"]) # f["path"] + if not os.path.isfile(ff): + print(f"Error: no such file '{ff}'") + return -1 + obj = bproc.loader.load_obj(ff) + obj[0].set_cp("category_id", 999) + coll = f["collision_shape"] + if len(coll) > 0: + obj[0].enable_rigidbody(False, collision_shape=coll) + par.scene.collision_objects += obj + par.scene.objs += obj #bproc.loader.load_blend(args.scene, data_blocks=["objects"]) + + if not par.scene.collision_objects: + print("Collision objects not found in the scene") + return 0 + par.scene.obj_data = objs + par.scene.light_data = lights + return par.scene.n_obj + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--cfg", required=True, help="Json-string with dataset parameters") + args = parser.parse_args() + + if args.cfg[-5:] == ".json": + if not os.path.isfile(args.cfg): + print(f"Error: no such file '{args.cfg}'") + exit(-1) + with open(args.cfg, "r") as f: + j_data = f.read() + else: + j_data = args.cfg + try: + cfg = json.loads(j_data) + except json.JSONDecodeError as e: + print(f"JSon error: {e}") + exit(-2) + + ds_cfg = cfg["formBuilder"]["output"] # dataset config + generation = ds_cfg["generation"] + cam_pos = ds_cfg["camera_position"] + models_randomization = ds_cfg["models_randomization"] + + rnd_par = lambda: None + rnd_par.single_object = True + rnd_par.ds_name = cfg["name"] + rnd_par.output_dir = cfg["local_path"] + rnd_par.dataset_objs = cfg["dataSetObjects"] + rnd_par.n_cam_pose = generation["n_cam_pose"] + rnd_par.n_sample_on_pose = generation["n_sample_on_pose"] + rnd_par.n_series = generation["n_series"] + rnd_par.image_format = generation["image_format"] + rnd_par.image_size_wh = generation["image_size_wh"] + rnd_par.center_shell = cam_pos["center_shell"] + rnd_par.radius_range = cam_pos["radius_range"] + rnd_par.elevation_range = cam_pos["elevation_range"] + rnd_par.loc_range_low = models_randomization["loc_range_low"] + rnd_par.loc_range_high = models_randomization["loc_range_high"] + + if not os.path.isdir(rnd_par.output_dir): + # os.mkdir(rnd_par.output_dir) + print(f"Error: invalid path '{rnd_par.output_dir}'") + exit(-3) + + bproc.init() + + all_meshs = [] + ret = _get_models(rnd_par, rnd_par.dataset_objs) + if ret <= 0: + print("Error: no models in config") + exit(-4) + if _get_scene(rnd_par, ds_cfg["scene"]) == 0: + print("Error: empty scene in config") + exit(-5) + exit(render()) \ No newline at end of file