Добавлены модули создания проектов сборки и подготовки датасетов
This commit is contained in:
parent
40b9b116c1
commit
f57438b404
173 changed files with 6750 additions and 1857 deletions
BIN
.DS_Store
vendored
BIN
.DS_Store
vendored
Binary file not shown.
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
p.py
|
3
.prettierrc
Normal file
3
.prettierrc
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"printWidth":120
|
||||
}
|
22
.vscode/settings.json
vendored
22
.vscode/settings.json
vendored
|
@ -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"
|
||||
]
|
||||
}
|
||||
|
|
56
README.md
56
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
|
||||
```
|
||||
|
|
1873
server/package-lock.json
generated
1873
server/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,6 +58,8 @@ export class App extends TypedEvent<ServerStatus> {
|
|||
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<ServerStatus> {
|
|||
await result.fold(
|
||||
async (_s) => {
|
||||
await new CheckAndCreateStaticFilesFolderUseCase().call();
|
||||
await new SetLastActivePipelineToRealTimeServiceScenario().call();
|
||||
// await new SetLastActivePipelineToRealTimeServiceScenario().call();
|
||||
},
|
||||
async (_e) => {
|
||||
this.setServerStatus(ServerStatus.error);
|
||||
|
|
|
@ -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<T> {
|
||||
|
@ -122,12 +122,12 @@ export class CoreHttpController<V> 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<V> 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<V> 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) });
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
export enum StaticFiles {
|
||||
robossembler_assets = "robossembler_assets.json",
|
||||
assets = "/assets/assets.json",
|
||||
}
|
||||
|
|
|
@ -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<Buffer> {
|
||||
if ((await this.lsStat(path)).isDirectory()) {
|
||||
return (
|
||||
|
@ -40,4 +41,5 @@ export class FileSystemRepository {
|
|||
});
|
||||
return filesToDir;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -25,7 +25,12 @@ export class ExecutorProgramService
|
|||
this.maxTime = maxTime;
|
||||
}
|
||||
|
||||
private async workerExecuted(command: string, workerType: WorkerType, args: Array<string> | undefined = undefined) {
|
||||
private async workerExecuted(
|
||||
command: string,
|
||||
workerType: WorkerType,
|
||||
args: Array<string> | 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<string> | undefined = undefined): Promise<void> {
|
||||
public deleteWorker() {
|
||||
if (this.worker) this.worker.kill();
|
||||
this.worker = undefined;
|
||||
}
|
||||
public async call(
|
||||
type: EXEC_TYPE,
|
||||
command: string,
|
||||
args: Array<string> | undefined = undefined,
|
||||
id: string | undefined = undefined
|
||||
): Promise<void> {
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<TriggerSuccessResult | TriggerErrorReport>;
|
||||
|
|
12
server/src/core/usecases/delete_recursive_folder_usecase.ts
Normal file
12
server/src/core/usecases/delete_recursive_folder_usecase.ts
Normal file
|
@ -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<Result<void,void>> =>{
|
||||
console.log(path)
|
||||
await this.repository.deleteDirRecursive(path)
|
||||
return Result.ok()
|
||||
}
|
||||
|
||||
}
|
44
server/src/core/usecases/exec_process_usecase.ts
Normal file
44
server/src/core/usecases/exec_process_usecase.ts
Normal file
|
@ -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<Result<undefined, string>> => {
|
||||
executorProgramService.deleteWorker();
|
||||
return Result.ok("ok");
|
||||
};
|
||||
}
|
||||
|
||||
export class IsHaveActiveProcessUseCase extends CallbackStrategyWithEmpty {
|
||||
call = async (): Promise<Result<string, string>> => {
|
||||
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<Result<ExecError | SpawnError, ExecutorResult>>
|
||||
): Promise<Result<Error, string>> => {
|
||||
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);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -9,7 +9,15 @@ export class ReadByIdDataBaseModelUseCase<D> {
|
|||
call = async (id: string): Promise<Result<Error, D>> => {
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -7,10 +7,10 @@ export class SearchDataBaseModelUseCase<T> {
|
|||
this.model = model;
|
||||
}
|
||||
|
||||
call = async (findFilter: Partial<T>): Promise<Result<null, T>> => {
|
||||
call = async (findFilter: Partial<T>, error: string = "not found database"): Promise<Result<string, T>> => {
|
||||
const result = await this.model.findOne(findFilter);
|
||||
if (result === null) {
|
||||
return Result.error(null);
|
||||
return Result.error(error);
|
||||
} else {
|
||||
return Result.ok(result);
|
||||
}
|
||||
|
|
|
@ -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 {}
|
||||
|
|
@ -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: {
|
|
@ -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()
|
|
@ -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()
|
|
@ -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<Schema.Types.ObjectId>,
|
||||
ref: schemaPipeline,
|
||||
autopopulate: true,
|
||||
default: null,
|
||||
},
|
||||
description: {
|
||||
type: String,
|
||||
},
|
||||
rootDir: {
|
||||
type: String,
|
||||
},
|
||||
isActive: {
|
||||
type: Boolean,
|
||||
default: false,
|
13
server/src/features/_projects/projects_presentation.ts
Normal file
13
server/src/features/_projects/projects_presentation.ts
Normal file
|
@ -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<ProjectValidationModel, typeof ProjectDBModel> {
|
||||
constructor() {
|
||||
super({
|
||||
url: "project",
|
||||
validationModel: ProjectValidationModel,
|
||||
databaseModel: ProjectDBModel,
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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";
|
||||
|
|
@ -11,4 +11,3 @@ export class TriggerPresentation extends CrudController<TriggerModelValidationMo
|
|||
});
|
||||
}
|
||||
}
|
||||
"".isEmpty();
|
42
server/src/features/datasets/datasets_presentation.ts
Normal file
42
server/src/features/datasets/datasets_presentation.ts
Normal file
|
@ -0,0 +1,42 @@
|
|||
import { CrudController } from "../../core/controllers/crud_controller";
|
||||
import { IsHaveActiveProcessUseCase, KillLastProcessUseCase } from "../../core/usecases/exec_process_usecase";
|
||||
import { CreateDataSetScenario } from "./domain/create_dataset_scenario";
|
||||
import { DeleteDatasetUseCase } from "./domain/delete_dataset_use_case";
|
||||
import { ExecDatasetProcessScenario } from "./domain/exec_process_scenario";
|
||||
import { GetDatasetActiveProjectScenario } from "./domain/get_dataset_active_project_scenario";
|
||||
import { DatasetDBModel } from "./models/dataset_database_model";
|
||||
import { DatasetValidationModel } from "./models/dataset_validation_model";
|
||||
|
||||
export class DatasetsPresentation extends CrudController<DatasetValidationModel, typeof DatasetDBModel> {
|
||||
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()
|
||||
})
|
||||
|
||||
}
|
||||
}
|
|
@ -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<Result<ExecError | SpawnError, ExecutorResult>> {
|
||||
databaseId: ObjectId;
|
||||
constructor(databaseId: ObjectId) {
|
||||
super();
|
||||
this.databaseId = databaseId;
|
||||
this.on((event) => this.lister(event));
|
||||
}
|
||||
|
||||
lister(event: Result<ExecError | SpawnError, ExecutorResult>) {
|
||||
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<DatasetValidationModel> {
|
||||
validationModel: DatasetValidationModel;
|
||||
call = async (model: DatasetValidationModel): ResponseBase => {
|
||||
return (
|
||||
await new SearchDataBaseModelUseCase<IProjectModel>(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");
|
||||
});
|
||||
};
|
||||
}
|
|
@ -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<IDatasetModel>(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" }))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
27
server/src/features/datasets/domain/exec_process_scenario.ts
Normal file
27
server/src/features/datasets/domain/exec_process_scenario.ts
Normal file
|
@ -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<IDatasetModel>(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)
|
||||
);
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
|
@ -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<IProjectModel>(ProjectDBModel).call({ isActive: true }, "no active projects")
|
||||
).map(async (project) => {
|
||||
return Result.ok(await DatasetDBModel.find({ project: project._id }));
|
||||
});
|
||||
};
|
||||
}
|
|
@ -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<IDatasetModel>(datasetSchema, DatasetSchema);
|
|
@ -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;
|
||||
}
|
|
@ -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<Result<Error, any>> => {
|
||||
try {
|
||||
// (await new SetActiveProjectScenario().call(id)).map(() => {
|
||||
// return Result.ok({ status: "ok" });
|
||||
// });
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return Result.error(error as Error);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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<RobossemblerAssets> {
|
||||
constructor() {
|
||||
super({
|
||||
url: "robossembler_assets",
|
||||
validationModel: RobossemblerAssets,
|
||||
});
|
||||
super.get(new RobossemblerAssetsNetworkMapperScenario().call);
|
||||
super.post(new SaveActiveSceneScenario().call);
|
||||
}
|
||||
}
|
|
@ -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<Result<any, any>> => {
|
||||
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);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -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<IProjectModel>(ProjectDBModel).call({ isActive: true }, "no active projects")
|
||||
).map((model) => Result.ok({ id: model._id }));
|
||||
}
|
||||
}
|
|
@ -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>(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`
|
||||
)
|
||||
);
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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<RobossemblerAssets> {
|
||||
validationModel: RobossemblerAssets = new RobossemblerAssets();
|
|
@ -12,24 +12,31 @@ export class SetActiveProjectScenario extends CallbackStrategyWithIdQuery {
|
|||
idValidationExpression = new MongoIdValidation();
|
||||
|
||||
async call(id: string): ResponseBase {
|
||||
const result = await new ReadByIdDataBaseModelUseCase<IProjectInstanceModel>(ProjectInstanceDbModel).call(id);
|
||||
// id
|
||||
try {
|
||||
const result = await new ReadByIdDataBaseModelUseCase<IProjectInstanceModel>(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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<IProjectInstanceModel>(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/'`
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
},
|
|
@ -1,9 +1,6 @@
|
|||
import { IsMongoId, IsOptional, IsString } from "class-validator";
|
||||
|
||||
export class ProjectInstanceValidationModel {
|
||||
@IsMongoId()
|
||||
public project: string;
|
||||
|
||||
@IsString()
|
||||
public description: string;
|
||||
|
|
@ -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<ProjectValidationModel, typeof ProjectDBModel> {
|
||||
export class ProjectsPresentation extends CrudController<ProjectsValidationModel, typeof ProjectDBModel> {
|
||||
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(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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[] = [
|
||||
{
|
||||
|
|
|
@ -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<boolean>;
|
||||
|
|
|
@ -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";
|
||||
|
|
315
ui/package-lock.json
generated
315
ui/package-lock.json
generated
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -17,5 +17,13 @@
|
|||
<div id="root"></div>
|
||||
|
||||
</body>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap')
|
||||
</style>
|
||||
<style>
|
||||
[contenteditable]:focus {
|
||||
outline: 0px solid transparent;
|
||||
}
|
||||
</style>
|
||||
|
||||
</html>
|
|
@ -1,12 +0,0 @@
|
|||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
|
||||
<svg width="64px" height="64px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
|
||||
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
|
||||
|
||||
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
|
||||
<g id="SVGRepo_iconCarrier"> <path fill-rule="evenodd" clip-rule="evenodd" d="M2 5.75C2 5.33579 2.33579 5 2.75 5H20.75C21.1642 5 21.5 5.33579 21.5 5.75C21.5 6.16421 21.1642 6.5 20.75 6.5H2.75C2.33579 6.5 2 6.16421 2 5.75ZM2 9.75C2 9.33579 2.33579 9 2.75 9H20.75C21.1642 9 21.5 9.33579 21.5 9.75C21.5 10.1642 21.1642 10.5 20.75 10.5H2.75C2.33579 10.5 2 10.1642 2 9.75ZM20.2113 12.6586C20.5379 12.9134 20.5961 13.3847 20.3414 13.7113L16.4414 18.7113C16.3022 18.8897 16.0899 18.9958 15.8636 18.9999C15.6373 19.004 15.4213 18.9057 15.2757 18.7324L13.1757 16.2324C12.9093 15.9152 12.9504 15.4421 13.2676 15.1757C13.5848 14.9093 14.0579 14.9504 14.3243 15.2676L15.8284 17.0582L19.1586 12.7887C19.4134 12.4621 19.8847 12.4039 20.2113 12.6586ZM2 13.75C2 13.3358 2.33579 13 2.75 13H9.75C10.1642 13 10.5 13.3358 10.5 13.75C10.5 14.1642 10.1642 14.5 9.75 14.5H2.75C2.33579 14.5 2 14.1642 2 13.75ZM2 17.75C2 17.3358 2.33579 17 2.75 17H9.75C10.1642 17 10.5 17.3358 10.5 17.75C10.5 18.1642 10.1642 18.5 9.75 18.5H2.75C2.33579 18.5 2 18.1642 2 17.75Z" fill="#f46036"/> </g>
|
||||
|
||||
</svg>
|
Before Width: | Height: | Size: 1.4 KiB |
BIN
ui/src/core/assets/images/pose_estemation.jpg
Normal file
BIN
ui/src/core/assets/images/pose_estemation.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
|
@ -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]);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
@ -4,8 +4,12 @@ import { NumberExtensions } from "./number";
|
|||
import { StringExtensions } from "./string";
|
||||
|
||||
export type CallBackVoidFunction = <T>(value: T) => void;
|
||||
|
||||
export type CallBackStringVoidFunction = (value: string) => void;
|
||||
export type CallBackEventTarget = (value: EventTarget) => void;
|
||||
export type OptionalProperties<T> = {
|
||||
[P in keyof T]?: T[P];
|
||||
};
|
||||
|
||||
declare global {
|
||||
interface Array<T> {
|
||||
|
@ -15,22 +19,36 @@ declare global {
|
|||
isEmpty(): boolean;
|
||||
isNotEmpty(): boolean;
|
||||
hasIncludeElement(element: T): boolean;
|
||||
repeat(quantity: number): Array<T>;
|
||||
}
|
||||
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<K, V> {
|
||||
addValueOrMakeCallback(key: K, value: V, callBack: CallBackVoidFunction): void;
|
||||
getKeyFromValueIsExists(value: V): K | undefined;
|
||||
overrideValue(key: K, value: OptionalProperties<V>): void;
|
||||
keysToJson(): string;
|
||||
toArray(): V[];
|
||||
getPredicateValue(callBack: (value: V) => boolean): K[];
|
||||
}
|
||||
interface Vector3 {}
|
||||
}
|
||||
export const extensions = () => {
|
||||
ArrayExtensions();
|
||||
StringExtensions();
|
||||
ArrayExtensions();
|
||||
NumberExtensions();
|
||||
MapExtensions();
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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 },
|
|
@ -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,12 +33,7 @@ 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",
|
||||
|
@ -157,6 +150,15 @@ export class CoreThreeRepository extends TypedEvent<BaseSceneItemModel> {
|
|||
}
|
||||
});
|
||||
}
|
||||
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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<any> {
|
||||
serverURL = "ws://localhost:4001";
|
||||
socket: Socket | undefined;
|
||||
async connect() {
|
||||
|
||||
async connect():Promise<Result<boolean, boolean>> {
|
||||
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()
|
|
@ -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: <PipelineInstanceScreen />,
|
||||
},
|
||||
{
|
||||
path: SelectProjectScreenPath,
|
||||
element: <SelectProjectScreen />,
|
||||
},
|
||||
{
|
||||
path: CreatePipelineScreenPath,
|
||||
element: <CreatePipelineScreen />,
|
||||
},
|
||||
|
||||
{
|
||||
path: CreateProjectScreenPath,
|
||||
element: <CreateProjectScreen />,
|
||||
},
|
||||
|
||||
{
|
||||
path: CreateTriggerScreenPath,
|
||||
element: <TriggerScreen />,
|
||||
},
|
||||
{
|
||||
path: CreateProcessScreenPath,
|
||||
element: <CreateProcessScreen />,
|
||||
},
|
||||
{
|
||||
path: CreateProjectInstancePath + idURL,
|
||||
element: <CreateProjectInstanceScreen />,
|
||||
},
|
||||
{
|
||||
path: SceneManagerPath + idURL,
|
||||
path: SceneManagerPath,
|
||||
element: <SceneManger />,
|
||||
},
|
||||
{
|
||||
path: BehaviorTreeBuilderPath,
|
||||
element: <BehaviorTreeBuilderScreen />,
|
||||
},
|
||||
{
|
||||
path: StickObjectsMarkingScreenPath,
|
||||
element: <StickObjectsMarkingScreen />,
|
||||
},
|
||||
{
|
||||
path: DatasetsScreenPath,
|
||||
element: <DataSetScreen />,
|
||||
},
|
||||
{
|
||||
path: DetailsScreenPath,
|
||||
element: <DetailsScreen />,
|
||||
},
|
||||
{
|
||||
path: AssemblesScreenPath,
|
||||
element: <AssemblesScreen />,
|
||||
},
|
||||
{
|
||||
path: SimulationScreenPath,
|
||||
element: <SimulationScreen />,
|
||||
},
|
||||
|
||||
{
|
||||
path: EstimateScreenPath,
|
||||
element: <EstimateScreen />,
|
||||
},
|
||||
]);
|
||||
|
|
33
ui/src/core/ui/button/button.tsx
Normal file
33
ui/src/core/ui/button/button.tsx
Normal file
|
@ -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 (
|
||||
<div
|
||||
onClick={() => 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)}
|
||||
>
|
||||
<CoreText
|
||||
text={props.text ?? ""}
|
||||
type={CoreTextType.medium}
|
||||
color={props.block ? "#1D1B20" : props.filled ? "white" : "rgba(103, 80, 164, 1)"}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
155
ui/src/core/ui/form_builder/form_builder.tsx
Normal file
155
ui/src/core/ui/form_builder/form_builder.tsx
Normal file
|
@ -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 (
|
||||
<div>
|
||||
{store.isError ? (
|
||||
<>Error</>
|
||||
) : (
|
||||
<div>
|
||||
{store.formViewModel?.inputs.map((element) => {
|
||||
if (element.type.isEqual(InputType.ENUM)) {
|
||||
const values = element.values as string[];
|
||||
return (
|
||||
<SelectCore
|
||||
items={values}
|
||||
value={element.totalValue ?? element.defaultValue}
|
||||
onChange={(value) => store.changeTotalValue(element.id, value)}
|
||||
label={element.name}
|
||||
style={{ margin: 20 }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (element.type.isEqual(InputType.ARRAY)) {
|
||||
return (
|
||||
<div style={{ border: "1px black solid", margin: 20 }}>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
margin: 20,
|
||||
alignItems: "center",
|
||||
paddingRight: 20,
|
||||
}}
|
||||
onClick={() => {
|
||||
store.open(element.id);
|
||||
}}
|
||||
>
|
||||
<CoreText text={element.name} type={CoreTextType.large} />
|
||||
<Icon type="PlusCircle" style={{ width: 33 }} />
|
||||
</div>
|
||||
|
||||
{element.isOpen ? (
|
||||
<div style={{ margin: 20 }}>
|
||||
{element.totalValue instanceof Array
|
||||
? element.totalValue?.map((subArray, index) => {
|
||||
return (
|
||||
<div style={{ margin: 20 }}>
|
||||
<div style={{ display: "flex" }}>
|
||||
<CoreText text={(element.subType ?? "") + ` ${index}`} type={CoreTextType.medium} />
|
||||
<Icon
|
||||
style={{ paddingLeft: 20 }}
|
||||
type="DeleteCircle"
|
||||
onClick={() => store.deleteTotalValueSubItem(element.id, index)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{subArray.map((subSubArrayItem: InputBuilderViewModel, subIndex: number) => {
|
||||
if (subSubArrayItem.type.isEqual(InputType.ENUM)) {
|
||||
return (
|
||||
<>
|
||||
<SelectCore
|
||||
items={subSubArrayItem.values?.map((el) => 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 (
|
||||
<div>
|
||||
<CoreInput
|
||||
style={{ margin: 5 }}
|
||||
onChange={(e) => {
|
||||
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}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
return <>Error</>;
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
})
|
||||
: null}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (element.type.isEqualMany([InputType.NUMBER, InputType.STRING]))
|
||||
return (
|
||||
<div>
|
||||
<CoreInput
|
||||
validation={element.type.isEqual(InputType.NUMBER) ? (el) => Number().isValid(el) : undefined}
|
||||
onChange={(e) => {
|
||||
store.changeTotalValue(element.id, e);
|
||||
}}
|
||||
value={element.totalValue ?? element.defaultValue}
|
||||
error="только числа"
|
||||
label={element.name}
|
||||
style={{ margin: 20 }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
return <>Error</>;
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
});
|
101
ui/src/core/ui/form_builder/form_builder_store.ts
Normal file
101
ui/src/core/ui/form_builder/form_builder_store.ts
Normal file
|
@ -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<FormBuilderValidationModel | undefined> {}
|
||||
|
||||
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)
|
||||
);
|
||||
}
|
||||
}
|
241
ui/src/core/ui/form_builder/form_view_model.ts
Normal file
241
ui/src/core/ui/form_builder/form_view_model.ts
Normal file
|
@ -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<void, FormViewModel> {
|
||||
try {
|
||||
const enums = new Map<string, string[]>();
|
||||
const types = new Map<string, InputBuilderViewModel[]>();
|
||||
|
||||
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<string, string[]>,
|
||||
types: Map<string, InputBuilderViewModel[]> | 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[];
|
||||
}
|
||||
}
|
33
ui/src/core/ui/form_builder/readme.md
Normal file
33
ui/src/core/ui/form_builder/readme.md
Normal file
|
@ -0,0 +1,33 @@
|
|||
# RESULT
|
||||
|
||||
```
|
||||
{
|
||||
"scene":{
|
||||
"objects": \${OBJECTS_SCENE:Array<OBJECTS_SCENE>:[]},
|
||||
},
|
||||
"camera_position":{
|
||||
"center_shell": [\${CENTER_SHELL_1:string:0}, \${COLISION_SHAPE:Enum<L>:"BOX"}]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# CONTEXT
|
||||
|
||||
### хранит в себе type и enum для result
|
||||
|
||||
```
|
||||
ENUM T = "ObjectDetection","PoseEstimation";
|
||||
ENUM L = "POINT","SUN";
|
||||
|
||||
type MODELS = {
|
||||
"id": \${IMAGE_FORMAT:Enum<L>:"jpg"},,
|
||||
"name": \${NAME:string:""},
|
||||
"model": \${MODEL:string:"models/1.fbx"}
|
||||
};
|
||||
|
||||
type OBJECTS_SCENE = {
|
||||
"name": \${NAME:string:""},
|
||||
"collision_shape": \${COLISION_SHAPE:Enum<L>:"BOX"},
|
||||
"loc_xyz": [\${TEST123:string:"test123"}, \${LOC_XYZ_2:number:0}, \${LOC_XYZ_3:number:0}],
|
||||
};
|
||||
```
|
334
ui/src/core/ui/icons/icons.tsx
Normal file
334
ui/src/core/ui/icons/icons.tsx
Normal file
|
@ -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 (
|
||||
<div
|
||||
onClick={() => {
|
||||
if (props.onClick) props.onClick();
|
||||
}}
|
||||
style={props.style}
|
||||
>
|
||||
{node}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
() => (
|
||||
<div
|
||||
onClick={() => {
|
||||
if (props.onClick) props.onClick();
|
||||
}}
|
||||
style={props.style}
|
||||
>
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M6 12C9.31371 12 12 9.31371 12 6C12 2.68629 9.31371 0 6 0C2.68629 0 0 2.68629 0 6C0 9.31371 2.68629 12 6 12Z"
|
||||
fill="#49454F"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
const getIconSvg = (type: string): Result<undefined, React.JSX.Element> => {
|
||||
switch (type) {
|
||||
case "":
|
||||
return Result.ok();
|
||||
case "Setting":
|
||||
return Result.ok(
|
||||
<svg width="13" height="13" viewBox="0 0 13 13" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_68_595)">
|
||||
<path
|
||||
d="M4.85647 2.08936L5.25576 1.05864C5.3231 0.884129 5.44158 0.734023 5.59566 0.627981C5.74976 0.52194 5.93228 0.464908 6.11933 0.464355H6.88076C7.06781 0.464908 7.25034 0.52194 7.40443 0.627981C7.55852 0.734023 7.67699 0.884129 7.74433 1.05864L8.14362 2.08936L9.49929 2.86936L10.595 2.70221C10.7775 2.67745 10.9632 2.70748 11.1286 2.7885C11.2939 2.86951 11.4314 2.99785 11.5236 3.15721L11.895 3.80721C11.9902 3.96911 12.0341 4.15606 12.0208 4.34339C12.0076 4.53072 11.9378 4.70963 11.8207 4.8565L11.1429 5.72007V7.28007L11.8393 8.14364C11.9564 8.29051 12.0261 8.46942 12.0394 8.65675C12.0527 8.84408 12.0088 9.03103 11.9136 9.19293L11.5422 9.84293C11.4499 10.0023 11.3124 10.1306 11.1471 10.2117C10.9818 10.2926 10.796 10.3227 10.6136 10.2979L9.51786 10.1308L8.16219 10.9108L7.7629 11.9415C7.69556 12.116 7.57709 12.2661 7.423 12.3722C7.26891 12.4782 7.08638 12.5352 6.89933 12.5358H6.11933C5.93228 12.5352 5.74976 12.4782 5.59566 12.3722C5.44158 12.2661 5.3231 12.116 5.25576 11.9415L4.85647 10.9108L3.50076 10.1308L2.40504 10.2979C2.2226 10.3227 2.0369 10.2926 1.87156 10.2117C1.70623 10.1306 1.56871 10.0023 1.47647 9.84293L1.10504 9.19293C1.00987 9.03103 0.966019 8.84408 0.979279 8.65675C0.992539 8.46942 1.06229 8.29051 1.17933 8.14364L1.85719 7.28007V5.72007L1.16076 4.8565C1.04372 4.70963 0.973968 4.53072 0.960708 4.34339C0.947448 4.15606 0.991295 3.96911 1.08647 3.80721L1.4579 3.15721C1.55014 2.99785 1.68766 2.86951 1.85299 2.7885C2.01833 2.70748 2.20403 2.67745 2.38647 2.70221L3.48219 2.86936L4.85647 2.08936ZM4.6429 6.50007C4.6429 6.86738 4.75182 7.22644 4.95589 7.53184C5.15996 7.83725 5.45 8.07528 5.78934 8.21585C6.1287 8.3564 6.50211 8.39319 6.86235 8.32153C7.2226 8.24987 7.55352 8.073 7.81325 7.81326C8.07297 7.55354 8.24984 7.22263 8.3215 6.86238C8.39316 6.50213 8.35639 6.12872 8.21582 5.78937C8.07526 5.45002 7.83723 5.15997 7.53182 4.95591C7.22641 4.75185 6.86735 4.64293 6.50004 4.64293C6.0075 4.64293 5.53513 4.83859 5.18685 5.18687C4.83857 5.53515 4.6429 6.00753 4.6429 6.50007Z"
|
||||
stroke="#373737"
|
||||
stroke-width="1.3"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_68_595">
|
||||
<rect width="13" height="13" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
case "Assembly":
|
||||
return Result.ok(
|
||||
<svg width="13" height="13" viewBox="0 0 13 13" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M1.84615 1H4.38462C4.60903 1 4.82425 1.08915 4.98294 1.24783C5.14162 1.40652 5.23077 1.62174 5.23077 1.84615V9.88462C5.23077 10.4456 5.0079 10.9837 4.61119 11.3804C4.21448 11.7771 3.67642 12 3.11538 12C2.83759 12 2.56252 11.9453 2.30586 11.839C2.04921 11.7327 1.81601 11.5768 1.61958 11.3804C1.22287 10.9837 1 10.4456 1 9.88462V1.84615C1 1.62174 1.08915 1.40652 1.24783 1.24783C1.40652 1.08915 1.62174 1 1.84615 1Z"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M5.23079 4.80777L8.19233 1.82931C8.35086 1.67171 8.56532 1.58325 8.78887 1.58325C9.01241 1.58325 9.2269 1.67171 9.38539 1.82931L11.1708 3.62315C11.3284 3.78169 11.4168 3.99615 11.4168 4.21969C11.4168 4.44323 11.3284 4.65769 11.1708 4.81623L4.6131 11.3824"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M8.19231 7.76904H11.1538C11.3782 7.76904 11.5935 7.85819 11.7522 8.01687C11.9108 8.17556 12 8.39078 12 8.6152V11.1537C12 11.3781 11.9108 11.5933 11.7522 11.752C11.5935 11.9106 11.3782 11.9998 11.1538 11.9998H3.11539"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path d="M1 4.38477H5.23077" stroke="#373737" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M1 7.76904H5.23077" stroke="#373737" stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
);
|
||||
case "Datasets":
|
||||
return Result.ok(
|
||||
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.03125 7.5271L3.38477 10" stroke="#373737" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M11.6087 5L10.8444 7.5" stroke="#373737" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M6.75 6.75732L8 8" stroke="#373737" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path
|
||||
d="M13 5H11C10.4477 5 10 4.55228 10 4V2C10 1.44771 10.4477 1 11 1H13C13.5523 1 14 1.44771 14 2V4C14 4.55228 13.5523 5 13 5Z"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M11 11.5H9C8.44772 11.5 8 11.0523 8 10.5V8.5C8 7.94772 8.44772 7.5 9 7.5H11C11.5523 7.5 12 7.94771 12 8.5V10.5C12 11.0523 11.5523 11.5 11 11.5Z"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M5.75 7.5271H3.75C3.19772 7.5271 2.75 7.07938 2.75 6.5271V4.5271C2.75 3.97481 3.19772 3.5271 3.75 3.5271H5.75C6.30228 3.5271 6.75 3.97481 6.75 4.5271V6.5271C6.75 7.07938 6.30228 7.5271 5.75 7.5271Z"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M4 14H2C1.44771 14 1 13.5523 1 13V11C1 10.4477 1.44771 10 2 10H4C4.55228 10 5 10.4477 5 11V13C5 13.5523 4.55228 14 4 14Z"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
case "Layers":
|
||||
return Result.ok(
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M6.6306 6.16821C6.51077 6.22024 6.38154 6.24708 6.25092 6.24708C6.12028 6.24708 5.99105 6.22024 5.87124 6.16821L1.2666 4.03554C1.19183 3.99783 1.12899 3.9401 1.08509 3.86878C1.04119 3.79747 1.01794 3.71537 1.01794 3.63162C1.01794 3.54787 1.04119 3.46577 1.08509 3.39446C1.12899 3.32315 1.19183 3.26542 1.2666 3.22771L5.87124 1.07888C5.99105 1.02685 6.12028 1 6.25092 1C6.38154 1 6.51077 1.02685 6.6306 1.07888L11.2352 3.21155C11.31 3.24927 11.3729 3.30699 11.4167 3.3783C11.4607 3.44962 11.4839 3.53172 11.4839 3.61546C11.4839 3.69921 11.4607 3.78131 11.4167 3.85263C11.3729 3.92394 11.31 3.98167 11.2352 4.01938L6.6306 6.16821Z"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M11.5018 6.53174L6.57403 8.80176C6.46878 8.84974 6.35445 8.87462 6.23878 8.87462C6.1231 8.87462 6.00877 8.84974 5.90353 8.80176L1 6.53174"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M11.5018 9.15723L6.57403 11.4272C6.46878 11.4752 6.35445 11.5001 6.23878 11.5001C6.1231 11.5001 6.00877 11.4752 5.90353 11.4272L1 9.15723"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
case "Rocket":
|
||||
return Result.ok(
|
||||
<svg width="12" height="13" viewBox="0 0 12 13" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M4.93241 4.22177C3.33247 3.11335 1.84086 3.95947 0.590912 5.2625L3.35747 6.95474"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M8.24897 7.58936C9.34058 9.21388 8.50727 10.7284 7.22401 11.9976L5.5574 9.18849"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M3.35748 6.94646L5.56573 9.18868C7.33233 8.1141 9.34058 7.07337 10.2572 6.10879C12.2405 4.09502 11.0905 1.33667 11.0905 1.33667C11.0905 1.33667 8.37396 0.169022 6.3907 2.18279C5.44074 3.11352 4.40744 5.16114 3.35748 6.94646Z"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path d="M4.57413 4.82251L7.65734 7.95316" stroke="#373737" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path
|
||||
d="M8.92393 3.95965C9.154 3.95965 9.34058 3.77023 9.34058 3.53659C9.34058 3.30294 9.154 3.11353 8.92393 3.11353C8.69385 3.11353 8.50728 3.30294 8.50728 3.53659C8.50728 3.77023 8.69385 3.95965 8.92393 3.95965Z"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M3.24081 11.1008C2.7575 11.5746 0.590912 11.9977 0.590912 11.9977C0.590912 11.9977 1.00756 9.79774 1.47421 9.30699C1.58864 9.18236 1.72664 9.08235 1.87996 9.01295C2.03329 8.94358 2.19881 8.90629 2.36664 8.90327C2.53447 8.90027 2.70118 8.93162 2.85683 8.99545C3.01246 9.05925 3.15385 9.15427 3.27254 9.27484C3.39123 9.39533 3.4848 9.53891 3.54767 9.69697C3.61054 9.85494 3.64141 10.0242 3.63845 10.1947C3.63549 10.3651 3.59876 10.5331 3.53043 10.6888C3.46212 10.8445 3.36361 10.9846 3.24081 11.1008Z"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
case "Simulation":
|
||||
return Result.ok(
|
||||
<svg width="13" height="13" viewBox="0 0 13 13" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M3.61364 1H1.87121C1.39005 1 1 1.39606 1 1.88462V3.65385C1 4.1424 1.39005 4.53846 1.87121 4.53846H3.61364C4.09479 4.53846 4.48485 4.1424 4.48485 3.65385V1.88462C4.48485 1.39606 4.09479 1 3.61364 1Z"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M3.61364 8.96143H1.87121C1.39005 8.96143 1 9.35749 1 9.84604V11.6153C1 12.1038 1.39005 12.4999 1.87121 12.4999H3.61364C4.09479 12.4999 4.48485 12.1038 4.48485 11.6153V9.84604C4.48485 9.35749 4.09479 8.96143 3.61364 8.96143Z"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M11.4545 8.96143H9.71212C9.23097 8.96143 8.84091 9.35749 8.84091 9.84604V11.6153C8.84091 12.1038 9.23097 12.4999 9.71212 12.4999H11.4545C11.9357 12.4999 12.3258 12.1038 12.3258 11.6153V9.84604C12.3258 9.35749 11.9357 8.96143 11.4545 8.96143Z"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path d="M2.74243 8.96165V4.53857" stroke="#373737" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path
|
||||
d="M10.5833 8.96159V4.53852C10.5833 4.06929 10.3998 3.61928 10.073 3.28749C9.74618 2.95568 9.30299 2.76929 8.8409 2.76929H6.66287"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M8.4053 1L6.66287 2.76923L8.4053 4.53846"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
case "Grade":
|
||||
return Result.ok(
|
||||
<svg width="13" height="13" viewBox="0 0 13 13" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M10.6786 0.464355H11.6071C11.8534 0.464355 12.0896 0.562187 12.2637 0.736328C12.4378 0.910469 12.5357 1.14665 12.5357 1.39293V2.3215"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M0.464279 2.3215V1.39293C0.464279 1.14665 0.562111 0.910469 0.736251 0.736328C0.910393 0.562187 1.14657 0.464355 1.39285 0.464355H2.32142"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path d="M5.10713 0.464355H7.89285" stroke="#373737" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M12.5357 5.10718V7.89289" stroke="#373737" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M0.464279 5.10718V7.89289" stroke="#373737" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path
|
||||
d="M10.6786 12.5359H11.6071C11.8534 12.5359 12.0896 12.438 12.2637 12.2639C12.4378 12.0898 12.5357 11.8535 12.5357 11.6073V10.6787"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M0.464279 10.6787V11.6073C0.464279 11.8535 0.562111 12.0898 0.736251 12.2639C0.910393 12.438 1.14657 12.5359 1.39285 12.5359H2.32142"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path d="M5.10713 12.5359H7.89285" stroke="#373737" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path
|
||||
d="M8.35714 3.71436H4.64285C4.13002 3.71436 3.71428 4.1301 3.71428 4.64293V8.35721C3.71428 8.87004 4.13002 9.28578 4.64285 9.28578H8.35714C8.86997 9.28578 9.28571 8.87004 9.28571 8.35721V4.64293C9.28571 4.1301 8.86997 3.71436 8.35714 3.71436Z"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
case "DeleteCircle":
|
||||
return Result.ok(
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M10 0C4.47 0 0 4.47 0 10C0 15.53 4.47 20 10 20C15.53 20 20 15.53 20 10C20 4.47 15.53 0 10 0ZM10 18C5.59 18 2 14.41 2 10C2 5.59 5.59 2 10 2C14.41 2 18 5.59 18 10C18 14.41 14.41 18 10 18ZM10 8.59L13.59 5L15 6.41L11.41 10L15 13.59L13.59 15L10 11.41L6.41 15L5 13.59L8.59 10L5 6.41L6.41 5L10 8.59Z"
|
||||
fill="#49454F"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
case "Check":
|
||||
return Result.ok(
|
||||
<svg width="12" height="10" viewBox="0 0 12 10" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4 9.4L0 5.4L1.4 4L4 6.6L10.6 0L12 1.4L4 9.4Z" fill="white" />
|
||||
</svg>
|
||||
);
|
||||
case "PlusCircle":
|
||||
return Result.ok(
|
||||
<svg width="33" height="33" viewBox="0 0 33 33" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M16.5003 1.32007C8.09487 1.32007 1.32031 8.09463 1.32031 16.5001C1.32031 24.9055 8.09487 31.6801 16.5003 31.6801C24.9058 31.6801 31.6803 24.9055 31.6803 16.5001C31.6803 8.09463 24.9058 1.32007 16.5003 1.32007ZM16.5003 2.64007C24.1989 2.64007 30.3603 8.80151 30.3603 16.5001C30.3603 24.1986 24.1989 30.3601 16.5003 30.3601C8.80176 30.3601 2.64031 24.1986 2.64031 16.5001C2.64031 8.80151 8.80176 2.64007 16.5003 2.64007ZM15.8403 8.58007V15.8401H8.58031V17.1601H15.8403V24.4201H17.1603V17.1601H24.4203V15.8401H17.1603V8.58007H15.8403Z"
|
||||
fill="black"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
case "Pencil":
|
||||
return Result.ok(
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M16.06 0.589883L17.41 1.93988C18.2 2.71988 18.2 3.98988 17.41 4.76988L4.18 17.9999H0V13.8199L10.4 3.40988L13.23 0.589883C14.01 -0.190117 15.28 -0.190117 16.06 0.589883ZM2 15.9999L3.41 16.0599L13.23 6.22988L11.82 4.81988L2 14.6399V15.9999Z"
|
||||
fill="#31111D"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
case "MenuFab":
|
||||
return Result.ok(
|
||||
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_54850_114)">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M11 16V14L29 14V16L11 16ZM11 21H29V19H11V21ZM11 26H29V24H11V26Z"
|
||||
fill="#49454F"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_54850_114">
|
||||
<rect width="40" height="40" rx="20" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
case "Settings":
|
||||
return Result.ok(
|
||||
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_54841_7268)">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M27.3102 33.03C27.2102 33.71 26.5902 34.25 25.8502 34.25H22.1502C21.4102 34.25 20.7902 33.71 20.7002 32.98L20.4302 31.09C20.1602 30.95 19.9002 30.8 19.6402 30.63L17.8402 31.35C17.1402 31.61 16.3702 31.32 16.0302 30.7L14.2002 27.53C13.8502 26.87 14.0002 26.09 14.5602 25.65L16.0902 24.46C16.0802 24.31 16.0702 24.16 16.0702 24C16.0702 23.85 16.0802 23.69 16.0902 23.54L14.5702 22.35C13.9802 21.9 13.8302 21.09 14.2002 20.47L16.0502 17.28C16.3902 16.66 17.1602 16.38 17.8402 16.65L19.6502 17.38C19.9102 17.21 20.1702 17.06 20.4302 16.92L20.7002 15.01C20.7902 14.31 21.4102 13.76 22.1402 13.76H25.8402C26.5802 13.76 27.2002 14.3 27.2902 15.03L27.5602 16.92C27.8302 17.06 28.0902 17.21 28.3502 17.38L30.1502 16.66C30.8602 16.4 31.6302 16.69 31.9702 17.31L33.8102 20.49C34.1702 21.15 34.0102 21.93 33.4502 22.37L31.9302 23.56C31.9402 23.71 31.9502 23.86 31.9502 24.02C31.9502 24.18 31.9402 24.33 31.9302 24.48L33.4502 25.67C34.0102 26.12 34.1702 26.9 33.8202 27.53L31.9602 30.75C31.6202 31.37 30.8502 31.65 30.1602 31.38L28.3602 30.66C28.1002 30.83 27.8402 30.98 27.5802 31.12L27.3102 33.03ZM22.6202 32.25H25.3802L25.7502 29.7L26.2802 29.48C26.7202 29.3 27.1602 29.04 27.6202 28.7L28.0702 28.36L30.4502 29.32L31.8302 26.92L29.8002 25.34L29.8702 24.78L29.8733 24.7531C29.9023 24.5027 29.9302 24.2607 29.9302 24C29.9302 23.73 29.9002 23.47 29.8702 23.22L29.8002 22.66L31.8302 21.08L30.4402 18.68L28.0502 19.64L27.6002 19.29C27.1802 18.97 26.7302 18.71 26.2702 18.52L25.7502 18.3L25.3802 15.75H22.6202L22.2502 18.3L21.7202 18.51C21.2802 18.7 20.8402 18.95 20.3802 19.3L19.9302 19.63L17.5502 18.68L16.1602 21.07L18.1902 22.65L18.1202 23.21C18.0902 23.47 18.0602 23.74 18.0602 24C18.0602 24.26 18.0802 24.53 18.1202 24.78L18.1902 25.34L16.1602 26.92L17.5402 29.32L19.9302 28.36L20.3802 28.71C20.8102 29.04 21.2402 29.29 21.7102 29.48L22.2402 29.7L22.6202 32.25ZM27.5002 24C27.5002 25.933 25.9332 27.5 24.0002 27.5C22.0672 27.5 20.5002 25.933 20.5002 24C20.5002 22.067 22.0672 20.5 24.0002 20.5C25.9332 20.5 27.5002 22.067 27.5002 24Z"
|
||||
fill="#49454F"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_54841_7268">
|
||||
<rect x="4" y="4" width="40" height="40" rx="20" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
return Result.error(undefined);
|
||||
};
|
78
ui/src/core/ui/input/input.tsx
Normal file
78
ui/src/core/ui/input/input.tsx
Normal file
|
@ -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<string>(() => props.value ?? "");
|
||||
const ref = React.useRef<HTMLDivElement>(null);
|
||||
const [isAppendInnerText, setAppendInnerText] = React.useState(true);
|
||||
React.useEffect(() => {
|
||||
if (ref.current && isAppendInnerText) {
|
||||
ref.current.innerText = value;
|
||||
setAppendInnerText(false);
|
||||
}
|
||||
}, [ref, value, isAppendInnerText, setAppendInnerText]);
|
||||
|
||||
|
||||
return (
|
||||
<div
|
||||
style={Object.assign(
|
||||
{
|
||||
backgroundColor: "rgba(230, 224, 233, 1)",
|
||||
height: 58,
|
||||
borderRadius: "4px 4px 0px 0px",
|
||||
borderBottom: "solid 1px black",
|
||||
padding: "10px 10px 10px 10px",
|
||||
},
|
||||
props.style
|
||||
)}
|
||||
>
|
||||
<CoreText type={CoreTextType.small} text={props.label} />
|
||||
|
||||
<input
|
||||
defaultValue={props.value}
|
||||
style={{
|
||||
backgroundColor: "#00008000",
|
||||
border: 1,
|
||||
fontSize: 16,
|
||||
fontFamily: "Roboto",
|
||||
color: "#1D1B20",
|
||||
height: 24,
|
||||
width: "100%",
|
||||
userSelect: 'none',
|
||||
outline:'none'
|
||||
}}
|
||||
onChange={(e) => {
|
||||
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 : (
|
||||
<div style={{ color: "#ff1d0c" }}>{props.error ? props.error : "error"}</div>
|
||||
)
|
||||
) : null
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
};
|
127
ui/src/core/ui/pages/main_page.tsx
Normal file
127
ui/src/core/ui/pages/main_page.tsx
Normal file
|
@ -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 (
|
||||
<div onClick={() => navigate(props.path)} style={{ height: 56, cursor: "pointer" }}>
|
||||
<div
|
||||
style={
|
||||
props.isActive
|
||||
? {
|
||||
textAlignLast: "center",
|
||||
height: 32,
|
||||
backgroundColor: "rgba(232, 222, 248, 1)",
|
||||
marginLeft: 5,
|
||||
marginRight: 5,
|
||||
alignContent: "center",
|
||||
borderRadius: 12,
|
||||
}
|
||||
: { textAlignLast: "center", alignContent: "center" }
|
||||
}
|
||||
>
|
||||
<Icon type={props.icon ?? ""} />
|
||||
</div>
|
||||
<div style={{ textAlignLast: "center" }}>{props.name}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
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 (
|
||||
<div style={{ display: "flex" }}>
|
||||
<div
|
||||
style={{
|
||||
width: 90,
|
||||
height: window.innerHeight,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<div style={{ paddingTop: 43 }}>
|
||||
<div style={{ textAlignLast: "center" }}>
|
||||
<Icon type="MenuFab" />
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
textAlignLast: "center",
|
||||
width: 56,
|
||||
height: 56,
|
||||
borderRadius: 12,
|
||||
backgroundColor: "#ffd9e4",
|
||||
alignContent: "center",
|
||||
}}
|
||||
>
|
||||
<Icon style={{ marginTop: 3 }} type="Pencil" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
{blocks.map((el) => (
|
||||
<Block isActive={el.isActive} name={el.name} path={el.path} icon={el.icon} />
|
||||
))}
|
||||
</div>
|
||||
<div style={{ paddingBottom: 10 }}>
|
||||
<Icon type={"Settings"} />
|
||||
</div>
|
||||
</div>
|
||||
{props.isLoading ? (
|
||||
<div style={{ alignContent: "center", width: "100%", textAlign: "center" }}>
|
||||
<Spin indicator={<LoadingOutlined style={{ fontSize: 68, color: "rgba(103, 80, 164, 1)" }} spin />} />
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div style={{ width: 241, height: window.innerHeight, backgroundColor: "#F7F2FA", borderRadius: 16 }}> </div>
|
||||
{props.bodyChildren}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
67
ui/src/core/ui/select/select.tsx
Normal file
67
ui/src/core/ui/select/select.tsx
Normal file
|
@ -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<HTMLDivElement>(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 (
|
||||
<div ref={ref} style={props.style}>
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: "rgba(230, 224, 233, 1)",
|
||||
height: 58,
|
||||
borderRadius: "4px 4px 0px 0px",
|
||||
borderBottom: "solid 1px black",
|
||||
padding: "10px 10px 10px 10px",
|
||||
}}
|
||||
>
|
||||
<CoreText type={CoreTextType.small} text={props.label} />
|
||||
<div style={{ fontSize: 16, fontFamily: "Roboto", color: "#1D1B20", height: 24 }}>{value}</div>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: "rgba(243, 237, 247, 1)",
|
||||
boxShadow: "0px 1px 2px rgba(0, 0, 0, 0.3), 0px 2px 6px 2px rgba(0, 0, 0, 0.15)",
|
||||
borderRadius: 4,
|
||||
}}
|
||||
>
|
||||
{cursorIsCorses
|
||||
? props.items.map((el) => (
|
||||
<div
|
||||
onClick={() => {
|
||||
setValue(el);
|
||||
props.onChange(el);
|
||||
}}
|
||||
style={{
|
||||
height: 48,
|
||||
textAlign: "center",
|
||||
alignContent: "center",
|
||||
cursor: "pointer",
|
||||
borderBottom: "1px solid",
|
||||
}}
|
||||
>
|
||||
{el}
|
||||
</div>
|
||||
))
|
||||
: null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
19
ui/src/core/ui/switch/switch.tsx
Normal file
19
ui/src/core/ui/switch/switch.tsx
Normal file
|
@ -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 (
|
||||
<div
|
||||
style={{ height: 40, width: 40, borderRadius: 2, alignContent: "center", cursor: "pointer" }}
|
||||
onClick={() => props.onChange(props.isSelected, props.id)}
|
||||
>
|
||||
<div style={{ backgroundColor: "rgba(104, 80, 164, 1)", width: 20, height: 20, textAlign: "center" }}>
|
||||
{props.isSelected ? <Icon type={"Check"} /> : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -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 <div style={{ color: "white", fontSize: "20px" }}>{props.text}</div>;
|
||||
if (props.type === CoreTextType.small) {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
color: props.color ?? "rgba(73, 69, 79, 1)",
|
||||
fontSize: 12,
|
||||
fontFamily: "Roboto",
|
||||
fontWeight: 400,
|
||||
fontSizeAdjust: 14,
|
||||
textOverflow: "ellipsis",
|
||||
}}
|
||||
>
|
||||
{props.text}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (props.type === CoreTextType.large) {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
color: props.color ?? "#1D1B20",
|
||||
fontSize: 16,
|
||||
fontFamily: "Roboto",
|
||||
fontWeight: 400,
|
||||
fontSizeAdjust: 14,
|
||||
textOverflow: "ellipsis",
|
||||
}}
|
||||
>
|
||||
{props.text}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (props.type === CoreTextType.medium)
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
color: props.color ?? "#1D1B20",
|
||||
fontSize: 14,
|
||||
fontFamily: "Roboto",
|
||||
fontWeight: 400,
|
||||
textOverflow: "ellipsis",
|
||||
fontSizeAdjust: 14,
|
||||
}}
|
||||
>
|
||||
{props.text}
|
||||
</div>
|
||||
);
|
||||
if (props.type === CoreTextType.header)
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
color: props.color ?? "#1D1B20",
|
||||
fontSize: 20,
|
||||
fontFamily: "Roboto",
|
||||
fontWeight: 500,
|
||||
textOverflow: "ellipsis",
|
||||
|
||||
fontSizeAdjust: 16,
|
||||
}}
|
||||
>
|
||||
{props.text}
|
||||
</div>
|
||||
);
|
||||
return <div>{props.text}</div>;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
export enum StickObjectsMarkingStoreMode {
|
||||
objectsToWhichItSticks = "objectsToWhichItSticks",
|
||||
objectThatSticks = "objectThatSticks",
|
||||
addPointsObjectsToWhichItSticks = "addPointsObjectsToWhichItSticks",
|
||||
addPointsObjectThatSticks = "addPointsObjectThatSticks",
|
||||
move = "move",
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
export enum StickyLoaderMode {
|
||||
IN = "IN",
|
||||
OUT = "OUT",
|
||||
}
|
|
@ -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<StickButtonsProps> = observer((props) => {
|
||||
return (
|
||||
<>
|
||||
{props.isVisible ? (
|
||||
<>
|
||||
{props.name}
|
||||
<Button
|
||||
style={{
|
||||
backgroundColor: props.storeMode === props.storeModePoints ? "#ff7e1e" : "",
|
||||
}}
|
||||
onClick={() => props.setPointMode()}
|
||||
>
|
||||
add points
|
||||
</Button>
|
||||
{props.points.map((el) => {
|
||||
return (
|
||||
<>
|
||||
{el.x} {el.y} {el.z}
|
||||
</>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
) : (
|
||||
<Button
|
||||
style={{
|
||||
backgroundColor: props.groupMode === props.storeMode ? "#ff7e1e" : "",
|
||||
}}
|
||||
onClick={() => props.setMode(props.storeMode)}
|
||||
>
|
||||
{props.body}
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
});
|
||||
export const StickObjectsMarkingScreenPath = "/sticky/objects/mark";
|
||||
export const StickObjectsMarkingScreen = observer(() => {
|
||||
const canvasRef = React.useRef<HTMLCanvasElement>(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 (
|
||||
<div>
|
||||
<canvas ref={canvasRef} style={{ position: "absolute", overflow: "hidden" }} />
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
alignContent: "center",
|
||||
justifyContent: "space-between",
|
||||
position: "absolute",
|
||||
width: "100vw",
|
||||
}}
|
||||
>
|
||||
<div style={{ backgroundColor: "aqua", display: "flex", flexDirection: "column", padding: "5px" }}>
|
||||
{store.stickyObjects?.map((el) => {
|
||||
return (
|
||||
<div>
|
||||
<div style={{ display: "flex" }}>
|
||||
<Button onClick={() => store.loadAsset(el, StickyLoaderMode.IN)}>Sticky in</Button>
|
||||
<Button onClick={() => store.loadAsset(el, StickyLoaderMode.OUT)}>Sticky Out</Button>
|
||||
</div>
|
||||
<div>{el.name}</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{/* <div style={{ backgroundColor: "white", padding: "20px" }}>
|
||||
{Object.keys(store.points).map((el) => {
|
||||
// @ts-expect-error
|
||||
const v = store.points[el];
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
{el as string}:{v}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
})}
|
||||
<div>Marking objects for sticking</div>
|
||||
<div>
|
||||
<StickButtons
|
||||
storeMode={store.mode}
|
||||
setMode={() => 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"
|
||||
/>
|
||||
<StickButtons
|
||||
body="objectsToWhichItSticksName"
|
||||
points={store.objectsToWhichItSticksPoints}
|
||||
storeMode={store.mode}
|
||||
setMode={() => store.setMode(StickObjectsMarkingStoreMode.objectsToWhichItSticks)}
|
||||
groupMode={StickObjectsMarkingStoreMode.objectsToWhichItSticks}
|
||||
name={store.objectsToWhichItSticksName}
|
||||
storeModePoints={StickObjectsMarkingStoreMode.addPointsObjectsToWhichItSticks}
|
||||
isVisible={store.objectsToWhichItSticksName !== undefined}
|
||||
setPointMode={() => store.setMode(StickObjectsMarkingStoreMode.addPointsObjectsToWhichItSticks)}
|
||||
/>
|
||||
<Button onClick={() => store.onSaveSticky()}>save</Button>
|
||||
<Button
|
||||
onClick={() => store.setMode(StickObjectsMarkingStoreMode.move)}
|
||||
style={{ backgroundColor: store.mode === StickObjectsMarkingStoreMode.move ? "#ff7e1e" : "" }}
|
||||
>
|
||||
run
|
||||
</Button>
|
||||
</div>
|
||||
</div> */}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
|
@ -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<HttpError> {
|
||||
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<any> {
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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<IProjectModel[]>(HttpMethod.GET, "/project_instance");
|
||||
return this._jsonRequest<IProjectModel[]>(HttpMethod.GET, "/projects");
|
||||
}
|
||||
|
||||
async getActivePipeline() {
|
||||
return this._jsonRequest<ActivePipeline>(HttpMethod.GET, "/realtime");
|
||||
return this._jsonRequest<UUID>(HttpMethod.GET, "/projects/get/active/project/id");
|
||||
}
|
||||
async setActivePipeline(id: string) {
|
||||
return this._jsonRequest(HttpMethod.POST, `/project_instance/set/active/project?id=${id}`);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue