This commit is contained in:
IDONTSUDO 2024-05-13 20:43:11 +03:00
parent 4585d079f6
commit 5162612a77
49 changed files with 1108 additions and 732 deletions

View file

@ -2,13 +2,16 @@
"cSpell.words": [
"введите",
"Ведите",
"дерево",
"Количество",
"может",
"навык",
"Навыки",
"Новое",
"отрицательное",
"принимать",
"скила",
"создано",
"число",
"эпох",
"эпоха",

6
p.json Normal file
View file

@ -0,0 +1,6 @@
{
"dependency":{
"weights_path":"",
"object_name":""
}
}

View file

@ -1,4 +1,4 @@
import { BehaviorTreesPresentation } from "../../features/behavior_trees/behavior_trees";
import { BehaviorTreesPresentation } from "../../features/behavior_trees/behavior_trees_presentation";
import { DatasetsPresentation } from "../../features/datasets/datasets_presentation";
import { WeightsPresentation } from "../../features/weights/weights_presentation";
import { ProjectsPresentation } from "../../features/projects/projects_presentation";

View file

@ -0,0 +1,22 @@
import { CallbackStrategyWithIdQuery } from "../controllers/http_controller";
import { Result } from "../helpers/result";
import { ReadByIdDataBaseModelUseCase } from "../usecases/read_by_id_database_model_usecase";
import { CoreValidation } from "../validations/core_validation";
import { MongoIdValidation } from "../validations/mongo_id_validation";
export class ReadByIdDataBaseModelScenario<D> extends CallbackStrategyWithIdQuery {
idValidationExpression: CoreValidation = new MongoIdValidation();
databaseModel: D;
constructor(model) {
super();
this.databaseModel = model;
}
call = async (id: string): Promise<Result<Error, D>> => {
try {
return new ReadByIdDataBaseModelUseCase<D>(this.databaseModel).call(id);
} catch (error) {
return Result.error(error);
}
};
}

View file

@ -1,12 +0,0 @@
import { CoreHttpController } from "../../core/controllers/http_controller";
import { DatasetValidationModel } from "../datasets/models/dataset_validation_model";
import { GetBehaviorTreeSkillsUseCase } from "./get_bt_usecase";
export class BehaviorTreesPresentation extends CoreHttpController<DatasetValidationModel> {
constructor() {
super({
url: "behavior/trees",
validationModel: DatasetValidationModel,
});
super.get(new GetBehaviorTreeSkillsUseCase().call);
}
}

View file

@ -0,0 +1,20 @@
import { CrudController } from "../../core/controllers/crud_controller";
import { GetBehaviorTreeSkillsTemplatesUseCase } from "./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";
export class BehaviorTreesPresentation extends CrudController<BehaviorTreeValidationModel, typeof BehaviorTreeDBModel> {
constructor() {
super({
url: "behavior/trees",
validationModel: BehaviorTreeValidationModel,
databaseModel: BehaviorTreeDBModel,
});
this.subRoutes.push({ method: "GET", subUrl: "templates", fn: new GetBehaviorTreeSkillsTemplatesUseCase() });
this.subRoutes.push({
method: "GET",
subUrl: "by_id",
fn: new ReadByIdDataBaseModelScenario<BehaviorTreeValidationModel>(BehaviorTreeDBModel),
});
}
}

View file

@ -1,7 +1,8 @@
import { CallbackStrategyWithEmpty, ResponseBase } from "../../core/controllers/http_controller";
import { Result } from "../../core/helpers/result";
export class GetBehaviorTreeSkillsUseCase {
call = () => {
export class GetBehaviorTreeSkillsTemplatesUseCase extends CallbackStrategyWithEmpty {
call = async (): ResponseBase => {
return Result.ok({
skills: [
{
@ -25,8 +26,17 @@ export class GetBehaviorTreeSkillsUseCase {
name: "peConfigure",
format: "yaml",
type: "run",
param: ["object_name", "weights_file"],
result: ["Pose"],
param: [
{
type: "weights",
dependency: {},
},
{
type: "form",
dependency: {},
},
],
result: ["POSE"],
},
{
name: "peStop",
@ -82,72 +92,6 @@ export class GetBehaviorTreeSkillsUseCase {
topicCameraInfo: "/inner_rgbd_camera/camera_info",
},
},
{
SkillPackage: {
name: "Robossembler",
version: "1.0",
format: "1",
},
Module: {
name: "ObjectDetection",
description: "Object detection skill with YOLOv8",
},
Launch: {
executable: "detection_lifecycle.py",
},
ROS2: {
node_name: "lc_detection",
},
BTAction: [
{
name: "odConfigure",
format: "yaml",
args: ["SettingPath", "server_name", "server_timeout"],
result: ["boundBox"],
},
{
name: "odStop",
format: "yaml",
args: ["server_name", "server_timeout"],
result: [],
},
],
Interface: {
Input: [
{
name: "cameraLink",
type: "string",
group: "STD_USER",
},
{
name: "topicImage",
type: "Image",
group: "sensor_msgs.msg",
},
],
Output: [
{
name: "boundBox",
type: "BoundBox",
group: ".msg",
},
],
},
Settings: [
{
name: "publishDelay",
value: 0.5,
},
{
name: "server_timeout",
value: 1000,
},
{
name: "server_name",
value: "/object_detection/change_state",
},
],
},
],
});
};

View file

@ -0,0 +1,39 @@
import { Schema, model } from "mongoose";
import { IProjectModel, projectSchema } from "../../_projects/models/project_database_model";
export interface IBehaviorTreeModel {
name: string;
project?: IProjectModel;
unixTime?: number;
local_path?: string;
}
export const BehaviorTreeSchema = new Schema({
name: {
type: String,
},
local_path: {
type: String,
},
unixTime: {
type: Number,
default: Date.now(),
},
isFinished: {
type: Boolean,
default: false,
},
dependency: {
type: Schema.Types.Mixed,
},
project: {
type: Schema.Types.ObjectId,
ref: projectSchema,
autopopulate: true,
require: true,
},
}).plugin(require("mongoose-autopopulate"));
export const behaviorTreeSchema = "Behavior";
export const BehaviorTreeDBModel = model<IBehaviorTreeModel>(behaviorTreeSchema, BehaviorTreeSchema);

View file

@ -0,0 +1,7 @@
import { IsString } from "class-validator";
import { IBehaviorTreeModel } from "./behavior_tree_database_model";
export class BehaviorTreeValidationModel implements IBehaviorTreeModel {
@IsString()
public name: string;
}

View file

@ -1,4 +1,4 @@
import { IsNumber, IsString } from "class-validator";
import { IsNumber, IsOptional, IsString } from "class-validator";
import { IWeightModel } from "./weights_validation_model";
export class WeightValidationModel implements IWeightModel {
@ -10,4 +10,6 @@ export class WeightValidationModel implements IWeightModel {
public name: string;
public datasetId: string;
public project: string;
@IsOptional()
public isFinished:boolean;
}

6
ui/package-lock.json generated
View file

@ -48,6 +48,7 @@
"three-transform-controls": "^1.0.4",
"ts-pattern": "^5.1.1",
"typescript": "^5.4",
"typescript-mixin": "^1.0.3",
"urdf-loader": "^0.12.1",
"uuid": "^9.0.1",
"web-vitals": "^2.1.4",
@ -17191,6 +17192,11 @@
"node": ">=14.17"
}
},
"node_modules/typescript-mixin": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/typescript-mixin/-/typescript-mixin-1.0.3.tgz",
"integrity": "sha512-+GbEURqtLnR0WY+P1IFv9r3MK4u96Elci85QWfBZWktIxI89DVLWAAKy858org2tbom61URDQvL1QtU72knzkQ=="
},
"node_modules/unbox-primitive": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz",

View file

@ -43,6 +43,7 @@
"three-transform-controls": "^1.0.4",
"ts-pattern": "^5.1.1",
"typescript": "^5.4",
"typescript-mixin": "^1.0.3",
"urdf-loader": "^0.12.1",
"uuid": "^9.0.1",
"web-vitals": "^2.1.4",

View file

@ -3,10 +3,10 @@
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
<svg width="64px" height="64px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
<g id="SVGRepo_bgCarrier" strokeWidth="0"/>
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>
<g id="SVGRepo_tracerCarrier" strokeLinecap="round" strokeLinejoin="round"/>
<g id="SVGRepo_iconCarrier"> <path d="M12 7.75732L12 16.2426" stroke="#f46036" stroke-width="2" stroke-linecap="round"/> <path d="M7.75735 12L16.2426 12" stroke="#f46036" stroke-width="2" stroke-linecap="round"/> <rect x="3" y="3" width="18" height="18" rx="3" stroke="#f46036" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </g>
<g id="SVGRepo_iconCarrier"> <path d="M12 7.75732L12 16.2426" stroke="#f46036" strokeWidth="2" strokeLinecap="round"/> <path d="M7.75735 12L16.2426 12" stroke="#f46036" strokeWidth="2" strokeLinecap="round"/> <rect x="3" y="3" width="18" height="18" rx="3" stroke="#f46036" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/> </g>
</svg>

Before

Width:  |  Height:  |  Size: 769 B

After

Width:  |  Height:  |  Size: 759 B

Before After
Before After

View file

@ -1,7 +1,12 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
<svg width="64px" height="64px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>
<g id="SVGRepo_iconCarrier"> <g id="Interface / Check"> <path id="Vector" d="M6 12L10.2426 16.2426L18.727 7.75732" stroke="#f8f9fa" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> </g> </g>
</svg>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
<svg width="64px" height="12" "strokeWidth="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="SVGRepo_bgCarrier" stroke-width="0"/>strokeWidth
<g id="SVGRepo_tracerCarrier" strokeLinecap="round" strokeLinejoin="round"/>
<g id="SVGRepo_iconCarrier"> <g id="Interface / Check"> <path id="Vector" d="M6 12L10.2426 16.2426L18.727 7.75732" stroke="#f8f9fa" stroke-width="2" strokeLinecap="round" strokeLinejoin="round"/> </g> </g>
</svg>

Before

Width:  |  Height:  |  Size: 628 B

After

Width:  |  Height:  |  Size: 638 B

Before After
Before After

View file

@ -1,7 +1,12 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
<svg width="64px" height="64px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>
<g id="SVGRepo_iconCarrier"> <path d="M15.8351 11.6296L9.20467 5.1999C8.79094 4.79869 8 5.04189 8 5.5703L8 18.4297C8 18.9581 8.79094 19.2013 9.20467 18.8001L15.8351 12.3704C16.055 12.1573 16.0549 11.8427 15.8351 11.6296Z" fill="#013a63"/> </g>
</svg>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
<svg width="64px" height="6strokeWidth="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
<g id="SVGRepo_tracerCarrier" strokeLinecap="round" strokeLinejoin="round"/>
<g id="SVGRepo_iconCarrier"> <path d="M15.8351 11.6296L9.20467 5.1999C8.79094 4.79869 8 5.04189 8 5.5703L8 18.4297C8 18.9581 8.79094 19.2013 9.20467 18.8001L15.8351 12.3704C16.055 12.1573 16.0549 11.8427 15.8351 11.6296Z" fill="#013a63"/> </g>
</svg>

Before

Width:  |  Height:  |  Size: 664 B

After

Width:  |  Height:  |  Size: 661 B

Before After
Before After

View file

@ -3,11 +3,11 @@
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
<svg width="64px" height="64px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
<g id="SVGRepo_bgCarrier" strokeWidth="0"/>
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>
<g id="SVGRepo_tracerCarrier" strokeLinecap="round" strokeLinejoin="round"/>
<g id="SVGRepo_iconCarrier">
<rect width="24" height="24" fill="none"/> <path d="M7 17L16.8995 7.10051" stroke="#f55d3e" stroke-linecap="round" stroke-linejoin="round"/> <path d="M7 7.00001L16.8995 16.8995" stroke="#f46036" stroke-linecap="round" stroke-linejoin="round"/> </g>
<rect width="24" height="24" fill="none"/> <path d="M7 17L16.8995 7.10051" stroke="#f55d3e" strokeLinecap="round" strokeLinejoin="round"/> <path d="M7 7.00001L16.8995 16.8995" stroke="#f46036" strokeLinecap="round" strokeLinejoin="round"/> </g>
</svg>

Before

Width:  |  Height:  |  Size: 701 B

After

Width:  |  Height:  |  Size: 694 B

Before After
Before After

View file

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" data-name="Layer 1" viewBox="0 0 64 64" id="error"><circle cx="32" cy="32" r="28" fill="none" stroke="#010101" stroke-miterlimit="10" stroke-width="4"></circle><line x1="32" x2="32" y1="18" y2="38" fill="none" stroke="#010101" stroke-miterlimit="10" stroke-width="4"></line><line x1="32" x2="32" y1="42" y2="46" fill="none" stroke="#010101" stroke-miterlimit="10" stroke-width="4"></line></svg>
<svg xmlns="http://www.w3.org/2000/svg" data-name="Layer 1" viewBox="0 0 64 64" id="error"><circle cx="32" cy="32" r="28" fill="none" stroke="#010101" stroke-miterlimit="10" strokeWidth="4"></circle><line x1="32" x2="32" y1="18" y2="38" fill="none" stroke="#010101" stroke-miterlimit="10" strokeWidth="4"></line><line x1="32" x2="32" y1="42" y2="46" fill="none" stroke="#010101" stroke-miterlimit="10" strokeWidth="4"></line></svg>

Before

Width:  |  Height:  |  Size: 434 B

After

Width:  |  Height:  |  Size: 431 B

Before After
Before After

View file

@ -1,3 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="800px" height="800px" viewBox="0 0 24 24" fill="none">
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.7071 4.29289C12.0976 4.68342 12.0976 5.31658 11.7071 5.70711L6.41421 11H20C20.5523 11 21 11.4477 21 12C21 12.5523 20.5523 13 20 13H6.41421L11.7071 18.2929C12.0976 18.6834 12.0976 19.3166 11.7071 19.7071C11.3166 20.0976 10.6834 20.0976 10.2929 19.7071L3.29289 12.7071C3.10536 12.5196 3 12.2652 3 12C3 11.7348 3.10536 11.4804 3.29289 11.2929L10.2929 4.29289C10.6834 3.90237 11.3166 3.90237 11.7071 4.29289Z" fill="#000000"/>
<path fillRule="evenodd" clipRule="evenodd" d="M11.7071 4.29289C12.0976 4.68342 12.0976 5.31658 11.7071 5.70711L6.41421 11H20C20.5523 11 21 11.4477 21 12C21 12.5523 20.5523 13 20 13H6.41421L11.7071 18.2929C12.0976 18.6834 12.0976 19.3166 11.7071 19.7071C11.3166 20.0976 10.6834 20.0976 10.2929 19.7071L3.29289 12.7071C3.10536 12.5196 3 12.2652 3 12C3 11.7348 3.10536 11.4804 3.29289 11.2929L10.2929 4.29289C10.6834 3.90237 11.3166 3.90237 11.7071 4.29289Z" fill="#000000"/>
</svg>

Before

Width:  |  Height:  |  Size: 584 B

After

Width:  |  Height:  |  Size: 582 B

Before After
Before After

View file

@ -1,3 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="800px" height="800px" viewBox="0 0 24 24" fill="none">
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.7071 1.29289C14.0976 1.68342 14.0976 2.31658 13.7071 2.70711L12.4053 4.00896C17.1877 4.22089 21 8.16524 21 13C21 17.9706 16.9706 22 12 22C7.02944 22 3 17.9706 3 13C3 12.4477 3.44772 12 4 12C4.55228 12 5 12.4477 5 13C5 16.866 8.13401 20 12 20C15.866 20 19 16.866 19 13C19 9.2774 16.0942 6.23349 12.427 6.01281L13.7071 7.29289C14.0976 7.68342 14.0976 8.31658 13.7071 8.70711C13.3166 9.09763 12.6834 9.09763 12.2929 8.70711L9.29289 5.70711C9.10536 5.51957 9 5.26522 9 5C9 4.73478 9.10536 4.48043 9.29289 4.29289L12.2929 1.29289C12.6834 0.902369 13.3166 0.902369 13.7071 1.29289Z" fill="#0F1729"/>
<path fillRule="evenodd" clipRule="evenodd" d="M13.7071 1.29289C14.0976 1.68342 14.0976 2.31658 13.7071 2.70711L12.4053 4.00896C17.1877 4.22089 21 8.16524 21 13C21 17.9706 16.9706 22 12 22C7.02944 22 3 17.9706 3 13C3 12.4477 3.44772 12 4 12C4.55228 12 5 12.4477 5 13C5 16.866 8.13401 20 12 20C15.866 20 19 16.866 19 13C19 9.2774 16.0942 6.23349 12.427 6.01281L13.7071 7.29289C14.0976 7.68342 14.0976 8.31658 13.7071 8.70711C13.3166 9.09763 12.6834 9.09763 12.2929 8.70711L9.29289 5.70711C9.10536 5.51957 9 5.26522 9 5C9 4.73478 9.10536 4.48043 9.29289 4.29289L12.2929 1.29289C12.6834 0.902369 13.3166 0.902369 13.7071 1.29289Z" fill="#0F1729"/>
</svg>

Before

Width:  |  Height:  |  Size: 755 B

After

Width:  |  Height:  |  Size: 753 B

Before After
Before After

View file

@ -1,3 +1,5 @@
import { Result } from "../helper/result";
/* eslint-disable @typescript-eslint/no-this-alias */
export const ArrayExtensions = () => {
if ([].equals === undefined) {
@ -57,4 +59,15 @@ export const ArrayExtensions = () => {
return Array(quantity).fill(this).flat(1);
};
}
if ([].rFind === undefined) {
// 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);
};
}
};

View file

@ -1,3 +1,4 @@
import { Result } from "../helper/result";
import { ArrayExtensions } from "./array";
import { MapExtensions } from "./map";
import { NumberExtensions } from "./number";
@ -11,6 +12,7 @@ export type OptionalProperties<T> = {
[P in keyof T]?: T[P];
};
declare global {
interface Array<T> {
// @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.
@ -20,6 +22,10 @@ declare global {
isNotEmpty(): boolean;
hasIncludeElement(element: T): boolean;
repeat(quantity: number): Array<T>;
rFind<T>(
predicate: (value: T, index: number, obj: never[]) => boolean,
thisArg?: any
): Result<void, T>;
}
interface Number {
fromArray(): number[];

View file

@ -13,12 +13,20 @@ export interface ISkillPoseEstimation {
Settings: ISetting[];
xxx: IXxx;
}
export interface IWeightsDependency {
objectName: string;
weightsPath: string;
}
export interface IParam {
type: string;
dependency: IWeightsDependency;
}
export interface IBTAction {
name: string;
format: string;
type: string;
param: string[];
param: IParam[];
result: string[];
}
@ -84,7 +92,7 @@ export class BTAction implements IBTAction {
@IsString()
type: string;
@IsArray()
param: string[];
param: IParam[];
@IsArray()
result: string[];
}
@ -152,32 +160,74 @@ export class Skills {
@IsArray()
@Type(() => SkillModelPoseEstimation)
skills: SkillModelPoseEstimation[];
toSkillView(): ISkillView[] {
return this.skills.map((el) => {
toSkillView = (): ISkillView[] =>
this.skills.map((el) => {
return {
name: el.Module.name,
children: el.BTAction.map((act) => {
// return { name: `${el.Module.name} - ${act.name}` };
return { name: act.name, uuid: v4() };
}),
};
});
}
getSkillDo(name: string) {
return this.skills.reduce((acc, el) => {
getSkilsOut = (name: string) =>
this.skills
.reduce<string[][]>((acc, el) => {
if (el.BTAction.find((el) => el.name.isEqual(name))) {
acc = el.BTAction.filter((f) => f.name.isEqual(name)).map((z) => z.result);
}
return acc;
}, [])
.flat(1);
getSkillParams = (name: string) =>
this.skills
.reduce<IParam[][]>((acc, el) => {
if (el.BTAction.find((el) => el.name.isEqual(name))) {
acc = el.BTAction.filter((f) => f.name.isEqual(name)).map((z) => z.param);
}
return acc;
}, [])
.flat(1);
getSkillDo = (name: string) =>
this.skills.reduce((acc, el) => {
if (el.BTAction.find((el) => el.name.isEqual(name))) {
acc = el.Module.name;
}
return acc;
}, "error");
}
getSkillsNames() {
return this.skills
getSkillsNames = () =>
this.skills
.map((el) => {
return el.BTAction.map((act) => {
return { name: act.name };
});
})
.flat(1);
}
getForms = (skillLabel: string) =>
this.skills
.reduce<SkillModelPoseEstimation[]>((acc, el) => {
if (el.BTAction.find((el) => el.name.isEqual(skillLabel))) {
acc.push(el);
}
return acc;
}, [])
.map((el) => el.BTAction.map((act) => act.param.map((el) => el.type).flat(1)))
.flat(1)
.flat(1)
.filter((el) => el !== "");
getDependencyBySkillLabelAndType = <T>(skillLabel: string, skillType: string) =>
this.skills
.reduce<SkillModelPoseEstimation[]>((acc, el) => {
if (el.BTAction.find((el) => el.name.isEqual(skillLabel))) {
acc.push(el);
}
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;
}

View file

@ -48,6 +48,10 @@ export const router = createBrowserRouter([
path: BehaviorTreeBuilderPath,
element: <BehaviorTreeBuilderScreen />,
},
{
path: BehaviorTreeBuilderPath + idURL,
element: <BehaviorTreeBuilderScreen />,
},
{
path: StickObjectsMarkingScreenPath,
element: <StickObjectsMarkingScreen />,

View file

@ -5,6 +5,12 @@ import { HttpError } from "../repository/http_repository";
import { message } from "antd";
export type CoreError = HttpError | Error;
export interface Drawer {
name: string;
status: boolean;
}
interface IMessage {
successMessage?: string;
errorMessage?: string;
@ -60,3 +66,40 @@ export abstract class UiErrorState<T> extends UiLoader {
dispose() {}
errors: UiBaseError[] = [];
}
export abstract class DrawerState<E> extends UiErrorState<E> {
titleDrawer: string = "";
drawers: Drawer[];
constructor(drawerEnum: Object) {
super();
this.drawers = Object.entries(drawerEnum).map((k, v) => {
return {
name: k.at(1) ?? "",
status: false,
};
});
}
edtDrawer(drawerName: string, status: boolean): void {
this.titleDrawer = drawerName;
this.drawers = this.drawers.map((el) => {
if (el.name === drawerName) {
el.status = status;
}
return el;
});
}
}
export abstract class UiFormState<V, E> extends DrawerState<E> {
abstract viewModel: V;
updateForm(value: Partial<V>) {
//@ts-ignore
this.viewModel = Object.assign(this.viewModel, value);
}
}
export abstract class FormState<V, E> extends UiErrorState<E> {
abstract viewModel: V;
updateForm(value: Partial<V>) {
//@ts-ignore
this.viewModel = Object.assign(this.viewModel, value);
}
}

View file

@ -31,8 +31,8 @@ export function Icon(props: IIconsProps) {
>
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fill-rule="evenodd"
clip-rule="evenodd"
fillRule="evenodd"
clipRule="evenodd"
d="M6 12C9.31371 12 12 9.31371 12 6C12 2.68629 9.31371 0 6 0C2.68629 0 0 2.68629 0 6C0 9.31371 2.68629 12 6 12Z"
fill="#49454F"
/>
@ -45,16 +45,125 @@ const getIconSvg = (type: string): Result<undefined, React.JSX.Element> => {
switch (type) {
case "":
return Result.ok();
case "ArrowRightMD":
return Result.ok(
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M5 12H19M19 12L13 6M19 12L13 18"
stroke="white"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
);
case "Menu":
return Result.ok(
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M17 18C17 18.5523 17.4477 19 18 19C18.5523 19 19 18.5523 19 18C19 17.4477 18.5523 17 18 17C17.4477 17 17 17.4477 17 18Z"
stroke="white"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M11 18C11 18.5523 11.4477 19 12 19C12.5523 19 13 18.5523 13 18C13 17.4477 12.5523 17 12 17C11.4477 17 11 17.4477 11 18Z"
stroke="white"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M5 18C5 18.5523 5.44772 19 6 19C6.55228 19 7 18.5523 7 18C7 17.4477 6.55228 17 6 17C5.44772 17 5 17.4477 5 18Z"
stroke="white"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M17 12C17 12.5523 17.4477 13 18 13C18.5523 13 19 12.5523 19 12C19 11.4477 18.5523 11 18 11C17.4477 11 17 11.4477 17 12Z"
stroke="white"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M11 12C11 12.5523 11.4477 13 12 13C12.5523 13 13 12.5523 13 12C13 11.4477 12.5523 11 12 11C11.4477 11 11 11.4477 11 12Z"
stroke="white"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M5 12C5 12.5523 5.44772 13 6 13C6.55228 13 7 12.5523 7 12C7 11.4477 6.55228 11 6 11C5.44772 11 5 11.4477 5 12Z"
stroke="white"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M17 6C17 6.55228 17.4477 7 18 7C18.5523 7 19 6.55228 19 6C19 5.44772 18.5523 5 18 5C17.4477 5 17 5.44772 17 6Z"
stroke="white"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M11 6C11 6.55228 11.4477 7 12 7C12.5523 7 13 6.55228 13 6C13 5.44772 12.5523 5 12 5C11.4477 5 11 5.44772 11 6Z"
stroke="white"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M5 6C5 6.55228 5.44772 7 6 7C6.55228 7 7 6.55228 7 6C7 5.44772 6.55228 5 6 5C5.44772 5 5 5.44772 5 6Z"
stroke="white"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
);
case "Close":
return Result.ok(
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M21 21L12 12M12 12L3 3M12 12L21.0001 3M12 12L3 21.0001"
stroke="white"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
);
case "code":
return Result.ok(
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M13.5 10.5V2.5C13.5 2.22386 13.2761 2 13 2H1C0.734784 2 0.5 2.23478 0.5 2.5V10.5C0.5 10.7652 0.734784 11 1 11H13C13.2652 11 13.5 10.7652 13.5 10.5Z"
stroke="black"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path d="M6 11L5 13.5" stroke="black" strokeLinecap="round" strokeLinejoin="round" />
<path d="M8 11L9 13.5" stroke="black" strokeLinecap="round" strokeLinejoin="round" />
<path d="M4 13.5H10" stroke="black" strokeLinecap="round" strokeLinejoin="round" />
<path d="M5 5L3.5 6.5L5 8" stroke="black" strokeLinecap="round" strokeLinejoin="round" />
<path d="M9 5L10.5 6.5L9 8" stroke="black" strokeLinecap="round" strokeLinejoin="round" />
</svg>
);
case "Setting":
return Result.ok(
<svg width="13" height="13" viewBox="0 0 13 13" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_68_595)">
<g clipPath="url(#clip0_68_595)">
<path
d="M4.85647 2.08936L5.25576 1.05864C5.3231 0.884129 5.44158 0.734023 5.59566 0.627981C5.74976 0.52194 5.93228 0.464908 6.11933 0.464355H6.88076C7.06781 0.464908 7.25034 0.52194 7.40443 0.627981C7.55852 0.734023 7.67699 0.884129 7.74433 1.05864L8.14362 2.08936L9.49929 2.86936L10.595 2.70221C10.7775 2.67745 10.9632 2.70748 11.1286 2.7885C11.2939 2.86951 11.4314 2.99785 11.5236 3.15721L11.895 3.80721C11.9902 3.96911 12.0341 4.15606 12.0208 4.34339C12.0076 4.53072 11.9378 4.70963 11.8207 4.8565L11.1429 5.72007V7.28007L11.8393 8.14364C11.9564 8.29051 12.0261 8.46942 12.0394 8.65675C12.0527 8.84408 12.0088 9.03103 11.9136 9.19293L11.5422 9.84293C11.4499 10.0023 11.3124 10.1306 11.1471 10.2117C10.9818 10.2926 10.796 10.3227 10.6136 10.2979L9.51786 10.1308L8.16219 10.9108L7.7629 11.9415C7.69556 12.116 7.57709 12.2661 7.423 12.3722C7.26891 12.4782 7.08638 12.5352 6.89933 12.5358H6.11933C5.93228 12.5352 5.74976 12.4782 5.59566 12.3722C5.44158 12.2661 5.3231 12.116 5.25576 11.9415L4.85647 10.9108L3.50076 10.1308L2.40504 10.2979C2.2226 10.3227 2.0369 10.2926 1.87156 10.2117C1.70623 10.1306 1.56871 10.0023 1.47647 9.84293L1.10504 9.19293C1.00987 9.03103 0.966019 8.84408 0.979279 8.65675C0.992539 8.46942 1.06229 8.29051 1.17933 8.14364L1.85719 7.28007V5.72007L1.16076 4.8565C1.04372 4.70963 0.973968 4.53072 0.960708 4.34339C0.947448 4.15606 0.991295 3.96911 1.08647 3.80721L1.4579 3.15721C1.55014 2.99785 1.68766 2.86951 1.85299 2.7885C2.01833 2.70748 2.20403 2.67745 2.38647 2.70221L3.48219 2.86936L4.85647 2.08936ZM4.6429 6.50007C4.6429 6.86738 4.75182 7.22644 4.95589 7.53184C5.15996 7.83725 5.45 8.07528 5.78934 8.21585C6.1287 8.3564 6.50211 8.39319 6.86235 8.32153C7.2226 8.24987 7.55352 8.073 7.81325 7.81326C8.07297 7.55354 8.24984 7.22263 8.3215 6.86238C8.39316 6.50213 8.35639 6.12872 8.21582 5.78937C8.07526 5.45002 7.83723 5.15997 7.53182 4.95591C7.22641 4.75185 6.86735 4.64293 6.50004 4.64293C6.0075 4.64293 5.53513 4.83859 5.18685 5.18687C4.83857 5.53515 4.6429 6.00753 4.6429 6.50007Z"
stroke="#373737"
stroke-width="1.3"
stroke-linecap="round"
stroke-linejoin="round"
strokeWidth="1.3"
strokeLinecap="round"
strokeLinejoin="round"
/>
</g>
<defs>
@ -70,54 +179,54 @@ const getIconSvg = (type: string): Result<undefined, React.JSX.Element> => {
<path
d="M1.84615 1H4.38462C4.60903 1 4.82425 1.08915 4.98294 1.24783C5.14162 1.40652 5.23077 1.62174 5.23077 1.84615V9.88462C5.23077 10.4456 5.0079 10.9837 4.61119 11.3804C4.21448 11.7771 3.67642 12 3.11538 12C2.83759 12 2.56252 11.9453 2.30586 11.839C2.04921 11.7327 1.81601 11.5768 1.61958 11.3804C1.22287 10.9837 1 10.4456 1 9.88462V1.84615C1 1.62174 1.08915 1.40652 1.24783 1.24783C1.40652 1.08915 1.62174 1 1.84615 1Z"
stroke="#373737"
stroke-linecap="round"
stroke-linejoin="round"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M5.23079 4.80777L8.19233 1.82931C8.35086 1.67171 8.56532 1.58325 8.78887 1.58325C9.01241 1.58325 9.2269 1.67171 9.38539 1.82931L11.1708 3.62315C11.3284 3.78169 11.4168 3.99615 11.4168 4.21969C11.4168 4.44323 11.3284 4.65769 11.1708 4.81623L4.6131 11.3824"
stroke="#373737"
stroke-linecap="round"
stroke-linejoin="round"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M8.19231 7.76904H11.1538C11.3782 7.76904 11.5935 7.85819 11.7522 8.01687C11.9108 8.17556 12 8.39078 12 8.6152V11.1537C12 11.3781 11.9108 11.5933 11.7522 11.752C11.5935 11.9106 11.3782 11.9998 11.1538 11.9998H3.11539"
stroke="#373737"
stroke-linecap="round"
stroke-linejoin="round"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path d="M1 4.38477H5.23077" stroke="#373737" stroke-linecap="round" stroke-linejoin="round" />
<path d="M1 7.76904H5.23077" stroke="#373737" stroke-linecap="round" stroke-linejoin="round" />
<path d="M1 4.38477H5.23077" stroke="#373737" strokeLinecap="round" strokeLinejoin="round" />
<path d="M1 7.76904H5.23077" stroke="#373737" strokeLinecap="round" strokeLinejoin="round" />
</svg>
);
case "Datasets":
return Result.ok(
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.03125 7.5271L3.38477 10" stroke="#373737" stroke-linecap="round" stroke-linejoin="round" />
<path d="M11.6087 5L10.8444 7.5" stroke="#373737" stroke-linecap="round" stroke-linejoin="round" />
<path d="M6.75 6.75732L8 8" stroke="#373737" stroke-linecap="round" stroke-linejoin="round" />
<path d="M4.03125 7.5271L3.38477 10" stroke="#373737" strokeLinecap="round" strokeLinejoin="round" />
<path d="M11.6087 5L10.8444 7.5" stroke="#373737" strokeLinecap="round" strokeLinejoin="round" />
<path d="M6.75 6.75732L8 8" stroke="#373737" strokeLinecap="round" strokeLinejoin="round" />
<path
d="M13 5H11C10.4477 5 10 4.55228 10 4V2C10 1.44771 10.4477 1 11 1H13C13.5523 1 14 1.44771 14 2V4C14 4.55228 13.5523 5 13 5Z"
stroke="#373737"
stroke-linecap="round"
stroke-linejoin="round"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M11 11.5H9C8.44772 11.5 8 11.0523 8 10.5V8.5C8 7.94772 8.44772 7.5 9 7.5H11C11.5523 7.5 12 7.94771 12 8.5V10.5C12 11.0523 11.5523 11.5 11 11.5Z"
stroke="#373737"
stroke-linecap="round"
stroke-linejoin="round"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M5.75 7.5271H3.75C3.19772 7.5271 2.75 7.07938 2.75 6.5271V4.5271C2.75 3.97481 3.19772 3.5271 3.75 3.5271H5.75C6.30228 3.5271 6.75 3.97481 6.75 4.5271V6.5271C6.75 7.07938 6.30228 7.5271 5.75 7.5271Z"
stroke="#373737"
stroke-linecap="round"
stroke-linejoin="round"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M4 14H2C1.44771 14 1 13.5523 1 13V11C1 10.4477 1.44771 10 2 10H4C4.55228 10 5 10.4477 5 11V13C5 13.5523 4.55228 14 4 14Z"
stroke="#373737"
stroke-linecap="round"
stroke-linejoin="round"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
@ -127,20 +236,20 @@ const getIconSvg = (type: string): Result<undefined, React.JSX.Element> => {
<path
d="M6.6306 6.16821C6.51077 6.22024 6.38154 6.24708 6.25092 6.24708C6.12028 6.24708 5.99105 6.22024 5.87124 6.16821L1.2666 4.03554C1.19183 3.99783 1.12899 3.9401 1.08509 3.86878C1.04119 3.79747 1.01794 3.71537 1.01794 3.63162C1.01794 3.54787 1.04119 3.46577 1.08509 3.39446C1.12899 3.32315 1.19183 3.26542 1.2666 3.22771L5.87124 1.07888C5.99105 1.02685 6.12028 1 6.25092 1C6.38154 1 6.51077 1.02685 6.6306 1.07888L11.2352 3.21155C11.31 3.24927 11.3729 3.30699 11.4167 3.3783C11.4607 3.44962 11.4839 3.53172 11.4839 3.61546C11.4839 3.69921 11.4607 3.78131 11.4167 3.85263C11.3729 3.92394 11.31 3.98167 11.2352 4.01938L6.6306 6.16821Z"
stroke="#373737"
stroke-linecap="round"
stroke-linejoin="round"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M11.5018 6.53174L6.57403 8.80176C6.46878 8.84974 6.35445 8.87462 6.23878 8.87462C6.1231 8.87462 6.00877 8.84974 5.90353 8.80176L1 6.53174"
stroke="#373737"
stroke-linecap="round"
stroke-linejoin="round"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M11.5018 9.15723L6.57403 11.4272C6.46878 11.4752 6.35445 11.5001 6.23878 11.5001C6.1231 11.5001 6.00877 11.4752 5.90353 11.4272L1 9.15723"
stroke="#373737"
stroke-linecap="round"
stroke-linejoin="round"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
@ -151,33 +260,33 @@ const getIconSvg = (type: string): Result<undefined, React.JSX.Element> => {
<path
d="M4.93241 4.22177C3.33247 3.11335 1.84086 3.95947 0.590912 5.2625L3.35747 6.95474"
stroke="#373737"
stroke-linecap="round"
stroke-linejoin="round"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M8.24897 7.58936C9.34058 9.21388 8.50727 10.7284 7.22401 11.9976L5.5574 9.18849"
stroke="#373737"
stroke-linecap="round"
stroke-linejoin="round"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M3.35748 6.94646L5.56573 9.18868C7.33233 8.1141 9.34058 7.07337 10.2572 6.10879C12.2405 4.09502 11.0905 1.33667 11.0905 1.33667C11.0905 1.33667 8.37396 0.169022 6.3907 2.18279C5.44074 3.11352 4.40744 5.16114 3.35748 6.94646Z"
stroke="#373737"
stroke-linecap="round"
stroke-linejoin="round"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path d="M4.57413 4.82251L7.65734 7.95316" stroke="#373737" stroke-linecap="round" stroke-linejoin="round" />
<path d="M4.57413 4.82251L7.65734 7.95316" stroke="#373737" strokeLinecap="round" strokeLinejoin="round" />
<path
d="M8.92393 3.95965C9.154 3.95965 9.34058 3.77023 9.34058 3.53659C9.34058 3.30294 9.154 3.11353 8.92393 3.11353C8.69385 3.11353 8.50728 3.30294 8.50728 3.53659C8.50728 3.77023 8.69385 3.95965 8.92393 3.95965Z"
stroke="#373737"
stroke-linecap="round"
stroke-linejoin="round"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M3.24081 11.1008C2.7575 11.5746 0.590912 11.9977 0.590912 11.9977C0.590912 11.9977 1.00756 9.79774 1.47421 9.30699C1.58864 9.18236 1.72664 9.08235 1.87996 9.01295C2.03329 8.94358 2.19881 8.90629 2.36664 8.90327C2.53447 8.90027 2.70118 8.93162 2.85683 8.99545C3.01246 9.05925 3.15385 9.15427 3.27254 9.27484C3.39123 9.39533 3.4848 9.53891 3.54767 9.69697C3.61054 9.85494 3.64141 10.0242 3.63845 10.1947C3.63549 10.3651 3.59876 10.5331 3.53043 10.6888C3.46212 10.8445 3.36361 10.9846 3.24081 11.1008Z"
stroke="#373737"
stroke-linecap="round"
stroke-linejoin="round"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
@ -187,33 +296,33 @@ const getIconSvg = (type: string): Result<undefined, React.JSX.Element> => {
<path
d="M3.61364 1H1.87121C1.39005 1 1 1.39606 1 1.88462V3.65385C1 4.1424 1.39005 4.53846 1.87121 4.53846H3.61364C4.09479 4.53846 4.48485 4.1424 4.48485 3.65385V1.88462C4.48485 1.39606 4.09479 1 3.61364 1Z"
stroke="#373737"
stroke-linecap="round"
stroke-linejoin="round"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M3.61364 8.96143H1.87121C1.39005 8.96143 1 9.35749 1 9.84604V11.6153C1 12.1038 1.39005 12.4999 1.87121 12.4999H3.61364C4.09479 12.4999 4.48485 12.1038 4.48485 11.6153V9.84604C4.48485 9.35749 4.09479 8.96143 3.61364 8.96143Z"
stroke="#373737"
stroke-linecap="round"
stroke-linejoin="round"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M11.4545 8.96143H9.71212C9.23097 8.96143 8.84091 9.35749 8.84091 9.84604V11.6153C8.84091 12.1038 9.23097 12.4999 9.71212 12.4999H11.4545C11.9357 12.4999 12.3258 12.1038 12.3258 11.6153V9.84604C12.3258 9.35749 11.9357 8.96143 11.4545 8.96143Z"
stroke="#373737"
stroke-linecap="round"
stroke-linejoin="round"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path d="M2.74243 8.96165V4.53857" stroke="#373737" stroke-linecap="round" stroke-linejoin="round" />
<path d="M2.74243 8.96165V4.53857" stroke="#373737" strokeLinecap="round" strokeLinejoin="round" />
<path
d="M10.5833 8.96159V4.53852C10.5833 4.06929 10.3998 3.61928 10.073 3.28749C9.74618 2.95568 9.30299 2.76929 8.8409 2.76929H6.66287"
stroke="#373737"
stroke-linecap="round"
stroke-linejoin="round"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M8.4053 1L6.66287 2.76923L8.4053 4.53846"
stroke="#373737"
stroke-linecap="round"
stroke-linejoin="round"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
@ -223,36 +332,36 @@ const getIconSvg = (type: string): Result<undefined, React.JSX.Element> => {
<path
d="M10.6786 0.464355H11.6071C11.8534 0.464355 12.0896 0.562187 12.2637 0.736328C12.4378 0.910469 12.5357 1.14665 12.5357 1.39293V2.3215"
stroke="#373737"
stroke-linecap="round"
stroke-linejoin="round"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M0.464279 2.3215V1.39293C0.464279 1.14665 0.562111 0.910469 0.736251 0.736328C0.910393 0.562187 1.14657 0.464355 1.39285 0.464355H2.32142"
stroke="#373737"
stroke-linecap="round"
stroke-linejoin="round"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path d="M5.10713 0.464355H7.89285" stroke="#373737" stroke-linecap="round" stroke-linejoin="round" />
<path d="M12.5357 5.10718V7.89289" stroke="#373737" stroke-linecap="round" stroke-linejoin="round" />
<path d="M0.464279 5.10718V7.89289" stroke="#373737" stroke-linecap="round" stroke-linejoin="round" />
<path d="M5.10713 0.464355H7.89285" stroke="#373737" strokeLinecap="round" strokeLinejoin="round" />
<path d="M12.5357 5.10718V7.89289" stroke="#373737" strokeLinecap="round" strokeLinejoin="round" />
<path d="M0.464279 5.10718V7.89289" stroke="#373737" strokeLinecap="round" strokeLinejoin="round" />
<path
d="M10.6786 12.5359H11.6071C11.8534 12.5359 12.0896 12.438 12.2637 12.2639C12.4378 12.0898 12.5357 11.8535 12.5357 11.6073V10.6787"
stroke="#373737"
stroke-linecap="round"
stroke-linejoin="round"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M0.464279 10.6787V11.6073C0.464279 11.8535 0.562111 12.0898 0.736251 12.2639C0.910393 12.438 1.14657 12.5359 1.39285 12.5359H2.32142"
stroke="#373737"
stroke-linecap="round"
stroke-linejoin="round"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path d="M5.10713 12.5359H7.89285" stroke="#373737" stroke-linecap="round" stroke-linejoin="round" />
<path d="M5.10713 12.5359H7.89285" stroke="#373737" strokeLinecap="round" strokeLinejoin="round" />
<path
d="M8.35714 3.71436H4.64285C4.13002 3.71436 3.71428 4.1301 3.71428 4.64293V8.35721C3.71428 8.87004 4.13002 9.28578 4.64285 9.28578H8.35714C8.86997 9.28578 9.28571 8.87004 9.28571 8.35721V4.64293C9.28571 4.1301 8.86997 3.71436 8.35714 3.71436Z"
stroke="#373737"
stroke-linecap="round"
stroke-linejoin="round"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
@ -260,8 +369,8 @@ const getIconSvg = (type: string): Result<undefined, React.JSX.Element> => {
return Result.ok(
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fill-rule="evenodd"
clip-rule="evenodd"
fillRule="evenodd"
clipRule="evenodd"
d="M10 0C4.47 0 0 4.47 0 10C0 15.53 4.47 20 10 20C15.53 20 20 15.53 20 10C20 4.47 15.53 0 10 0ZM10 18C5.59 18 2 14.41 2 10C2 5.59 5.59 2 10 2C14.41 2 18 5.59 18 10C18 14.41 14.41 18 10 18ZM10 8.59L13.59 5L15 6.41L11.41 10L15 13.59L13.59 15L10 11.41L6.41 15L5 13.59L8.59 10L5 6.41L6.41 5L10 8.59Z"
fill="#49454F"
/>
@ -286,8 +395,8 @@ const getIconSvg = (type: string): Result<undefined, React.JSX.Element> => {
return Result.ok(
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fill-rule="evenodd"
clip-rule="evenodd"
fillRule="evenodd"
clipRule="evenodd"
d="M16.06 0.589883L17.41 1.93988C18.2 2.71988 18.2 3.98988 17.41 4.76988L4.18 17.9999H0V13.8199L10.4 3.40988L13.23 0.589883C14.01 -0.190117 15.28 -0.190117 16.06 0.589883ZM2 15.9999L3.41 16.0599L13.23 6.22988L11.82 4.81988L2 14.6399V15.9999Z"
fill="#31111D"
/>
@ -296,10 +405,10 @@ const getIconSvg = (type: string): Result<undefined, React.JSX.Element> => {
case "MenuFab":
return Result.ok(
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_54850_114)">
<g clipPath="url(#clip0_54850_114)">
<path
fill-rule="evenodd"
clip-rule="evenodd"
fillRule="evenodd"
clipRule="evenodd"
d="M11 16V14L29 14V16L11 16ZM11 21H29V19H11V21ZM11 26H29V24H11V26Z"
fill="#49454F"
/>
@ -314,10 +423,10 @@ const getIconSvg = (type: string): Result<undefined, React.JSX.Element> => {
case "Settings":
return Result.ok(
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_54841_7268)">
<g clipPath="url(#clip0_54841_7268)">
<path
fill-rule="evenodd"
clip-rule="evenodd"
fillRule="evenodd"
clipRule="evenodd"
d="M27.3102 33.03C27.2102 33.71 26.5902 34.25 25.8502 34.25H22.1502C21.4102 34.25 20.7902 33.71 20.7002 32.98L20.4302 31.09C20.1602 30.95 19.9002 30.8 19.6402 30.63L17.8402 31.35C17.1402 31.61 16.3702 31.32 16.0302 30.7L14.2002 27.53C13.8502 26.87 14.0002 26.09 14.5602 25.65L16.0902 24.46C16.0802 24.31 16.0702 24.16 16.0702 24C16.0702 23.85 16.0802 23.69 16.0902 23.54L14.5702 22.35C13.9802 21.9 13.8302 21.09 14.2002 20.47L16.0502 17.28C16.3902 16.66 17.1602 16.38 17.8402 16.65L19.6502 17.38C19.9102 17.21 20.1702 17.06 20.4302 16.92L20.7002 15.01C20.7902 14.31 21.4102 13.76 22.1402 13.76H25.8402C26.5802 13.76 27.2002 14.3 27.2902 15.03L27.5602 16.92C27.8302 17.06 28.0902 17.21 28.3502 17.38L30.1502 16.66C30.8602 16.4 31.6302 16.69 31.9702 17.31L33.8102 20.49C34.1702 21.15 34.0102 21.93 33.4502 22.37L31.9302 23.56C31.9402 23.71 31.9502 23.86 31.9502 24.02C31.9502 24.18 31.9402 24.33 31.9302 24.48L33.4502 25.67C34.0102 26.12 34.1702 26.9 33.8202 27.53L31.9602 30.75C31.6202 31.37 30.8502 31.65 30.1602 31.38L28.3602 30.66C28.1002 30.83 27.8402 30.98 27.5802 31.12L27.3102 33.03ZM22.6202 32.25H25.3802L25.7502 29.7L26.2802 29.48C26.7202 29.3 27.1602 29.04 27.6202 28.7L28.0702 28.36L30.4502 29.32L31.8302 26.92L29.8002 25.34L29.8702 24.78L29.8733 24.7531C29.9023 24.5027 29.9302 24.2607 29.9302 24C29.9302 23.73 29.9002 23.47 29.8702 23.22L29.8002 22.66L31.8302 21.08L30.4402 18.68L28.0502 19.64L27.6002 19.29C27.1802 18.97 26.7302 18.71 26.2702 18.52L25.7502 18.3L25.3802 15.75H22.6202L22.2502 18.3L21.7202 18.51C21.2802 18.7 20.8402 18.95 20.3802 19.3L19.9302 19.63L17.5502 18.68L16.1602 21.07L18.1902 22.65L18.1202 23.21C18.0902 23.47 18.0602 23.74 18.0602 24C18.0602 24.26 18.0802 24.53 18.1202 24.78L18.1902 25.34L16.1602 26.92L17.5402 29.32L19.9302 28.36L20.3802 28.71C20.8102 29.04 21.2402 29.29 21.7102 29.48L22.2402 29.7L22.6202 32.25ZM27.5002 24C27.5002 25.933 25.9332 27.5 24.0002 27.5C22.0672 27.5 20.5002 25.933 20.5002 24C20.5002 22.067 22.0672 20.5 24.0002 20.5C25.9332 20.5 27.5002 22.067 27.5002 24Z"
fill="#49454F"
/>

View file

@ -11,6 +11,7 @@ import { SimulationScreenPath } from "../../../features/simulations/simulations_
import { EstimateScreenPath } from "../../../features/estimate/estimate_screen";
import { BehaviorTreeBuilderPath } from "../../../features/behavior_tree_builder/presentation/behavior_tree_builder_screen";
import { SkillPath as SkillScreenPath } from "../../../features/skils/skills_screen";
import { UiBaseError } from "../../model/ui_base_error";
export interface IBlockProps {
name: string;
isActive: boolean;
@ -47,7 +48,9 @@ export interface IMainPageProps {
page: string;
bodyChildren?: JSX.Element;
panelChildren?: JSX.Element;
panelStyle?: React.CSSProperties;
isLoading?: boolean;
error?: UiBaseError[];
}
export const MainPage = (props: IMainPageProps) => {
const blocksNames = [
@ -107,8 +110,8 @@ export const MainPage = (props: IMainPageProps) => {
</div>
</div>
<div>
{blocks.map((el) => (
<Block isActive={el.isActive} name={el.name} path={el.path} icon={el.icon} />
{blocks.map((el, index) => (
<Block isActive={el.isActive} name={el.name} path={el.path} icon={el.icon} key={index} />
))}
</div>
<div style={{ paddingBottom: 10 }}>
@ -121,10 +124,25 @@ export const MainPage = (props: IMainPageProps) => {
</div>
) : (
<>
<div style={{ width: 241, height: window.innerHeight, backgroundColor: "#F7F2FA", borderRadius: 16 }}>
{props.panelChildren}{" "}
</div>
{props.bodyChildren}
{props.error?.isNotEmpty() ? (
<>
{props.error.map((el) => (
<div>{el.text}</div>
))}
</>
) : (
<>
<div
style={Object.assign(
{ width: 241, height: window.innerHeight, backgroundColor: "#F7F2FA", borderRadius: 16 },
props.panelStyle
)}
>
{props.panelChildren}
</div>
{props.bodyChildren}
</>
)}
</>
)}
</div>

View file

@ -1,9 +1,18 @@
import { Result } from "../../../core/helper/result";
import { Skills } from "../../../core/model/skill_model";
import { HttpError, HttpMethod, HttpRepository } from "../../../core/repository/http_repository";
import { BehaviorTreeViewModel } from "../model/behavior_tree_view_model";
import { BtTreeModel } from "../model/bt_tree_model";
export class BehaviorTreeBuilderRepository extends HttpRepository {
getBtSkills = async ():Promise<Result<HttpError, Skills>> => {
return await this._jsonToClassInstanceRequest<Skills>(HttpMethod.GET, "/behavior/trees", Skills) as unknown as Promise<Result<HttpError, Skills>>;
export class BehaviorTreeBuilderHttpRepository extends HttpRepository {
getAllBtInstances = async () => this._jsonRequest<BtTreeModel[]>(HttpMethod.GET, "/behavior/trees");
getBtSkills = async (): Promise<Result<HttpError, Skills>> => {
return (await this._jsonToClassInstanceRequest<Skills>(
HttpMethod.GET,
"/behavior/trees/templates",
Skills
)) as unknown as Promise<Result<HttpError, Skills>>;
};
saveNewBt = async (model: BehaviorTreeViewModel) => this._jsonRequest(HttpMethod.POST, "/behavior/trees", model);
getBtById = async (id: string) => this._jsonRequest<BtTreeModel>(HttpMethod.GET, `/behavior/trees/by_id?id=${id}`);
}

View file

@ -0,0 +1,14 @@
import { Result } from "../../../core/helper/result";
export class BehaviorTreeViewModel {
constructor(public name: string) {}
static empty() {
return new BehaviorTreeViewModel("");
}
valid(): Result<string, BehaviorTreeViewModel> {
if (this.name.isEmpty()) {
return Result.error("name is empty");
}
return Result.ok(this);
}
}

View file

@ -0,0 +1,6 @@
export interface BtTreeModel {
_id: string;
name: string;
unixTime: number;
__v: number;
}

View file

@ -2,63 +2,178 @@ import * as React from "react";
import { useRete } from "rete-react-plugin";
import { createEditor } from "./ui/editor/editor";
import { SkillTree } from "./ui/skill_tree/skill_tree";
import { BehaviorTreeBuilderStore } from "./behavior_tree_builder_store";
import { BehaviorTreeBuilderStore, DrawerState, StoreUIType } from "./behavior_tree_builder_store";
import { MainPage } from "../../../core/ui/pages/main_page";
import { CoreButton } from "../../../core/ui/button/button";
import { observer } from "mobx-react-lite";
import { match } from "ts-pattern";
import { Icon } from "../../../core/ui/icons/icons";
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";
export interface DOMReact {
x: number;
y: number;
width: number;
height: number;
top: number;
right: number;
bottom: number;
left: number;
}
export const behaviorTreeBuilderStore = new BehaviorTreeBuilderStore();
export const BehaviorTreeBuilderPath = "/behavior/tree/";
export const BehaviorTreeBuilderScreen = observer(() => {
const navigate = useNavigate();
const { id } = useParams();
const store = behaviorTreeBuilderStore;
// @ts-expect-error
const [ref] = useRete<HTMLDivElement>(createEditor);
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();
if (ref.current)
store.dragZoneSetOffset(
// @ts-expect-error
ref.current.offsetTop,
// @ts-expect-error
ref.current.offsetLeft,
// @ts-expect-error
ref.current.clientWidth,
// @ts-expect-error
ref.current.clientHeight
);
store.init(navigate);
store.initParams(id);
return () => {
store.dispose();
};
}, [store, ref]);
}, [store, ref, id, navigate]);
return (
<MainPage
page={"Навыки"}
page={"Поведение"}
error={store.errors}
panelStyle={{ padding: 20 }}
panelChildren={
<>
{store.skills ? <SkillTree skills={store.skillTree} dragEnd={store.dragEnd} /> : null}
<div style={{ width: 100, height: 40 }}>
<CoreButton onClick={() => store.saveBt()} text="SAVE BT" />
</div>
{match(store.type)
.with(StoreUIType.SelectBehaviorTree, () => (
<>
<div
style={{
width: "100%",
height: "100%",
backgroundColor: "rgb(255, 217, 228)",
padding: 10,
textAlign: "center",
}}
>
{store.btTreeModels?.map((el) => (
<div style={{ display: "flex", justifyContent: "center", height: 30, alignItems: "center" }}>
<CoreText text={el.name} type={CoreTextType.medium} />
<div style={{ width: 10 }} />
<Icon
type={"code"}
style={{ height: "100%", placeContent: "center" }}
onClick={() => BehaviorTreeBuilderPath + navigate(el._id)}
/>
</div>
))}
<Icon type="PlusCircle" onClick={() => store.edtDrawer(DrawerState.newBehaviorTree, true)} />
</div>
</>
))
.with(StoreUIType.ViewBehaviorTree, () => (
<div
style={{ height: "100%", display: "flex", flexDirection: "column", justifyContent: "space-between" }}
>
<div>
<div style={{ height: 58, alignContent: "center" }}>
<CoreText text="Навыки" type={CoreTextType.medium} />
</div>
{store.skillTemplates ? <SkillTree skills={store.skillTree} dragEnd={store.dragEnd} /> : null}
</div>
<div style={{ width: 100, height: 40 }}>
<CoreButton onClick={() => store.saveBt()} text="SAVE BT" />
</div>
</div>
))
.otherwise(() => (
<></>
))}
</>
}
bodyChildren={
<>
<div style={{ display: "flex", width: "100%" }}>
<div
ref={ref}
style={{
width: "100%",
height: window.innerHeight,
background: "white",
}}
></div>
</div>
{match(store.type)
.with(StoreUIType.SelectBehaviorTree, () => <></>)
.with(StoreUIType.ViewBehaviorTree, () => (
<div style={{ display: "flex", width: "100%" }}>
<div
ref={ref}
style={{
width: "100%",
height: window.innerHeight,
background: "white",
}}
></div>
</div>
))
.otherwise(() => (
<></>
))}
<Drawer
title={store.titleDrawer}
destroyOnClose={true}
onClose={() => store.edtDrawer(DrawerState.newBehaviorTree, false)}
open={store.drawers.find((el) => el.name === DrawerState.newBehaviorTree)?.status}
>
<div style={{ display: "flex", flexDirection: "column", justifyContent: "space-between", height: "100%" }}>
<div>
<CoreInput label="Имя дерева" onChange={(text) => store.updateForm({ name: text })} />
</div>
<div style={{ display: "flex" }}>
<CoreButton text="Сохранить" filled={true} onClick={() => store.createNewBt()} />
<div style={{ width: 10 }} />
<CoreButton text="Отмена" onClick={() => store.edtDrawer(DrawerState.newBehaviorTree, false)} />
</div>
</div>
</Drawer>
<Drawer
title={store.titleDrawer}
destroyOnClose={true}
onClose={() => store.edtDrawer(DrawerState.editThreadBehaviorTree, false)}
open={store.drawers.find((el) => el.name === DrawerState.editThreadBehaviorTree)?.status}
>
<div style={{ display: "flex", flexDirection: "column", justifyContent: "space-between", height: "100%" }}>
<div>
{store.skillTemplates?.getForms(store.selected ?? "").map((formType) =>
forms
.rFind<IForms>((form) => form.name.isEqual(formType))
.fold(
(s) => (
<div>
{/* {s.component(store.skillTemplates?.getDependencyBySkillLabelAndType(store.selected ?? "", s.name))} */}
forms.at(0)?.component({})
</div>
),
() => <div>Error: Unknown form type {formType}</div>
)
)}
</div>
<div style={{ display: "flex" }}>
<CoreButton text="Сохранить" filled={true} onClick={() => store.createNewBt()} />
<div style={{ width: 10 }} />
<CoreButton text="Отмена" onClick={() => store.edtDrawer(DrawerState.newBehaviorTree, false)} />
</div>
</div>
</Drawer>
</>
}
/>

View file

@ -1,15 +1,20 @@
import makeAutoObservable from "mobx-store-inheritance";
import xmlFormat from "xml-formatter";
import { HttpError } from "../../../core/repository/http_repository";
import { UiErrorState } from "../../../core/store/base_store";
import { CoreError, UiFormState } from "../../../core/store/base_store";
import { BtBuilderModel, BtNodeView, ReteObserver } from "../model/editor_view";
import { NodeEditor } from "rete";
import { AreaExtra, Schemes } from "./ui/editor/editor";
import { AreaPlugin } from "rete-area-plugin";
import { NodeBehaviorTree } from "../model/node_behavior_tree";
import { BehaviorTreeBuilderRepository } from "../data/behavior_tree_builder_repository";
import { BehaviorTreeBuilderHttpRepository } from "../data/behavior_tree_builder_repository";
import { 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";
interface I2DArea {
x: number;
@ -17,28 +22,34 @@ interface I2DArea {
w: number;
h: number;
}
export class BehaviorTreeBuilderStore extends UiErrorState<HttpError> {
saveBt(): void {
this.reteNode?.emit("200");
}
validateBt() {}
skills?: Skills;
export enum DrawerState {
newBehaviorTree = "Новое дерево поведения",
editThreadBehaviorTree = "Редактирование",
}
export enum StoreUIType {
SelectBehaviorTree,
ViewBehaviorTree,
}
export enum SystemPrimitive {
Sequence = "Sequence",
Fallback = "Fallback",
}
export class BehaviorTreeBuilderStore extends UiFormState<BehaviorTreeViewModel, CoreError> {
type: StoreUIType;
viewModel: BehaviorTreeViewModel = BehaviorTreeViewModel.empty();
skillTemplates?: Skills;
area?: I2DArea;
btNodeView: BtNodeView = new BtNodeView();
reteNode?: ReteObserver;
behaviorTreeBuilderRepository = new BehaviorTreeBuilderRepository();
btTreeModels?: BtTreeModel[];
behaviorTreeBuilderRepository = new BehaviorTreeBuilderHttpRepository();
canRun = true;
nodes: NodeBehaviorTree[] = [
{
label: "Sequence",
id: "1",
outputs: ["a"],
inputs: ["a"],
position: { x: -488.1303402823156, y: -227.90861570911895 },
},
];
shiftIsPressed = false;
selected?:string;
deleteIsPressed = false;
nodes: NodeBehaviorTree[] = [];
navigate?: NavigateFunction;
skillTree: ISkillView = {
name: "",
children: [
@ -56,17 +67,19 @@ export class BehaviorTreeBuilderStore extends UiErrorState<HttpError> {
],
};
constructor() {
super();
super(DrawerState);
makeAutoObservable(this);
this.type = StoreUIType.SelectBehaviorTree;
}
setBt(value: string): void {
JSON.parse(value) as BtNodeView[];
}
bt = async (editor: NodeEditor<Schemes>, area: AreaPlugin<Schemes, AreaExtra>) => {
(await BtBuilderModel.fromReteScene(editor, area, this.skills)).fold(
(await BtBuilderModel.fromReteScene(editor, area, this.skillTemplates)).fold(
(s) => {
console.log(xmlFormat(`<?xml version="1.0" encoding="UTF-8"?>
console.log(
xmlFormat(`<?xml version="1.0" encoding="UTF-8"?>
<root main_tree_to_execute="Main">
<BehaviorTree ID="Main">
${s}
@ -81,18 +94,20 @@ export class BehaviorTreeBuilderStore extends UiErrorState<HttpError> {
</Action>
</TreeNodesModel>
</root>`));
</root>`)
);
},
(_) => _
);
};
errorHandingStrategy: (error: HttpError) => void;
errorHandingStrategy: (error: CoreError) => void;
dragEnd = (e: EventTarget) => {
if (this.canRun) {
//@ts-expect-error
this.drawSkillCheck(e.x as number, e.y as number, e.target.innerText as string);
this.drawSkillCheck(e.clientX as number, e.clientY as number, e.target.innerText as string);
this.canRun = false;
setTimeout(() => {
this.canRun = true;
@ -101,7 +116,6 @@ export class BehaviorTreeBuilderStore extends UiErrorState<HttpError> {
};
drawSkillCheck(x: number, y: number, name: string) {
const drawPoint = { x: x, y: y, w: 1, h: 1 };
if (
drawPoint.x < this.area!.x + this.area!.w &&
drawPoint.x + drawPoint.w > this.area!.x &&
@ -116,10 +130,10 @@ export class BehaviorTreeBuilderStore extends UiErrorState<HttpError> {
}
}
async init(): Promise<any> {
async init(navigate: NavigateFunction): Promise<any> {
(await this.behaviorTreeBuilderRepository.getBtSkills()).fold(
(model) => {
this.skills = model;
this.skillTemplates = model;
this.skillTree.children = this.skillTree.children?.map((el) => {
if (el.name === "Actions") {
el.children = model.toSkillView();
@ -129,7 +143,39 @@ export class BehaviorTreeBuilderStore extends UiErrorState<HttpError> {
},
(e) => console.log(e)
);
await this.mapOk("btTreeModels", this.behaviorTreeBuilderRepository.getAllBtInstances());
this.navigate = navigate;
document.addEventListener("keydown", (event) => {
const keyCode = event.keyCode || event.which;
if (keyCode === 16) {
this.shiftIsPressed = true;
}
if (keyCode === 8) {
this.deleteIsPressed = true;
}
});
document.addEventListener("keyup", (event) => {
const keyCode = event.keyCode || event.which;
if (keyCode === 16) {
this.shiftIsPressed = false;
}
if (keyCode === 8) {
this.deleteIsPressed = false;
}
});
}
initParams = async (id?: string) => {
if (id) {
this.type = StoreUIType.ViewBehaviorTree;
(await this.behaviorTreeBuilderRepository.getBtById(id ?? "")).fold(
() => {},
() => this.errors.push(new UiBaseError(`не найдено дерево с id:${id}`))
);
} else {
this.type = StoreUIType.SelectBehaviorTree;
}
};
dragZoneSetOffset(offsetTop: number, offsetWidth: number, width: number, height: number) {
this.area = {
x: offsetTop,
@ -138,5 +184,82 @@ export class BehaviorTreeBuilderStore extends UiErrorState<HttpError> {
h: height,
};
}
dispose() {}
dispose() {
// TODO(IDONTSUDO): DELETE DOCUMENT LISTENERS
}
saveBt(): void {
this.reteNode?.emit("");
}
validateBt() {}
createNewBt = async () => {
this.viewModel.valid().fold(
async (model) => {
await this.messageHttp(this.behaviorTreeBuilderRepository.saveNewBt(model), {
successMessage: "Новое дерево создано",
});
this.mapOk("btTreeModels", this.behaviorTreeBuilderRepository.getAllBtInstances());
},
async (error) => message.error(error)
);
};
setSelected = (label: string, selected: boolean) => {
if (this.shiftIsPressed) {
this.edtDrawer(DrawerState.editThreadBehaviorTree, selected);
this.selected = label
}
};
getBodyNode(label: string): React.ReactNode | null {
if (Object.keys(SystemPrimitive).includes(label)) {
return null;
}
return (
<div style={{ width: "100%", display: "flex", flexDirection: "column", alignItems: "flex-start" }}>
{this.skillTemplates?.getSkillParams(label).map((el) => (
<div style={{ display: "flex", flexDirection: "row", width: "100%", marginBottom: 5 }}>
<div style={{ marginRight: 8 }}>IN</div>
<div style={{ display: "flex", width: "100%", backgroundColor: "rgba(255, 255, 255, 0.33)" }}>
{el.type}
</div>
</div>
))}
{this.skillTemplates?.getSkilsOut(label).map((el) => (
<div style={{ display: "flex", flexDirection: "row", width: "100%", marginBottom: 5 }}>
<div style={{ marginRight: 8 }}>OUT</div>
<div style={{ display: "flex", width: "100%", backgroundColor: "rgba(255, 255, 255, 0.33)" }}>{el}</div>
</div>
))}
</div>
);
}
getInputs(name: string) {
if (Object.keys(SystemPrimitive).includes(name)) {
return {
input: "a",
output: "a",
};
}
return {
input: "a",
output: null,
};
}
getStylesByLabelNode(label: string): React.CSSProperties | undefined {
if (label.isEqual(SystemPrimitive.Fallback)) {
return {
border: "1px solid #003E61",
borderRadius: 33,
background: "#BDE8AE",
};
}
if (label.isEqual(SystemPrimitive.Sequence)) {
return {
border: "1px solid #003E61",
background: "rgba(189, 232, 174, 1)",
};
}
return {
border: "1px solid rgba(0, 62, 97, 1)",
background: "rgba(153, 195, 234, 1)",
};
}
}

View file

@ -9,11 +9,10 @@
}
.background {
background-color: #ffffff;
background: white;
/* background: url(https://sport.mail.ru/img/_main/subnav.png);
opacity: 1;
background-image: linear-gradient(#f1f1f1 3.2px, transparent 3.2px),
linear-gradient(90deg, #f1f1f1 3.2px, transparent 3.2px), linear-gradient(#f1f1f1 1.6px, transparent 1.6px),
linear-gradient(90deg, #f1f1f1 1.6px, #ffffff 1.6px);
background-image:linear-gradient(black 3.2px, transparent 3.2px), linear-gradient(90deg, black 3.2px, transparent 3.2px), linear-gradient(black 1.6px, transparent 1.6px), linear-gradient(90deg, black 1.6px, #101010ed 1.6px);
background-size: 80px 80px, 80px 80px, 16px 16px, 16px 16px;
background-position: -3.2px -3.2px, -3.2px -3.2px, -1.6px -1.6px, -1.6px -1.6px;
background-position: -3.2px -3.2px, -3.2px -3.2px, -1.6px -1.6px, -1.6px -1.6px; */
}

View file

@ -1,11 +1,10 @@
import { BaseSchemes } from "rete";
import { AreaPlugin } from "rete-area-plugin";
import './background.css'
export function addCustomBackground<S extends BaseSchemes, K>(area: AreaPlugin<S, K>) {
const background = document.createElement("div");
background.classList.add("background");
background.classList.add("fill-area");
area.area.content.add(background);
}

View file

@ -14,7 +14,7 @@ const Svg = styled.svg`
const Path = styled.path<{ styles?: (props: any) => any }>`
fill: none;
stroke-width: 3px;
strokeWidth: 3px;
stroke: black;
pointer-events: auto;
${(props) => props.styles && props.styles(props)}

View file

@ -1,164 +0,0 @@
import * as React from "react";
import { ClassicScheme, RenderEmit, Presets } from "rete-react-plugin";
import styled, { css } from "styled-components";
import { $nodewidth, $socketmargin, $socketsize } from "./vars";
const { RefSocket, RefControl } = Presets.classic;
type NodeExtraData = { width?: number; height?: number };
export const NodeStyles = styled.div<NodeExtraData & { selected: boolean; styles?: (props: any) => any }>`
background: black;
border: 2px solid grey;
border-radius: 10px;
cursor: pointer;
box-sizing: border-box;
width: ${(props) => (Number.isFinite(props.width) ? `${props.width}px` : `${$nodewidth}px`)};
height: ${(props) => (Number.isFinite(props.height) ? `${props.height}px` : "auto")};
padding-bottom: 6px;
position: relative;
user-select: none;
&:hover {
background: #333;
}
${(props) =>
props.selected &&
css`
border-color: red;
`}
.title {
color: white;
font-family: sans-serif;
font-size: 18px;
padding: 8px;
}
.output {
text-align: right;
}
.input {
text-align: left;
}
.output-socket {
text-align: right;
margin-right: -1px;
display: inline-block;
}
.input-socket {
text-align: left;
margin-left: -1px;
display: inline-block;
}
.input-title,
.output-title {
vertical-align: middle;
color: white;
display: inline-block;
font-family: sans-serif;
font-size: 14px;
margin: ${$socketmargin}px;
line-height: ${$socketsize}px;
}
.input-control {
z-index: 1;
width: calc(100% - ${$socketsize + 2 * $socketmargin}px);
vertical-align: middle;
display: inline-block;
}
.control {
display: block;
padding: ${$socketmargin}px ${$socketsize / 2 + $socketmargin}px;
}
${(props) => props.styles && props.styles(props)}
`;
function sortByIndex<T extends [string, undefined | { index?: number }][]>(entries: T) {
entries.sort((a, b) => {
const ai = a[1]?.index || 0;
const bi = b[1]?.index || 0;
return ai - bi;
});
}
type Props<S extends ClassicScheme> = {
data: S["Node"] & NodeExtraData;
styles?: () => any;
emit: RenderEmit<S>;
};
export type NodeComponent<Scheme extends ClassicScheme> = (props: Props<Scheme>) => JSX.Element;
export function CustomNode<Scheme extends ClassicScheme>(props: Props<Scheme>) {
const inputs = Object.entries(props.data.inputs);
const outputs = Object.entries(props.data.outputs);
const controls = Object.entries(props.data.controls);
const selected = props.data.selected || false;
const { id, label, width, height } = props.data;
console.log(label)
sortByIndex(inputs);
sortByIndex(outputs);
sortByIndex(controls);
return (
<NodeStyles selected={selected} width={width} height={height} styles={props.styles} data-testid="node">
<div
onPointerDown={(e) => {
e.stopPropagation();
console.log(">>>");
}}
className="title"
data-testid="title"
>
{label}
</div>
{outputs.map(
([key, output]) =>
output && (
<div className="output" key={key} data-testid={`output-${key}`}>
<div style={{ color: "white" }}>BODY</div>
<div className="output-title" data-testid="output-title">
{output?.label}
</div>
<RefSocket
name="output-socket"
side="output"
emit={props.emit}
socketKey={key}
nodeId={id}
payload={output.socket}
/>
</div>
)
)}
{controls.map(([key, control]) => {
return control ? <RefControl key={key} name="control" emit={props.emit} payload={control} /> : null;
})}
{inputs.map(
([key, input]) =>
input && (
<div className="input" key={key} data-testid={`input-${key}`}>
<RefSocket
name="input-socket"
emit={props.emit}
side="input"
socketKey={key}
nodeId={id}
payload={input.socket}
/>
{input && (!input.control || !input.showControl) && (
<div className="input-title" data-testid="input-title">
{input?.label}
</div>
)}
{input?.control && input?.showControl && (
<span className="input-control">
<RefControl key={key} name="input-control" emit={props.emit} payload={input.control} />
</span>
)}
</div>
)
)}
</NodeStyles>
);
}

View file

@ -5,11 +5,11 @@ import styled from "styled-components";
const Styles = styled.div`
display: inline-block;
cursor: pointer;
border: 1px solid black;
width: 20px;
height: 20px;
border: 1px solid rgba(50, 50, 50, 1);
width: 10px;
height: 10px;
vertical-align: middle;
background: black;
background: rgba(50, 50, 50, 1);
z-index: 2;
box-sizing: border-box;
&:hover {

View file

@ -3,12 +3,12 @@ import { NodeEditor, GetSchemes, ClassicPreset } from "rete";
import { AreaPlugin, AreaExtensions } from "rete-area-plugin";
import { ConnectionPlugin, Presets as ConnectionPresets } from "rete-connection-plugin";
import { ReactPlugin, Presets, ReactArea2D } from "rete-react-plugin";
import { CustomSocket } from "./custom_socket";
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";
export type Schemes = GetSchemes<ClassicPreset.Node, ClassicPreset.Connection<ClassicPreset.Node, ClassicPreset.Node>>;
export type AreaExtra = ReactArea2D<Schemes>;
@ -21,13 +21,11 @@ export async function createEditor(container: HTMLElement) {
behaviorTreeBuilderStore.btNodeView.on(async (event) => {
setTimeout(async () => {
console.log(event)
const node = new ClassicPreset.Node(event.name);
const { x, y } = areaContainer.area.pointer;
node.addOutput("a", new ClassicPreset.Output(socket));
node.addInput("a", new ClassicPreset.Input(socket));
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));
await editor.addNode(node);
await areaContainer.translate(node.id, { x, y });
}, 100);
@ -39,13 +37,13 @@ export async function createEditor(container: HTMLElement) {
const editor = new NodeEditor<Schemes>();
const areaContainer = new AreaPlugin<Schemes, AreaExtra>(container);
const connection = new ConnectionPlugin<Schemes, AreaExtra>();
const render = new ReactPlugin<Schemes, AreaExtra>({ createRoot });
AreaExtensions.selectableNodes(areaContainer, AreaExtensions.selector(), {
accumulating: AreaExtensions.accumulateOnCtrl(),
});
render.addPreset(
Presets.classic.setup({
customize: {
@ -65,6 +63,16 @@ 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);

View file

@ -2,20 +2,19 @@ import * as React from "react";
import { ClassicScheme, RenderEmit, Presets } from "rete-react-plugin";
import styled, { css } from "styled-components";
import { $nodewidth, $socketmargin, $socketsize } from "./vars";
import { behaviorTreeBuilderStore } from "../../../behavior_tree_builder_screen";
const { RefSocket, RefControl } = Presets.classic;
type NodeExtraData = { width?: number; height?: number };
export const NodeStyles = styled.div<NodeExtraData & { selected: boolean; styles?: (props: any) => any }>`
background: red;
border: 2px solid grey;
background: #292a2d;
border: 3px solid #715a7c;
border-radius: 3px;
cursor: pointer;
box-sizing: border-box;
width: ${(props) => (Number.isFinite(props.width) ? `${props.width}px` : `${$nodewidth}px`)};
height: ${(props) => (Number.isFinite(props.height) ? `${props.height}px` : "auto")};
padding-bottom: 6px;
user-select: none;
&:hover {
@ -27,30 +26,42 @@ export const NodeStyles = styled.div<NodeExtraData & { selected: boolean; styles
border-color: red;
`}
.title {
color: white;
color: black;
font-family: sans-serif;
font-size: 18px;
padding: 8px;
position: relative;
top: 12px;
height: ${(props) => (Number.isFinite(props.height) ? `${props.height}px` : "auto")};
text-align-last: center;
align-content: center;
}
.output {
text-align: right;
align-content: center;
}
.input {
text-align: left;
align-content: center;
width: min-content;
}
.output-socket {
text-align: right;
margin-right: -1px;
display: inline-block;
position: relative;
left: -17px;
top: -1px;
}
.input-socket {
z-index: -10;
text-align: left;
margin-left: -1px;
display: inline-block;
align-content: center;
position: relative;
left: 17px;
top: -1px;
}
.input-title,
.output-title {
vertical-align: middle;
@ -60,17 +71,9 @@ export const NodeStyles = styled.div<NodeExtraData & { selected: boolean; styles
font-size: 14px;
margin: ${$socketmargin}px;
line-height: ${$socketsize}px;
align-content: center;
}
.input-control {
z-index: 1;
width: calc(100% - ${$socketsize + 2 * $socketmargin}px);
vertical-align: middle;
display: inline-block;
}
.control {
display: block;
padding: ${$socketmargin}px ${$socketsize / 2 + $socketmargin}px;
}
${(props) => props.styles && props.styles(props)}
`;
@ -95,66 +98,88 @@ export function SequenceNode<Scheme extends ClassicScheme>(props: Props<Scheme>)
const outputs = Object.entries(props.data.outputs);
const controls = Object.entries(props.data.controls);
const selected = props.data.selected || false;
const { id, label, width, height } = props.data;
const { id, label, width } = props.data;
sortByIndex(inputs);
sortByIndex(outputs);
sortByIndex(controls);
behaviorTreeBuilderStore.setSelected(label, selected);
return (
<NodeStyles selected={selected} width={width} height={height} styles={props.styles} data-testid="node">
<div onPointerDown={(e) => {}} className="title" data-testid="title">
{label}
<div>{id}</div>
</div>
<>
<NodeStyles
selected={selected}
width={width}
onDoubleClick={() => {
console.log("double");
}}
style={behaviorTreeBuilderStore.getStylesByLabelNode(label)}
data-testid="node"
className="node"
id="node"
>
<div style={{ display: "inline-flex", width: "100%", justifyContent: "space-between" }}>
{controls.map(([key, control]) => {
return control ? <RefControl key={key} name="control" emit={props.emit} payload={control} /> : null;
})}
{outputs.map(
([key, output]) =>
output && (
<div className="output" key={key} data-testid={`output-${key}`}>
<div className="output-title" data-testid="output-title">
{output?.label}
</div>
<RefSocket
name="output-socket"
side="output"
emit={props.emit}
socketKey={key}
nodeId={id}
payload={output.socket}
/>
</div>
)
)}
{inputs.map(
([key, input]) =>
input && (
<div
style={{ position: "relative", left: "-22px", alignContent: "center" }}
className="input"
key={key}
data-testid={`input-${key}`}
>
<RefSocket
name="input-socket"
emit={props.emit}
side="input"
socketKey={key}
nodeId={id}
payload={input.socket}
/>
{controls.map(([key, control]) => {
return control ? <RefControl key={key} name="control" emit={props.emit} payload={control} /> : null;
})}
{inputs.map(
([key, input]) =>
input && (
<div className="input" key={key} data-testid={`input-${key}`}>
<RefSocket
name="input-socket"
emit={props.emit}
side="input"
socketKey={key}
nodeId={id}
payload={input.socket}
/>
{input && (!input.control || !input.showControl) && (
<div className="input-title" data-testid="input-title">
{input?.label}
{input?.control && input?.showControl && (
<span className="input-control">
<RefControl key={key} name="input-control" emit={props.emit} payload={input.control} />
</span>
)}
</div>
)}
{input?.control && input?.showControl && (
<span className="input-control">
<RefControl key={key} name="input-control" emit={props.emit} payload={input.control} />
</span>
)}
</div>
)
)}
</NodeStyles>
)
)}
<div
style={{ marginTop: 20, marginBottom: 20 }}
onPointerDown={(e) => {}}
className="title"
data-testid="title"
>
<div>{label}</div>
<div>{behaviorTreeBuilderStore.getBodyNode(label)}</div>
</div>
{outputs.map(
([key, output]) =>
output && (
<div
className="output"
key={key}
data-testid={`output-${key}`}
style={{ position: "relative", left: "22px" }}
>
<RefSocket
name="output-socket"
side="output"
emit={props.emit}
socketKey={key}
nodeId={id}
payload={output.socket}
/>
</div>
)
)}
{outputs.isEmpty() ? <div style={{ width: 10 }} /> : null}
</div>
</NodeStyles>
</>
);
}

View file

@ -1,164 +0,0 @@
import * as React from "react";
import { ClassicScheme, RenderEmit, Presets } from "rete-react-plugin";
import styled, { css } from "styled-components";
import { $nodewidth, $socketmargin, $socketsize } from "./vars";
const { RefSocket, RefControl } = Presets.classic;
type NodeExtraData = { width?: number; height?: number };
export const NodeStyles = styled.div<NodeExtraData & { selected: boolean; styles?: (props: any) => any }>`
background: black;
border: 2px solid grey;
border-radius: 10px;
cursor: pointer;
box-sizing: border-box;
width: ${(props) => (Number.isFinite(props.width) ? `${props.width}px` : `${$nodewidth}px`)};
height: ${(props) => (Number.isFinite(props.height) ? `${props.height}px` : "auto")};
padding-bottom: 6px;
position: relative;
user-select: none;
&:hover {
background: #333;
}
${(props) =>
props.selected &&
css`
border-color: red;
`}
.title {
color: white;
font-family: sans-serif;
font-size: 18px;
padding: 8px;
height: 100%;
}
.output {
text-align: right;
}
.input {
text-align: left;
}
.output-socket {
text-align: right;
margin-right: -1px;
display: inline-block;
}
.input-socket {
text-align: left;
margin-left: -1px;
display: inline-block;
}
.input-title,
.output-title {
vertical-align: middle;
color: white;
display: inline-block;
font-family: sans-serif;
font-size: 14px;
margin: ${$socketmargin}px;
line-height: ${$socketsize}px;
}
.input-control {
z-index: 1;
width: calc(100% - ${$socketsize + 2 * $socketmargin}px);
vertical-align: middle;
display: inline-block;
}
.control {
display: block;
padding: ${$socketmargin}px ${$socketsize / 2 + $socketmargin}px;
}
${(props) => props.styles && props.styles(props)}
`;
function sortByIndex<T extends [string, undefined | { index?: number }][]>(entries: T) {
entries.sort((a, b) => {
const ai = a[1]?.index || 0;
const bi = b[1]?.index || 0;
return ai - bi;
});
}
type Props<S extends ClassicScheme> = {
data: S["Node"] & NodeExtraData;
styles?: () => any;
emit: RenderEmit<S>;
};
export type NodeComponent<Scheme extends ClassicScheme> = (props: Props<Scheme>) => JSX.Element;
export function CustomNode<Scheme extends ClassicScheme>(props: Props<Scheme>) {
const inputs = Object.entries(props.data.inputs);
const outputs = Object.entries(props.data.outputs);
const controls = Object.entries(props.data.controls);
const selected = props.data.selected || false;
const { id, label, width, height } = props.data;
sortByIndex(inputs);
sortByIndex(outputs);
sortByIndex(controls);
return (
<NodeStyles selected={selected} width={width} height={height} styles={props.styles} data-testid="node">
<div
onPointerDown={(e) => {
e.stopPropagation();
}}
className="title"
data-testid="title"
>
{label}
</div>
{outputs.map(
([key, output]) =>
output && (
<div className="output" key={key} data-testid={`output-${key}`}>
<div style={{ color: "white" }}>BODY</div>
<div className="output-title" data-testid="output-title">
{output?.label}
</div>
<RefSocket
name="output-socket"
side="output"
emit={props.emit}
socketKey={key}
nodeId={id}
payload={output.socket}
/>
</div>
)
)}
{controls.map(([key, control]) => {
return control ? <RefControl key={key} name="control" emit={props.emit} payload={control} /> : null;
})}
{inputs.map(
([key, input]) =>
input && (
<div className="input" key={key} data-testid={`input-${key}`}>
<RefSocket
name="input-socket"
emit={props.emit}
side="input"
socketKey={key}
nodeId={id}
payload={input.socket}
/>
{input && (!input.control || !input.showControl) && (
<div className="input-title" data-testid="input-title">
{input?.label}
</div>
)}
{input?.control && input?.showControl && (
<span className="input-control">
<RefControl key={key} name="input-control" emit={props.emit} payload={input.control} />
</span>
)}
</div>
)
)}
</NodeStyles>
);
}

View file

@ -0,0 +1,8 @@
import WeightsForm from "./weights_form";
export interface IForms {
name: string;
component: any;
}
export const forms = [{ name: "weights", component: WeightsForm }];

View file

@ -0,0 +1,39 @@
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 { ISkils, SkillsHttpRepository } from "../../../../skils/skills_http_repository";
import { Spin } from "antd";
import { observer } from "mobx-react-lite";
interface IWeightsFormProps {
dependency?: IWeightsDependency;
}
export class WeightsViewModel {
static empty() {
return new WeightsViewModel();
}
}
export class WeightsFormStore extends FormState<WeightsViewModel, CoreError> {
errorHandingStrategy = (error: CoreError) => {};
weights: ISkils[];
viewModel: WeightsViewModel = WeightsViewModel.empty();
skillsHttpRepository: SkillsHttpRepository = new SkillsHttpRepository();
constructor() {
super();
makeAutoObservable(this);
}
init = async () => {
this.mapOk("weights", this.skillsHttpRepository.getAllSkills());
};
}
const WeightsForm = observer((props: IWeightsFormProps) => {
// const [store] = React.useState(() => new WeightsFormStore());
// React.useEffect(() => {
// store.init();
// }, []);
return <div>123</div>;
});
export default WeightsForm;

View file

@ -2,6 +2,7 @@ import * as React from "react";
import TreeView, { EventCallback, IBranchProps, INode, LeafProps, flattenTree } from "react-accessible-treeview";
import { IFlatMetadata } from "react-accessible-treeview/dist/TreeView/utils";
import { CallBackEventTarget } from "../../../../../core/extensions/extensions";
import "./styles.css";
export interface ISkillView {
name: string;
@ -21,12 +22,14 @@ interface IRefListerProps {
handleExpand: EventCallback;
handleSelect: EventCallback;
dragEnd: CallBackEventTarget;
isBranch: boolean;
style: React.CSSProperties;
}
export const RefListener = (props: IRefListerProps) => {
const ref = React.useRef<HTMLSpanElement>(null);
const ref = React.useRef<HTMLDivElement>(null);
React.useEffect(() => {
if (ref.current) {
if (ref.current && props.isBranch === false) {
ref.current.addEventListener("dragend", (e) => {
// @ts-expect-error
if (e && e.target && e.target.innerHTML) {
@ -38,31 +41,34 @@ export const RefListener = (props: IRefListerProps) => {
}, [ref, props]);
return (
<div {...props.getNodeProps({ onClick: props.handleExpand })}>
<div style={props.style} {...props.getNodeProps({ onClick: props.handleExpand })}>
<div
onClick={(e) => {
props.handleSelect(e);
}}
/>
<span ref={ref} style={{ color: "black" }} draggable="true">
<div ref={ref} style={{ color: "black" }} draggable={props.isBranch ? undefined : "true"}>
{props.isBranch ? "* ":""}
{props.element.name}
</span>
</div>
</div>
);
};
export const SkillTree = (props: ISkillTreeProps) => {
return (
<div>
<div className="directory">
<TreeView
data={flattenTree(props.skills)}
aria-label="onSelect"
onSelect={() => {}}
multiSelect
defaultExpandedIds={[1, 2]}
nodeAction="check"
nodeRenderer={({ element, getNodeProps, handleSelect, handleExpand }) => {
nodeRenderer={({ element, getNodeProps, handleSelect, handleExpand, isBranch, level }) => {
return (
<RefListener
style={{ paddingLeft: 20 * (level - 1) }}
isBranch={isBranch}
dragEnd={props.dragEnd}
getNodeProps={getNodeProps}
element={element}

View file

@ -0,0 +1,44 @@
.directory {
user-select: none;
font-size: 10px;
border-radius: 0.4em;
}
.directory .tree,
.directory .tree-node,
.directory .tree-node-group {
list-style: none;
margin: 0;
padding: 0;
}
.directory .tree-branch-wrapper,
.directory .tree-node__leaf {
outline: none;
outline: none;
}
.directory .tree-node {
cursor: pointer;
}
.directory .tree-node:hover {
background: rgba(255, 255, 255, 0.1);
}
.directory .tree .tree-node--focused {
background: rgba(255, 255, 255, 0.2);
}
.directory .tree .tree-node--selected {
background: rgba(48, 107, 176);
}
.directory .tree-node__branch {
display: block;
}
.directory .icon {
vertical-align: middle;
padding-right: 5px;
}

View file

@ -1,6 +1,6 @@
import makeAutoObservable from "mobx-store-inheritance";
import { DataSetRepository } from "./dataset_repository";
import { UiErrorState } from "../../core/store/base_store";
import { Drawer, UiErrorState } from "../../core/store/base_store";
import { HttpError } from "../../core/repository/http_repository";
import { Asset, Assets, DataSetModel, IDatasetModel, ProcessStatus } from "./dataset_model";
import { message } from "antd";
@ -11,10 +11,7 @@ export enum DrawersDataset {
NewDataset = "Новый датасет",
FormBuilderDrawer = "Редактировать датасет",
}
export interface Drawer {
name: string;
status: boolean;
}
export class DataSetStore extends UiErrorState<HttpError> {
dataSetRepository: DataSetRepository;
assets?: Assets;

View file

@ -89,6 +89,11 @@ export const SkillCard = (props: ISkillCardProps) => {
<div style={{ height: 10 }} />
<CoreText text={"обученных эпох: " + props.epoch?.toString() ?? ""} type={CoreTextType.small} />
<CoreText text={"обучится эпох: " + props.epoch?.toString() ?? ""} type={CoreTextType.small} />
<div style={{ display: "flex", alignItems: "center" }}>
<CoreText text={"закончен:"} type={CoreTextType.small} />
<div style={{ width: 5 }} />
<div style={{ backgroundColor: props.isFinished ? "green" : "red", width: 10, height: 10 }} />
</div>
<CoreText text={"Датасет: " + props.datasetName ?? ""} type={CoreTextType.small} />
</div>
@ -117,7 +122,6 @@ export const SkillCard = (props: ISkillCardProps) => {
/>
</>
))
.with(ProcessStatus.END, () => (
<>
<CoreButton

View file

@ -5,17 +5,19 @@ import { ISkils } from "./skills_http_repository";
export class SkillModel {
numberOfTrainedEpochs?: number;
id?: string;
isFinished?:boolean;
constructor(
public name: string,
public datasetId: string,
public project: string,
public epoch: number,
numberOfTrainedEpochs?: number,
id?: string
id?: string,
isFinished?:boolean
) {
this.numberOfTrainedEpochs = numberOfTrainedEpochs;
this.id = id;
this.isFinished = isFinished
makeAutoObservable(this);
}
@ -26,7 +28,8 @@ export class SkillModel {
model.project._id,
model.epoch,
model.numberOfTrainedEpochs,
model._id
model._id,
model.isFinished
);
}

View file

@ -74,7 +74,23 @@ export const SkillScreen = observer(() => {
open={store.drawers.find((el) => el.name === DrawersSkill.EDIT_SKILL)?.status}
>
<div style={{ display: "flex", flexDirection: "column", justifyContent: "space-between", height: "100%" }}>
<CoreInput value={(store.skill.epoch ?? "").toString()} label={"добавить эпох"} />
<div>
<CoreInput
value={(store.viewModel.epoch ?? "").toString()}
label={"добавить эпох"}
validation={(value) => Number().isValid(value)}
error="Должно быть введено число"
onChange={(text) => store.updateForm({ epoch: Number(text) })}
/>
<div style={{ display: "flex", alignItems: "center",margin:10 }}>
<CoreText text={"Завершен?"} type={CoreTextType.header} /> <div style={{ width: 10 }} />
<CoreSwitch
id={store.viewModel.id ?? ""}
isSelected={store.viewModel.isFinished ?? false}
onChange={() => store.updateForm({isFinished: !store.viewModel.isFinished})}
/>
</div>
</div>
<div style={{ display: "flex" }}>
<CoreButton text="Сохранить" filled={true} onClick={() => store.saveEdiSkill()} />
<div style={{ width: 10 }} />
@ -89,11 +105,15 @@ export const SkillScreen = observer(() => {
open={store.drawers.find((el) => el.name === DrawersSkill.NEW_SKILL)?.status}
>
<div style={{ display: "flex", flexDirection: "column", justifyContent: "space-between", height: "100%" }}>
<CoreInput value={store.skill.name} label={"Имя навыка"} onChange={(e) => store.changeSkillName(e)} />
<CoreInput
value={store.skill.epoch.toString()}
value={store.viewModel.name}
label={"Имя навыка"}
onChange={(text) => store.updateForm({ name: text })}
/>
<CoreInput
value={store.viewModel.epoch.toString()}
label="Количество эпох"
onChange={(e) => store.changeEpoch(Number(e))}
onChange={(e) => store.updateForm({ epoch: Number(e) })}
/>
<div style={{ height: "100%" }}>
{store.datasets?.map((el) => (
@ -111,9 +131,9 @@ export const SkillScreen = observer(() => {
>
{el.name}
<CoreSwitch
isSelected={store.skill.datasetId.isEqual(el._id)}
isSelected={store.viewModel.datasetId.isEqual(el._id)}
id={el._id}
onChange={() => store.selectDataset(el._id)}
onChange={() => store.updateForm({ datasetId: el._id })}
/>
</div>
))}

View file

@ -1,14 +1,12 @@
import makeAutoObservable from "mobx-store-inheritance";
import { NavigateFunction } from "react-router-dom";
import { HttpError } from "../../core/repository/http_repository";
import { UiErrorState } from "../../core/store/base_store";
import { Drawer, UiFormState } from "../../core/store/base_store";
import { ISkils, SkillsHttpRepository } from "./skills_http_repository";
import { Drawer } from "../dataset/dataset_store";
import { IDatasetModel } from "../dataset/dataset_model";
import { message } from "antd";
import { UUID } from "../all_projects/data/project_repository";
import { SkillModel } from "./skill_model";
import { SocketRepository, socketRepository } from "../../core/repository/socket_repository";
import { ProcessUpdate, SkillSocketRepository } from "./skill_socket_repository";
export enum DrawersSkill {
@ -16,25 +14,25 @@ export enum DrawersSkill {
EDIT_SKILL = "Редактировать навык",
}
export class SkillStore extends UiErrorState<HttpError> {
drawers: Drawer[];
export class SkillStore extends UiFormState<SkillModel, HttpError> {
skillsHttpRepository: SkillsHttpRepository;
skillSocketRepository: SkillSocketRepository = new SkillSocketRepository();
skils?: ISkils[];
datasets?: IDatasetModel[];
activeProjectId?: UUID;
skill: SkillModel;
viewModel: SkillModel;
titleDrawer: string = DrawersSkill.NEW_SKILL;
skillSocketRepository: SkillSocketRepository = new SkillSocketRepository();
drawers: Drawer[];
constructor() {
super();
super({});
this.drawers = Object.entries(DrawersSkill).map((k, v) => {
return {
name: k.at(1) ?? "",
status: false,
};
});
this.skill = SkillModel.empty();
this.viewModel = SkillModel.empty();
this.skillsHttpRepository = new SkillsHttpRepository();
this.skillSocketRepository.on(this.socketUpdate);
makeAutoObservable(this);
@ -77,20 +75,14 @@ export class SkillStore extends UiErrorState<HttpError> {
await this.mapOk("datasets", this.skillsHttpRepository.getDatasetsActiveProject());
await this.mapOk("activeProjectId", this.skillsHttpRepository.getActiveProjectId());
};
changeSkillName(name: string): void {
this.skill.name = name;
}
changeEpoch(epoch: number) {
this.skill.epoch = epoch;
}
saveSkill() {
this.skill.project = this.activeProjectId?.id ?? "";
this.skill.valid().fold(
this.viewModel.project = this.activeProjectId?.id ?? "";
this.viewModel.valid().fold(
async (model) => {
(await this.skillsHttpRepository.addNewSkill(model)).fold(
async (s) => {
message.success("Новый ");
this.skill = SkillModel.empty();
this.viewModel = SkillModel.empty();
await this.mapOk("skils", this.skillsHttpRepository.getAllSkills());
},
async (e) => message.error(e.message)
@ -99,9 +91,7 @@ export class SkillStore extends UiErrorState<HttpError> {
async (e) => message.error(e)
);
}
selectDataset(id: string): void {
this.skill.datasetId = id;
}
edtDrawer(drawerName: DrawersSkill, status: boolean): void {
this.titleDrawer = drawerName;
this.drawers = this.drawers.map((el) => {
@ -116,14 +106,14 @@ export class SkillStore extends UiErrorState<HttpError> {
await this.init();
};
fromEditSkill(id: string): void {
this.skill = SkillModel.fromISkill(this.getSkillById(id) as ISkils);
this.viewModel = SkillModel.fromISkill(this.getSkillById(id) as ISkils);
this.edtDrawer(DrawersSkill.EDIT_SKILL, true);
}
saveEdiSkill = async () => {
const skillOrigin = this.getSkillById(this.skill.id as string) as ISkils;
skillOrigin.numberOfTrainedEpochs = this.skill.epoch;
const skillOrigin = this.getSkillById(this.viewModel.id as string) as ISkils;
skillOrigin.numberOfTrainedEpochs = this.viewModel.epoch;
skillOrigin.isFinished = this.viewModel.isFinished ?? false;
await this.skillsHttpRepository.editSkill(skillOrigin);
this.edtDrawer(DrawersSkill.EDIT_SKILL, false);
};

View file

@ -6,7 +6,11 @@ import { extensions } from "./core/extensions/extensions";
import { SocketLister } from "./features/socket_lister/socket_lister";
import { RouterProvider } from "react-router-dom";
import { router } from "./core/routers/routers";
import { configure } from "mobx"
configure({
enforceActions: "never",
})
extensions();
const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement);