From a920f5fb4560c4009c31a27b7b24dfaf1b5b5388 Mon Sep 17 00:00:00 2001 From: IDONTSUDO Date: Thu, 29 Aug 2024 12:26:54 +0300 Subject: [PATCH] skills screen --- server/src/core/controllers/routes.ts | 2 + server/src/core/models/skill_model.ts | 52 ++++- .../scenarios/create_many_folder_scenario.ts | 1 + .../domain/get_bt_skills_templates_usecase.ts | 21 ++ .../skills/model/skills_database_model.ts | 25 +++ .../skills/model/skills_validation_model.ts | 17 ++ .../src/features/skills/skill_presentation.ts | 12 ++ .../model/skills_instance_database_model.ts | 9 - .../model/skills_instance_validation_model.ts | 1 - .../skill_instance_presentation.ts | 1 - .../model/skills_template_database_model.ts | 9 - .../model/skills_template_validation_model.ts | 1 - .../skills_template_presentation.ts | 1 - ui/src/core/extensions/array.ts | 18 ++ ui/src/core/extensions/extensions.ts | 2 + ui/src/core/model/skill_model.ts | 113 ++++++----- ui/src/core/model/validation_model.ts | 2 + .../core/repository/core_http_repository.ts | 30 +++ ui/src/core/routers/routers.tsx | 16 +- ui/src/core/ui/pages/main_page.tsx | 2 +- .../presentation/all_projects_screen.tsx | 7 + .../behavior_tree_builder_store.tsx | 1 + .../presentation/ui/forms/forms.tsx | 14 +- .../forms/move_to_pose/move_to_pose_form.tsx | 10 +- .../topic_dependency_view_model.ts | 27 +-- .../ui/forms/topics_form/topics_form.tsx | 66 +++---- .../ui/forms/topics_form/topics_form_store.ts | 12 +- .../ui/forms/weights_form/weights_store.ts | 2 +- .../data/calculation_http_repository.ts | 0 .../data/calculation_socket_repository.ts | 0 .../model/calculation_model.ts | 0 .../calculation_instance_screen.tsx} | 8 +- .../calculation_instance_store.tsx} | 10 +- .../presentation/ui/cards/get_model_card.tsx | 1 - .../cards/pose_estimate_card/model_card.tsx | 9 +- .../presentation/ui/template_model_card.tsx | 0 .../calculations_template_http_repository.ts | 3 + .../calculations_template_screen.tsx | 13 ++ .../calculations_template_store.ts | 16 ++ .../calculations_template_view_model.ts | 7 + .../features/skills/skills_http_repository.ts | 8 + ui/src/features/skills/skills_screen.tsx | 184 ++++++++++++++++++ ui/src/features/skills/skills_store.ts | 78 ++++++++ .../socket_listener.tsx} | 8 +- .../socket_listener_store.ts} | 4 +- ui/src/features/topics/topic_view_model.ts | 14 +- ui/src/features/topics/topics_repository.ts | 3 +- ui/src/features/topics/topics_screen.tsx | 2 +- ui/src/index.tsx | 7 +- 49 files changed, 681 insertions(+), 168 deletions(-) create mode 100644 server/src/features/skills/model/skills_database_model.ts create mode 100644 server/src/features/skills/model/skills_validation_model.ts create mode 100644 server/src/features/skills/skill_presentation.ts delete mode 100644 server/src/features/skills_instance/model/skills_instance_database_model.ts delete mode 100644 server/src/features/skills_instance/model/skills_instance_validation_model.ts delete mode 100644 server/src/features/skills_instance/skill_instance_presentation.ts delete mode 100644 server/src/features/skills_template/model/skills_template_database_model.ts delete mode 100644 server/src/features/skills_template/model/skills_template_validation_model.ts delete mode 100644 server/src/features/skills_template/skills_template_presentation.ts rename ui/src/features/{calculation => calculation_instance}/data/calculation_http_repository.ts (100%) rename ui/src/features/{calculation => calculation_instance}/data/calculation_socket_repository.ts (100%) rename ui/src/features/{calculation => calculation_instance}/model/calculation_model.ts (100%) rename ui/src/features/{calculation/presentation/calculation_screen.tsx => calculation_instance/presentation/calculation_instance_screen.tsx} (96%) rename ui/src/features/{calculation/presentation/calculation_store.tsx => calculation_instance/presentation/calculation_instance_store.tsx} (98%) rename ui/src/features/{calculation => calculation_instance}/presentation/ui/cards/get_model_card.tsx (97%) rename ui/src/features/{calculation => calculation_instance}/presentation/ui/cards/pose_estimate_card/model_card.tsx (54%) rename ui/src/features/{calculation => calculation_instance}/presentation/ui/template_model_card.tsx (100%) create mode 100644 ui/src/features/calculations_template/calculations_template_http_repository.ts create mode 100644 ui/src/features/calculations_template/calculations_template_screen.tsx create mode 100644 ui/src/features/calculations_template/calculations_template_store.ts create mode 100644 ui/src/features/calculations_template/calculations_template_view_model.ts create mode 100644 ui/src/features/skills/skills_http_repository.ts create mode 100644 ui/src/features/skills/skills_screen.tsx create mode 100644 ui/src/features/skills/skills_store.ts rename ui/src/features/{socket_lister/socket_lister.tsx => socket_listener/socket_listener.tsx} (77%) rename ui/src/features/{socket_lister/socket_lister_store.ts => socket_listener/socket_listener_store.ts} (86%) diff --git a/server/src/core/controllers/routes.ts b/server/src/core/controllers/routes.ts index 3a80e86..538118a 100644 --- a/server/src/core/controllers/routes.ts +++ b/server/src/core/controllers/routes.ts @@ -9,6 +9,7 @@ import { CalculationsInstancesPresentation } from "../../features/calculations_i import { DigitalTwinsInstancePresentation } from "../../features/digital_twins_instance/digital_twins_instance_presentation"; import { DigitalTwinsTemplatePresentation } from "../../features/digital_twins_template/digital_twins_template_presentation"; import { TopicsPresentation } from "../../features/topics/topics_presentation"; +import { SkillsPresentation } from "../../features/skills/skill_presentation"; extensions(); @@ -22,4 +23,5 @@ export const httpRoutes: Routes[] = [ new DigitalTwinsTemplatePresentation(), new DigitalTwinsInstancePresentation(), new TopicsPresentation(), + new SkillsPresentation(), ].map((el) => el.call()); diff --git a/server/src/core/models/skill_model.ts b/server/src/core/models/skill_model.ts index 97121ec..c22ab7d 100644 --- a/server/src/core/models/skill_model.ts +++ b/server/src/core/models/skill_model.ts @@ -1,7 +1,26 @@ -import { IsArray, IsString, ValidateNested } from "class-validator"; +import { IsArray, IsEnum, IsNotEmpty, IsString, ValidateNested } from "class-validator"; import { Type } from "class-transformer"; +export class TopicViewModel { + @IsNotEmpty() + @IsString() + name: string; + @IsNotEmpty() + @IsString() + type: string; + constructor(name: string, type: string) { + this.name = name; + this.type = type; + } + static empty() { + return new TopicViewModel("", ""); + } +} -export interface SkillPoseEstimation { +export interface IParam { + type: string; + dependency: Object; +} +export interface Skill { SkillPackage: ISkillPackage; Module: IModule; Launch: ILaunch; @@ -16,7 +35,7 @@ export interface IBTAction { name: string; format: string; type: string; - param: string[]; + param: IParam[]; result: string[]; } @@ -82,7 +101,7 @@ export class BTAction implements IBTAction { @IsString() type: string; @IsArray() - param: string[]; + param: IParam[]; @IsArray() result: string[]; } @@ -117,7 +136,7 @@ export class Xxx implements IXxx { topicImage: string; topicCameraInfo: string; } -export class SkillModelPoseEstimation implements SkillPoseEstimation { +export class SkillModel implements Skill { @ValidateNested() @Type(() => SkillPackage) SkillPackage: ISkillPackage; @@ -145,3 +164,26 @@ export class SkillModelPoseEstimation implements SkillPoseEstimation { @Type(() => Xxx) xxx: IXxx; } + +export enum BtAction { + ACTION = "ACTION", + CONDITION = "CONDITION", +} +export class BtActionViewModel implements IBTAction { + @IsNotEmpty() + @IsString() + name: string; + @IsNotEmpty() + @IsString() + format: string; + @IsNotEmpty() + @IsString() + type: string; + param: IParam[]; + @IsNotEmpty() + @IsString() + result: string[]; + @IsNotEmpty() + @IsEnum(BtAction) + typeAction: BtAction; +} diff --git a/server/src/core/scenarios/create_many_folder_scenario.ts b/server/src/core/scenarios/create_many_folder_scenario.ts index c5d24be..60b93f7 100644 --- a/server/src/core/scenarios/create_many_folder_scenario.ts +++ b/server/src/core/scenarios/create_many_folder_scenario.ts @@ -13,3 +13,4 @@ export class CreateManyFolderScenario { } }; } + 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 index eb7f3ce..a0f0a04 100644 --- 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 @@ -34,6 +34,27 @@ export class GetBehaviorTreeSkillsTemplatesUseCase extends CallbackStrategyWithE { name: "end_effector_acceleration", value: 1.0 }, ], }, + { + Module: { name: "MMM", description: "Move to Pose skill with cartesian controllers" }, + topicOut: [ + { + topicName: "", + topicType: "", + }, + ], + BTAction: [ + { + name: "move", + type: "action", + param: [ + { + type: "move_to_pose", + dependency: {}, + }, + ], + }, + ], + }, { SkillPackage: { name: "Robossembler", version: "1.0", format: "1", type: "Action" }, Module: { name: "PoseEstimation", description: "Pose Estimation skill with DOPE" }, diff --git a/server/src/features/skills/model/skills_database_model.ts b/server/src/features/skills/model/skills_database_model.ts new file mode 100644 index 0000000..19cdaf9 --- /dev/null +++ b/server/src/features/skills/model/skills_database_model.ts @@ -0,0 +1,25 @@ +import { Schema, model } from "mongoose"; + +export interface ISkillsModel {} + +export const SkillsSchema = new Schema({ + SkillPackage: { + type: Schema.Types.Mixed, + }, + Module: { + type: Schema.Types.Mixed, + }, + BTAction: { + type: Schema.Types.Mixed, + }, + topicsOut: { + type: Schema.Types.Mixed, + }, + param: { + type: Schema.Types.Mixed, + }, +}).plugin(require("mongoose-autopopulate")); + +export const skillsSchema = "skills"; + +export const SkillsDBModel = model(skillsSchema, SkillsSchema); diff --git a/server/src/features/skills/model/skills_validation_model.ts b/server/src/features/skills/model/skills_validation_model.ts new file mode 100644 index 0000000..77324f6 --- /dev/null +++ b/server/src/features/skills/model/skills_validation_model.ts @@ -0,0 +1,17 @@ +import { Type } from "class-transformer"; +import { IsOptional, IsString, ValidateNested } from "class-validator"; +import { SkillPackage, ISkillPackage, Module, IModule, BtActionViewModel, TopicViewModel } from "../../../core/models/skill_model"; + +export class SkillInstanceValidationModel { + @IsOptional() + @IsString() + sid?: string; + @ValidateNested() + @Type(() => SkillPackage) + SkillPackage: ISkillPackage; + @ValidateNested() + @Type(() => Module) + Module: IModule; + BTAction: BtActionViewModel[]; + topicsOut: TopicViewModel[] = []; +} diff --git a/server/src/features/skills/skill_presentation.ts b/server/src/features/skills/skill_presentation.ts new file mode 100644 index 0000000..589c184 --- /dev/null +++ b/server/src/features/skills/skill_presentation.ts @@ -0,0 +1,12 @@ +import { CrudController } from "../../core/controllers/crud_controller"; +import { SkillInstanceValidationModel } from "./model/skills_validation_model"; +import { SkillsDBModel } from "./model/skills_database_model"; +export class SkillsPresentation extends CrudController { + constructor() { + super({ + url: "skills", + validationModel: SkillInstanceValidationModel, + databaseModel: SkillsDBModel, + }); + } +} diff --git a/server/src/features/skills_instance/model/skills_instance_database_model.ts b/server/src/features/skills_instance/model/skills_instance_database_model.ts deleted file mode 100644 index 1c8a502..0000000 --- a/server/src/features/skills_instance/model/skills_instance_database_model.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Schema, model } from "mongoose"; - -export interface ISkillsInstanceModel {} - -export const SkillsInstanceSchema = new Schema({}).plugin(require("mongoose-autopopulate")); - -export const skillsInstanceSchema = "skills_instances"; - -export const SkillsInstanceDBModel = model(skillsInstanceSchema, SkillsInstanceSchema); diff --git a/server/src/features/skills_instance/model/skills_instance_validation_model.ts b/server/src/features/skills_instance/model/skills_instance_validation_model.ts deleted file mode 100644 index ac4034a..0000000 --- a/server/src/features/skills_instance/model/skills_instance_validation_model.ts +++ /dev/null @@ -1 +0,0 @@ -export class SkillInstanceValidationModel {} diff --git a/server/src/features/skills_instance/skill_instance_presentation.ts b/server/src/features/skills_instance/skill_instance_presentation.ts deleted file mode 100644 index 793a4ae..0000000 --- a/server/src/features/skills_instance/skill_instance_presentation.ts +++ /dev/null @@ -1 +0,0 @@ -export class SkillsInstancePresentation {} diff --git a/server/src/features/skills_template/model/skills_template_database_model.ts b/server/src/features/skills_template/model/skills_template_database_model.ts deleted file mode 100644 index 1234643..0000000 --- a/server/src/features/skills_template/model/skills_template_database_model.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Schema, model } from "mongoose"; - -export interface ISkillsTemplateModel {} - -export const SkillsTemplateSchema = new Schema({}).plugin(require("mongoose-autopopulate")); - -export const skillsTemplateSchema = "skills_templates"; - -export const SkillsTemplateDBModel = model(skillsTemplateSchema, SkillsTemplateSchema); diff --git a/server/src/features/skills_template/model/skills_template_validation_model.ts b/server/src/features/skills_template/model/skills_template_validation_model.ts deleted file mode 100644 index afa305c..0000000 --- a/server/src/features/skills_template/model/skills_template_validation_model.ts +++ /dev/null @@ -1 +0,0 @@ -export class SkillTemplateValidationModel {} diff --git a/server/src/features/skills_template/skills_template_presentation.ts b/server/src/features/skills_template/skills_template_presentation.ts deleted file mode 100644 index d4d8262..0000000 --- a/server/src/features/skills_template/skills_template_presentation.ts +++ /dev/null @@ -1 +0,0 @@ -export class SkillsTemplatePresentation {} diff --git a/ui/src/core/extensions/array.ts b/ui/src/core/extensions/array.ts index 74f5a1c..cfb32fd 100644 --- a/ui/src/core/extensions/array.ts +++ b/ui/src/core/extensions/array.ts @@ -31,6 +31,24 @@ export const ArrayExtensions = () => { return true; }; } + if ([].replacePropIndex === undefined) { + Array.prototype.replacePropIndex = function (property, index) { + return this.map((element, i) => { + if (i === index) { + element = Object.assign(element, property); + } + return element; + }); + }; + } + if ([].someR === undefined) { + Array.prototype.someR = function (predicate) { + if (this.some(predicate)) { + return Result.error(undefined); + } + return Result.ok(this); + }; + } if ([].lastElement === undefined) { Array.prototype.lastElement = function () { const instanceCheck = this; diff --git a/ui/src/core/extensions/extensions.ts b/ui/src/core/extensions/extensions.ts index b7e4186..b37b0c0 100644 --- a/ui/src/core/extensions/extensions.ts +++ b/ui/src/core/extensions/extensions.ts @@ -26,6 +26,8 @@ declare global { maxLength(length: number): Array; add(element: T): Array; indexOfR(element: T): Result>; + replacePropIndex(property: Partial, index: number): T[]; + someR(predicate: (value: T) => boolean): Result>; } interface Date { formatDate(): string; diff --git a/ui/src/core/model/skill_model.ts b/ui/src/core/model/skill_model.ts index 394dfef..83c2219 100644 --- a/ui/src/core/model/skill_model.ts +++ b/ui/src/core/model/skill_model.ts @@ -1,8 +1,11 @@ -import { IsArray, IsOptional, IsString, ValidateNested } from "class-validator"; +import { IsArray, IsEnum, IsNotEmpty, 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 { ValidationModel } from "./validation_model"; +import { TopicViewModel } from "../../features/topics/topic_view_model"; +import makeAutoObservable from "mobx-store-inheritance"; import clone from "just-clone"; export interface IDependency { @@ -18,12 +21,9 @@ export interface ISkill { sid?: string; SkillPackage: ISkillPackage; Module: IModule; - Launch: ILaunch; - ROS2: IRos2; BTAction: IBTAction[]; - Interface: IInterface; - Settings: ISetting[]; - xxx: IXxx; + // Interface: IInterface; + // Settings: ISetting[]; } export interface IWeightsDependency { weights_name: string; @@ -39,16 +39,55 @@ export interface IParam { type: string; dependency: Object; } +export class ParamViewModel implements IParam { + type: string; + dependency: Object; + constructor(type: string, dependency: Object) { + this.type = type; + this.dependency = dependency; + } + static empty = () => new ParamViewModel("", {}); +} export interface IBTAction { name: string; format: string; - // TODO: Нужно выпилить его отсюда - // sid?: string; type: string; param: IParam[]; result: string[]; } - +export enum BtAction { + ACTION = "ACTION", + CONDITION = "CONDITION", +} +export class BtActionViewModel extends ValidationModel implements IBTAction { + @IsNotEmpty() + @IsString() + name: string; + @IsNotEmpty() + @IsString() + format: string; + @IsNotEmpty() + @IsString() + type: string; + param: IParam[]; + @IsNotEmpty() + @IsString() + result: string[]; + @IsNotEmpty() + @IsEnum(BtAction) + typeAction: BtAction; + constructor(name: string, format: string, type: string, param: IParam[], result: string[], typeAction: BtAction) { + super(); + this.name = name; + this.format = format; + this.type = type; + this.param = param; + this.result = result; + this.typeAction = typeAction; + } + static empty = () => new BtActionViewModel("", "", "", [], [], BtAction.ACTION); + public validParam = (type: string) => this.param.someR((param) => param.type === type); +} export interface IInterface { Input: IPut[]; Output: IPut[]; @@ -83,19 +122,22 @@ export interface ISkillPackage { format: string; } -export interface IXxx { - cameraLink: string; - topicImage: string; - topicCameraInfo: string; -} - export class SkillPackage implements ISkillPackage { + @IsNotEmpty() @IsString() name: string; + @IsNotEmpty() @IsString() version: string; + @IsNotEmpty() @IsString() format: string; + constructor(name: string, version: string, format: string) { + this.name = name; + this.format = format; + this.version = version; + } + static empty = () => new SkillPackage("", "", ""); } export class Module implements IModule { @IsString() @@ -142,12 +184,12 @@ export class Setting implements ISetting { name: string; value: string | number; } -export class Xxx implements IXxx { - cameraLink: string; - topicImage: string; - topicCameraInfo: string; -} -export class SkillModel implements ISkill { + +export class SkillModel extends ValidationModel implements ISkill { + constructor() { + super(); + makeAutoObservable(this); + } @IsOptional() @IsString() sid?: string; @@ -157,29 +199,12 @@ export class SkillModel implements ISkill { @ValidateNested() @Type(() => Module) Module: IModule; - @ValidateNested() - @Type(() => Launch) - Launch: ILaunch; - @ValidateNested() - @Type(() => Ros2) - ROS2: IRos2; - @ValidateNested() - @IsArray() - @Type(() => BTAction) - BTAction: IBTAction[]; - @ValidateNested() - @Type(() => Interface) - Interface: IInterface; - @ValidateNested() - @IsArray() - @Type(() => Setting) - Settings: ISetting[]; - @ValidateNested() - @Type(() => Xxx) - xxx: IXxx; + BTAction: BtActionViewModel[]; + topicsOut: TopicViewModel[] = []; static empty() { const skillModel = new SkillModel(); skillModel.BTAction = []; + skillModel.SkillPackage = SkillPackage.empty(); return skillModel; } public static isEmpty(skill: SkillModel): Result { @@ -350,12 +375,6 @@ export class Skills { 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(); } }); diff --git a/ui/src/core/model/validation_model.ts b/ui/src/core/model/validation_model.ts index 06843a0..81a94ba 100644 --- a/ui/src/core/model/validation_model.ts +++ b/ui/src/core/model/validation_model.ts @@ -14,6 +14,8 @@ export class ValidationModel { if (error.constraints) return Object.values(error.constraints).join(", "); return ""; }); + console.log(errors) + console.log(this) return Result.error(message.join(",")); } else { return Result.ok(this as unknown as T); diff --git a/ui/src/core/repository/core_http_repository.ts b/ui/src/core/repository/core_http_repository.ts index 4ba9435..779ebef 100644 --- a/ui/src/core/repository/core_http_repository.ts +++ b/ui/src/core/repository/core_http_repository.ts @@ -71,6 +71,36 @@ export class HttpRepository { } return Result.ok(response.text as T); } + public async _arrayJsonToClassInstanceRequest( + method: HttpMethod, + url: string, + instance: ClassConstructor, + data?: any + ): Promise> { + try { + const reqInit = { + body: data, + method: method, + headers: { "Content-Type": "application/json" }, + }; + if (data !== undefined) { + reqInit["body"] = JSON.stringify(data); + } + const response = await fetch(this.server + url, reqInit); + + if (response.status !== 200) { + return Result.error(new HttpError(this.server + url, response.status)); + } + const array: any[] = await response.json(); + return Result.ok( + array.map((el) => { + return plainToInstance(instance, el) as T; + }) + ); + } catch (error) { + return Result.error(new HttpError(error, 0)); + } + } public async _jsonToClassInstanceRequest( method: HttpMethod, url: string, diff --git a/ui/src/core/routers/routers.tsx b/ui/src/core/routers/routers.tsx index 793bdc0..f86090f 100644 --- a/ui/src/core/routers/routers.tsx +++ b/ui/src/core/routers/routers.tsx @@ -9,10 +9,12 @@ import { DataSetScreen, DatasetsScreenPath } from "../../features/dataset/datase import { AssemblesScreen, AssemblesScreenPath } from "../../features/assembles/assembles_screen"; import { SimulationScreen, SimulationScreenPath } from "../../features/simulations/simulations_screen"; import { EstimateScreen, EstimateScreenPath } from "../../features/estimate/estimate_screen"; -import { CalculationScreenPath, CalculationScreen } from "../../features/calculation/presentation/calculation_screen"; +import { CalculationInstanceScreenPath, CalculationInstanceScreen } from "../../features/calculation_instance/presentation/calculation_instance_screen"; import { DetailsScreenPath, DetailsScreen } from "../../features/details/details_screen"; import { DigitalTwinsScreen, DigitalTwinsScreenPath } from "../../features/digital_twins/digital_twins_screen"; import { TopicsScreen, TopicsScreenPath } from "../../features/topics/topics_screen"; +import { SkillsScreen, SkillsScreenPath } from "../../features/skills/skills_screen"; +import { CalculationsTemplateScreenPath } from "../../features/calculations_template/calculations_template_screen"; const idURL = ":id"; @@ -58,8 +60,8 @@ export const router = createBrowserRouter([ element: , }, { - path: CalculationScreenPath, - element: , + path: CalculationInstanceScreenPath, + element: , }, { path: DigitalTwinsScreenPath, @@ -69,4 +71,12 @@ export const router = createBrowserRouter([ path: TopicsScreenPath, element: , }, + { + path: SkillsScreenPath, + element: , + }, + { + path: CalculationsTemplateScreenPath, + element: , + }, ]); diff --git a/ui/src/core/ui/pages/main_page.tsx b/ui/src/core/ui/pages/main_page.tsx index 9d6b328..296fe1f 100644 --- a/ui/src/core/ui/pages/main_page.tsx +++ b/ui/src/core/ui/pages/main_page.tsx @@ -9,7 +9,7 @@ import { AssemblesScreenPath } from "../../../features/assembles/assembles_scree 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"; -import { CalculationScreenPath as SkillScreenPath } from "../../../features/calculation/presentation/calculation_screen"; +import { CalculationInstanceScreenPath as SkillScreenPath } from "../../../features/calculation_instance/presentation/calculation_instance_screen"; import { UiBaseError } from "../../model/ui_base_error"; import { DetailsScreenPath } from "../../../features/details/details_screen"; export interface IBlockProps { diff --git a/ui/src/features/all_projects/presentation/all_projects_screen.tsx b/ui/src/features/all_projects/presentation/all_projects_screen.tsx index 456f034..0a045a7 100644 --- a/ui/src/features/all_projects/presentation/all_projects_screen.tsx +++ b/ui/src/features/all_projects/presentation/all_projects_screen.tsx @@ -9,6 +9,9 @@ import { CoreButton } from "../../../core/ui/button/button"; import { CoreInput } from "../../../core/ui/input/input"; import { DetailsScreenPath } from "../../details/details_screen"; import { DigitalTwinsScreenPath } from "../../digital_twins/digital_twins_screen"; +import { TopicsScreenPath } from "../../topics/topics_screen"; +import { SkillsScreenPath } from "../../skills/skills_screen"; +import { CalculationsTemplateScreenPath } from "../../calculations_template/calculations_template_screen"; export const AllProjectScreenPath = "/"; export const AllProjectScreen: React.FunctionComponent = observer(() => { @@ -30,6 +33,10 @@ export const AllProjectScreen: React.FunctionComponent = observer(() => { children={ <>
navigate(DigitalTwinsScreenPath)}>Digital twins
+
navigate(TopicsScreenPath)}>Topics
+
navigate(SkillsScreenPath)}>Skills
+
navigate(CalculationsTemplateScreenPath)}>Calculation template
+ {store.projectsModels?.map((el) => { return (
{ + this.skillTemplates = model; this.skillTree.children = this.skillTree.children?.map((el) => { if (el.name === "Действия") { 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 3957ff7..f69a647 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 @@ -2,12 +2,13 @@ import { BehaviorTreeBuilderStore } from "../../behavior_tree_builder_store"; import { CameraDeviceForm } from "./camera_device_form/camera_device_form_form"; import { MoveToPose } from "./move_to_pose/move_to_pose_form"; import { RobotDeviceForm } from "./robot_device_form/robot_device_form_form"; +import { TopicDependencyViewModel } from "./topics_form/topic_dependency_view_model"; import { TopicsForm } from "./topics_form/topics_form"; import { WeightsForm } from "./weights_form/weights_form"; export interface IPropsForm { dependency: T; - store: BehaviorTreeBuilderStore; + store?: BehaviorTreeBuilderStore; onChange: (dependency: Object) => void; } @@ -22,7 +23,18 @@ export enum Form { topic = "topic", moveToPose = "move_to_pose", } +export interface BtDependencyFormBuilder { + form: Form; + component?: React.ReactNode; +} +export const btDependencyFormBuilder = (onChange: (dependency: Object) => void) => [ + { form: Form.weights, component: null }, + { + form: Form.topic, + component: , + }, +]; export const forms = ( props: any, onChange: (dependency: Object) => void, diff --git a/ui/src/features/behavior_tree_builder/presentation/ui/forms/move_to_pose/move_to_pose_form.tsx b/ui/src/features/behavior_tree_builder/presentation/ui/forms/move_to_pose/move_to_pose_form.tsx index 0357e8f..1a87217 100644 --- a/ui/src/features/behavior_tree_builder/presentation/ui/forms/move_to_pose/move_to_pose_form.tsx +++ b/ui/src/features/behavior_tree_builder/presentation/ui/forms/move_to_pose/move_to_pose_form.tsx @@ -8,9 +8,11 @@ import { PointModel } from "../../../../../../core/model/point_model"; import { ObjectIsNotEmpty } from "../../../../../../core/extensions/object"; import { CoreButton } from "../../../../../../core/ui/button/button"; import { message } from "antd"; +import { BehaviorTreeBuilderStore } from "../../../behavior_tree_builder_store"; export const MoveToPose = observer((props: IPropsForm) => { - const [store] = React.useState(() => new MoveToPoseStore(props.store)); + const propStore = props.store as BehaviorTreeBuilderStore; + const [store] = React.useState(() => new MoveToPoseStore(propStore)); React.useEffect(() => { if (ObjectIsNotEmpty(props.dependency)) store.loadDependency(props.dependency); store.init(); @@ -18,16 +20,16 @@ export const MoveToPose = observer((props: IPropsForm store.updateForm({ robot_name: text })} /> - store.updateForm({ pose: props.store.sceneAsset?.getElementByName(text).toDependency() }) + store.updateForm({ pose: propStore.sceneAsset?.getElementByName(text).toDependency() }) } value={store.viewModel?.pose?.name ?? ""} /> diff --git a/ui/src/features/behavior_tree_builder/presentation/ui/forms/topics_form/topic_dependency_view_model.ts b/ui/src/features/behavior_tree_builder/presentation/ui/forms/topics_form/topic_dependency_view_model.ts index 187ce8a..9afc488 100644 --- a/ui/src/features/behavior_tree_builder/presentation/ui/forms/topics_form/topic_dependency_view_model.ts +++ b/ui/src/features/behavior_tree_builder/presentation/ui/forms/topics_form/topic_dependency_view_model.ts @@ -1,18 +1,21 @@ import makeAutoObservable from "mobx-store-inheritance"; -import { Result } from "../../../../../../core/helper/result"; -import { SidViewModel } from "../../../../../../core/model/device_dependency_view_model"; +import { ValidationModel } from "../../../../../../core/model/validation_model"; +import { IsNotEmpty, IsString } from "class-validator"; +import { StoreTopicType } from "./topics_form_store"; -export class TopicDependencyViewModel extends SidViewModel { - axis: boolean; - constructor(sid: string, axis: boolean) { - super(sid); - this.axis = axis; - makeAutoObservable(this) - } - isValid = (): Result => { - return Result.ok(); +export class TopicDependencyViewModel extends ValidationModel { + @IsNotEmpty() + @IsString() + type: string; + mode: StoreTopicType; + constructor(type: string, mode: StoreTopicType) { + super(); + makeAutoObservable(this); + this.type = type; + this.mode = mode; } + static empty() { - return new TopicDependencyViewModel('', false); + return new TopicDependencyViewModel("", StoreTopicType.specifiesDependencies); } } diff --git a/ui/src/features/behavior_tree_builder/presentation/ui/forms/topics_form/topics_form.tsx b/ui/src/features/behavior_tree_builder/presentation/ui/forms/topics_form/topics_form.tsx index 92355c9..08fb325 100644 --- a/ui/src/features/behavior_tree_builder/presentation/ui/forms/topics_form/topics_form.tsx +++ b/ui/src/features/behavior_tree_builder/presentation/ui/forms/topics_form/topics_form.tsx @@ -1,11 +1,11 @@ import React from "react"; import { observer } from "mobx-react-lite"; -import { TopicsFormStore } from "./topics_form_store"; +import { StoreTopicType, TopicsFormStore } from "./topics_form_store"; import { IPropsForm } from "../forms"; import { TopicDependencyViewModel } from "./topic_dependency_view_model"; import { CoreText, CoreTextType } from "../../../../../../core/ui/text/text"; -import { CoreSwitch } from "../../../../../../core/ui/switch/switch"; -import { CoreSelect } from "../../../../../../core/ui/select/select"; +import { match } from "ts-pattern"; +import { CoreInput } from "../../../../../../core/ui/input/input"; import { CoreButton } from "../../../../../../core/ui/button/button"; import { message } from "antd"; @@ -14,40 +14,38 @@ export const TopicsForm = observer((props: IPropsForm { store.init(); - }, [store, props]); + store.loadClassInstance(TopicDependencyViewModel, props.dependency as TopicDependencyViewModel); + console.log(store.viewModel); + }, [props]); return ( -
+
- el.name) ?? []} - value={props.dependency?.sid ?? ""} - label={"Выберите топик"} - onChange={(value: string) => store.updateForm({ sid: value })} - /> - -
- is input:{" "} - { - store.updateForm({ axis: !status }); - }} - /> -
- { - (await store.viewModel.valid()).fold( - (s) => { - props.onChange(s); - }, - (e) => message.error(e) - ); - }} - /> + {match(store.viewModel.mode) + .with(StoreTopicType.btExecute, () => ( + <> + + + )) + .with(StoreTopicType.specifiesDependencies, () => ( + <> + store.updateForm({ type: text })} /> +
+ { + (await store.viewModel.valid()).fold( + (s) => props.onChange(s), + (e) => message.error(e) + ); + }} + /> + + )) + .otherwise(() => ( + <> + ))}
); }); diff --git a/ui/src/features/behavior_tree_builder/presentation/ui/forms/topics_form/topics_form_store.ts b/ui/src/features/behavior_tree_builder/presentation/ui/forms/topics_form/topics_form_store.ts index 2be73ed..089b8b9 100644 --- a/ui/src/features/behavior_tree_builder/presentation/ui/forms/topics_form/topics_form_store.ts +++ b/ui/src/features/behavior_tree_builder/presentation/ui/forms/topics_form/topics_form_store.ts @@ -4,7 +4,10 @@ import { TopicDependencyViewModel } from "./topic_dependency_view_model"; import { FormState, CoreError } from "../../../../../../core/store/base_store"; import { TopicsFormHttpRepository } from "./topics_form_http_repository"; import { Topics } from "../../../../../../core/model/topics"; - +export enum StoreTopicType { + specifiesDependencies = "specifiesDependencies", + btExecute = "btExecute", +} export class TopicsFormStore extends FormState { constructor() { super(); @@ -15,13 +18,6 @@ export class TopicsFormStore extends FormState {}; init = async (navigate?: NavigateFunction | undefined) => { - try { - throw new Error('213') - } catch (error) { - - } - - // await this.mapOk('topics', this.cameraDeviceHttpRepository.getAllTopics()) }; } diff --git a/ui/src/features/behavior_tree_builder/presentation/ui/forms/weights_form/weights_store.ts b/ui/src/features/behavior_tree_builder/presentation/ui/forms/weights_form/weights_store.ts index 2b94f7e..a23fcee 100644 --- a/ui/src/features/behavior_tree_builder/presentation/ui/forms/weights_form/weights_store.ts +++ b/ui/src/features/behavior_tree_builder/presentation/ui/forms/weights_form/weights_store.ts @@ -1,7 +1,7 @@ import makeAutoObservable from "mobx-store-inheritance"; import { FormState, CoreError } from "../../../../../../core/store/base_store"; import { DataSetHttpRepository } from "../../../../../dataset/dataset_http_repository"; -import { ISkils, CalculationHttpRepository } from "../../../../../calculation/data/calculation_http_repository"; +import { ISkils, CalculationHttpRepository } from "../../../../../calculation_instance/data/calculation_http_repository"; import { WeightsViewModel } from "./weights_view_model"; import { Parts } from "../../../../../details/details_http_repository"; diff --git a/ui/src/features/calculation/data/calculation_http_repository.ts b/ui/src/features/calculation_instance/data/calculation_http_repository.ts similarity index 100% rename from ui/src/features/calculation/data/calculation_http_repository.ts rename to ui/src/features/calculation_instance/data/calculation_http_repository.ts diff --git a/ui/src/features/calculation/data/calculation_socket_repository.ts b/ui/src/features/calculation_instance/data/calculation_socket_repository.ts similarity index 100% rename from ui/src/features/calculation/data/calculation_socket_repository.ts rename to ui/src/features/calculation_instance/data/calculation_socket_repository.ts diff --git a/ui/src/features/calculation/model/calculation_model.ts b/ui/src/features/calculation_instance/model/calculation_model.ts similarity index 100% rename from ui/src/features/calculation/model/calculation_model.ts rename to ui/src/features/calculation_instance/model/calculation_model.ts diff --git a/ui/src/features/calculation/presentation/calculation_screen.tsx b/ui/src/features/calculation_instance/presentation/calculation_instance_screen.tsx similarity index 96% rename from ui/src/features/calculation/presentation/calculation_screen.tsx rename to ui/src/features/calculation_instance/presentation/calculation_instance_screen.tsx index ad3fb35..5569d9a 100644 --- a/ui/src/features/calculation/presentation/calculation_screen.tsx +++ b/ui/src/features/calculation_instance/presentation/calculation_instance_screen.tsx @@ -3,7 +3,7 @@ import { Drawer, Modal } from "antd"; import { observer } from "mobx-react-lite"; import { MainPage } from "../../../core/ui/pages/main_page"; import { CoreText, CoreTextType } from "../../../core/ui/text/text"; -import { DrawersSkill, CalculationStore, StoreTypes } from "./calculation_store"; +import { DrawersSkill, CalculationInstanceStore, StoreTypes } from "./calculation_instance_store"; import { CoreInput } from "../../../core/ui/input/input"; import { CoreButton } from "../../../core/ui/button/button"; import { CoreSelect } from "../../../core/ui/select/select"; @@ -22,10 +22,10 @@ interface IItem { const skills: IItem[] = [{ name: "ML", isActive: true }]; -export const CalculationScreenPath = "/calculation"; +export const CalculationInstanceScreenPath = "/calculation"; -export const CalculationScreen = observer(() => { - const [store] = React.useState(() => new CalculationStore()); +export const CalculationInstanceScreen = observer(() => { + const [store] = React.useState(() => new CalculationInstanceStore()); React.useEffect(() => { store.init(); diff --git a/ui/src/features/calculation/presentation/calculation_store.tsx b/ui/src/features/calculation_instance/presentation/calculation_instance_store.tsx similarity index 98% rename from ui/src/features/calculation/presentation/calculation_store.tsx rename to ui/src/features/calculation_instance/presentation/calculation_instance_store.tsx index d8faf6d..1ca399a 100644 --- a/ui/src/features/calculation/presentation/calculation_store.tsx +++ b/ui/src/features/calculation_instance/presentation/calculation_instance_store.tsx @@ -20,11 +20,7 @@ export enum StoreTypes { empty = "empty", } -export class CalculationStore extends UiDrawerFormState { - deleteInstance = async (id: string) => { - await this.calculationHttpRepository.deleteInstance(id); - await this.init(); - }; +export class CalculationInstanceStore extends UiDrawerFormState { calculationHttpRepository: CalculationHttpRepository = new CalculationHttpRepository(); calculationSocketRepository: CalculationSocketRepository = new CalculationSocketRepository(); activeProjectId?: UUID; @@ -49,6 +45,10 @@ export class CalculationStore extends UiDrawerFormState { + await this.calculationHttpRepository.deleteInstance(id); + await this.init(); + }; socketUpdate = (data: ProcessUpdate) => { this.calculationInstances?.map((el) => { if (el?._id && el._id.isEqual(data.id)) { diff --git a/ui/src/features/calculation/presentation/ui/cards/get_model_card.tsx b/ui/src/features/calculation_instance/presentation/ui/cards/get_model_card.tsx similarity index 97% rename from ui/src/features/calculation/presentation/ui/cards/get_model_card.tsx rename to ui/src/features/calculation_instance/presentation/ui/cards/get_model_card.tsx index 201ac32..9872c67 100644 --- a/ui/src/features/calculation/presentation/ui/cards/get_model_card.tsx +++ b/ui/src/features/calculation_instance/presentation/ui/cards/get_model_card.tsx @@ -1,6 +1,5 @@ import { match } from "ts-pattern"; import { PoseEstimateCard } from "./pose_estimate_card/model_card"; -import { FormBuilderValidationModel } from "../../../../dataset/dataset_model"; import { Dropdown, MenuProps, message } from "antd"; import { CoreText, CoreTextType } from "../../../../../core/ui/text/text"; import { IMenuItem } from "../../../../dataset/card_dataset"; diff --git a/ui/src/features/calculation/presentation/ui/cards/pose_estimate_card/model_card.tsx b/ui/src/features/calculation_instance/presentation/ui/cards/pose_estimate_card/model_card.tsx similarity index 54% rename from ui/src/features/calculation/presentation/ui/cards/pose_estimate_card/model_card.tsx rename to ui/src/features/calculation_instance/presentation/ui/cards/pose_estimate_card/model_card.tsx index 7c1d21c..a0513b7 100644 --- a/ui/src/features/calculation/presentation/ui/cards/pose_estimate_card/model_card.tsx +++ b/ui/src/features/calculation_instance/presentation/ui/cards/pose_estimate_card/model_card.tsx @@ -1,11 +1,4 @@ -import { CoreButton } from "../../../../../../core/ui/button/button"; -import { Icon } from "../../../../../../core/ui/icons/icons"; -import { Dropdown } from "antd"; -import { CoreText, CoreTextType } from "../../../../../../core/ui/text/text"; -import { ProcessStatus } from "../../../../../dataset/dataset_model"; -import { IMenuItem } from "../../../../../dataset/card_dataset"; -import type { MenuProps } from "antd"; -import { match } from "ts-pattern"; + export interface IModelCardProps { dependency?: Object; diff --git a/ui/src/features/calculation/presentation/ui/template_model_card.tsx b/ui/src/features/calculation_instance/presentation/ui/template_model_card.tsx similarity index 100% rename from ui/src/features/calculation/presentation/ui/template_model_card.tsx rename to ui/src/features/calculation_instance/presentation/ui/template_model_card.tsx diff --git a/ui/src/features/calculations_template/calculations_template_http_repository.ts b/ui/src/features/calculations_template/calculations_template_http_repository.ts new file mode 100644 index 0000000..04dee15 --- /dev/null +++ b/ui/src/features/calculations_template/calculations_template_http_repository.ts @@ -0,0 +1,3 @@ +import { CoreHttpRepository } from "../../core/repository/core_http_repository"; + +export class CalculationsTemplateHttpRepository extends CoreHttpRepository {} diff --git a/ui/src/features/calculations_template/calculations_template_screen.tsx b/ui/src/features/calculations_template/calculations_template_screen.tsx new file mode 100644 index 0000000..7ba7fb4 --- /dev/null +++ b/ui/src/features/calculations_template/calculations_template_screen.tsx @@ -0,0 +1,13 @@ +import { observer } from "mobx-react-lite"; +import { CalculationsTemplateStore } from "./calculations_template_store"; +import React from "react"; + +export const CalculationsTemplateScreenPath = "/calculations/template"; + +export const CalculationsTemplateScreen = observer(() => { + const [store] = React.useState(() => new CalculationsTemplateStore()); + React.useEffect(() => { + store.init(); + }, []); + return <>; +}); diff --git a/ui/src/features/calculations_template/calculations_template_store.ts b/ui/src/features/calculations_template/calculations_template_store.ts new file mode 100644 index 0000000..e02b971 --- /dev/null +++ b/ui/src/features/calculations_template/calculations_template_store.ts @@ -0,0 +1,16 @@ +import makeAutoObservable from "mobx-store-inheritance"; +import { UiDrawerFormState } from "../../core/store/base_store"; +import { HttpError } from "../../core/repository/core_http_repository"; +import { CalculationsTemplateViewModel } from "./calculations_template_view_model"; +import { NavigateFunction } from "react-router-dom"; +export enum CalculationTemplateDrawer { + newSkill = "Новый навык", +} +export class CalculationsTemplateStore extends UiDrawerFormState { + viewModel: CalculationsTemplateViewModel = CalculationsTemplateViewModel.empty(); + constructor() { + super(CalculationTemplateDrawer); + makeAutoObservable(this); + } + init = async (navigate?: NavigateFunction | undefined): Promise => {}; +} diff --git a/ui/src/features/calculations_template/calculations_template_view_model.ts b/ui/src/features/calculations_template/calculations_template_view_model.ts new file mode 100644 index 0000000..1be29f6 --- /dev/null +++ b/ui/src/features/calculations_template/calculations_template_view_model.ts @@ -0,0 +1,7 @@ +import { ValidationModel } from "../../core/model/validation_model"; + +export class CalculationsTemplateViewModel extends ValidationModel { + static empty() { + return new CalculationsTemplateViewModel(); + } +} diff --git a/ui/src/features/skills/skills_http_repository.ts b/ui/src/features/skills/skills_http_repository.ts new file mode 100644 index 0000000..765ea75 --- /dev/null +++ b/ui/src/features/skills/skills_http_repository.ts @@ -0,0 +1,8 @@ +import { SkillModel } from "../../core/model/skill_model"; +import { HttpMethod, HttpRepository } from "../../core/repository/core_http_repository"; + +export class SkillsHttpRepository extends HttpRepository { + featureApi = "/skills"; + createNewSkill = (model: SkillModel) => this._jsonRequest(HttpMethod.POST, this.featureApi, model); + getAllSkills = () => this._arrayJsonToClassInstanceRequest(HttpMethod.GET, this.featureApi, SkillModel); +} diff --git a/ui/src/features/skills/skills_screen.tsx b/ui/src/features/skills/skills_screen.tsx new file mode 100644 index 0000000..33362d8 --- /dev/null +++ b/ui/src/features/skills/skills_screen.tsx @@ -0,0 +1,184 @@ +import React from "react"; +import { observer } from "mobx-react-lite"; +import { DrawersSkills, SkillsStore } from "./skills_store"; +import { Drawer, Modal } from "antd"; +import { CoreInput } from "../../core/ui/input/input"; +import { CoreText, CoreTextType } from "../../core/ui/text/text"; +import { TopicViewModel } from "../topics/topic_view_model"; +import { BtAction, BtActionViewModel } from "../../core/model/skill_model"; +import { btDependencyFormBuilder } from "../behavior_tree_builder/presentation/ui/forms/forms"; +import { CoreButton } from "../../core/ui/button/button"; +import { CoreSelect } from "../../core/ui/select/select"; + +export const SkillsScreenPath = "/skills"; + +export const SkillsScreen = observer(() => { + const [store] = React.useState(() => new SkillsStore()); + React.useEffect(() => { + store.init(); + }, []); + + return ( + <> +
+ store.editDrawer(DrawersSkills.newSkill, true)} + /> + {store.skills?.map((el) => ( +
+ +
+ + + {el.BTAction.map((el) => ( +
+ + {el.param.map((element) => ( +
+
{element.type}
+
{JSON.stringify(element.dependency)}
+
+ ))} +
+ ))} +
+
+ ))} +
+ + store.editDrawer(DrawersSkills.newSkill, false)} + open={store.drawers.find((el) => el.name === DrawersSkills.newSkill)?.status} + > +
+
+ + store.updateForm({ SkillPackage: Object.assign(store.viewModel.SkillPackage, { name: text }) }) + } + /> + + store.updateForm({ SkillPackage: Object.assign(store.viewModel.SkillPackage, { format: text }) }) + } + /> + + store.updateForm({ SkillPackage: Object.assign(store.viewModel.SkillPackage, { version: text }) }) + } + /> + store.updateForm({ topicsOut: store.viewModel.topicsOut.add(TopicViewModel.empty()) })} + /> +
+ {store.viewModel.topicsOut.map((el, index) => ( +
+ + store.updateForm({ topicsOut: store.viewModel.topicsOut.replacePropIndex({ name: text }, index) }) + } + /> + + store.updateForm({ topicsOut: store.viewModel.topicsOut.replacePropIndex({ type: text }, index) }) + } + /> +
+ ))} +
+ store.updateForm({ BTAction: store.viewModel.BTAction.add(BtActionViewModel.empty()) })} + /> + {store.viewModel.BTAction.map((el, index) => ( +
+ + store.updateForm({ BTAction: store.viewModel.BTAction.replacePropIndex({ name: text }, index) }) + } + /> + + store.updateForm({ BTAction: store.viewModel.BTAction.replacePropIndex({ type: text }, index) }) + } + /> + + store.updateForm({ BTAction: store.viewModel.BTAction.replacePropIndex({ format: text }, index) }) + } + /> + + store.updateForm({ + BTAction: store.viewModel.BTAction.replacePropIndex({ typeAction: value as BtAction }, index), + }) + } + /> + store.addNewParam(index)} + /> + {el.param.map((param) => ( +
store.showModal()}> +
{param.type}
+
{JSON.stringify(param.dependency)}
+
+ ))} +
+ ))} +
+ store.saveNewSkill()} /> +
+
+ (store.selectParam = undefined)} + open={store.isModalOpen} + footer={null} + closable={false} + closeIcon={null} + onCancel={store.handleCancel} + > + + {store.selectParam !== undefined + ? store.selectParam.component + : btDependencyFormBuilder((dependency) => store.onChangeBtDependency(dependency)).map((el) => ( +
store.clickParam(el)}>{el.form}
+ ))} +
+ + ); +}); diff --git a/ui/src/features/skills/skills_store.ts b/ui/src/features/skills/skills_store.ts new file mode 100644 index 0000000..8b7f64c --- /dev/null +++ b/ui/src/features/skills/skills_store.ts @@ -0,0 +1,78 @@ +import makeAutoObservable from "mobx-store-inheritance"; +import { UiDrawerFormState } from "../../core/store/base_store"; +import { NavigateFunction } from "react-router-dom"; +import { HttpError } from "../../core/repository/core_http_repository"; +import { ParamViewModel, SkillModel } from "../../core/model/skill_model"; +import { Form } from "../behavior_tree_builder/presentation/ui/forms/forms"; +import { message } from "antd"; +import { SkillsHttpRepository } from "./skills_http_repository"; +export enum DrawersSkills { + newSkill = "Новый навык", +} +export class SkillsStore extends UiDrawerFormState { + isModalOpen: boolean = false; + activeIndex?: number; + viewModel: SkillModel = SkillModel.empty(); + selectParam?: { + form: Form; + component?: React.ReactNode; + }; + skills: SkillModel[]; + skillsHttpRepository: SkillsHttpRepository = new SkillsHttpRepository(); + + constructor() { + super(DrawersSkills); + makeAutoObservable(this); + } + init = async (navigate?: NavigateFunction | undefined) => { + this.mapOk("skills", this.skillsHttpRepository.getAllSkills()); + }; + showModal = () => { + this.isModalOpen = true; + }; + + handleOk = () => { + this.isModalOpen = false; + }; + + handleCancel = () => { + this.isModalOpen = false; + }; + addNewParam = (index: number) => { + this.activeIndex = index; + this.showModal(); + }; + onChangeBtDependency = (dependency: Object) => + this.viewModel.BTAction.at(this.activeIndex ?? 0) + ?.validParam(this.selectParam?.form ?? "") + .fold( + () => { + this.updateForm({ + BTAction: this.viewModel.BTAction.replacePropIndex( + { + param: this.viewModel.BTAction.at(this.activeIndex ?? 0)?.param.add( + new ParamViewModel(this.selectParam?.form ?? "", dependency) + ), + }, + this.activeIndex ?? 0 + ), + }); + this.handleCancel(); + }, + () => { + message.error(`${this.selectParam?.form} is filled`); + this.handleCancel(); + } + ); + + clickParam(el: { form: Form; component: null } | { form: Form; component: React.JSX.Element }): void { + this.selectParam = el; + if (el.component === null) this.onChangeBtDependency({}); + } + saveNewSkill = async () => { + (await this.viewModel.valid()).fold( + async (model) => this.skillsHttpRepository.createNewSkill(model), + async (e) => message.error(e) + ); + }; +} diff --git a/ui/src/features/socket_lister/socket_lister.tsx b/ui/src/features/socket_listener/socket_listener.tsx similarity index 77% rename from ui/src/features/socket_lister/socket_lister.tsx rename to ui/src/features/socket_listener/socket_listener.tsx index 44f6c5a..c1db646 100644 --- a/ui/src/features/socket_lister/socket_lister.tsx +++ b/ui/src/features/socket_listener/socket_listener.tsx @@ -1,15 +1,15 @@ import * as React from "react"; -import { socketListerStore } from "./socket_lister_store"; +import { socketListenerStore } from "./socket_listener_store"; import { observer } from "mobx-react-lite"; import { CoreText, CoreTextType } from "../../core/ui/text/text"; -export interface ISocketListerProps { +export interface ISocketListenerProps { children?: JSX.Element; } -export const SocketLister = observer((props: ISocketListerProps) => { +export const SocketListener = observer((props: ISocketListenerProps) => { React.useEffect(() => { - socketListerStore.init(); + socketListenerStore.init(); }, []); return ( <> diff --git a/ui/src/features/socket_lister/socket_lister_store.ts b/ui/src/features/socket_listener/socket_listener_store.ts similarity index 86% rename from ui/src/features/socket_lister/socket_lister_store.ts rename to ui/src/features/socket_listener/socket_listener_store.ts index b1ba190..4566ea2 100644 --- a/ui/src/features/socket_lister/socket_lister_store.ts +++ b/ui/src/features/socket_listener/socket_listener_store.ts @@ -1,7 +1,7 @@ import { makeAutoObservable } from "mobx"; import { SocketRepository, socketRepository } from "../../core/repository/core_socket_repository"; -class SocketListerStore { +class SocketListenerStore { socketRepository: SocketRepository; socketHasDisconnect = false; @@ -28,4 +28,4 @@ class SocketListerStore { } } -export const socketListerStore = new SocketListerStore(socketRepository); +export const socketListenerStore = new SocketListenerStore(socketRepository); diff --git a/ui/src/features/topics/topic_view_model.ts b/ui/src/features/topics/topic_view_model.ts index e54d5da..e47c852 100644 --- a/ui/src/features/topics/topic_view_model.ts +++ b/ui/src/features/topics/topic_view_model.ts @@ -1,8 +1,20 @@ +import { IsNotEmpty, IsString } from "class-validator"; import { ValidationModel } from "../../core/model/validation_model"; export class TopicViewModel extends ValidationModel { + @IsNotEmpty() + @IsString() + name: string; + @IsNotEmpty() + @IsString() + type: string; + constructor(name: string, type: string) { + super(); + this.name = name; + this.type = type; + } static empty() { - return new TopicViewModel(); + return new TopicViewModel("", ""); } } export interface ITopicModel { diff --git a/ui/src/features/topics/topics_repository.ts b/ui/src/features/topics/topics_repository.ts index 04d247e..852926b 100644 --- a/ui/src/features/topics/topics_repository.ts +++ b/ui/src/features/topics/topics_repository.ts @@ -1,5 +1,6 @@ import { HttpMethod, HttpRepository } from "../../core/repository/core_http_repository"; export class TopicsHttpRepository extends HttpRepository { - getAllTopics = () => this._jsonRequest(HttpMethod.GET, "/topics"); + featureApi = "/topics"; + getAllTopics = () => this._jsonRequest(HttpMethod.GET, this.featureApi); } diff --git a/ui/src/features/topics/topics_screen.tsx b/ui/src/features/topics/topics_screen.tsx index 823ac7e..5f4d415 100644 --- a/ui/src/features/topics/topics_screen.tsx +++ b/ui/src/features/topics/topics_screen.tsx @@ -12,7 +12,7 @@ export const TopicsScreen = observer(() => { return ( <> {store.topics?.map((el) => ( -
+
{el.name}
{el.type}
diff --git a/ui/src/index.tsx b/ui/src/index.tsx index 01eff66..db91325 100644 --- a/ui/src/index.tsx +++ b/ui/src/index.tsx @@ -2,7 +2,7 @@ import "reflect-metadata"; import "antd/dist/antd.min.css"; import ReactDOM from "react-dom/client"; import { extensions } from "./core/extensions/extensions"; -import { SocketLister } from "./features/socket_lister/socket_lister"; +import { SocketListener } from "./features/socket_listener/socket_listener"; import { RouterProvider } from "react-router-dom"; import { router } from "./core/routers/routers"; import { configure } from "mobx"; @@ -17,8 +17,9 @@ const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement) root.render( <> - + - + ); + \ No newline at end of file