alexander bt fixed
This commit is contained in:
parent
61118a7423
commit
8f8fc3107d
46 changed files with 1109 additions and 304 deletions
|
@ -5,6 +5,9 @@ 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 { GetBehaviorTreeActiveProjectScenario } from "./domain/get_behavior_tree_active_project_scenario";
|
||||||
import { SaveBtScenario as FillBtScenario } from "./domain/save_bt_scenario";
|
import { SaveBtScenario as FillBtScenario } from "./domain/save_bt_scenario";
|
||||||
|
import { GetCameraUseCase } from "./domain/get_cameras_usecase";
|
||||||
|
import { GetRobotsUseCase } from "./domain/get_robots_usecase";
|
||||||
|
import { GetTopicsUseCase } from "./domain/get_topics_usecase";
|
||||||
|
|
||||||
export class BehaviorTreesPresentation extends CrudController<BehaviorTreeValidationModel, typeof BehaviorTreeDBModel> {
|
export class BehaviorTreesPresentation extends CrudController<BehaviorTreeValidationModel, typeof BehaviorTreeDBModel> {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -25,5 +28,21 @@ export class BehaviorTreesPresentation extends CrudController<BehaviorTreeValida
|
||||||
subUrl: "by_id",
|
subUrl: "by_id",
|
||||||
fn: new ReadByIdDataBaseModelScenario<BehaviorTreeValidationModel>(BehaviorTreeDBModel),
|
fn: new ReadByIdDataBaseModelScenario<BehaviorTreeValidationModel>(BehaviorTreeDBModel),
|
||||||
});
|
});
|
||||||
|
this.subRoutes.push({
|
||||||
|
method: "GET",
|
||||||
|
subUrl: "cameras",
|
||||||
|
fn: new GetCameraUseCase()
|
||||||
|
})
|
||||||
|
this.subRoutes.push({
|
||||||
|
method: "GET",
|
||||||
|
subUrl: "robots",
|
||||||
|
fn: new GetRobotsUseCase()
|
||||||
|
})
|
||||||
|
this.subRoutes.push({
|
||||||
|
method: "GET",
|
||||||
|
subUrl: "topics",
|
||||||
|
fn: new GetTopicsUseCase()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ export class GetBehaviorTreeSkillsTemplatesUseCase extends CallbackStrategyWithE
|
||||||
return Result.ok({
|
return Result.ok({
|
||||||
skills: [
|
skills: [
|
||||||
{
|
{
|
||||||
SkillPackage: { name: "Robossembler", version: "1.0", format: "1" },
|
SkillPackage: { name: "Robossembler", version: "1.0", format: "1", type: "Action" },
|
||||||
Module: { name: "PoseEstimation", description: "Pose Estimation skill with DOPE" },
|
Module: { name: "PoseEstimation", description: "Pose Estimation skill with DOPE" },
|
||||||
Launch: { package: "rbs_perception", executable: "pe_dope_lc.py", name: "lc_dope" },
|
Launch: { package: "rbs_perception", executable: "pe_dope_lc.py", name: "lc_dope" },
|
||||||
BTAction: [
|
BTAction: [
|
||||||
|
@ -18,6 +18,14 @@ export class GetBehaviorTreeSkillsTemplatesUseCase extends CallbackStrategyWithE
|
||||||
type: "weights",
|
type: "weights",
|
||||||
dependency: {},
|
dependency: {},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: "camera",
|
||||||
|
dependency: {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "topic",
|
||||||
|
dependency: {}
|
||||||
|
}
|
||||||
],
|
],
|
||||||
result: ["POSE"],
|
result: ["POSE"],
|
||||||
},
|
},
|
||||||
|
@ -38,6 +46,28 @@ export class GetBehaviorTreeSkillsTemplatesUseCase extends CallbackStrategyWithE
|
||||||
{ name: "mesh_scale", value: 0.001 },
|
{ name: "mesh_scale", value: 0.001 },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
SkillPackage: { name: "Robossembler", version: "1.0", format: "1" },
|
||||||
|
Module: { name: "MoveToPose", description: "Move to Pose skill with ? controllers" },
|
||||||
|
Launch: { package: "rbss_movetopose", executable: "movetopose.py", name: "skill_move" },
|
||||||
|
BTAction: [
|
||||||
|
{
|
||||||
|
name: "move",
|
||||||
|
type: "action",
|
||||||
|
param: [{ type: "topic", dependency: { } }],
|
||||||
|
result: []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
Interface: {
|
||||||
|
Input: [
|
||||||
|
{ name: "robotName", type: "ROBOT" },
|
||||||
|
{ name: "pose", type: "POSE" }
|
||||||
|
],
|
||||||
|
Output: []
|
||||||
|
},
|
||||||
|
Settings: []
|
||||||
|
}
|
||||||
|
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
120
server/src/features/behavior_trees/domain/get_cameras_usecase.ts
Normal file
120
server/src/features/behavior_trees/domain/get_cameras_usecase.ts
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
import { CallbackStrategyWithEmpty, ResponseBase } from "../../../core/controllers/http_controller"
|
||||||
|
import { Result } from "../../../core/helpers/result"
|
||||||
|
|
||||||
|
export class GetCameraUseCase extends CallbackStrategyWithEmpty {
|
||||||
|
call = async (): ResponseBase => {
|
||||||
|
return Result.ok({
|
||||||
|
"camera": [
|
||||||
|
{
|
||||||
|
"sid": "1",
|
||||||
|
"DevicePackage": {
|
||||||
|
"name": "Robossembler",
|
||||||
|
"version": "1.0",
|
||||||
|
"format": "1"
|
||||||
|
},
|
||||||
|
"Module": {
|
||||||
|
"name": "RealSense Dxx",
|
||||||
|
"description": "ROS Wrapper for Intel(R) RealSense(TM) Cameras"
|
||||||
|
},
|
||||||
|
"Launch": {
|
||||||
|
"package": "realsense2_camera",
|
||||||
|
"executable": "rs_launch.py"
|
||||||
|
},
|
||||||
|
"DTwin": [
|
||||||
|
{
|
||||||
|
"interface": {
|
||||||
|
"input": [
|
||||||
|
{
|
||||||
|
"camera_namespace": "robot1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"camera_name": "455_1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"serial_port": "USB_1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pose": "[0.0,0.0,0.0,0.0,0.0,0.0]"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Interface": {
|
||||||
|
"param": [
|
||||||
|
{
|
||||||
|
"type": "camera",
|
||||||
|
"dependency": {}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Settings": [
|
||||||
|
{
|
||||||
|
"name": "camera_config",
|
||||||
|
"description": "Camera Config",
|
||||||
|
"type": "file",
|
||||||
|
"defaultValue": "{ rgb_camera.profile: 1280x720x15 }"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sid": "2",
|
||||||
|
"DevicePackage": {
|
||||||
|
"name": "Robossembler",
|
||||||
|
"version": "1.0",
|
||||||
|
"format": "1"
|
||||||
|
},
|
||||||
|
"Module": {
|
||||||
|
"name": "RealSense Dxx",
|
||||||
|
"description": "ROS Wrapper for Intel(R) RealSense(TM) Cameras"
|
||||||
|
},
|
||||||
|
"Launch": {
|
||||||
|
"package": "realsense2_camera",
|
||||||
|
"executable": "rs_launch.py"
|
||||||
|
},
|
||||||
|
|
||||||
|
"DTwin": [
|
||||||
|
{
|
||||||
|
"interface": {
|
||||||
|
"input": [
|
||||||
|
{
|
||||||
|
"camera_namespace": "robot1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"camera_name": "455_1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"serial_port": "USB_1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pose": "[0.0,0.0,0.0,0.0,0.0,0.0]"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Interface": {
|
||||||
|
"param": [
|
||||||
|
{
|
||||||
|
"type": "camera",
|
||||||
|
"dependency": {}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Settings": [
|
||||||
|
{
|
||||||
|
"name": "camera_config",
|
||||||
|
"description": "Camera Config",
|
||||||
|
"type": "file",
|
||||||
|
"defaultValue": "{ rgb_camera.profile: 1280x720x15 }"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
import { CallbackStrategyWithEmpty, ResponseBase } from "../../../core/controllers/http_controller";
|
||||||
|
import { Result } from "../../../core/helpers/result";
|
||||||
|
|
||||||
|
export class GetRobotsUseCase extends CallbackStrategyWithEmpty {
|
||||||
|
call = async (): ResponseBase => {
|
||||||
|
return Result.ok({
|
||||||
|
"robots": [
|
||||||
|
{
|
||||||
|
"sid": "1",
|
||||||
|
"DevicePackage": {
|
||||||
|
"name": "Robossembler",
|
||||||
|
"version": "1.0",
|
||||||
|
"format": "1"
|
||||||
|
},
|
||||||
|
"Module": {
|
||||||
|
"name": "RBS",
|
||||||
|
"description": "Main Robot"
|
||||||
|
},
|
||||||
|
"Launch": {
|
||||||
|
"package": "rbs_bringup",
|
||||||
|
"executable": "single_robot.launch.py"
|
||||||
|
},
|
||||||
|
"DTwin": [
|
||||||
|
{
|
||||||
|
"interface": {
|
||||||
|
"input": [
|
||||||
|
{
|
||||||
|
"robot_namespace": "robot1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dof": 6
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Settings": [
|
||||||
|
{
|
||||||
|
"name": "robot_type",
|
||||||
|
"description": "Type of robot by name",
|
||||||
|
"defaultValue": "rbs_arm"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { CallbackStrategyWithEmpty, ResponseBase } from "../../../core/controllers/http_controller";
|
||||||
|
import { Result } from "../../../core/helpers/result";
|
||||||
|
|
||||||
|
export class GetTopicsUseCase extends CallbackStrategyWithEmpty {
|
||||||
|
call = async (): ResponseBase => {
|
||||||
|
return Result.ok({
|
||||||
|
"topics": [
|
||||||
|
{
|
||||||
|
"sid": "1",
|
||||||
|
"name": "topic camera 1",
|
||||||
|
"type": "sensor_msgs/Image"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sid": "2",
|
||||||
|
"name": "topic camera 2",
|
||||||
|
"type": "sensor_msgs/Image"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
1
ui/.mason/bricks.json
Normal file
1
ui/.mason/bricks.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"base_feature":"/Users/idontsudo/ozon/ui/bricks/base_feature","form":"/Users/idontsudo/webservice/ui/bricks/form"}
|
|
@ -0,0 +1,11 @@
|
||||||
|
import { Result } from "../../core/helper/result";
|
||||||
|
|
||||||
|
export class NumberTriviaModel {
|
||||||
|
constructor() {}
|
||||||
|
isValid(): Result<string, void> {
|
||||||
|
return Result.ok();
|
||||||
|
}
|
||||||
|
static empty() {
|
||||||
|
return new NumberTriviaModel();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
export class {{name.pascalCase()}}Repository {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { observer } from "mobx-react-lite";
|
||||||
|
import { {{name.pascalCase()}}Store } from "./{{name.snakeCase()}}_store";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
export const {{name.pascalCase()}}ScreenPath = "/auth";
|
||||||
|
|
||||||
|
export const {{name.pascalCase()}}Screen = observer(() => {
|
||||||
|
const [store] = React.useState(() => new {{name.pascalCase()}}Store());
|
||||||
|
return <></>;
|
||||||
|
});
|
|
@ -0,0 +1,7 @@
|
||||||
|
import makeAutoObservable from "mobx-store-inheritance";
|
||||||
|
|
||||||
|
export class {{name.pascalCase()}}Store {
|
||||||
|
constructor() {
|
||||||
|
makeAutoObservable(this);
|
||||||
|
}
|
||||||
|
}
|
16
ui/bricks/base_feature/brick.yaml
Normal file
16
ui/bricks/base_feature/brick.yaml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
name: base_feature
|
||||||
|
description: A brick to create Clean Architecture Feature
|
||||||
|
|
||||||
|
version: 0.0.1
|
||||||
|
|
||||||
|
# The following defines the environment for the current brick.
|
||||||
|
# It includes the version of mason that the brick requires.
|
||||||
|
environment:
|
||||||
|
mason: ">=0.1.0-dev.26 <0.1.0"
|
||||||
|
|
||||||
|
vars:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
description: The feature name
|
||||||
|
default: my feature
|
||||||
|
prompt: What's your feature name (e.g. dance school)
|
|
@ -0,0 +1,11 @@
|
||||||
|
import { Result } from "../../../../../../core/helper/result";
|
||||||
|
|
||||||
|
export class NumberTriviaModel {
|
||||||
|
constructor() {}
|
||||||
|
isValid(): Result<string, void> {
|
||||||
|
return Result.ok();
|
||||||
|
}
|
||||||
|
static empty() {
|
||||||
|
return new NumberTriviaModel();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
import React from "react";
|
||||||
|
import { observer } from "mobx-react-lite";
|
||||||
|
import { {{ name.pascalCase() }}Store } from "./{{name.snakeCase()}}_store";
|
||||||
|
import { IPropsForm } from "../forms";
|
||||||
|
import { NumberTriviaModel } from "./number_trivia";
|
||||||
|
|
||||||
|
export const {{ name.pascalCase()}} = observer((props: IPropsForm<Partial<NumberTriviaModel>>) => {
|
||||||
|
const [store] = React.useState(() => new {{ name.pascalCase() }}Store());
|
||||||
|
React.useEffect(() => {
|
||||||
|
store.init();
|
||||||
|
}, [store]);
|
||||||
|
|
||||||
|
return <></>;
|
||||||
|
});
|
|
@ -0,0 +1,5 @@
|
||||||
|
import { HttpRepository } from "../../../../../../core/repository/http_repository";
|
||||||
|
|
||||||
|
export class {{name.pascalCase()}}HttpRepository extends HttpRepository {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
import makeAutoObservable from "mobx-store-inheritance";
|
||||||
|
import { NavigateFunction } from "react-router-dom";
|
||||||
|
import { NumberTriviaModel } from "./number_trivia";
|
||||||
|
import { FormState, CoreError } from "../../../../../../core/store/base_store";
|
||||||
|
import { {{name.pascalCase()}}HttpRepository } from "./{{name.snakeCase()}}_http_repository";
|
||||||
|
|
||||||
|
export class {{name.pascalCase()}}Store extends FormState<NumberTriviaModel, CoreError> {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
makeAutoObservable(this);
|
||||||
|
}
|
||||||
|
viewModel: NumberTriviaModel = NumberTriviaModel.empty();
|
||||||
|
cameraDeviceHttpRepository: {{name.pascalCase()}}HttpRepository = new {{name.pascalCase()}}HttpRepository();
|
||||||
|
errorHandingStrategy = (error: CoreError) => { }
|
||||||
|
init = async (navigate?: NavigateFunction | undefined) => {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
16
ui/bricks/form/brick.yaml
Normal file
16
ui/bricks/form/brick.yaml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
name: form
|
||||||
|
description: A brick to create form at behavior_tree_builder
|
||||||
|
|
||||||
|
version: 0.0.1
|
||||||
|
|
||||||
|
# The following defines the environment for the current brick.
|
||||||
|
# It includes the version of mason that the brick requires.
|
||||||
|
environment:
|
||||||
|
mason: ">=0.1.0-dev.26 <0.1.0"
|
||||||
|
|
||||||
|
vars:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
description: The feature name
|
||||||
|
default: my feature
|
||||||
|
prompt: What's your feature name (e.g. dance school)
|
1
ui/mason-lock.json
Normal file
1
ui/mason-lock.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"bricks":{"base_feature":{"path":"bricks/base_feature"},"form":{"path":"bricks/form"}}}
|
5
ui/mason.yaml
Normal file
5
ui/mason.yaml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
bricks:
|
||||||
|
base_feature:
|
||||||
|
path: base_feature
|
||||||
|
form:
|
||||||
|
path: form
|
46
ui/readme.md
Normal file
46
ui/readme.md
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
# Зависимости
|
||||||
|
|
||||||
|
brew install mason
|
||||||
|
|
||||||
|
### Инициализация
|
||||||
|
|
||||||
|
В корне проекта вызовите команду `init`, которая создаст папку `.mason/`
|
||||||
|
|
||||||
|
```
|
||||||
|
mason init
|
||||||
|
```
|
||||||
|
|
||||||
|
### Использование 👷
|
||||||
|
|
||||||
|
Все готовые `brick'и` хранятся в папке `/bricks`
|
||||||
|
|
||||||
|
Для примера попробуем использовать `brick` под названием `base_feature`
|
||||||
|
|
||||||
|
```
|
||||||
|
# Добавляем `brick` себе в mason (посмотреть уже добавленные можно через `mason ls/list`, а удалить через `mason remove`)
|
||||||
|
mason add base_feature --path bricks/base_feature
|
||||||
|
|
||||||
|
# Используем `brick` для в интересующей нас папке (поскольку это шаблон фичи, выбрана папка src/features/)
|
||||||
|
mason make base_feature -o lib/features
|
||||||
|
```
|
||||||
|
|
||||||
|
Далее необходимо ответить на вопросы задаваемые в CLI и на основе ответов `brick` сгенериует фичу
|
||||||
|
|
||||||
|
### Разработка
|
||||||
|
|
||||||
|
В папке `bricks/` вы можете создать свой `brick`
|
||||||
|
|
||||||
|
```
|
||||||
|
# создать hello brick
|
||||||
|
mason new my_brick_name
|
||||||
|
```
|
||||||
|
|
||||||
|
Далее всю структуру папок и файлов необходимо описать в папке `__brick__`
|
||||||
|
|
||||||
|
Для большей информации [читайте и смотрите примеры](https://github.com/felangel/mason/blob/master/packages/mason_cli/README.md)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Добавить новую форму
|
||||||
|
mason make form -o ./src/features/behavior_tree_builder/presentation/ui/forms
|
||||||
|
# Добавить новый экран
|
63
ui/src/core/model/cameras.ts
Normal file
63
ui/src/core/model/cameras.ts
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
export interface Cameras {
|
||||||
|
camera: Camera[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Camera {
|
||||||
|
sid: string;
|
||||||
|
DevicePackage: DevicePackage;
|
||||||
|
Module: Module;
|
||||||
|
Launch: Launch;
|
||||||
|
DTwin: DTwin[];
|
||||||
|
Interface: InterfaceClass;
|
||||||
|
Settings: Setting[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DTwin {
|
||||||
|
interface: Interface;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Interface {
|
||||||
|
input: Input[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Input {
|
||||||
|
camera_namespace?: string;
|
||||||
|
camera_name?: string;
|
||||||
|
serial_port?: string;
|
||||||
|
pose?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DevicePackage {
|
||||||
|
name: string;
|
||||||
|
version: string;
|
||||||
|
format: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InterfaceClass {
|
||||||
|
param: Param[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Param {
|
||||||
|
type: string;
|
||||||
|
dependency: Dependency;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Dependency {
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Launch {
|
||||||
|
package: string;
|
||||||
|
executable: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Module {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Setting {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
type: string;
|
||||||
|
defaultValue: string;
|
||||||
|
}
|
18
ui/src/core/model/device_dependency_view_model.ts
Normal file
18
ui/src/core/model/device_dependency_view_model.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import { Result } from "../helper/result";
|
||||||
|
import { IDeviceDependency } from "./skill_model";
|
||||||
|
|
||||||
|
export class SidViewModel implements IDeviceDependency {
|
||||||
|
sid: string;
|
||||||
|
constructor(sid: string) {
|
||||||
|
this.sid = sid;
|
||||||
|
}
|
||||||
|
valid = (): Result<string, SidViewModel> => {
|
||||||
|
if (this.sid.isEmpty()) {
|
||||||
|
return Result.error('sid is empty')
|
||||||
|
}
|
||||||
|
return Result.ok(this)
|
||||||
|
}
|
||||||
|
static empty() {
|
||||||
|
return new SidViewModel('')
|
||||||
|
}
|
||||||
|
}
|
47
ui/src/core/model/robots.ts
Normal file
47
ui/src/core/model/robots.ts
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
export interface Robots {
|
||||||
|
robots: Robot[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Robot {
|
||||||
|
sid: string;
|
||||||
|
DevicePackage: DevicePackage;
|
||||||
|
Module: Module;
|
||||||
|
Launch: Launch;
|
||||||
|
DTwin: DTwin[];
|
||||||
|
Settings: Setting[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DTwin {
|
||||||
|
interface: Interface;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Interface {
|
||||||
|
input: Input[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Input {
|
||||||
|
robot_namespace?: string;
|
||||||
|
dof?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DevicePackage {
|
||||||
|
name: string;
|
||||||
|
version: string;
|
||||||
|
format: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Launch {
|
||||||
|
package: string;
|
||||||
|
executable: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Module {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Setting {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
defaultValue: string;
|
||||||
|
}
|
|
@ -26,11 +26,14 @@ export interface ISkill {
|
||||||
xxx: IXxx;
|
xxx: IXxx;
|
||||||
}
|
}
|
||||||
export interface IWeightsDependency {
|
export interface IWeightsDependency {
|
||||||
weights_name:string;
|
weights_name: string;
|
||||||
object_name: string;
|
object_name: string;
|
||||||
weights_file: string;
|
weights_file: string;
|
||||||
dimensions: number[];
|
dimensions: number[];
|
||||||
}
|
}
|
||||||
|
export interface IDeviceDependency {
|
||||||
|
sid: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IParam {
|
export interface IParam {
|
||||||
type: string;
|
type: string;
|
||||||
|
@ -195,7 +198,7 @@ export class SkillModel implements ISkill {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SkillDependency implements IDependency {
|
export class SkillDependency implements IDependency {
|
||||||
constructor(public skills: ISkillDependency[]) {}
|
constructor(public skills: ISkillDependency[]) { }
|
||||||
static empty() {
|
static empty() {
|
||||||
return new SkillDependency([]);
|
return new SkillDependency([]);
|
||||||
}
|
}
|
||||||
|
|
9
ui/src/core/model/topics.ts
Normal file
9
ui/src/core/model/topics.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
export interface Topics {
|
||||||
|
topics: Topic[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Topic {
|
||||||
|
sid: string;
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
}
|
|
@ -44,10 +44,10 @@ export abstract class UiLoader {
|
||||||
};
|
};
|
||||||
messageHttp = async <T>(callBack: Promise<Result<CoreError, T>>, report?: IMessage) => {
|
messageHttp = async <T>(callBack: Promise<Result<CoreError, T>>, report?: IMessage) => {
|
||||||
return (await this.httpHelper(callBack)).fold(
|
return (await this.httpHelper(callBack)).fold(
|
||||||
(s) => {
|
(_s) => {
|
||||||
if (report && report.successMessage) message.success(report.successMessage);
|
if (report && report.successMessage) message.success(report.successMessage);
|
||||||
},
|
},
|
||||||
(e) => {
|
(_e) => {
|
||||||
if (report && report.errorMessage) message.error(report.errorMessage);
|
if (report && report.errorMessage) message.error(report.errorMessage);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,81 +1,75 @@
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
import { IStyle } from "../../model/style";
|
||||||
|
|
||||||
export enum CoreTextType {
|
export enum CoreTextType {
|
||||||
header,
|
header = 'header',
|
||||||
medium,
|
medium = 'medium',
|
||||||
large,
|
large = 'large',
|
||||||
small,
|
small = 'small',
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ITextProps {
|
export interface ITextProps extends IStyle {
|
||||||
text: string;
|
text: string;
|
||||||
type: CoreTextType;
|
type: CoreTextType;
|
||||||
color?: string;
|
color?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CoreText(props: ITextProps) {
|
const getStyle = (type: CoreTextType, color: string | undefined) => {
|
||||||
if (props.type === CoreTextType.small) {
|
if (type.isEqual(CoreTextType.small)) return {
|
||||||
return (
|
color: color ?? "rgba(73, 69, 79, 1)",
|
||||||
<div
|
fontSize: 12,
|
||||||
style={{
|
fontFamily: "Roboto",
|
||||||
color: props.color ?? "rgba(73, 69, 79, 1)",
|
fontWeight: 400,
|
||||||
fontSize: 12,
|
fontSizeAdjust: 14,
|
||||||
fontFamily: "Roboto",
|
textOverflow: "ellipsis",
|
||||||
fontWeight: 400,
|
|
||||||
fontSizeAdjust: 14,
|
|
||||||
textOverflow: "ellipsis",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{props.text}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if (props.type === CoreTextType.large) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
color: props.color ?? "#1D1B20",
|
|
||||||
fontSize: 16,
|
|
||||||
fontFamily: "Roboto",
|
|
||||||
fontWeight: 400,
|
|
||||||
fontSizeAdjust: 14,
|
|
||||||
textOverflow: "ellipsis",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{props.text}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (props.type === CoreTextType.medium)
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
color: props.color ?? "#1D1B20",
|
|
||||||
fontSize: 14,
|
|
||||||
fontFamily: "Roboto",
|
|
||||||
fontWeight: 400,
|
|
||||||
textOverflow: "ellipsis",
|
|
||||||
fontSizeAdjust: 14,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{props.text}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
if (props.type === CoreTextType.header)
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
color: props.color ?? "#1D1B20",
|
|
||||||
fontSize: 20,
|
|
||||||
fontFamily: "Roboto",
|
|
||||||
fontWeight: 500,
|
|
||||||
textOverflow: "ellipsis",
|
|
||||||
|
|
||||||
fontSizeAdjust: 16,
|
if (type.isEqual(CoreTextType.large)) return {
|
||||||
}}
|
color: color ?? "#1D1B20",
|
||||||
>
|
fontSize: 16,
|
||||||
{props.text}
|
fontFamily: "Roboto",
|
||||||
</div>
|
fontWeight: 400,
|
||||||
);
|
fontSizeAdjust: 14,
|
||||||
return <div>{props.text}</div>;
|
textOverflow: "ellipsis",
|
||||||
|
}
|
||||||
|
if (type.isEqual(CoreTextType.medium)) return {
|
||||||
|
color: color ?? "#1D1B20",
|
||||||
|
fontSize: 14,
|
||||||
|
fontFamily: "Roboto",
|
||||||
|
fontWeight: 400,
|
||||||
|
textOverflow: "ellipsis",
|
||||||
|
fontSizeAdjust: 14,
|
||||||
|
}
|
||||||
|
if (type.isEqual(CoreTextType.header)) return {
|
||||||
|
color: color ?? "#1D1B20",
|
||||||
|
fontSize: 20,
|
||||||
|
fontFamily: "Roboto",
|
||||||
|
fontWeight: 500,
|
||||||
|
textOverflow: "ellipsis",
|
||||||
|
|
||||||
|
fontSizeAdjust: 16,
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
color: color ?? "rgba(73, 69, 79, 1)",
|
||||||
|
fontSize: 12,
|
||||||
|
fontFamily: "Roboto",
|
||||||
|
fontWeight: 400,
|
||||||
|
fontSizeAdjust: 14,
|
||||||
|
textOverflow: "ellipsis",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const appendStyle = (type: CoreTextType, color: string | undefined, style: React.CSSProperties | undefined) => {
|
||||||
|
return Object.assign(getStyle(type, color), style)
|
||||||
|
}
|
||||||
|
export function CoreText(props: ITextProps) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={appendStyle(props.type, props.color, props.style)}
|
||||||
|
>
|
||||||
|
{props.text}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
import { extensions } from "../../../core/extensions/extensions";
|
||||||
|
import { Form } from "../presentation/ui/forms/forms";
|
||||||
|
import { ISkillView } from "../presentation/ui/skill_tree/skill_tree";
|
||||||
|
|
||||||
|
extensions();
|
||||||
|
|
||||||
|
export enum SystemPrimitive {
|
||||||
|
Sequence = 'Sequence',
|
||||||
|
Fallback = "Fallback",
|
||||||
|
ReactiveSequence = "ReactiveSequence",
|
||||||
|
SequenceWithMemory = 'SequenceWithMemory',
|
||||||
|
ReactiveFallback = "ReactiveFallback",
|
||||||
|
Decorator = "Decorator",
|
||||||
|
Inverter = "Inverter",
|
||||||
|
ForceSuccess = "ForceSuccess",
|
||||||
|
ForceFailure = "ForceFailure",
|
||||||
|
Repeat = "Repeat",
|
||||||
|
RetryUntilSuccessful = "RetryUntilSuccessful",
|
||||||
|
KeepRunningUntilFailure = "KeepRunningUntilFailure",
|
||||||
|
Delay = "Delay",
|
||||||
|
RunOnce = "RunOnce",
|
||||||
|
}
|
||||||
|
interface ISystemPrimitive {
|
||||||
|
label: string;
|
||||||
|
group: string;
|
||||||
|
css?: React.CSSProperties;
|
||||||
|
input: boolean;
|
||||||
|
output: boolean;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class PrimitiveViewModel {
|
||||||
|
primitives: ISystemPrimitive[] = [
|
||||||
|
{
|
||||||
|
label: SystemPrimitive.Inverter,
|
||||||
|
group: SystemPrimitive.Decorator,
|
||||||
|
input: true,
|
||||||
|
output: true,
|
||||||
|
css: {
|
||||||
|
border: "1px solid #003E61",
|
||||||
|
borderRadius: 33,
|
||||||
|
background: "#BDE8AE",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: SystemPrimitive.ForceSuccess,
|
||||||
|
group: SystemPrimitive.Decorator,
|
||||||
|
input: true,
|
||||||
|
output: true,
|
||||||
|
css: {
|
||||||
|
border: "1px solid #003E61",
|
||||||
|
borderRadius: 33,
|
||||||
|
background: "#BDE8AE",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ label: SystemPrimitive.ForceFailure, group: SystemPrimitive.Decorator, input: true, output: true },
|
||||||
|
{ label: SystemPrimitive.Repeat, group: SystemPrimitive.Decorator, input: true, output: true },
|
||||||
|
{ label: SystemPrimitive.RetryUntilSuccessful, group: SystemPrimitive.Decorator, input: true, output: true },
|
||||||
|
{ label: SystemPrimitive.KeepRunningUntilFailure, group: SystemPrimitive.Decorator, input: true, output: true },
|
||||||
|
{ label: SystemPrimitive.Delay, group: SystemPrimitive.Decorator, input: true, output: true },
|
||||||
|
{ label: SystemPrimitive.RunOnce, group: SystemPrimitive.Decorator, input: true, output: true },
|
||||||
|
{ label: SystemPrimitive.ReactiveSequence, group: SystemPrimitive.Sequence, input: true, output: true },
|
||||||
|
{ label: SystemPrimitive.Sequence, group: SystemPrimitive.Sequence, input: true, output: true },
|
||||||
|
{ label: SystemPrimitive.SequenceWithMemory, group: SystemPrimitive.Sequence, input: true, output: true },
|
||||||
|
{ label: SystemPrimitive.ReactiveFallback, group: SystemPrimitive.Fallback, input: true, output: true },
|
||||||
|
{ label: SystemPrimitive.Fallback, group: SystemPrimitive.Fallback, input: true, output: true },
|
||||||
|
|
||||||
|
];
|
||||||
|
getPrimitiveAtLabel = (name: string) => {
|
||||||
|
return this.primitives.find((el) => el.label.isEqual(name));
|
||||||
|
}
|
||||||
|
toSkillView = () => this.primitives.reduce<ISkillView[]>((acc, primitive) => {
|
||||||
|
acc.rFind<ISkillView>((el) => el.name.isEqual(primitive.group)).fold(() => {
|
||||||
|
acc.at(acc.findIndex((eeee) => eeee.name === primitive.group))?.children?.push({ name: primitive.label })
|
||||||
|
}, () => { acc.push({ name: primitive.group, children: [{ name: primitive.label }] }) })
|
||||||
|
return acc;
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
}
|
|
@ -169,3 +169,4 @@ export const BehaviorTreeBuilderScreen = observer(() => {
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -23,6 +23,8 @@ import { v4 } from "uuid";
|
||||||
import { behaviorTreeBuilderStore } from "./behavior_tree_builder_screen";
|
import { behaviorTreeBuilderStore } from "./behavior_tree_builder_screen";
|
||||||
import clone from "just-clone";
|
import clone from "just-clone";
|
||||||
import { BehaviorTreeModel } from "../model/behavior_tree_model";
|
import { BehaviorTreeModel } from "../model/behavior_tree_model";
|
||||||
|
import { PrimitiveViewModel, SystemPrimitive } from "../model/primitive_view_model";
|
||||||
|
|
||||||
|
|
||||||
interface I2DArea {
|
interface I2DArea {
|
||||||
x: number;
|
x: number;
|
||||||
|
@ -41,10 +43,6 @@ export enum StoreUIType {
|
||||||
ViewBehaviorTree,
|
ViewBehaviorTree,
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum SystemPrimitive {
|
|
||||||
Sequence = "Sequence",
|
|
||||||
Fallback = "Fallback",
|
|
||||||
}
|
|
||||||
|
|
||||||
export class BehaviorTreeBuilderStore extends UiFormState<BehaviorTreeViewModel, CoreError> {
|
export class BehaviorTreeBuilderStore extends UiFormState<BehaviorTreeViewModel, CoreError> {
|
||||||
type: StoreUIType = StoreUIType.ViewBehaviorTree;
|
type: StoreUIType = StoreUIType.ViewBehaviorTree;
|
||||||
|
@ -67,6 +65,8 @@ export class BehaviorTreeBuilderStore extends UiFormState<BehaviorTreeViewModel,
|
||||||
navigate?: NavigateFunction;
|
navigate?: NavigateFunction;
|
||||||
editor?: NodeEditor<Schemes>;
|
editor?: NodeEditor<Schemes>;
|
||||||
areaPlugin?: AreaPlugin<Schemes, AreaExtra>;
|
areaPlugin?: AreaPlugin<Schemes, AreaExtra>;
|
||||||
|
nodeUpdateObserver?: NodeRerenderObserver;
|
||||||
|
primitiveViewModel: PrimitiveViewModel;
|
||||||
skillTree: ISkillView = {
|
skillTree: ISkillView = {
|
||||||
name: "",
|
name: "",
|
||||||
children: [
|
children: [
|
||||||
|
@ -74,20 +74,17 @@ export class BehaviorTreeBuilderStore extends UiFormState<BehaviorTreeViewModel,
|
||||||
name: "Действия",
|
name: "Действия",
|
||||||
children: [],
|
children: [],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "Примитивы BT",
|
|
||||||
children: [
|
|
||||||
{ name: "Fallback", interface: "Vector3", out: "vo" },
|
|
||||||
{ name: "Sequence", interface: "Vector3", out: "vo" },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
nodeUpdateObserver?: NodeRerenderObserver;
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super(DrawerState);
|
super(DrawerState);
|
||||||
makeAutoObservable(this);
|
makeAutoObservable(this);
|
||||||
this.type = StoreUIType.SelectBehaviorTree;
|
this.type = StoreUIType.SelectBehaviorTree;
|
||||||
|
|
||||||
|
this.primitiveViewModel = new PrimitiveViewModel()
|
||||||
|
this.skillTree.children?.push({ name: "Примитивы BT", children: this.primitiveViewModel.toSkillView() })
|
||||||
}
|
}
|
||||||
|
|
||||||
syncScene = async (editor: NodeEditor<Schemes>, area: AreaPlugin<Schemes, AreaExtra>) => {
|
syncScene = async (editor: NodeEditor<Schemes>, area: AreaPlugin<Schemes, AreaExtra>) => {
|
||||||
|
@ -95,7 +92,7 @@ export class BehaviorTreeBuilderStore extends UiFormState<BehaviorTreeViewModel,
|
||||||
this.areaPlugin = area;
|
this.areaPlugin = area;
|
||||||
};
|
};
|
||||||
|
|
||||||
errorHandingStrategy = (_: CoreError) => {};
|
errorHandingStrategy = (_: CoreError) => { };
|
||||||
|
|
||||||
dragEnd = (e: EventTarget) => {
|
dragEnd = (e: EventTarget) => {
|
||||||
if (this.canRun) {
|
if (this.canRun) {
|
||||||
|
@ -205,8 +202,8 @@ export class BehaviorTreeBuilderStore extends UiFormState<BehaviorTreeViewModel,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
dispose(): void {
|
dispose(): void {
|
||||||
document.removeEventListener("keyup", () => {});
|
document.removeEventListener("keyup", () => { });
|
||||||
document.removeEventListener("keydown", () => {});
|
document.removeEventListener("keydown", () => { });
|
||||||
}
|
}
|
||||||
onClickSaveBehaviorTree = async (): Promise<void> => {
|
onClickSaveBehaviorTree = async (): Promise<void> => {
|
||||||
this.filledOutTemplates.validation().fold(
|
this.filledOutTemplates.validation().fold(
|
||||||
|
@ -236,7 +233,7 @@ export class BehaviorTreeBuilderStore extends UiFormState<BehaviorTreeViewModel,
|
||||||
async () => message.error(`Дерево поведения не заполнено`)
|
async () => message.error(`Дерево поведения не заполнено`)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
validateBt() {}
|
validateBt() { }
|
||||||
createNewBehaviorTree = async () => {
|
createNewBehaviorTree = async () => {
|
||||||
this.viewModel.project = this.activeProject;
|
this.viewModel.project = this.activeProject;
|
||||||
this.viewModel.valid().fold(
|
this.viewModel.valid().fold(
|
||||||
|
@ -267,7 +264,6 @@ export class BehaviorTreeBuilderStore extends UiFormState<BehaviorTreeViewModel,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
formUpdateDependency = (dependency: Object, formType: string) => {
|
formUpdateDependency = (dependency: Object, formType: string) => {
|
||||||
this.edtDrawer(DrawerState.editThreadBehaviorTree, false);
|
|
||||||
this.filledOutTemplates?.skillBySid(this.selectedSid ?? "").fold(
|
this.filledOutTemplates?.skillBySid(this.selectedSid ?? "").fold(
|
||||||
(m) => {
|
(m) => {
|
||||||
const model = clone(m);
|
const model = clone(m);
|
||||||
|
@ -286,6 +282,7 @@ export class BehaviorTreeBuilderStore extends UiFormState<BehaviorTreeViewModel,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.filledOutTemplates.updateSkill(model);
|
this.filledOutTemplates.updateSkill(model);
|
||||||
|
console.log(this.filledOutTemplates)
|
||||||
},
|
},
|
||||||
() => console.log("UNKNOWN SID: " + this.selectedSid)
|
() => console.log("UNKNOWN SID: " + this.selectedSid)
|
||||||
);
|
);
|
||||||
|
@ -293,7 +290,8 @@ export class BehaviorTreeBuilderStore extends UiFormState<BehaviorTreeViewModel,
|
||||||
this.nodeUpdateObserver?.emit({ id: this.selectedSid, type: UpdateEvent.UPDATE });
|
this.nodeUpdateObserver?.emit({ id: this.selectedSid, type: UpdateEvent.UPDATE });
|
||||||
};
|
};
|
||||||
getBodyNode(label: string, sid: string): React.ReactNode | null {
|
getBodyNode(label: string, sid: string): React.ReactNode | null {
|
||||||
if (Object.keys(SystemPrimitive).includes(label)) {
|
const primitive = this.primitiveViewModel.getPrimitiveAtLabel(label);
|
||||||
|
if (primitive) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
|
@ -323,18 +321,24 @@ export class BehaviorTreeBuilderStore extends UiFormState<BehaviorTreeViewModel,
|
||||||
}
|
}
|
||||||
isFilledInput = (type: string, sid: string): boolean => this.filledOutTemplates?.dependencyIsFilled(type, sid);
|
isFilledInput = (type: string, sid: string): boolean => this.filledOutTemplates?.dependencyIsFilled(type, sid);
|
||||||
getInputs(name: string) {
|
getInputs(name: string) {
|
||||||
if (Object.keys(SystemPrimitive).includes(name)) {
|
const result = this.primitiveViewModel.getPrimitiveAtLabel(name)
|
||||||
|
if (result) {
|
||||||
return {
|
return {
|
||||||
input: "a",
|
input: result.input ? "a" : null,
|
||||||
output: "a",
|
output: result.output ? "a" : null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
input: "a",
|
input: 'a',
|
||||||
output: null,
|
output: null
|
||||||
};
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
getStylesByLabelNode(label: string): React.CSSProperties | undefined {
|
getStylesByLabelNode(label: string): React.CSSProperties | undefined {
|
||||||
|
const result = this.primitiveViewModel.getPrimitiveAtLabel(label)
|
||||||
|
if (result) {
|
||||||
|
return clone(result.css as Object);
|
||||||
|
}
|
||||||
if (label.isEqual(SystemPrimitive.Fallback)) {
|
if (label.isEqual(SystemPrimitive.Fallback)) {
|
||||||
return {
|
return {
|
||||||
border: "1px solid #003E61",
|
border: "1px solid #003E61",
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
import React from "react"
|
||||||
|
import { observer } from "mobx-react-lite"
|
||||||
|
import { message } from "antd";
|
||||||
|
import { CameraDeviceStore } from "./camera_device_store"
|
||||||
|
import { IPropsForm } from "../forms";
|
||||||
|
import { IDeviceDependency } from "../../../../../../core/model/skill_model";
|
||||||
|
import { CoreButton } from "../../../../../../core/ui/button/button";
|
||||||
|
import { CoreSelect } from "../../../../../../core/ui/select/select";
|
||||||
|
import { CoreText, CoreTextType } from "../../../../../../core/ui/text/text";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const CameraDeviceForm = observer((props: IPropsForm<Partial<IDeviceDependency>>) => {
|
||||||
|
const [store] = React.useState(() => new CameraDeviceStore());
|
||||||
|
React.useEffect(() => {
|
||||||
|
store.init();
|
||||||
|
}, [store]);
|
||||||
|
return (
|
||||||
|
<div style={{ border: '1px solid', margin: 10, padding: 10 }}>
|
||||||
|
<CoreText text={"Cameras"} type={CoreTextType.header} style={{ padding: 10 }} />
|
||||||
|
<CoreSelect
|
||||||
|
items={store.cameras?.camera.map((el) => el.sid) ?? []}
|
||||||
|
value={props.dependency?.sid ?? ""}
|
||||||
|
label={'Выберите камеру'}
|
||||||
|
onChange={(value: string) =>
|
||||||
|
store.updateForm({ sid: value })
|
||||||
|
|
||||||
|
} />
|
||||||
|
<CoreButton style={{ margin: 10, width: 100 }} text="OK" onClick={() => {
|
||||||
|
store.viewModel.valid().fold((s) => {
|
||||||
|
props.onChange(s);
|
||||||
|
}, (e) => message.error(e))
|
||||||
|
}} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})
|
|
@ -0,0 +1,6 @@
|
||||||
|
import { Cameras } from "../../../../../../core/model/cameras";
|
||||||
|
import { HttpMethod, HttpRepository } from "../../../../../../core/repository/http_repository";
|
||||||
|
|
||||||
|
export class CameraDeviceHttpRepository extends HttpRepository {
|
||||||
|
getAllCameras = () => this._jsonRequest<Cameras>(HttpMethod.GET, '/behavior/trees/cameras');
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
import makeAutoObservable from "mobx-store-inheritance";
|
||||||
|
import { NavigateFunction } from "react-router-dom";
|
||||||
|
import { Cameras } from "../../../../../../core/model/cameras";
|
||||||
|
import { SidViewModel } from "../../../../../../core/model/device_dependency_view_model";
|
||||||
|
import { FormState, CoreError } from "../../../../../../core/store/base_store";
|
||||||
|
import { CameraDeviceHttpRepository } from "./camera_device_form_http_repository";
|
||||||
|
|
||||||
|
export class CameraDeviceStore extends FormState<SidViewModel, CoreError> {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
makeAutoObservable(this);
|
||||||
|
}
|
||||||
|
viewModel: SidViewModel = SidViewModel.empty();
|
||||||
|
cameras?: Cameras;
|
||||||
|
cameraDeviceHttpRepository: CameraDeviceHttpRepository = new CameraDeviceHttpRepository();
|
||||||
|
errorHandingStrategy = (error: CoreError) => { }
|
||||||
|
init = async (navigate?: NavigateFunction | undefined) => {
|
||||||
|
this.mapOk('cameras', this.cameraDeviceHttpRepository.getAllCameras())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,6 +1,8 @@
|
||||||
import { SimpleForm } from "./simple_form";
|
|
||||||
import { TestForm } from "./test";
|
import { CameraDeviceForm } from "./camera_device_form/camera_device_form_form";
|
||||||
import WeightsForm from "./weights_form";
|
import { RobotDeviceForm } from "./robot_device_form/robot_device_form_form";
|
||||||
|
import { TopicsForm } from "./topics_form/topics_form";
|
||||||
|
import { WeightsForm } from "./weights_form/weights_form";
|
||||||
|
|
||||||
export interface IPropsForm<T> {
|
export interface IPropsForm<T> {
|
||||||
dependency: T;
|
dependency: T;
|
||||||
|
@ -11,9 +13,15 @@ export interface IForms {
|
||||||
name: string;
|
name: string;
|
||||||
component: JSX.Element;
|
component: JSX.Element;
|
||||||
}
|
}
|
||||||
|
export enum Form {
|
||||||
|
weights = 'weights',
|
||||||
|
robotName = 'robot_name',
|
||||||
|
cameraDeviceForm = 'camera',
|
||||||
|
topic = 'topic'
|
||||||
|
}
|
||||||
export const forms = (props: any, onChange: (dependency: Object) => void): IForms[] => [
|
export const forms = (props: any, onChange: (dependency: Object) => void): IForms[] => [
|
||||||
{ name: "weights", component: <WeightsForm dependency={props} onChange={onChange} /> },
|
{ name: Form.weights, component: <WeightsForm dependency={props} onChange={onChange} /> },
|
||||||
{ name: "simple", component: <SimpleForm dependency={props} onChange={onChange} /> },
|
{ name: Form.robotName, component: <RobotDeviceForm dependency={props} onChange={onChange} /> },
|
||||||
{ name: "test", component: <TestForm dependency={props} onChange={onChange} /> },
|
{ name: Form.cameraDeviceForm, component: <CameraDeviceForm dependency={props} onChange={onChange} /> },
|
||||||
|
{ name: Form.topic, component: <TopicsForm dependency={props} onChange={onChange} /> }
|
||||||
];
|
];
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
import React from "react";
|
||||||
|
import { observer } from "mobx-react-lite";
|
||||||
|
import { RobotDeviceFormStore } from "./robot_device_form_store";
|
||||||
|
import { IPropsForm } from "../forms";
|
||||||
|
import { SidViewModel } from "../../../../../../core/model/device_dependency_view_model";
|
||||||
|
|
||||||
|
export const RobotDeviceForm = observer((props: IPropsForm<Partial<SidViewModel>>) => {
|
||||||
|
const [store] = React.useState(() => new RobotDeviceFormStore());
|
||||||
|
React.useEffect(() => {
|
||||||
|
store.init();
|
||||||
|
}, [store]);
|
||||||
|
|
||||||
|
return <div></div>;
|
||||||
|
});
|
|
@ -0,0 +1,5 @@
|
||||||
|
import { HttpRepository } from "../../../../../../core/repository/http_repository";
|
||||||
|
|
||||||
|
export class RobotDeviceFormHttpRepository extends HttpRepository {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { makeAutoObservable } from "mobx";
|
||||||
|
import { NavigateFunction } from "react-router-dom";
|
||||||
|
import { FormState, CoreError } from "../../../../../../core/store/base_store";
|
||||||
|
import { RobotDeviceFormHttpRepository } from "./robot_device_form_http_repository";
|
||||||
|
import { SidViewModel } from "../../../../../../core/model/device_dependency_view_model";
|
||||||
|
|
||||||
|
export class RobotDeviceFormStore extends FormState<SidViewModel, CoreError> {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
makeAutoObservable(this);
|
||||||
|
}
|
||||||
|
viewModel: SidViewModel = SidViewModel.empty();
|
||||||
|
cameraDeviceHttpRepository: RobotDeviceFormHttpRepository = new RobotDeviceFormHttpRepository();
|
||||||
|
errorHandingStrategy = (error: CoreError) => { }
|
||||||
|
init = async (navigate?: NavigateFunction | undefined) => {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,14 +0,0 @@
|
||||||
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>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -1,13 +0,0 @@
|
||||||
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>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
import makeAutoObservable from "mobx-store-inheritance";
|
||||||
|
import { Result } from "../../../../../../core/helper/result";
|
||||||
|
import { SidViewModel } from "../../../../../../core/model/device_dependency_view_model";
|
||||||
|
|
||||||
|
export class TopicDependencyViewModel extends SidViewModel {
|
||||||
|
axis: boolean;
|
||||||
|
constructor(sid: string, axis: boolean) {
|
||||||
|
super(sid);
|
||||||
|
this.axis = axis;
|
||||||
|
makeAutoObservable(this)
|
||||||
|
}
|
||||||
|
isValid = (): Result<string, void> => {
|
||||||
|
return Result.ok();
|
||||||
|
}
|
||||||
|
static empty() {
|
||||||
|
return new TopicDependencyViewModel('', false);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
import React from "react";
|
||||||
|
import { observer } from "mobx-react-lite";
|
||||||
|
import { TopicsFormStore } from "./topics_form_store";
|
||||||
|
import { IPropsForm } from "../forms";
|
||||||
|
import { TopicDependencyViewModel } from "./topic_dependency_view_model";
|
||||||
|
import { CoreText, CoreTextType } from "../../../../../../core/ui/text/text";
|
||||||
|
import { CoreSwitch } from "../../../../../../core/ui/switch/switch";
|
||||||
|
import { CoreSelect } from "../../../../../../core/ui/select/select";
|
||||||
|
import { CoreButton } from "../../../../../../core/ui/button/button";
|
||||||
|
import { message } from "antd";
|
||||||
|
|
||||||
|
export const TopicsForm = observer((props: IPropsForm<Partial<TopicDependencyViewModel>>) => {
|
||||||
|
const [store] = React.useState(() => new TopicsFormStore());
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
store.init();
|
||||||
|
}, [store, props]);
|
||||||
|
|
||||||
|
return <div style={{ border: '1px solid', margin: 10, padding: 10 }}>
|
||||||
|
<CoreText text={"Topics"} type={CoreTextType.header} style={{ padding: 10 }} />
|
||||||
|
<CoreSelect
|
||||||
|
items={store.topics?.topics?.map((el) => el.name) ?? []}
|
||||||
|
value={props.dependency?.sid ?? ""}
|
||||||
|
label={'Выберите топик'}
|
||||||
|
onChange={(value: string) =>
|
||||||
|
store.updateForm({ sid: value })
|
||||||
|
} />
|
||||||
|
|
||||||
|
<div>is input: <CoreSwitch isSelected={store.viewModel.axis} id={""} onChange={(status: boolean, id: string) => {
|
||||||
|
console.log(200)
|
||||||
|
store.updateForm({ axis: !status })
|
||||||
|
|
||||||
|
}} /></div>
|
||||||
|
<CoreButton style={{ margin: 10, width: 100 }} text="OK" onClick={() => {
|
||||||
|
store.viewModel.valid().fold((s) => {
|
||||||
|
props.onChange(s);
|
||||||
|
}, (e) => message.error(e))
|
||||||
|
}} />
|
||||||
|
</div>;
|
||||||
|
});
|
|
@ -0,0 +1,6 @@
|
||||||
|
import { Topics } from "../../../../../../core/model/topics";
|
||||||
|
import { HttpMethod, HttpRepository } from "../../../../../../core/repository/http_repository";
|
||||||
|
|
||||||
|
export class TopicsFormHttpRepository extends HttpRepository {
|
||||||
|
getAllTopics = () => this._jsonRequest<Topics>(HttpMethod.GET, '/behavior/trees/topics');
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
import makeAutoObservable from "mobx-store-inheritance";
|
||||||
|
import { NavigateFunction } from "react-router-dom";
|
||||||
|
import { TopicDependencyViewModel } from "./topic_dependency_view_model";
|
||||||
|
import { FormState, CoreError } from "../../../../../../core/store/base_store";
|
||||||
|
import { TopicsFormHttpRepository } from "./topics_form_http_repository";
|
||||||
|
import { Topics } from "../../../../../../core/model/topics";
|
||||||
|
|
||||||
|
export class TopicsFormStore extends FormState<TopicDependencyViewModel, CoreError> {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
makeAutoObservable(this);
|
||||||
|
}
|
||||||
|
topics?: Topics;
|
||||||
|
viewModel: TopicDependencyViewModel = TopicDependencyViewModel.empty();
|
||||||
|
cameraDeviceHttpRepository: TopicsFormHttpRepository = new TopicsFormHttpRepository();
|
||||||
|
errorHandingStrategy = (error: CoreError) => { }
|
||||||
|
init = async (navigate?: NavigateFunction | undefined) => {
|
||||||
|
await this.mapOk('topics', this.cameraDeviceHttpRepository.getAllTopics())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,173 +0,0 @@
|
||||||
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 { 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";
|
|
||||||
|
|
||||||
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> {
|
|
||||||
weights?: ISkils[];
|
|
||||||
assets?: Assets;
|
|
||||||
suitableWeights: string[] = [];
|
|
||||||
viewModel: WeightsViewModel = WeightsViewModel.empty();
|
|
||||||
skillsHttpRepository: SkillsHttpRepository = new SkillsHttpRepository();
|
|
||||||
datasetHttpRepository: DataSetHttpRepository = new DataSetHttpRepository();
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
makeAutoObservable(this);
|
|
||||||
}
|
|
||||||
init = async () => {
|
|
||||||
await this.mapOk("weights", this.skillsHttpRepository.getAllSkills());
|
|
||||||
await this.mapOk("assets", this.datasetHttpRepository.getAssetsActiveProject());
|
|
||||||
};
|
|
||||||
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);
|
|
||||||
|
|
||||||
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;
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
import { observer } from "mobx-react-lite";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
import { Select, message } from "antd";
|
||||||
|
import { WeightsFormStore } from "./weights_store";
|
||||||
|
import { IWeightsDependency } from "../../../../../../core/model/skill_model";
|
||||||
|
import { CoreButton } from "../../../../../../core/ui/button/button";
|
||||||
|
import { CoreInput } from "../../../../../../core/ui/input/input";
|
||||||
|
import { CoreText, CoreTextType } from "../../../../../../core/ui/text/text";
|
||||||
|
|
||||||
|
|
||||||
|
interface IWeightsFormProps {
|
||||||
|
dependency?: IWeightsDependency;
|
||||||
|
onChange: (dependency: IWeightsDependency) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WeightsForm = observer((props: IWeightsFormProps) => {
|
||||||
|
const [store] = React.useState(() => new WeightsFormStore());
|
||||||
|
React.useEffect(() => {
|
||||||
|
store.init();
|
||||||
|
}, [store]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ border: '1px solid', padding: 10, margin: 10 }}>
|
||||||
|
<CoreText text="Weights" type={CoreTextType.header} style={{ padding: 10 }} />
|
||||||
|
<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>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
import makeAutoObservable from "mobx-store-inheritance";
|
||||||
|
import { FormState, CoreError } from "../../../../../../core/store/base_store";
|
||||||
|
import { DataSetHttpRepository } from "../../../../../dataset/dataset_http_repository";
|
||||||
|
import { Assets } from "../../../../../dataset/dataset_model";
|
||||||
|
import { ISkils, SkillsHttpRepository } from "../../../../../skils/skills_http_repository";
|
||||||
|
import { WeightsViewModel } from "./weights_view_model";
|
||||||
|
|
||||||
|
export class WeightsFormStore extends FormState<WeightsViewModel, CoreError> {
|
||||||
|
weights?: ISkils[];
|
||||||
|
assets?: Assets;
|
||||||
|
suitableWeights: string[] = [];
|
||||||
|
viewModel: WeightsViewModel = WeightsViewModel.empty();
|
||||||
|
skillsHttpRepository: SkillsHttpRepository = new SkillsHttpRepository();
|
||||||
|
datasetHttpRepository: DataSetHttpRepository = new DataSetHttpRepository();
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
makeAutoObservable(this);
|
||||||
|
}
|
||||||
|
init = async () => {
|
||||||
|
await this.mapOk("weights", this.skillsHttpRepository.getAllSkills());
|
||||||
|
await this.mapOk("assets", this.datasetHttpRepository.getAssetsActiveProject());
|
||||||
|
};
|
||||||
|
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);
|
||||||
|
|
||||||
|
this.updateForm({ weights_file: `${model?.datasetId.local_path}/weights/${model?.name}/${model?.name}.pt` });
|
||||||
|
};
|
||||||
|
errorHandingStrategy = (_: CoreError) => { };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
import { Result } from "../../../../../../core/helper/result";
|
||||||
|
import { IWeightsDependency } from "../../../../../../core/model/skill_model";
|
||||||
|
|
||||||
|
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);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue