progress
3
.vscode/settings.json
vendored
|
@ -2,13 +2,16 @@
|
|||
"cSpell.words": [
|
||||
"введите",
|
||||
"Ведите",
|
||||
"дерево",
|
||||
"Количество",
|
||||
"может",
|
||||
"навык",
|
||||
"Навыки",
|
||||
"Новое",
|
||||
"отрицательное",
|
||||
"принимать",
|
||||
"скила",
|
||||
"создано",
|
||||
"число",
|
||||
"эпох",
|
||||
"эпоха",
|
||||
|
|
6
p.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"dependency":{
|
||||
"weights_path":"",
|
||||
"object_name":""
|
||||
}
|
||||
}
|
|
@ -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";
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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),
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
};
|
|
@ -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);
|
|
@ -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;
|
||||
}
|
|
@ -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
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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 |
|
@ -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);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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[];
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -48,6 +48,10 @@ export const router = createBrowserRouter([
|
|||
path: BehaviorTreeBuilderPath,
|
||||
element: <BehaviorTreeBuilderScreen />,
|
||||
},
|
||||
{
|
||||
path: BehaviorTreeBuilderPath + idURL,
|
||||
element: <BehaviorTreeBuilderScreen />,
|
||||
},
|
||||
{
|
||||
path: StickObjectsMarkingScreenPath,
|
||||
element: <StickObjectsMarkingScreen />,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
/>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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}`);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
export interface BtTreeModel {
|
||||
_id: string;
|
||||
name: string;
|
||||
unixTime: number;
|
||||
__v: number;
|
||||
}
|
|
@ -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>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
|
|
|
@ -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)",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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; */
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)}
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
import WeightsForm from "./weights_form";
|
||||
|
||||
export interface IForms {
|
||||
name: string;
|
||||
component: any;
|
||||
}
|
||||
|
||||
export const forms = [{ name: "weights", component: WeightsForm }];
|
|
@ -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;
|
|
@ -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}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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>
|
||||
))}
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|