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