diff --git a/.vscode/settings.json b/.vscode/settings.json index 566af4b..2f6b35b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -15,7 +15,9 @@ "число", "эпох", "эпоха", + "Contolls", "skils", - "typedataset" + "typedataset", + "usecases" ] } diff --git a/p.json b/p.json deleted file mode 100644 index aba7bb9..0000000 --- a/p.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "dependency":{ - "weights_path":"", - "object_name":"" - } -} \ No newline at end of file diff --git a/server/src/core/controllers/app.ts b/server/src/core/controllers/app.ts index 8908e2e..ba5a450 100644 --- a/server/src/core/controllers/app.ts +++ b/server/src/core/controllers/app.ts @@ -6,7 +6,6 @@ import { Server } from "socket.io"; import { createServer } from "http"; import { SocketSubscriber } from "./socket_controller"; import { dirname } from "path"; -import { SetLastActivePipelineToRealTimeServiceScenario } from "../scenarios/set_active_pipeline_to_realtime_service_scenario"; import { CheckAndCreateStaticFilesFolderUseCase } from "../usecases/check_and_create_static_files_folder_usecase"; import { DataBaseConnectUseCase } from "../usecases/database_connect_usecase"; import { TypedEvent } from "../helpers/typed_event"; @@ -58,8 +57,6 @@ export class App extends TypedEvent { io.on("connection", (socket) => { this.socketSubscribers.map((el) => { el.emitter.on((e) => { - console.log(el.event) - console.log(e) socket.emit(el.event, e); }); }); @@ -125,3 +122,4 @@ export class App extends TypedEvent { }; } + diff --git a/server/src/core/controllers/http_controller.ts b/server/src/core/controllers/http_controller.ts index 7fd5561..736e8e0 100644 --- a/server/src/core/controllers/http_controller.ts +++ b/server/src/core/controllers/http_controller.ts @@ -3,6 +3,7 @@ import { Result } from "../helpers/result"; import { Router, Request, Response } from "express"; import { IRouteModel, Routes } from "../interfaces/router"; import { CoreValidation } from "../validations/core_validation"; +import { plainToInstance } from "class-transformer"; export type HttpMethodType = "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "PATCH" | "HEAD"; @@ -78,11 +79,11 @@ export class CoreHttpController implements ICoreHttpController { } call(): Routes { if (this.subRoutes.isNotEmpty()) { - this.subRoutes.map((el) => { + this.subRoutes.map(async (el) => { this.router[el.method.toLowerCase()](this.mainURL + "/" + el.subUrl, async (req, res) => { if (el.fn instanceof CallbackStrategyWithValidationModel) { - // TODO(IDONTSUDO): - throw Error("needs to be implimed"); + this.responseHelper(res, el.fn.call(req.body)); + return; } if (el.fn instanceof CallbackStrategyWithIdQuery) { if (req.query.id === undefined) { diff --git a/server/src/core/middlewares/validation_model.ts b/server/src/core/middlewares/validation_model.ts index 91525e2..f146d17 100644 --- a/server/src/core/middlewares/validation_model.ts +++ b/server/src/core/middlewares/validation_model.ts @@ -10,11 +10,13 @@ export const validationModelMiddleware = ( forbidNonWhitelisted = true ): RequestHandler => { return (req, res, next) => { + if (type === null && type == undefined) { next(); return; } const model = plainToInstance(type, req[value]); + validate(model, { skipMissingProperties, whitelist, forbidNonWhitelisted }).then((errors: ValidationError[]) => { if (errors.length > 0) { const message = errors.map((error: ValidationError) => Object.values(error.constraints)).join(", "); diff --git a/server/src/features/behavior_trees/behavior_trees_presentation.ts b/server/src/features/behavior_trees/behavior_trees_presentation.ts index 53ae4a0..dd1618c 100644 --- a/server/src/features/behavior_trees/behavior_trees_presentation.ts +++ b/server/src/features/behavior_trees/behavior_trees_presentation.ts @@ -1,8 +1,11 @@ import { CrudController } from "../../core/controllers/crud_controller"; -import { GetBehaviorTreeSkillsTemplatesUseCase } from "./get_bt_skills_templates_usecase"; +import { GetBehaviorTreeSkillsTemplatesUseCase } from "./domain/get_bt_skills_templates_usecase"; import { BehaviorTreeValidationModel } from "./models/behavior_tree_validation_model"; import { BehaviorTreeDBModel } from "./models/behavior_tree_database_model"; import { ReadByIdDataBaseModelScenario } from "../../core/scenarios/read_by_id_database_model_scenario"; +import { GetBehaviorTreeActiveProjectScenario } from "./domain/get_behavior_tree_active_project_scenario"; +import { SaveBtScenario as FillBtScenario } from "./domain/save_bt_scenario"; + export class BehaviorTreesPresentation extends CrudController { constructor() { super({ @@ -10,6 +13,12 @@ export class BehaviorTreesPresentation extends CrudController + ( + await new SearchOneDataBaseModelUseCase(ProjectDBModel).call( + { isActive: true }, + "no active projects" + ) + ).map(async (project) => Result.ok(await BehaviorTreeDBModel.find({ project: project._id }))); +} diff --git a/server/src/features/behavior_trees/domain/get_bt_skills_templates_usecase.ts b/server/src/features/behavior_trees/domain/get_bt_skills_templates_usecase.ts new file mode 100644 index 0000000..cb5e367 --- /dev/null +++ b/server/src/features/behavior_trees/domain/get_bt_skills_templates_usecase.ts @@ -0,0 +1,44 @@ +import { CallbackStrategyWithEmpty, ResponseBase } from "../../../core/controllers/http_controller"; +import { Result } from "../../../core/helpers/result"; + +export class GetBehaviorTreeSkillsTemplatesUseCase extends CallbackStrategyWithEmpty { + call = async (): ResponseBase => { + return Result.ok({ + skills: [ + { + SkillPackage: { name: "Robossembler", version: "1.0", format: "1" }, + Module: { name: "PoseEstimation", description: "Pose Estimation skill with DOPE" }, + Launch: { package: "rbs_perception", executable: "pe_dope_lc.py", name: "lc_dope" }, + BTAction: [ + { + name: "peConfigure", + type: "run", + param: [ + { + type: "weights", + dependency: {}, + }, + ], + result: ["POSE"], + }, + { name: "peStop", type: "stop", param: [], result: [] }, + ], + Interface: { + Input: [ + { name: "cameraLink", type: "CAMERA" }, + { name: "object_name", type: "MODEL" }, + ], + Output: [{ name: "pose_estimation_topic", type: "POSE" }], + }, + Settings: [ + { name: "cameraLink", value: "inner_rgbd_camera" }, + { name: "pose", value: "" }, + { name: "publishDelay", value: 0.5 }, + { name: "tf2_send_pose", value: 1 }, + { name: "mesh_scale", value: 0.001 }, + ], + }, + ], + }); + }; +} diff --git a/server/src/features/behavior_trees/domain/save_bt_scenario.ts b/server/src/features/behavior_trees/domain/save_bt_scenario.ts new file mode 100644 index 0000000..05bca8c --- /dev/null +++ b/server/src/features/behavior_trees/domain/save_bt_scenario.ts @@ -0,0 +1,32 @@ +import { CallbackStrategyWithValidationModel, ResponseBase } from "../../../core/controllers/http_controller"; +import { CreateFileUseCase } from "../../../core/usecases/create_file_usecase"; +import { CreateFolderUseCase } from "../../../core/usecases/create_folder_usecase"; +import { SearchOneDataBaseModelUseCase } from "../../../core/usecases/search_database_model_usecase"; +import { IProjectModel, ProjectDBModel } from "../../_projects/models/project_database_model"; +import { FolderStructure } from "../../projects/domain/upload_file_to_to_project_scenario"; +import { BehaviorTreeValidationModel } from "../models/behavior_tree_validation_model"; + +export class SaveBtScenario extends CallbackStrategyWithValidationModel { + validationModel: BehaviorTreeValidationModel = new BehaviorTreeValidationModel(); + call = async (model: BehaviorTreeValidationModel): ResponseBase => + ( + await new SearchOneDataBaseModelUseCase(ProjectDBModel).call( + { isActive: true }, + "no active projects" + ) + ).map(async (project) => { + const folder = `${project.rootDir}/${FolderStructure.behaviorTrees}/`; + + (await new CreateFolderUseCase().call(folder)).map(async () => + (await new CreateFolderUseCase().call(`${folder}${model.name}/`)).map(async () => + (await new CreateFileUseCase().call(`${folder}${model.name}/bt.xml`, Buffer.from(model.xml))).map( + async () => + await new CreateFileUseCase().call( + `${folder}${model.name}/skills.json`, + Buffer.from(JSON.stringify(model.skills)) + ) + ) + ) + ); + }); +} diff --git a/server/src/features/behavior_trees/get_bt_skills_templates_usecase.ts b/server/src/features/behavior_trees/get_bt_skills_templates_usecase.ts deleted file mode 100644 index cce943b..0000000 --- a/server/src/features/behavior_trees/get_bt_skills_templates_usecase.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { CallbackStrategyWithEmpty, ResponseBase } from "../../core/controllers/http_controller"; -import { Result } from "../../core/helpers/result"; - -export class GetBehaviorTreeSkillsTemplatesUseCase extends CallbackStrategyWithEmpty { - call = async (): ResponseBase => { - return Result.ok({ - skills: [ - { - SkillPackage: { - name: "Robossembler", - version: "1.0", - format: "1", - }, - Module: { - name: "PoseEstimation", - description: "Pose Estimation skill with DOPE", - }, - Launch: { - executable: "pe_dope_lc.py", - }, - ROS2: { - node_name: "lc_dope", - }, - BTAction: [ - { - name: "peConfigure", - format: "yaml", - type: "run", - param: [ - { - type: "weights", - dependency: {}, - }, - { - type: "form", - dependency: {}, - }, - ], - result: ["POSE"], - }, - { - name: "peStop", - format: "yaml", - type: "stop", - param: [], - result: [], - }, - ], - Interface: { - Input: [ - { - name: "cameraLink", - type: "CAMERA", - }, - { - name: "object_name", - type: "MODEL", - }, - ], - Output: [ - { - name: "pose_estimation_topic", - type: "POSE", - }, - ], - }, - Settings: [ - { - name: "cameraLink", - value: "inner_rgbd_camera", - }, - { - name: "pose", - value: "", - }, - { - name: "publishDelay", - value: 0.5, - }, - { - name: "tf2_send_pose", - value: 1, - }, - { - name: "mesh_scale", - value: 0.001, - }, - ], - xxx: { - cameraLink: "inner_rgbd_camera", - topicImage: "/inner_rgbd_camera/image", - topicCameraInfo: "/inner_rgbd_camera/camera_info", - }, - }, - ], - }); - }; -} diff --git a/server/src/features/behavior_trees/models/behavior_tree_database_model.ts b/server/src/features/behavior_trees/models/behavior_tree_database_model.ts index d7176ce..3d186d0 100644 --- a/server/src/features/behavior_trees/models/behavior_tree_database_model.ts +++ b/server/src/features/behavior_trees/models/behavior_tree_database_model.ts @@ -3,7 +3,7 @@ import { IProjectModel, projectSchema } from "../../_projects/models/project_dat export interface IBehaviorTreeModel { name: string; - project?: IProjectModel; + project?: IProjectModel | string; unixTime?: number; local_path?: string; } @@ -26,6 +26,13 @@ export const BehaviorTreeSchema = new Schema({ dependency: { type: Schema.Types.Mixed, }, + skills: { + type: Schema.Types.Mixed, + }, + scene: { + type: Array, + default: null, + }, project: { type: Schema.Types.ObjectId, ref: projectSchema, diff --git a/server/src/features/behavior_trees/models/behavior_tree_validation_model.ts b/server/src/features/behavior_trees/models/behavior_tree_validation_model.ts index 38a307d..9dfac34 100644 --- a/server/src/features/behavior_trees/models/behavior_tree_validation_model.ts +++ b/server/src/features/behavior_trees/models/behavior_tree_validation_model.ts @@ -1,7 +1,11 @@ -import { IsString } from "class-validator"; +import { IsMongoId, IsString } from "class-validator"; import { IBehaviorTreeModel } from "./behavior_tree_database_model"; export class BehaviorTreeValidationModel implements IBehaviorTreeModel { @IsString() public name: string; + @IsMongoId() + public project:string; + public skills:any; + public xml:string; } diff --git a/server/src/features/datasets/domain/exec_process_scenario.ts b/server/src/features/datasets/domain/exec_process_scenario.ts index f5db08f..c26e9f2 100644 --- a/server/src/features/datasets/domain/exec_process_scenario.ts +++ b/server/src/features/datasets/domain/exec_process_scenario.ts @@ -16,7 +16,6 @@ export class ExecDatasetProcessScenario extends CallbackStrategyWithIdQuery { return (await new IsHaveActiveProcessUseCase().call()).map(async () => { await DatasetDBModel.findById(id).updateOne({ processStatus: "RUN" }); model.local_path = `${model.local_path}/${FolderStructure.datasets}/`; - 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)}'`, diff --git a/server/src/features/projects/domain/upload_file_to_to_project_scenario.ts b/server/src/features/projects/domain/upload_file_to_to_project_scenario.ts index dfed44b..aeca418 100644 --- a/server/src/features/projects/domain/upload_file_to_to_project_scenario.ts +++ b/server/src/features/projects/domain/upload_file_to_to_project_scenario.ts @@ -13,6 +13,7 @@ export enum FolderStructure { assets = "assets", weights = "weights", datasets = "datasets", + behaviorTrees = "behavior_trees", } export class UploadCadFileToProjectScenario extends CallbackStrategyWithFileUpload { @@ -30,9 +31,12 @@ export class UploadCadFileToProjectScenario extends CallbackStrategyWithFileUplo "" ) ).map(async () => - (await new CreateManyFolderScenario().call(databaseModel.rootDir, Object.keys(FolderStructure).map((el) => `/${el}`))).map(() => - Result.ok("file upload and save") - ) + ( + await new CreateManyFolderScenario().call( + databaseModel.rootDir, + Object.keys(FolderStructure).map((el) => `/${el}`) + ) + ).map(() => Result.ok("file upload and save")) ) ) ); diff --git a/server/src/main.ts b/server/src/main.ts index 0130178..8413c11 100644 --- a/server/src/main.ts +++ b/server/src/main.ts @@ -9,5 +9,4 @@ extensions(); const socketSubscribers = [new SocketSubscriber(executorProgramService, "realtime")]; -new App(httpRoutes, socketSubscribers).listen(); - +new App(httpRoutes, socketSubscribers).listen(); \ No newline at end of file diff --git a/ui/package-lock.json b/ui/package-lock.json index 550815b..bc40a61 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -22,6 +22,7 @@ "class-validator": "^0.14.0", "formik-antd": "^2.0.4", "i18next": "^23.6.0", + "just-clone": "^6.2.0", "mobx": "^6.10.0", "mobx-react-lite": "^4.0.4", "mobx-store-inheritance": "^1.0.6", @@ -11116,6 +11117,11 @@ "node": ">=4.0" } }, + "node_modules/just-clone": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/just-clone/-/just-clone-6.2.0.tgz", + "integrity": "sha512-1IynUYEc/HAwxhi3WDpIpxJbZpMCvvrrmZVqvj9EhpvbH8lls7HhdhiByjL7DkAaWlLIzpC0Xc/VPvy/UxLNjA==" + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", diff --git a/ui/package.json b/ui/package.json index d2358b3..72c809e 100644 --- a/ui/package.json +++ b/ui/package.json @@ -17,6 +17,7 @@ "class-validator": "^0.14.0", "formik-antd": "^2.0.4", "i18next": "^23.6.0", + "just-clone": "^6.2.0", "mobx": "^6.10.0", "mobx-react-lite": "^4.0.4", "mobx-store-inheritance": "^1.0.6", @@ -50,7 +51,7 @@ "xml-formatter": "^3.6.2" }, "scripts": { - "dev": "react-scripts start", + "dev": "GENERATE_SOURCEMAP=false && react-scripts start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject", diff --git a/ui/src/core/extensions/array.ts b/ui/src/core/extensions/array.ts index 46c85bd..e5b2f0d 100644 --- a/ui/src/core/extensions/array.ts +++ b/ui/src/core/extensions/array.ts @@ -63,11 +63,20 @@ export const ArrayExtensions = () => { // eslint-disable-next-line no-extend-native Array.prototype.rFind = function (predicate) { const result = this.find(predicate as any); - console.log(result) if (result === undefined) { return Result.error(undefined); } return Result.ok(result); }; } + if ([].maxLength === undefined) { + // eslint-disable-next-line no-extend-native + Array.prototype.maxLength = function (length) { + if (this.length > length) { + return this; + } else { + return this.slice(0, length); + } + }; + } }; diff --git a/ui/src/core/extensions/extensions.ts b/ui/src/core/extensions/extensions.ts index ef9145e..802a400 100644 --- a/ui/src/core/extensions/extensions.ts +++ b/ui/src/core/extensions/extensions.ts @@ -12,7 +12,6 @@ export type OptionalProperties = { [P in keyof T]?: T[P]; }; - declare global { interface Array { // @strict: The parameter is determined whether the arrays must be exactly the same in content and order of this relationship or simply follow the same requirements. @@ -22,10 +21,8 @@ declare global { isNotEmpty(): boolean; hasIncludeElement(element: T): boolean; repeat(quantity: number): Array; - rFind( - predicate: (value: T, index: number, obj: never[]) => boolean, - thisArg?: any - ): Result; + rFind(predicate: (value: T, index: number, obj: never[]) => boolean, thisArg?: any): Result; + maxLength(length: number): Array; } interface Number { fromArray(): number[]; @@ -43,7 +40,10 @@ declare global { replaceMany(searchValues: string[], replaceValue: string): string; isEqual(str: string): boolean; isEqualMany(str: string[]): boolean; + hasPattern(pattern: string): boolean; + hasNoPattern(pattern: string): boolean; } + interface Map { addValueOrMakeCallback(key: K, value: V, callBack: CallBackVoidFunction): void; getKeyFromValueIsExists(value: V): K | undefined; @@ -51,6 +51,7 @@ declare global { keysToJson(): string; toArray(): V[]; getPredicateValue(callBack: (value: V) => boolean): K[]; + incrementValue(key: K): void; } interface Vector3 {} } diff --git a/ui/src/core/extensions/map.ts b/ui/src/core/extensions/map.ts index 8b349ff..4f4f32e 100644 --- a/ui/src/core/extensions/map.ts +++ b/ui/src/core/extensions/map.ts @@ -58,4 +58,14 @@ export const MapExtensions = () => { return result; }; } + if (Map.prototype.incrementValue === undefined) { + // eslint-disable-next-line no-extend-native + Map.prototype.incrementValue = function (key) { + if (this.get(key)) { + this.set(key, this.get(key) + 1); + } else { + this.set(key, 1); + } + }; + } }; diff --git a/ui/src/core/extensions/string.ts b/ui/src/core/extensions/string.ts index 79709c1..8ba0f08 100644 --- a/ui/src/core/extensions/string.ts +++ b/ui/src/core/extensions/string.ts @@ -36,4 +36,14 @@ export const StringExtensions = () => { return false; }; } + if ("".hasPattern === undefined) { + String.prototype.hasPattern = function (pattern) { + return new RegExp(pattern).test(this as string); + }; + } + if ("".hasNoPattern === undefined) { + String.prototype.hasNoPattern = function (pattern) { + return !this.hasPattern(pattern); + }; + } }; diff --git a/ui/src/core/model/skill_model.ts b/ui/src/core/model/skill_model.ts index 7c3608c..57de3fa 100644 --- a/ui/src/core/model/skill_model.ts +++ b/ui/src/core/model/skill_model.ts @@ -1,9 +1,21 @@ -import { IsArray, IsString, ValidateNested } from "class-validator"; +import { IsArray, IsOptional, IsString, ValidateNested } from "class-validator"; import { Type } from "class-transformer"; import { ISkillView } from "../../features/behavior_tree_builder/presentation/ui/skill_tree/skill_tree"; import { v4 } from "uuid"; +import { Result } from "../helper/result"; +import clone from "just-clone"; -export interface ISkillPoseEstimation { +export interface IDependency { + skills: ISkillDependency[]; +} + +export interface ISkillDependency { + sid: string; + dependency: Object; +} + +export interface ISkill { + sid?: string; SkillPackage: ISkillPackage; Module: IModule; Launch: ILaunch; @@ -14,17 +26,21 @@ export interface ISkillPoseEstimation { xxx: IXxx; } export interface IWeightsDependency { - objectName: string; - weightsPath: string; + weights_name:string; + object_name: string; + weights_file: string; + dimensions: number[]; } export interface IParam { type: string; - dependency: IWeightsDependency; + dependency: Object; } export interface IBTAction { name: string; format: string; + // TODO: Нужно выпилить его отсюда + // sid?: string; type: string; param: IParam[]; result: string[]; @@ -91,6 +107,7 @@ export class BTAction implements IBTAction { format: string; @IsString() type: string; + sid?: string; @IsArray() param: IParam[]; @IsArray() @@ -127,7 +144,10 @@ export class Xxx implements IXxx { topicImage: string; topicCameraInfo: string; } -export class SkillModelPoseEstimation implements ISkillPoseEstimation { +export class SkillModel implements ISkill { + @IsOptional() + @IsString() + sid?: string; @ValidateNested() @Type(() => SkillPackage) SkillPackage: ISkillPackage; @@ -154,12 +174,71 @@ export class SkillModelPoseEstimation implements ISkillPoseEstimation { @ValidateNested() @Type(() => Xxx) xxx: IXxx; + static empty() { + const skillModel = new SkillModel(); + skillModel.BTAction = []; + return skillModel; + } + public static isEmpty(skill: SkillModel): Result { + if (skill.BTAction.isEmpty()) { + return Result.error(undefined); + } + return Result.ok(Object.assign(skill, {})); + } + + public getSid = () => this.sid; + public setSid = (sid: string) => { + const result = clone(this); + result.sid = sid; + return result; + }; +} + +export class SkillDependency implements IDependency { + constructor(public skills: ISkillDependency[]) {} + static empty() { + return new SkillDependency([]); + } + static isEmpty = (skill: SkillDependency) => { + if (skill.skills.isEmpty()) { + return Result.error(undefined); + } + return Result.ok(skill); + }; } export class Skills { @IsArray() - @Type(() => SkillModelPoseEstimation) - skills: SkillModelPoseEstimation[]; + @Type(() => SkillModel) + skills: SkillModel[]; + validation = (): Result => { + const errors: string[] = []; + this.skills.forEach((skill) => { + skill.BTAction.forEach((action) => { + if (action.param.isNotEmpty()) { + action.param.forEach((param) => { + if (Object.keys(param.dependency).isEmpty()) { + errors.push(param.type); + } + }); + } + }); + }); + if (errors.isNotEmpty()) { + return Result.error(errors); + } + return Result.ok(undefined); + }; + skillBySid = (sid: string) => + SkillModel.isEmpty( + this.skills.reduce((acc, el) => { + if (el.sid?.isEqual(sid)) { + acc = el; + } + return acc; + }, SkillModel.empty()) + ); + toSkillView = (): ISkillView[] => this.skills.map((el) => { return { @@ -169,6 +248,20 @@ export class Skills { }), }; }); + getSkill = (name: string) => + SkillModel.isEmpty( + this.skills.reduce((acc, el) => { + if (el.BTAction.find((el) => el.name.isEqual(name))) { + el.BTAction.map((action) => { + action.param.map((param) => { + return param; + }); + }); + acc = el; + } + return acc; + }, SkillModel.empty()) + ); getSkilsOut = (name: string) => this.skills @@ -207,7 +300,7 @@ export class Skills { getForms = (skillLabel: string) => this.skills - .reduce((acc, el) => { + .reduce((acc, el) => { if (el.BTAction.find((el) => el.name.isEqual(skillLabel))) { acc.push(el); } @@ -217,17 +310,83 @@ export class Skills { .flat(1) .flat(1) .filter((el) => el !== ""); - getDependencyBySkillLabelAndType = (skillLabel: string, skillType: string) => + + getDependencyBySkillLabelAndType = (skillType: string, sid: string) => this.skills - .reduce((acc, el) => { - if (el.BTAction.find((el) => el.name.isEqual(skillLabel))) { - acc.push(el); + .reduce((acc, skill) => { + if (skill.sid?.isEqual(sid)) { + skill.BTAction.map((action) => { + action.param.map((param) => { + if (param.type.isEqual(skillType)) { + acc.push(param.dependency); + } + return param; + }); + return action; + }); } + return acc; }, []) - .map((el) => el.BTAction.map((act) => act.param.filter((el) => el.type.isEqual(skillType)))) - .flat(1) - .flat(1) - .map((el) => el.dependency) .at(0) as T; + static isEmpty(model: Skills): Result { + if (model.skills.isEmpty()) { + return Result.error(undefined); + } + return Result.ok(undefined); + } + static empty() { + const skills = new Skills(); + skills.skills = []; + return skills; + } + + public dependencyIsFilled = (skillType: string, sid: string) => + this.skills.reduce((acc, skill) => { + if (skill.sid?.isEqual(sid)) { + skill.BTAction.forEach((action) => { + action.param.forEach((param) => { + if (param.type.isEqual(skillType)) { + // console.log('SKILL TYPE') + // console.log(skillType); + // console.log("SID") + // console.log(sid) + // console.log("DEPENDENCY") + // console.log(param.dependency) + acc = Object.keys(param.dependency).isNotEmpty(); + } + }); + }); + } + + return acc; + }, false); + + getAllSids = () => + this.skills.reduce((acc, skill) => { + skill.BTAction.forEach((action) => + action.param.forEach((param) => { + // acc.incrementValue(param.sid ?? "empty"); + return param; + }) + ); + return acc; + }, new Map()); + + deleteSid(sid: string): SkillModel[] { + return this.skills.filter((skill) => !skill.sid?.isEqual(sid)); + } + updateSkill = (skill: SkillModel) => { + console.log(skill); + this.skills = this.skills.map((el) => { + if (el.sid?.isEqual(skill.sid ?? "")) { + el = skill; + } + return el; + }); + }; + skillHasForm = (label: string): boolean => { + // TODO:NEED IMPLEMENTS + return true; + }; } diff --git a/ui/src/core/repository/http_repository.ts b/ui/src/core/repository/http_repository.ts index d11463b..a898c15 100644 --- a/ui/src/core/repository/http_repository.ts +++ b/ui/src/core/repository/http_repository.ts @@ -96,4 +96,5 @@ export class HttpRepository { return Result.error(new HttpError(error, 0)); } } + } diff --git a/ui/src/core/ui/form_builder/form_builder.tsx b/ui/src/core/ui/form_builder/form_builder.tsx index fe30759..ac2dd7b 100644 --- a/ui/src/core/ui/form_builder/form_builder.tsx +++ b/ui/src/core/ui/form_builder/form_builder.tsx @@ -3,7 +3,7 @@ import { FormViewModel, InputBuilderViewModel, InputType } from "./form_view_mod 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 { CoreSelect } from "../select/select"; import { CoreInput } from "../input/input"; import { Icon } from "../icons/icons"; import { CoreText, CoreTextType } from "../text/text"; @@ -41,7 +41,7 @@ export const FormBuilder = observer((props: IFormBuilder) => { if (element.type.isEqual(InputType.ENUM)) { const values = element.values as string[]; return ( - store.changeTotalValue(element.id, value)} @@ -88,7 +88,7 @@ export const FormBuilder = observer((props: IFormBuilder) => { if (subSubArrayItem.type.isEqual(InputType.ENUM)) { return ( <> - String(el)) ?? []} value={subSubArrayItem.totalValue ?? subSubArrayItem.defaultValue} onChange={(value) => store.changeTotalSubValue(element.id, subIndex, value)} diff --git a/ui/src/core/ui/input/input.tsx b/ui/src/core/ui/input/input.tsx index 795f58a..0711597 100644 --- a/ui/src/core/ui/input/input.tsx +++ b/ui/src/core/ui/input/input.tsx @@ -5,6 +5,7 @@ import { IStyle } from "../../model/style"; interface IInputProps extends IStyle { label: string; value?: string; + subLabel?: React.ReactNode; onChange?: (value: string) => void; validation?: (value: string) => boolean; error?: string; @@ -19,8 +20,7 @@ export const CoreInput = (props: IInputProps) => { ref.current.innerText = value; setAppendInnerText(false); } - }, [ref, value, isAppendInnerText, setAppendInnerText]); - + }, [ref, value, isAppendInnerText, setAppendInnerText, props]); return (
{ color: "#1D1B20", height: 24, width: "100%", - userSelect: 'none', - outline:'none' + userSelect: "none", + outline: "none", }} onChange={(e) => { const val = e.target.value; - setValue(val) + + setValue(val); if (val) { if (props.validation !== undefined && props.validation(val) && props.onChange) { props.onChange(val); diff --git a/ui/src/core/ui/pages/main_page.tsx b/ui/src/core/ui/pages/main_page.tsx index dde55d5..2d05806 100644 --- a/ui/src/core/ui/pages/main_page.tsx +++ b/ui/src/core/ui/pages/main_page.tsx @@ -50,6 +50,7 @@ export interface IMainPageProps { panelChildren?: JSX.Element; panelStyle?: React.CSSProperties; isLoading?: boolean; + maskLoader?: boolean; error?: UiBaseError[]; } export const MainPage = (props: IMainPageProps) => { @@ -132,6 +133,22 @@ export const MainPage = (props: IMainPageProps) => { ) : ( <> + {props.maskLoader ? ( +
+ +
+ ) : null}
void; } -export const SelectCore = (props: ISelectCoreProps) => { +export const CoreSelect = (props: ICoreSelectProps) => { const ref = React.useRef(null); const [cursorIsCorses, setCursorIsCorses] = React.useState(false); const [value, setValue] = React.useState(props.value); diff --git a/ui/src/features/behavior_tree_builder/data/behavior_tree_builder_repository.ts b/ui/src/features/behavior_tree_builder/data/behavior_tree_builder_repository.ts index f54e6d3..e3c4e64 100644 --- a/ui/src/features/behavior_tree_builder/data/behavior_tree_builder_repository.ts +++ b/ui/src/features/behavior_tree_builder/data/behavior_tree_builder_repository.ts @@ -1,11 +1,12 @@ import { Result } from "../../../core/helper/result"; import { Skills } from "../../../core/model/skill_model"; import { HttpError, HttpMethod, HttpRepository } from "../../../core/repository/http_repository"; +import { UUID } from "../../all_projects/data/project_repository"; +import { BehaviorTreeModel } from "../model/behavior_tree_model"; import { BehaviorTreeViewModel } from "../model/behavior_tree_view_model"; -import { BtTreeModel } from "../model/bt_tree_model"; export class BehaviorTreeBuilderHttpRepository extends HttpRepository { - getAllBtInstances = async () => this._jsonRequest(HttpMethod.GET, "/behavior/trees"); + getAllBtInstances = async () => this._jsonRequest(HttpMethod.GET, "/behavior/trees"); getBtSkills = async (): Promise> => { return (await this._jsonToClassInstanceRequest( HttpMethod.GET, @@ -14,5 +15,17 @@ export class BehaviorTreeBuilderHttpRepository extends HttpRepository { )) as unknown as Promise>; }; saveNewBt = async (model: BehaviorTreeViewModel) => this._jsonRequest(HttpMethod.POST, "/behavior/trees", model); - getBtById = async (id: string) => this._jsonRequest(HttpMethod.GET, `/behavior/trees/by_id?id=${id}`); + getBtById = async (id: string): Promise> => + this._jsonToClassInstanceRequest( + HttpMethod.GET, + `/behavior/trees/by_id?id=${id}`, + BehaviorTreeModel + ) as unknown as Promise>; + getActiveProjectId(): Promise> { + return this._jsonRequest(HttpMethod.GET, "/projects/get/active/project/id"); + } + editBt = async (model: BehaviorTreeModel) => { + await this._jsonRequest(HttpMethod.POST, "/behavior/trees/fill/tree", model); + return await this._jsonRequest(HttpMethod.PUT, "/behavior/trees", model); + }; } diff --git a/ui/src/features/behavior_tree_builder/model/behavior_tree_model.ts b/ui/src/features/behavior_tree_builder/model/behavior_tree_model.ts new file mode 100644 index 0000000..0025418 --- /dev/null +++ b/ui/src/features/behavior_tree_builder/model/behavior_tree_model.ts @@ -0,0 +1,52 @@ +import { Type } from "class-transformer"; +import { Result } from "../../../core/helper/result"; +import { Skills } from "../../../core/model/skill_model"; +import { NodeBehaviorTree } from "./node_behavior_tree"; +import { IsOptional } from "class-validator"; + +export class BehaviorTreeModel { + @IsOptional() + @Type(() => Skills) + public skills?: Skills; + public scene: NodeBehaviorTree[]; + public xml: string; + public name: string; + public project: string; + public _id: string; + constructor(skills: Skills, scene: NodeBehaviorTree[], xml: string, name: string, project: string, _id: string) { + this.skills = skills; + this.scene = scene; + this.xml = xml; + this.name = name; + this.project = project; + this._id = _id; + } + updateDependency(skills: Skills, scene: NodeBehaviorTree[], xml: string) { + this.skills = skills; + this.scene = scene; + this.xml = xml; + } + static empty() { + return new BehaviorTreeModel(Skills.empty(), [], "", "", "", ""); + } + static isEmpty(model: BehaviorTreeModel) { + return Skills.isEmpty(model.skills ?? Skills.empty()).map(() => { + if (model.scene.isEmpty()) { + return Result.error(undefined); + } + if (model.xml.isEmpty()) { + return Result.error(undefined); + } + if (model.project.isEmpty()) { + return Result.error(undefined); + } + if (model.name.isEmpty()) { + return Result.error(undefined); + } + if (model._id.isEmpty()) { + return Result.error(undefined); + } + return Result.ok(); + }); + } +} diff --git a/ui/src/features/behavior_tree_builder/model/behavior_tree_view_model.ts b/ui/src/features/behavior_tree_builder/model/behavior_tree_view_model.ts index 464530d..208fe6c 100644 --- a/ui/src/features/behavior_tree_builder/model/behavior_tree_view_model.ts +++ b/ui/src/features/behavior_tree_builder/model/behavior_tree_view_model.ts @@ -1,11 +1,15 @@ import { Result } from "../../../core/helper/result"; export class BehaviorTreeViewModel { - constructor(public name: string) {} + constructor(public name: string, public project: string) {} static empty() { - return new BehaviorTreeViewModel(""); + return new BehaviorTreeViewModel("", ""); } + valid(): Result { + if (this.project.isEmpty()) { + return Result.error("project is empty"); + } if (this.name.isEmpty()) { return Result.error("name is empty"); } diff --git a/ui/src/features/behavior_tree_builder/model/bt_tree_model.ts b/ui/src/features/behavior_tree_builder/model/bt_tree_model.ts deleted file mode 100644 index 0c5fb4b..0000000 --- a/ui/src/features/behavior_tree_builder/model/bt_tree_model.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface BtTreeModel { - _id: string; - name: string; - unixTime: number; - __v: number; -} diff --git a/ui/src/features/behavior_tree_builder/model/editor_view.ts b/ui/src/features/behavior_tree_builder/model/editor_view.ts index 213b0b5..a227a50 100644 --- a/ui/src/features/behavior_tree_builder/model/editor_view.ts +++ b/ui/src/features/behavior_tree_builder/model/editor_view.ts @@ -4,17 +4,27 @@ import { AreaExtra, Schemes } from "../presentation/ui/editor/editor"; import { Result } from "../../../core/helper/result"; import { AreaPlugin } from "rete-area-plugin"; import { Skills } from "../../../core/model/skill_model"; +import xmlFormat from "xml-formatter"; export interface BtDrawDragAndDropView { x: number; y: number; name: string; + id: string; } export class BtNodeView extends TypedEvent {} - -export class ReteObserver extends TypedEvent {} -export class BtBuilderModel { +export enum UpdateEvent { + DELETE = "DELETE", + UPDATE = "UPDATE", +} +export interface IUpdateEvent { + type: UpdateEvent; + id: string; +} +export class NodeRerenderObserver extends TypedEvent {} +export class ReteForceUpdateObserver extends TypedEvent {} +export class BehaviorTreeBuilderModel { public static result = ""; static fromReteScene( editor: NodeEditor, @@ -23,6 +33,8 @@ export class BtBuilderModel { ): Result { try { this.result = ""; + + // eslint-disable-next-line array-callback-return this.getFirstSequence(editor).map((sortedSequence) => { const firstNodeId = sortedSequence.getKeyFromValueIsExists(1) as string; this.findSequence(firstNodeId, editor, sortedSequence, 2); @@ -30,16 +42,35 @@ export class BtBuilderModel { this.toXML(sortedSequence as Map, editor, area, firstNodeId, skills); this.result += ``; }); - return Result.ok(this.result); + + return Result.ok( + xmlFormat(` + + + ${this.result} + + + + + + + + + + + + `) + ); } catch (error) { return Result.error("BtBuilderModel fromReteScene error"); } } + public static getNodeLabelAtId(editor: NodeEditor, id: string, skills?: Skills) { if (skills?.getSkillsNames().find((el) => el.name.isEqual(editor.getNode(id).label))) { return `Action ID="RbsBtAction" do="${skills.getSkillDo(editor.getNode(id).label)}" command="${ editor.getNode(id).label - }" server_name="rbs_interface" server_timeout="1000"`; + }" sid=${id} server_name="rbs_interface" server_timeout="1000"`; } return editor.getNode(id).label; } @@ -60,7 +91,6 @@ export class BtBuilderModel { }); } - public static getBtPriorities(ids: string[]) {} public static findSequence( nodeId: string, editor: NodeEditor, diff --git a/ui/src/features/behavior_tree_builder/model/node_behavior_tree.ts b/ui/src/features/behavior_tree_builder/model/node_behavior_tree.ts index 00b0040..6b850e5 100644 --- a/ui/src/features/behavior_tree_builder/model/node_behavior_tree.ts +++ b/ui/src/features/behavior_tree_builder/model/node_behavior_tree.ts @@ -40,7 +40,6 @@ export class NodeBehaviorTree { ) ) ); - editor.getConnections().forEach((el) => console.log(el)); editor.getConnections().forEach((el) => nodes.overrideValue(el.target, { connectTo: el.source })); return nodes.toArray(); diff --git a/ui/src/features/behavior_tree_builder/presentation/behavior_tree_builder_screen.tsx b/ui/src/features/behavior_tree_builder/presentation/behavior_tree_builder_screen.tsx index 2897c44..fc57998 100644 --- a/ui/src/features/behavior_tree_builder/presentation/behavior_tree_builder_screen.tsx +++ b/ui/src/features/behavior_tree_builder/presentation/behavior_tree_builder_screen.tsx @@ -12,7 +12,6 @@ import { Drawer } from "antd"; import { CoreInput } from "../../../core/ui/input/input"; import { CoreText, CoreTextType } from "../../../core/ui/text/text"; import { useNavigate, useParams } from "react-router-dom"; -import { IWeightsDependency } from "../../../core/model/skill_model"; import { IForms, forms } from "./ui/forms/forms"; export const behaviorTreeBuilderScreenPath = "behavior/tree/screen/path"; @@ -34,31 +33,31 @@ export const BehaviorTreeBuilderPath = "/behavior/tree/"; export const BehaviorTreeBuilderScreen = observer(() => { const navigate = useNavigate(); const { id } = useParams(); - - const store = behaviorTreeBuilderStore; // @ts-expect-error const [ref] = useRete(createEditor); + const store = behaviorTreeBuilderStore; - if (ref.current) { - // @ts-expect-error - const domReact: DOMReact = ref.current.getBoundingClientRect(); - - store.dragZoneSetOffset(0, domReact.y, domReact.width, domReact.height); - } React.useEffect(() => { - store.init(navigate); - store.initParams(id); + store.init(navigate).then(() => { + store.initParam(id).then(() => { + if (ref.current) { + // @ts-expect-error + const domReact: DOMReact = ref.current.getBoundingClientRect(); + store.dragZoneSetOffset(0, domReact.y, domReact.width, domReact.height); + } + }); + }); return () => { store.dispose(); }; - }, [store, ref, id, navigate]); + }, [id, navigate, ref, store]); return ( {match(store.type) @@ -73,8 +72,11 @@ export const BehaviorTreeBuilderScreen = observer(() => { textAlign: "center", }} > - {store.btTreeModels?.map((el) => ( -
+ {store.btTreeModels?.map((el, index) => ( +
{ {store.skillTemplates ? : null}
- store.saveBt()} text="SAVE BT" /> + store.onClickSaveBehaviorTree()} text="Сохранить" />
)) @@ -110,23 +112,16 @@ export const BehaviorTreeBuilderScreen = observer(() => { } bodyChildren={ <> - {match(store.type) - .with(StoreUIType.SelectBehaviorTree, () => <>) - .with(StoreUIType.ViewBehaviorTree, () => ( -
-
-
- )) - .otherwise(() => ( - <> - ))} +
+
+
{ store.updateForm({ name: text })} />
- store.createNewBt()} /> + store.createNewBehaviorTree()} />
store.edtDrawer(DrawerState.newBehaviorTree, false)} />
@@ -152,26 +147,21 @@ export const BehaviorTreeBuilderScreen = observer(() => { >
- {store.skillTemplates?.getForms(store.selected ?? "").map((formType) => - forms + {store.skillTemplates?.getForms(store.selected ?? "").map((formType, index) => + forms( + store.filledOutTemplates?.getDependencyBySkillLabelAndType( + forms(null, () => {}).find((form) => form.name.isEqual(formType))?.name ?? "", + store.selectedSid ?? "" + ), + (dependency) => store.formUpdateDependency(dependency, formType) + ) .rFind((form) => form.name.isEqual(formType)) .fold( - (s) => ( -
- {/* {s.component(store.skillTemplates?.getDependencyBySkillLabelAndType(store.selected ?? "", s.name))} */} - - forms.at(0)?.component({}) -
- ), - () =>
Error: Unknown form type {formType}
+ (s) =>
{s.component}
, + () =>
Error: Unknown form type {formType}
) )}
-
- store.createNewBt()} /> -
- store.edtDrawer(DrawerState.newBehaviorTree, false)} /> -
diff --git a/ui/src/features/behavior_tree_builder/presentation/behavior_tree_builder_store.tsx b/ui/src/features/behavior_tree_builder/presentation/behavior_tree_builder_store.tsx index 72dd23d..9c8e2b5 100644 --- a/ui/src/features/behavior_tree_builder/presentation/behavior_tree_builder_store.tsx +++ b/ui/src/features/behavior_tree_builder/presentation/behavior_tree_builder_store.tsx @@ -1,20 +1,28 @@ import makeAutoObservable from "mobx-store-inheritance"; -import xmlFormat from "xml-formatter"; import { CoreError, UiFormState } from "../../../core/store/base_store"; -import { BtBuilderModel, BtNodeView, ReteObserver } from "../model/editor_view"; +import { + BehaviorTreeBuilderModel, + BtNodeView as BtNodeObserver, + NodeRerenderObserver, + ReteForceUpdateObserver, + UpdateEvent, +} from "../model/editor_view"; import { NodeEditor } from "rete"; import { AreaExtra, Schemes } from "./ui/editor/editor"; import { AreaPlugin } from "rete-area-plugin"; import { NodeBehaviorTree } from "../model/node_behavior_tree"; import { BehaviorTreeBuilderHttpRepository } from "../data/behavior_tree_builder_repository"; -import { Skills } from "../../../core/model/skill_model"; +import { IParam, Skills } from "../../../core/model/skill_model"; import { ISkillView } from "./ui/skill_tree/skill_tree"; import { message } from "antd"; -import { BtTreeModel } from "../model/bt_tree_model"; import { NavigateFunction } from "react-router-dom"; import { BehaviorTreeViewModel } from "../model/behavior_tree_view_model"; import { UiBaseError } from "../../../core/model/ui_base_error"; import React from "react"; +import { v4 } from "uuid"; +import { behaviorTreeBuilderStore } from "./behavior_tree_builder_screen"; +import clone from "just-clone"; +import { BehaviorTreeModel } from "../model/behavior_tree_model"; interface I2DArea { x: number; @@ -22,43 +30,52 @@ interface I2DArea { w: number; h: number; } + export enum DrawerState { newBehaviorTree = "Новое дерево поведения", editThreadBehaviorTree = "Редактирование", } + export enum StoreUIType { SelectBehaviorTree, ViewBehaviorTree, } + export enum SystemPrimitive { Sequence = "Sequence", Fallback = "Fallback", } + export class BehaviorTreeBuilderStore extends UiFormState { - - type: StoreUIType; + type: StoreUIType = StoreUIType.ViewBehaviorTree; viewModel: BehaviorTreeViewModel = BehaviorTreeViewModel.empty(); - skillTemplates?: Skills; + behaviorTreeModel: BehaviorTreeModel = BehaviorTreeModel.empty(); + skillTemplates: Skills = Skills.empty(); + filledOutTemplates: Skills = Skills.empty(); area?: I2DArea; - btNodeView: BtNodeView = new BtNodeView(); - reteNode?: ReteObserver; - btTreeModels?: BtTreeModel[]; - behaviorTreeBuilderRepository = new BehaviorTreeBuilderHttpRepository(); + btNodeObserver: BtNodeObserver = new BtNodeObserver(); + reteForceUpdateObserver?: ReteForceUpdateObserver; + btTreeModels: BehaviorTreeModel[] = []; + activeProject: string = ""; + behaviorTreeBuilderHttpRepository = new BehaviorTreeBuilderHttpRepository(); canRun = true; shiftIsPressed = false; - selected?:string; + selected: string = ""; + selectedSid: string = ""; deleteIsPressed = false; - nodes: NodeBehaviorTree[] = []; + nodeBehaviorTree: NodeBehaviorTree[] = []; navigate?: NavigateFunction; + editor?: NodeEditor; + areaPlugin?: AreaPlugin; skillTree: ISkillView = { name: "", children: [ { - name: "Actions", + name: "Действия", children: [], }, { - name: "Control", + name: "Примитивы BT", children: [ { name: "Fallback", interface: "Vector3", out: "vo" }, { name: "Sequence", interface: "Vector3", out: "vo" }, @@ -66,42 +83,19 @@ export class BehaviorTreeBuilderStore extends UiFormState, area: AreaPlugin) => { - (await BtBuilderModel.fromReteScene(editor, area, this.skillTemplates)).fold( - (s) => { - console.log( - xmlFormat(` - - - ${s} - - - - - - - - - - - - `) - ); - }, - (_) => _ - ); + syncScene = async (editor: NodeEditor, area: AreaPlugin) => { + this.editor = editor; + this.areaPlugin = area; }; - errorHandingStrategy: (error: CoreError) => void; + errorHandingStrategy = (_: CoreError) => {}; dragEnd = (e: EventTarget) => { if (this.canRun) { @@ -114,7 +108,7 @@ export class BehaviorTreeBuilderStore extends UiFormState { const drawPoint = { x: x, y: y, w: 1, h: 1 }; if ( drawPoint.x < this.area!.x + this.area!.w && @@ -122,20 +116,39 @@ export class BehaviorTreeBuilderStore extends UiFormState this.area!.y ) { - this.btNodeView.emit({ + const sid = v4(); + this.btNodeObserver.emit({ x: x, y: y - (this.area!.y + this.area!.h / 2), name: name, + id: sid, }); + if (!name.isEqualMany(Object.keys(SystemPrimitive))) { + this.skillTemplates?.getSkill(name).fold( + (m) => { + const model = clone(m); + const modelSetId = model.setSid(sid); + modelSetId.BTAction = m.BTAction.filter((el) => el.name.isEqual(name)); + + this.filledOutTemplates?.skills.push(modelSetId); + }, + () => console.log("error") + ); + } } - } + }; async init(navigate: NavigateFunction): Promise { - (await this.behaviorTreeBuilderRepository.getBtSkills()).fold( + (await this.behaviorTreeBuilderHttpRepository.getActiveProjectId()) + // eslint-disable-next-line array-callback-return + .map((el) => { + this.activeProject = el.id; + }); + (await this.behaviorTreeBuilderHttpRepository.getBtSkills()).fold( (model) => { this.skillTemplates = model; this.skillTree.children = this.skillTree.children?.map((el) => { - if (el.name === "Actions") { + if (el.name === "Действия") { el.children = model.toSkillView(); } return el; @@ -143,7 +156,7 @@ export class BehaviorTreeBuilderStore extends UiFormState console.log(e) ); - await this.mapOk("btTreeModels", this.behaviorTreeBuilderRepository.getAllBtInstances()); + await this.mapOk("btTreeModels", this.behaviorTreeBuilderHttpRepository.getAllBtInstances()); this.navigate = navigate; document.addEventListener("keydown", (event) => { @@ -165,11 +178,18 @@ export class BehaviorTreeBuilderStore extends UiFormState { + initParam = async (id?: string) => { + this.isLoading = true; + this.type = StoreUIType.ViewBehaviorTree; if (id) { - this.type = StoreUIType.ViewBehaviorTree; - (await this.behaviorTreeBuilderRepository.getBtById(id ?? "")).fold( - () => {}, + (await this.behaviorTreeBuilderHttpRepository.getBtById(id)).fold( + (model) => { + this.nodeBehaviorTree = model.scene; + this.behaviorTreeModel = model; + if (model.skills) this.filledOutTemplates = model.skills; + this.isLoading = false; + this.reteForceUpdateObserver?.emit(""); + }, () => this.errors.push(new UiBaseError(`не найдено дерево с id:${id}`)) ); } else { @@ -184,53 +204,124 @@ export class BehaviorTreeBuilderStore extends UiFormState {}); + document.removeEventListener("keydown", () => {}); } + onClickSaveBehaviorTree = async (): Promise => { + this.filledOutTemplates.validation().fold( + async () => { + ( + await BehaviorTreeBuilderModel.fromReteScene( + this.editor as NodeEditor, + this.areaPlugin as AreaPlugin, + this.filledOutTemplates + ) + ).fold( + (xml) => { + this.behaviorTreeModel.skills = this.filledOutTemplates; + this.behaviorTreeModel.scene = NodeBehaviorTree.fromReteScene( + this.editor as NodeEditor, + this.areaPlugin as AreaPlugin + ); + this.behaviorTreeModel.xml = xml; + this.behaviorTreeModel.project = this.activeProject; + this.messageHttp(this.behaviorTreeBuilderHttpRepository.editBt(this.behaviorTreeModel), { + successMessage: "Дерево поведения сохранено", + }); + }, + (_) => message.error(`Дерево поведения ошибка: ${_}`) + ); + }, + async () => message.error(`Дерево поведения не заполнено`) + ); + }; validateBt() {} - createNewBt = async () => { + createNewBehaviorTree = async () => { + this.viewModel.project = this.activeProject; this.viewModel.valid().fold( async (model) => { - await this.messageHttp(this.behaviorTreeBuilderRepository.saveNewBt(model), { + await this.messageHttp(this.behaviorTreeBuilderHttpRepository.saveNewBt(model), { successMessage: "Новое дерево создано", }); - this.mapOk("btTreeModels", this.behaviorTreeBuilderRepository.getAllBtInstances()); + this.mapOk("btTreeModels", this.behaviorTreeBuilderHttpRepository.getAllBtInstances()); }, async (error) => message.error(error) ); }; - setSelected = (label: string, selected: boolean) => { - if (this.shiftIsPressed) { + setSelected = (label: string, selected: boolean, sid: string) => { + this.selectedSid = sid; + this.selected = label; + if ( + this.shiftIsPressed && + !Object.keys(SystemPrimitive).includes(label) && + this.skillTemplates.skillHasForm(label) + ) { this.edtDrawer(DrawerState.editThreadBehaviorTree, selected); - this.selected = label + this.selected = label; + } + if (this.deleteIsPressed) { + this.nodeBehaviorTree = this.nodeBehaviorTree.filter((el) => !el.id.isEqual(sid)); + this.filledOutTemplates.skills = this.filledOutTemplates.deleteSid(sid); + this.nodeUpdateObserver?.emit({ type: UpdateEvent.DELETE, id: sid }); } }; - getBodyNode(label: string): React.ReactNode | null { + formUpdateDependency = (dependency: Object, formType: string) => { + this.edtDrawer(DrawerState.editThreadBehaviorTree, false); + this.filledOutTemplates?.skillBySid(this.selectedSid ?? "").fold( + (m) => { + const model = clone(m); + model.BTAction.forEach((action) => { + const result: IParam[] = []; + action.param.forEach((param) => { + const paramClone = clone(param); + if (param.type.isEqual(formType)) { + paramClone.dependency = dependency; + } + result.push(paramClone); + }); + + action.param = result; + return action; + }); + + this.filledOutTemplates.updateSkill(model); + }, + () => console.log("UNKNOWN SID: " + this.selectedSid) + ); + + this.nodeUpdateObserver?.emit({ id: this.selectedSid, type: UpdateEvent.UPDATE }); + }; + getBodyNode(label: string, sid: string): React.ReactNode | null { if (Object.keys(SystemPrimitive).includes(label)) { return null; } return (
- {this.skillTemplates?.getSkillParams(label).map((el) => ( -
-
IN
-
+ {this.skillTemplates?.getSkillParams(label).map((el, index) => ( +
+
IN
+
{el.type} + {behaviorTreeBuilderStore.isFilledInput(el.type, sid) ? "" : *}
))} - {this.skillTemplates?.getSkilsOut(label).map((el) => ( -
-
OUT
-
{el}
+ {this.skillTemplates?.getSkilsOut(label).map((el, index) => ( +
+
OUT
+
+ {el} +
))}
); } + isFilledInput = (type: string, sid: string): boolean => this.filledOutTemplates?.dependencyIsFilled(type, sid); getInputs(name: string) { if (Object.keys(SystemPrimitive).includes(name)) { return { diff --git a/ui/src/features/behavior_tree_builder/presentation/ui/editor/custom_background.tsx b/ui/src/features/behavior_tree_builder/presentation/ui/editor/custom_background.tsx deleted file mode 100644 index 5a3d649..0000000 --- a/ui/src/features/behavior_tree_builder/presentation/ui/editor/custom_background.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { BaseSchemes } from "rete"; -import { AreaPlugin } from "rete-area-plugin"; -import './background.css' -export function addCustomBackground(area: AreaPlugin) { - const background = document.createElement("div"); - - background.classList.add("background"); - background.classList.add("fill-area"); - area.area.content.add(background); -} diff --git a/ui/src/features/behavior_tree_builder/presentation/ui/editor/editor.tsx b/ui/src/features/behavior_tree_builder/presentation/ui/editor/editor.tsx index 5c94514..3f66821 100644 --- a/ui/src/features/behavior_tree_builder/presentation/ui/editor/editor.tsx +++ b/ui/src/features/behavior_tree_builder/presentation/ui/editor/editor.tsx @@ -4,25 +4,36 @@ import { AreaPlugin, AreaExtensions } from "rete-area-plugin"; import { ConnectionPlugin, Presets as ConnectionPresets } from "rete-connection-plugin"; import { ReactPlugin, Presets, ReactArea2D } from "rete-react-plugin"; import { CustomConnection } from "./custom_connection"; -import { addCustomBackground } from "./custom_background"; import { behaviorTreeBuilderStore } from "../../behavior_tree_builder_screen"; import { SequenceNode } from "./nodes/controls_node"; -import { ReteObserver } from "../../../model/editor_view"; import { CustomSocket } from "./custom_socket"; +import { BaseSchemes } from "rete"; +import { + NodeRerenderObserver as NodeUpdateObserver, + ReteForceUpdateObserver, + UpdateEvent, +} from "../../../model/editor_view"; +import { v4 } from "uuid"; export type Schemes = GetSchemes>; export type AreaExtra = ReactArea2D; +export function addCustomBackground(area: AreaPlugin) { + const background = document.createElement("div"); + + background.classList.add("background"); + background.classList.add("fill-area"); + area.area.content.add(background); +} + export async function createEditor(container: HTMLElement) { const socket = new ClassicPreset.Socket("socket"); - const observer = new ReteObserver(); - - behaviorTreeBuilderStore.reteNode = observer; - - behaviorTreeBuilderStore.btNodeView.on(async (event) => { + behaviorTreeBuilderStore.btNodeObserver.on(async (event) => { setTimeout(async () => { const node = new ClassicPreset.Node(event.name); const { x, y } = areaContainer.area.pointer; + + node.id = event.id; const { output, input } = behaviorTreeBuilderStore.getInputs(event.name); if (output) node.addOutput(output, new ClassicPreset.Output(socket)); if (input) node.addInput("b", new ClassicPreset.Input(socket)); @@ -30,24 +41,46 @@ export async function createEditor(container: HTMLElement) { await areaContainer.translate(node.id, { x, y }); }, 100); }); + behaviorTreeBuilderStore.reteForceUpdateObserver = new ReteForceUpdateObserver(); + behaviorTreeBuilderStore.reteForceUpdateObserver!.on(async () => { + for await (const el of behaviorTreeBuilderStore.nodeBehaviorTree) { + const node = new ClassicPreset.Node(el.label); + node.id = el.id; - observer.on(() => { - behaviorTreeBuilderStore.bt(editor, areaContainer); + el.outputs.forEach((outputName) => { + node.addOutput(outputName, new ClassicPreset.Output(socket)); + }); + el.inputs.forEach((inputName) => { + node.addInput(inputName, new ClassicPreset.Input(socket)); + }); + await editor.addNode(node); + await areaContainer.translate(node.id, el.position); + } + for await (const el of behaviorTreeBuilderStore.nodeBehaviorTree) { + if (el.connectTo) + editor.addConnection({ + id: v4(), + sourceOutput: "a", + targetInput: "b", + source: el.connectTo as string, + target: el.id, + }); + } }); - const editor = new NodeEditor(); const areaContainer = new AreaPlugin(container); const connection = new ConnectionPlugin(); const render = new ReactPlugin({ createRoot }); - - AreaExtensions.selectableNodes(areaContainer, AreaExtensions.selector(), { - accumulating: AreaExtensions.accumulateOnCtrl(), + render.addPipe((el) => { + behaviorTreeBuilderStore.syncScene(editor, areaContainer); + return el; }); + render.addPreset( Presets.classic.setup({ customize: { - node(context) { + node(_) { return SequenceNode; }, socket(_context) { @@ -59,28 +92,18 @@ export async function createEditor(container: HTMLElement) { }, }) ); - connection.addPreset(ConnectionPresets.classic.setup()); addCustomBackground(areaContainer); - render.addPipe((context) => { - if (context.type === "rendered") { - if (context.data.type === "node") { - context.data.element.addEventListener("dblclick", (event) => { - console.log(event); - }); - } - } - return context; - }); editor.use(areaContainer); areaContainer.use(connection); areaContainer.use(render); AreaExtensions.simpleNodesOrder(areaContainer); - - for await (const el of behaviorTreeBuilderStore.nodes) { + const nodeUpdateObserver = new NodeUpdateObserver(); + behaviorTreeBuilderStore.nodeUpdateObserver = nodeUpdateObserver; + for await (const el of behaviorTreeBuilderStore.nodeBehaviorTree) { const node = new ClassicPreset.Node(el.label); node.id = el.id; el.outputs.forEach((outputName) => { @@ -89,19 +112,37 @@ export async function createEditor(container: HTMLElement) { el.inputs.forEach((inputName) => { node.addInput(inputName, new ClassicPreset.Input(socket)); }); - await editor.addNode(node); await areaContainer.translate(node.id, el.position); } - - behaviorTreeBuilderStore.nodes.forEach(async (el) => { - if (el.connectTo) { - const nodeConnectTo = editor.getNode(el.connectTo!); - const nodeConnect = editor.getNode(el.id); - await editor.addConnection(new ClassicPreset.Connection(nodeConnectTo, "a", nodeConnect, "a")); + for await (const el of behaviorTreeBuilderStore.nodeBehaviorTree) { + if (el.connectTo) + editor.addConnection({ + id: v4(), + sourceOutput: "a", + targetInput: "b", + source: el.connectTo as string, + target: el.id, + }); + } + + nodeUpdateObserver.on(async (event) => { + if (event.type.isEqual(UpdateEvent.UPDATE)) { + areaContainer.update("node", event.id); + } + if (event.type.isEqual(UpdateEvent.DELETE)) { + editor + .getConnections() + .forEach((el) => + el.source.isEqual(event.id) || el.target.isEqual(event.id) ? editor.removeConnection(el.id) : null + ); + areaContainer.removeNodeView(event.id); } }); + AreaExtensions.selectableNodes(areaContainer, AreaExtensions.selector(), { + accumulating: AreaExtensions.accumulateOnCtrl(), + }); setTimeout(() => { AreaExtensions.zoomAt(areaContainer, editor.getNodes()); }, 100); diff --git a/ui/src/features/behavior_tree_builder/presentation/ui/editor/nodes/controls_node.tsx b/ui/src/features/behavior_tree_builder/presentation/ui/editor/nodes/controls_node.tsx index 57c83c8..1a89dca 100644 --- a/ui/src/features/behavior_tree_builder/presentation/ui/editor/nodes/controls_node.tsx +++ b/ui/src/features/behavior_tree_builder/presentation/ui/editor/nodes/controls_node.tsx @@ -102,18 +102,17 @@ export function SequenceNode(props: Props) sortByIndex(inputs); sortByIndex(outputs); sortByIndex(controls); - behaviorTreeBuilderStore.setSelected(label, selected); + behaviorTreeBuilderStore.setSelected(label, selected, id); return ( <> { - console.log("double"); - }} + style={behaviorTreeBuilderStore.getStylesByLabelNode(label)} data-testid="node" className="node" + id="node" >
@@ -155,7 +154,7 @@ export function SequenceNode(props: Props) data-testid="title" >
{label}
-
{behaviorTreeBuilderStore.getBodyNode(label)}
+
{behaviorTreeBuilderStore.getBodyNode(label,id)}
{outputs.map( ([key, output]) => diff --git a/ui/src/features/behavior_tree_builder/presentation/ui/editor/nodes/style_node.tsx b/ui/src/features/behavior_tree_builder/presentation/ui/editor/nodes/style_node.tsx deleted file mode 100644 index f0cb56b..0000000 --- a/ui/src/features/behavior_tree_builder/presentation/ui/editor/nodes/style_node.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { Presets } from "rete-react-plugin"; -import { css } from "styled-components"; -import "./background.css"; - -const styles = css<{ selected?: boolean }>` - background: #ebebeb; - border-color: #646464; - .title { - color: #646464; - } - &:hover { - background: #f2f2f2; - } - .output-socket { - margin-right: -1px; - } - .input-socket { - margin-left: -1px; - } - ${(props) => - props.selected && - css` - border-color: red; - `} -`; - -export function StyledNode(props: any) { - // eslint-disable-next-line react/jsx-pascal-case - return styles} {...props} />; -} diff --git a/ui/src/features/behavior_tree_builder/presentation/ui/editor/style_node.tsx b/ui/src/features/behavior_tree_builder/presentation/ui/editor/style_node.tsx deleted file mode 100644 index f0cb56b..0000000 --- a/ui/src/features/behavior_tree_builder/presentation/ui/editor/style_node.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { Presets } from "rete-react-plugin"; -import { css } from "styled-components"; -import "./background.css"; - -const styles = css<{ selected?: boolean }>` - background: #ebebeb; - border-color: #646464; - .title { - color: #646464; - } - &:hover { - background: #f2f2f2; - } - .output-socket { - margin-right: -1px; - } - .input-socket { - margin-left: -1px; - } - ${(props) => - props.selected && - css` - border-color: red; - `} -`; - -export function StyledNode(props: any) { - // eslint-disable-next-line react/jsx-pascal-case - return styles} {...props} />; -} diff --git a/ui/src/features/behavior_tree_builder/presentation/ui/forms/forms.tsx b/ui/src/features/behavior_tree_builder/presentation/ui/forms/forms.tsx index e58030e..3f756e8 100644 --- a/ui/src/features/behavior_tree_builder/presentation/ui/forms/forms.tsx +++ b/ui/src/features/behavior_tree_builder/presentation/ui/forms/forms.tsx @@ -1,8 +1,19 @@ +import { SimpleForm } from "./simple_form"; +import { TestForm } from "./test"; import WeightsForm from "./weights_form"; +export interface IPropsForm { + dependency: T; + onChange: (dependency: Object) => void; +} + export interface IForms { name: string; - component: any; + component: JSX.Element; } -export const forms = [{ name: "weights", component: WeightsForm }]; +export const forms = (props: any, onChange: (dependency: Object) => void): IForms[] => [ + { name: "weights", component: }, + { name: "simple", component: }, + { name: "test", component: }, +]; diff --git a/ui/src/features/behavior_tree_builder/presentation/ui/forms/simple_form.tsx b/ui/src/features/behavior_tree_builder/presentation/ui/forms/simple_form.tsx new file mode 100644 index 0000000..adfbc68 --- /dev/null +++ b/ui/src/features/behavior_tree_builder/presentation/ui/forms/simple_form.tsx @@ -0,0 +1,14 @@ +import { CoreButton } from "../../../../../core/ui/button/button"; +import { IPropsForm } from "./forms"; + +export interface ISimpleFormDependency { + simple?: string; +} + +export const SimpleForm = (props: IPropsForm) => { + return ( +
+ props.onChange({ simple: "132" })} text="OK" /> +
+ ); +}; diff --git a/ui/src/features/behavior_tree_builder/presentation/ui/forms/test.tsx b/ui/src/features/behavior_tree_builder/presentation/ui/forms/test.tsx new file mode 100644 index 0000000..63fac7f --- /dev/null +++ b/ui/src/features/behavior_tree_builder/presentation/ui/forms/test.tsx @@ -0,0 +1,13 @@ +import { CoreButton } from "../../../../../core/ui/button/button"; +import { CoreText, CoreTextType } from "../../../../../core/ui/text/text"; +import { IPropsForm } from "./forms"; +import { ISimpleFormDependency } from "./simple_form"; + +export const TestForm = (props: IPropsForm) => { + return ( +
+ + props.onChange({ test: "132" })} text="OK" /> +
+ ); +}; diff --git a/ui/src/features/behavior_tree_builder/presentation/ui/forms/weights_form.tsx b/ui/src/features/behavior_tree_builder/presentation/ui/forms/weights_form.tsx index f4449b1..c261917 100644 --- a/ui/src/features/behavior_tree_builder/presentation/ui/forms/weights_form.tsx +++ b/ui/src/features/behavior_tree_builder/presentation/ui/forms/weights_form.tsx @@ -1,39 +1,173 @@ -import { IWeightsDependency } from "../../../../../core/model/skill_model"; -import makeAutoObservable from "mobx-store-inheritance"; -import { CoreError, FormState } from "../../../../../core/store/base_store"; import React from "react"; +import makeAutoObservable from "mobx-store-inheritance"; +import { IWeightsDependency } from "../../../../../core/model/skill_model"; +import { CoreError, FormState } from "../../../../../core/store/base_store"; import { ISkils, SkillsHttpRepository } from "../../../../skils/skills_http_repository"; -import { Spin } from "antd"; import { observer } from "mobx-react-lite"; +import { CoreButton } from "../../../../../core/ui/button/button"; +import { Result } from "../../../../../core/helper/result"; +import { Select, message } from "antd"; +import { CoreInput } from "../../../../../core/ui/input/input"; +import { DataSetHttpRepository } from "../../../../dataset/dataset_http_repository"; +import { Assets } from "../../../../dataset/dataset_model"; -interface IWeightsFormProps { - dependency?: IWeightsDependency; -} -export class WeightsViewModel { +export class WeightsViewModel implements IWeightsDependency { + constructor( + public object_name: string = "", + public weights_file: string = "", + public dimensions: number[] = [], + public weights_name = "" + ) {} static empty() { return new WeightsViewModel(); } + isEmpty = (): Result => { + if (this.weights_name.isEmpty()) { + return Result.error("weights_name is empty"); + } + if (this.object_name.isEmpty()) { + return Result.error("object name is empty"); + } + if (this.weights_file.isEmpty()) { + return Result.error("weights_file is empty"); + } + if (this.dimensions.isEmpty()) { + return Result.error("dimensions is empty"); + } + return Result.ok(undefined); + }; } export class WeightsFormStore extends FormState { - errorHandingStrategy = (error: CoreError) => {}; - weights: ISkils[]; + weights?: ISkils[]; + assets?: Assets; + suitableWeights: string[] = []; viewModel: WeightsViewModel = WeightsViewModel.empty(); skillsHttpRepository: SkillsHttpRepository = new SkillsHttpRepository(); + datasetHttpRepository: DataSetHttpRepository = new DataSetHttpRepository(); constructor() { super(); makeAutoObservable(this); } init = async () => { - this.mapOk("weights", this.skillsHttpRepository.getAllSkills()); + await this.mapOk("weights", this.skillsHttpRepository.getAllSkills()); + await this.mapOk("assets", this.datasetHttpRepository.getAssetsActiveProject()); }; -} -const WeightsForm = observer((props: IWeightsFormProps) => { - // const [store] = React.useState(() => new WeightsFormStore()); + changeDimensions = (index: number, value: number) => { + this.viewModel.dimensions[index] = value; + }; + selectAsset = (): void => { + this.suitableWeights = + this.weights + ?.filter((el) => + el.datasetId.dataSetObjects.filter((datasetObject: string) => + this.viewModel.object_name.isEqual(datasetObject) + ) + ) + .map((el) => el.name) ?? []; + }; + updateWeights = (text: string) => { + const model = this.weights + ?.filter((el) => + el.datasetId.dataSetObjects.filter((datasetObject: string) => this.viewModel.object_name.isEqual(datasetObject)) + ) + .at(0); - // React.useEffect(() => { - // store.init(); - // }, []); - return
123
; + this.updateForm({ weights_file: `${model?.datasetId.local_path}/weights/${model?.name}/${model?.name}.pt` }); + }; + errorHandingStrategy = (_: CoreError) => {}; +} + +interface IWeightsFormProps { + dependency?: IWeightsDependency; + onChange: (dependency: IWeightsDependency) => void; +} + +const WeightsForm = observer((props: IWeightsFormProps) => { + const [store] = React.useState(() => new WeightsFormStore()); + React.useEffect(() => { + store.init(); + }, [store]); + + return ( +
+ { + store.updateForm({ weights_name: e }); + store.updateWeights(e); + }} + filterOption={(input: string, option?: { label: string; value: string }) => + (option?.label ?? "").toLowerCase().includes(input.toLowerCase()) + } + style={{ width: "100%" }} + options={ + store.suitableWeights?.map((el) => { + return { label: el, value: el }; + }) ?? [] + } + /> +
+ store.changeDimensions(0, Number(text))} + validation={(text) => Number().isValid(text)} + value={props.dependency?.dimensions?.at(0)?.toString()} + /> +
+ + store.changeDimensions(1, Number(text))} + validation={(text) => Number().isValid(text)} + value={props.dependency?.dimensions?.at(1)?.toString()} + /> +
+ + store.changeDimensions(2, Number(text))} + validation={(text) => Number().isValid(text)} + value={props.dependency?.dimensions?.at(2)?.toString()} + /> +
+ { + store.viewModel.isEmpty().fold( + () => { + props.onChange(store.viewModel); + }, + (e) => { + message.error(e); + } + ); + }} + text="OK" + style={{ width: 100 }} + /> +
+ ); }); export default WeightsForm; diff --git a/ui/src/features/behavior_tree_builder/presentation/ui/skill_tree/skill_tree.tsx b/ui/src/features/behavior_tree_builder/presentation/ui/skill_tree/skill_tree.tsx index 6b3d342..6d5fdfd 100644 --- a/ui/src/features/behavior_tree_builder/presentation/ui/skill_tree/skill_tree.tsx +++ b/ui/src/features/behavior_tree_builder/presentation/ui/skill_tree/skill_tree.tsx @@ -3,6 +3,7 @@ import TreeView, { EventCallback, IBranchProps, INode, LeafProps, flattenTree } import { IFlatMetadata } from "react-accessible-treeview/dist/TreeView/utils"; import { CallBackEventTarget } from "../../../../../core/extensions/extensions"; import "./styles.css"; +import { CoreText, CoreTextType } from "../../../../../core/ui/text/text"; export interface ISkillView { name: string; @@ -48,9 +49,31 @@ export const RefListener = (props: IRefListerProps) => { }} /> -
- {props.isBranch ? "* ":""} - {props.element.name} +
+ {props.isBranch ? ( + + + + ) : ( + "" + )} +
); diff --git a/ui/src/features/dataset/dataset_repository.ts b/ui/src/features/dataset/dataset_http_repository.ts similarity index 96% rename from ui/src/features/dataset/dataset_repository.ts rename to ui/src/features/dataset/dataset_http_repository.ts index 796a30f..5c6f347 100644 --- a/ui/src/features/dataset/dataset_repository.ts +++ b/ui/src/features/dataset/dataset_http_repository.ts @@ -3,7 +3,7 @@ import { HttpError, HttpMethod, HttpRepository } from "../../core/repository/htt import { UUID } from "../all_projects/data/project_repository"; import { Assets, DataSetModel, Dataset, IDatasetModel, ProcessStatus } from "./dataset_model"; -export class DataSetRepository extends HttpRepository { +export class DataSetHttpRepository extends HttpRepository { editDataset(dataSetModel: DataSetModel) { dataSetModel.processStatus = ProcessStatus.NEW; return this._jsonRequest(HttpMethod.PUT, `/datasets`, dataSetModel); diff --git a/ui/src/features/dataset/dataset_store.ts b/ui/src/features/dataset/dataset_store.ts index 33f2de9..bf2ba55 100644 --- a/ui/src/features/dataset/dataset_store.ts +++ b/ui/src/features/dataset/dataset_store.ts @@ -1,5 +1,5 @@ import makeAutoObservable from "mobx-store-inheritance"; -import { DataSetRepository } from "./dataset_repository"; +import { DataSetHttpRepository } from "./dataset_http_repository"; import { Drawer, UiErrorState } from "../../core/store/base_store"; import { HttpError } from "../../core/repository/http_repository"; import { Asset, Assets, DataSetModel, IDatasetModel, ProcessStatus } from "./dataset_model"; @@ -13,7 +13,7 @@ export enum DrawersDataset { } export class DataSetStore extends UiErrorState { - dataSetRepository: DataSetRepository; + dataSetRepository: DataSetHttpRepository; assets?: Assets; datasets?: IDatasetModel[]; activeProject: UUID; @@ -25,7 +25,7 @@ export class DataSetStore extends UiErrorState { constructor() { super(); this.socketRepository = socketRepository; - this.dataSetRepository = new DataSetRepository(); + this.dataSetRepository = new DataSetHttpRepository(); this.drawers = Object.entries(DrawersDataset).map((k, v) => { return { name: k.at(1) ?? "", diff --git a/ui/src/features/pipeline_instance_main_screen/pipeline_instance_screen.tsx b/ui/src/features/pipeline_instance_main_screen/pipeline_instance_screen.tsx index 655a6b0..bfe0c2a 100644 --- a/ui/src/features/pipeline_instance_main_screen/pipeline_instance_screen.tsx +++ b/ui/src/features/pipeline_instance_main_screen/pipeline_instance_screen.tsx @@ -1,8 +1,6 @@ import * as React from "react"; import { PipelineInstanceStore } from "./pipeline_instance_store"; import { useNavigate } from "react-router-dom"; -import { Icon } from "../../core/ui/icons/icons"; -import { CardDataSet, CardDataSetType } from "../dataset/card_dataset"; import { MainPage } from "../../core/ui/pages/main_page"; export const PipelineInstanceScreenPath = "/pipeline_instance/"; diff --git a/ui/src/features/scene_manager/presentation/scene_manager.tsx b/ui/src/features/scene_manager/presentation/scene_manager.tsx index c08ebcb..342917e 100644 --- a/ui/src/features/scene_manager/presentation/scene_manager.tsx +++ b/ui/src/features/scene_manager/presentation/scene_manager.tsx @@ -2,7 +2,6 @@ import * as React from "react"; import { SceneMangerStore } from "./scene_manager_store"; import { observer } from "mobx-react-lite"; import { useParams } from "react-router-dom"; -import { SceneManagerView, SceneMode } from "../model/scene_view"; import { MainPage } from "../../../core/ui/pages/main_page"; export const SceneManagerPath = "/scene/manager/"; @@ -22,12 +21,6 @@ export const SceneManger = observer(() => { }; }, [id, store]); - const sceneIcons: SceneManagerView[] = Object.values(SceneMode) - .filter((el) => el !== SceneMode.EMPTY) - .map((el) => { - return { name: el, clickHandel: () => store.setSceneMode(el as SceneMode) }; - }); - return (