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