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 { GetBehaviorTreeActiveProjectScenario } from "./domain/get_behavior_tree_active_project_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> {
|
||||
constructor() {
|
||||
|
@ -25,5 +28,21 @@ export class BehaviorTreesPresentation extends CrudController<BehaviorTreeValida
|
|||
subUrl: "by_id",
|
||||
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({
|
||||
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" },
|
||||
Launch: { package: "rbs_perception", executable: "pe_dope_lc.py", name: "lc_dope" },
|
||||
BTAction: [
|
||||
|
@ -18,6 +18,14 @@ export class GetBehaviorTreeSkillsTemplatesUseCase extends CallbackStrategyWithE
|
|||
type: "weights",
|
||||
dependency: {},
|
||||
},
|
||||
{
|
||||
type: "camera",
|
||||
dependency: {}
|
||||
},
|
||||
{
|
||||
type: "topic",
|
||||
dependency: {}
|
||||
}
|
||||
],
|
||||
result: ["POSE"],
|
||||
},
|
||||
|
@ -38,6 +46,28 @@ export class GetBehaviorTreeSkillsTemplatesUseCase extends CallbackStrategyWithE
|
|||
{ 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;
|
||||
}
|
||||
export interface IWeightsDependency {
|
||||
weights_name:string;
|
||||
weights_name: string;
|
||||
object_name: string;
|
||||
weights_file: string;
|
||||
dimensions: number[];
|
||||
}
|
||||
export interface IDeviceDependency {
|
||||
sid: string;
|
||||
}
|
||||
|
||||
export interface IParam {
|
||||
type: string;
|
||||
|
@ -195,7 +198,7 @@ export class SkillModel implements ISkill {
|
|||
}
|
||||
|
||||
export class SkillDependency implements IDependency {
|
||||
constructor(public skills: ISkillDependency[]) {}
|
||||
constructor(public skills: ISkillDependency[]) { }
|
||||
static empty() {
|
||||
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) => {
|
||||
return (await this.httpHelper(callBack)).fold(
|
||||
(s) => {
|
||||
(_s) => {
|
||||
if (report && report.successMessage) message.success(report.successMessage);
|
||||
},
|
||||
(e) => {
|
||||
(_e) => {
|
||||
if (report && report.errorMessage) message.error(report.errorMessage);
|
||||
}
|
||||
);
|
||||
|
|
|
@ -1,81 +1,75 @@
|
|||
import * as React from "react";
|
||||
import { IStyle } from "../../model/style";
|
||||
|
||||
export enum CoreTextType {
|
||||
header,
|
||||
medium,
|
||||
large,
|
||||
small,
|
||||
header = 'header',
|
||||
medium = 'medium',
|
||||
large = 'large',
|
||||
small = 'small',
|
||||
}
|
||||
|
||||
export interface ITextProps {
|
||||
export interface ITextProps extends IStyle {
|
||||
text: string;
|
||||
type: CoreTextType;
|
||||
color?: string;
|
||||
}
|
||||
|
||||
export function CoreText(props: ITextProps) {
|
||||
if (props.type === CoreTextType.small) {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
color: props.color ?? "rgba(73, 69, 79, 1)",
|
||||
fontSize: 12,
|
||||
fontFamily: "Roboto",
|
||||
fontWeight: 400,
|
||||
fontSizeAdjust: 14,
|
||||
textOverflow: "ellipsis",
|
||||
}}
|
||||
>
|
||||
{props.text}
|
||||
</div>
|
||||
);
|
||||
const getStyle = (type: CoreTextType, color: string | undefined) => {
|
||||
if (type.isEqual(CoreTextType.small)) return {
|
||||
color: color ?? "rgba(73, 69, 79, 1)",
|
||||
fontSize: 12,
|
||||
fontFamily: "Roboto",
|
||||
fontWeight: 400,
|
||||
fontSizeAdjust: 14,
|
||||
textOverflow: "ellipsis",
|
||||
}
|
||||
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,
|
||||
}}
|
||||
>
|
||||
{props.text}
|
||||
</div>
|
||||
);
|
||||
return <div>{props.text}</div>;
|
||||
if (type.isEqual(CoreTextType.large)) return {
|
||||
color: color ?? "#1D1B20",
|
||||
fontSize: 16,
|
||||
fontFamily: "Roboto",
|
||||
fontWeight: 400,
|
||||
fontSizeAdjust: 14,
|
||||
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 clone from "just-clone";
|
||||
import { BehaviorTreeModel } from "../model/behavior_tree_model";
|
||||
import { PrimitiveViewModel, SystemPrimitive } from "../model/primitive_view_model";
|
||||
|
||||
|
||||
interface I2DArea {
|
||||
x: number;
|
||||
|
@ -41,10 +43,6 @@ export enum StoreUIType {
|
|||
ViewBehaviorTree,
|
||||
}
|
||||
|
||||
export enum SystemPrimitive {
|
||||
Sequence = "Sequence",
|
||||
Fallback = "Fallback",
|
||||
}
|
||||
|
||||
export class BehaviorTreeBuilderStore extends UiFormState<BehaviorTreeViewModel, CoreError> {
|
||||
type: StoreUIType = StoreUIType.ViewBehaviorTree;
|
||||
|
@ -67,6 +65,8 @@ export class BehaviorTreeBuilderStore extends UiFormState<BehaviorTreeViewModel,
|
|||
navigate?: NavigateFunction;
|
||||
editor?: NodeEditor<Schemes>;
|
||||
areaPlugin?: AreaPlugin<Schemes, AreaExtra>;
|
||||
nodeUpdateObserver?: NodeRerenderObserver;
|
||||
primitiveViewModel: PrimitiveViewModel;
|
||||
skillTree: ISkillView = {
|
||||
name: "",
|
||||
children: [
|
||||
|
@ -74,20 +74,17 @@ export class BehaviorTreeBuilderStore extends UiFormState<BehaviorTreeViewModel,
|
|||
name: "Действия",
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
name: "Примитивы BT",
|
||||
children: [
|
||||
{ name: "Fallback", interface: "Vector3", out: "vo" },
|
||||
{ name: "Sequence", interface: "Vector3", out: "vo" },
|
||||
],
|
||||
},
|
||||
|
||||
],
|
||||
};
|
||||
nodeUpdateObserver?: NodeRerenderObserver;
|
||||
|
||||
constructor() {
|
||||
super(DrawerState);
|
||||
makeAutoObservable(this);
|
||||
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>) => {
|
||||
|
@ -95,7 +92,7 @@ export class BehaviorTreeBuilderStore extends UiFormState<BehaviorTreeViewModel,
|
|||
this.areaPlugin = area;
|
||||
};
|
||||
|
||||
errorHandingStrategy = (_: CoreError) => {};
|
||||
errorHandingStrategy = (_: CoreError) => { };
|
||||
|
||||
dragEnd = (e: EventTarget) => {
|
||||
if (this.canRun) {
|
||||
|
@ -205,8 +202,8 @@ export class BehaviorTreeBuilderStore extends UiFormState<BehaviorTreeViewModel,
|
|||
};
|
||||
}
|
||||
dispose(): void {
|
||||
document.removeEventListener("keyup", () => {});
|
||||
document.removeEventListener("keydown", () => {});
|
||||
document.removeEventListener("keyup", () => { });
|
||||
document.removeEventListener("keydown", () => { });
|
||||
}
|
||||
onClickSaveBehaviorTree = async (): Promise<void> => {
|
||||
this.filledOutTemplates.validation().fold(
|
||||
|
@ -236,7 +233,7 @@ export class BehaviorTreeBuilderStore extends UiFormState<BehaviorTreeViewModel,
|
|||
async () => message.error(`Дерево поведения не заполнено`)
|
||||
);
|
||||
};
|
||||
validateBt() {}
|
||||
validateBt() { }
|
||||
createNewBehaviorTree = async () => {
|
||||
this.viewModel.project = this.activeProject;
|
||||
this.viewModel.valid().fold(
|
||||
|
@ -267,7 +264,6 @@ export class BehaviorTreeBuilderStore extends UiFormState<BehaviorTreeViewModel,
|
|||
}
|
||||
};
|
||||
formUpdateDependency = (dependency: Object, formType: string) => {
|
||||
this.edtDrawer(DrawerState.editThreadBehaviorTree, false);
|
||||
this.filledOutTemplates?.skillBySid(this.selectedSid ?? "").fold(
|
||||
(m) => {
|
||||
const model = clone(m);
|
||||
|
@ -286,6 +282,7 @@ export class BehaviorTreeBuilderStore extends UiFormState<BehaviorTreeViewModel,
|
|||
});
|
||||
|
||||
this.filledOutTemplates.updateSkill(model);
|
||||
console.log(this.filledOutTemplates)
|
||||
},
|
||||
() => 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 });
|
||||
};
|
||||
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 (
|
||||
|
@ -323,18 +321,24 @@ export class BehaviorTreeBuilderStore extends UiFormState<BehaviorTreeViewModel,
|
|||
}
|
||||
isFilledInput = (type: string, sid: string): boolean => this.filledOutTemplates?.dependencyIsFilled(type, sid);
|
||||
getInputs(name: string) {
|
||||
if (Object.keys(SystemPrimitive).includes(name)) {
|
||||
const result = this.primitiveViewModel.getPrimitiveAtLabel(name)
|
||||
if (result) {
|
||||
return {
|
||||
input: "a",
|
||||
output: "a",
|
||||
input: result.input ? "a" : null,
|
||||
output: result.output ? "a" : null,
|
||||
};
|
||||
}
|
||||
return {
|
||||
input: "a",
|
||||
output: null,
|
||||
};
|
||||
input: 'a',
|
||||
output: null
|
||||
}
|
||||
|
||||
}
|
||||
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)) {
|
||||
return {
|
||||
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 WeightsForm from "./weights_form";
|
||||
|
||||
import { CameraDeviceForm } from "./camera_device_form/camera_device_form_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> {
|
||||
dependency: T;
|
||||
|
@ -11,9 +13,15 @@ export interface IForms {
|
|||
name: string;
|
||||
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[] => [
|
||||
{ name: "weights", component: <WeightsForm dependency={props} onChange={onChange} /> },
|
||||
{ name: "simple", component: <SimpleForm dependency={props} onChange={onChange} /> },
|
||||
{ name: "test", component: <TestForm dependency={props} onChange={onChange} /> },
|
||||
{ name: Form.weights, component: <WeightsForm dependency={props} onChange={onChange} /> },
|
||||
{ name: Form.robotName, component: <RobotDeviceForm 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