skills screen
This commit is contained in:
parent
cac5fad8ce
commit
a920f5fb45
49 changed files with 681 additions and 168 deletions
|
@ -9,6 +9,7 @@ import { CalculationsInstancesPresentation } from "../../features/calculations_i
|
|||
import { DigitalTwinsInstancePresentation } from "../../features/digital_twins_instance/digital_twins_instance_presentation";
|
||||
import { DigitalTwinsTemplatePresentation } from "../../features/digital_twins_template/digital_twins_template_presentation";
|
||||
import { TopicsPresentation } from "../../features/topics/topics_presentation";
|
||||
import { SkillsPresentation } from "../../features/skills/skill_presentation";
|
||||
|
||||
extensions();
|
||||
|
||||
|
@ -22,4 +23,5 @@ export const httpRoutes: Routes[] = [
|
|||
new DigitalTwinsTemplatePresentation(),
|
||||
new DigitalTwinsInstancePresentation(),
|
||||
new TopicsPresentation(),
|
||||
new SkillsPresentation(),
|
||||
].map((el) => el.call());
|
||||
|
|
|
@ -1,7 +1,26 @@
|
|||
import { IsArray, IsString, ValidateNested } from "class-validator";
|
||||
import { IsArray, IsEnum, IsNotEmpty, IsString, ValidateNested } from "class-validator";
|
||||
import { Type } from "class-transformer";
|
||||
export class TopicViewModel {
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
name: string;
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
type: string;
|
||||
constructor(name: string, type: string) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
}
|
||||
static empty() {
|
||||
return new TopicViewModel("", "");
|
||||
}
|
||||
}
|
||||
|
||||
export interface SkillPoseEstimation {
|
||||
export interface IParam {
|
||||
type: string;
|
||||
dependency: Object;
|
||||
}
|
||||
export interface Skill {
|
||||
SkillPackage: ISkillPackage;
|
||||
Module: IModule;
|
||||
Launch: ILaunch;
|
||||
|
@ -16,7 +35,7 @@ export interface IBTAction {
|
|||
name: string;
|
||||
format: string;
|
||||
type: string;
|
||||
param: string[];
|
||||
param: IParam[];
|
||||
result: string[];
|
||||
}
|
||||
|
||||
|
@ -82,7 +101,7 @@ export class BTAction implements IBTAction {
|
|||
@IsString()
|
||||
type: string;
|
||||
@IsArray()
|
||||
param: string[];
|
||||
param: IParam[];
|
||||
@IsArray()
|
||||
result: string[];
|
||||
}
|
||||
|
@ -117,7 +136,7 @@ export class Xxx implements IXxx {
|
|||
topicImage: string;
|
||||
topicCameraInfo: string;
|
||||
}
|
||||
export class SkillModelPoseEstimation implements SkillPoseEstimation {
|
||||
export class SkillModel implements Skill {
|
||||
@ValidateNested()
|
||||
@Type(() => SkillPackage)
|
||||
SkillPackage: ISkillPackage;
|
||||
|
@ -145,3 +164,26 @@ export class SkillModelPoseEstimation implements SkillPoseEstimation {
|
|||
@Type(() => Xxx)
|
||||
xxx: IXxx;
|
||||
}
|
||||
|
||||
export enum BtAction {
|
||||
ACTION = "ACTION",
|
||||
CONDITION = "CONDITION",
|
||||
}
|
||||
export class BtActionViewModel implements IBTAction {
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
name: string;
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
format: string;
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
type: string;
|
||||
param: IParam[];
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
result: string[];
|
||||
@IsNotEmpty()
|
||||
@IsEnum(BtAction)
|
||||
typeAction: BtAction;
|
||||
}
|
||||
|
|
|
@ -13,3 +13,4 @@ export class CreateManyFolderScenario {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,27 @@ export class GetBehaviorTreeSkillsTemplatesUseCase extends CallbackStrategyWithE
|
|||
{ name: "end_effector_acceleration", value: 1.0 },
|
||||
],
|
||||
},
|
||||
{
|
||||
Module: { name: "MMM", description: "Move to Pose skill with cartesian controllers" },
|
||||
topicOut: [
|
||||
{
|
||||
topicName: "",
|
||||
topicType: "",
|
||||
},
|
||||
],
|
||||
BTAction: [
|
||||
{
|
||||
name: "move",
|
||||
type: "action",
|
||||
param: [
|
||||
{
|
||||
type: "move_to_pose",
|
||||
dependency: {},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
SkillPackage: { name: "Robossembler", version: "1.0", format: "1", type: "Action" },
|
||||
Module: { name: "PoseEstimation", description: "Pose Estimation skill with DOPE" },
|
||||
|
|
25
server/src/features/skills/model/skills_database_model.ts
Normal file
25
server/src/features/skills/model/skills_database_model.ts
Normal file
|
@ -0,0 +1,25 @@
|
|||
import { Schema, model } from "mongoose";
|
||||
|
||||
export interface ISkillsModel {}
|
||||
|
||||
export const SkillsSchema = new Schema({
|
||||
SkillPackage: {
|
||||
type: Schema.Types.Mixed,
|
||||
},
|
||||
Module: {
|
||||
type: Schema.Types.Mixed,
|
||||
},
|
||||
BTAction: {
|
||||
type: Schema.Types.Mixed,
|
||||
},
|
||||
topicsOut: {
|
||||
type: Schema.Types.Mixed,
|
||||
},
|
||||
param: {
|
||||
type: Schema.Types.Mixed,
|
||||
},
|
||||
}).plugin(require("mongoose-autopopulate"));
|
||||
|
||||
export const skillsSchema = "skills";
|
||||
|
||||
export const SkillsDBModel = model<ISkillsModel>(skillsSchema, SkillsSchema);
|
17
server/src/features/skills/model/skills_validation_model.ts
Normal file
17
server/src/features/skills/model/skills_validation_model.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { Type } from "class-transformer";
|
||||
import { IsOptional, IsString, ValidateNested } from "class-validator";
|
||||
import { SkillPackage, ISkillPackage, Module, IModule, BtActionViewModel, TopicViewModel } from "../../../core/models/skill_model";
|
||||
|
||||
export class SkillInstanceValidationModel {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
sid?: string;
|
||||
@ValidateNested()
|
||||
@Type(() => SkillPackage)
|
||||
SkillPackage: ISkillPackage;
|
||||
@ValidateNested()
|
||||
@Type(() => Module)
|
||||
Module: IModule;
|
||||
BTAction: BtActionViewModel[];
|
||||
topicsOut: TopicViewModel[] = [];
|
||||
}
|
12
server/src/features/skills/skill_presentation.ts
Normal file
12
server/src/features/skills/skill_presentation.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
import { CrudController } from "../../core/controllers/crud_controller";
|
||||
import { SkillInstanceValidationModel } from "./model/skills_validation_model";
|
||||
import { SkillsDBModel } from "./model/skills_database_model";
|
||||
export class SkillsPresentation extends CrudController<SkillInstanceValidationModel, typeof SkillsDBModel> {
|
||||
constructor() {
|
||||
super({
|
||||
url: "skills",
|
||||
validationModel: SkillInstanceValidationModel,
|
||||
databaseModel: SkillsDBModel,
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
import { Schema, model } from "mongoose";
|
||||
|
||||
export interface ISkillsInstanceModel {}
|
||||
|
||||
export const SkillsInstanceSchema = new Schema({}).plugin(require("mongoose-autopopulate"));
|
||||
|
||||
export const skillsInstanceSchema = "skills_instances";
|
||||
|
||||
export const SkillsInstanceDBModel = model<ISkillsInstanceModel>(skillsInstanceSchema, SkillsInstanceSchema);
|
|
@ -1 +0,0 @@
|
|||
export class SkillInstanceValidationModel {}
|
|
@ -1 +0,0 @@
|
|||
export class SkillsInstancePresentation {}
|
|
@ -1,9 +0,0 @@
|
|||
import { Schema, model } from "mongoose";
|
||||
|
||||
export interface ISkillsTemplateModel {}
|
||||
|
||||
export const SkillsTemplateSchema = new Schema({}).plugin(require("mongoose-autopopulate"));
|
||||
|
||||
export const skillsTemplateSchema = "skills_templates";
|
||||
|
||||
export const SkillsTemplateDBModel = model<ISkillsTemplateModel>(skillsTemplateSchema, SkillsTemplateSchema);
|
|
@ -1 +0,0 @@
|
|||
export class SkillTemplateValidationModel {}
|
|
@ -1 +0,0 @@
|
|||
export class SkillsTemplatePresentation {}
|
|
@ -31,6 +31,24 @@ export const ArrayExtensions = () => {
|
|||
return true;
|
||||
};
|
||||
}
|
||||
if ([].replacePropIndex === undefined) {
|
||||
Array.prototype.replacePropIndex = function (property, index) {
|
||||
return this.map((element, i) => {
|
||||
if (i === index) {
|
||||
element = Object.assign(element, property);
|
||||
}
|
||||
return element;
|
||||
});
|
||||
};
|
||||
}
|
||||
if ([].someR === undefined) {
|
||||
Array.prototype.someR = function (predicate) {
|
||||
if (this.some(predicate)) {
|
||||
return Result.error(undefined);
|
||||
}
|
||||
return Result.ok(this);
|
||||
};
|
||||
}
|
||||
if ([].lastElement === undefined) {
|
||||
Array.prototype.lastElement = function () {
|
||||
const instanceCheck = this;
|
||||
|
|
|
@ -26,6 +26,8 @@ declare global {
|
|||
maxLength(length: number): Array<T>;
|
||||
add(element: T): Array<T>;
|
||||
indexOfR(element: T): Result<void, Array<T>>;
|
||||
replacePropIndex(property: Partial<T>, index: number): T[];
|
||||
someR(predicate: (value: T) => boolean): Result<void, Array<T>>;
|
||||
}
|
||||
interface Date {
|
||||
formatDate(): string;
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
import { IsArray, IsOptional, IsString, ValidateNested } from "class-validator";
|
||||
import { IsArray, IsEnum, IsNotEmpty, IsOptional, IsString, ValidateNested } from "class-validator";
|
||||
import { Type } from "class-transformer";
|
||||
import { ISkillView } from "../../features/behavior_tree_builder/presentation/ui/skill_tree/skill_tree";
|
||||
import { v4 } from "uuid";
|
||||
import { Result } from "../helper/result";
|
||||
import { ValidationModel } from "./validation_model";
|
||||
import { TopicViewModel } from "../../features/topics/topic_view_model";
|
||||
import makeAutoObservable from "mobx-store-inheritance";
|
||||
import clone from "just-clone";
|
||||
|
||||
export interface IDependency {
|
||||
|
@ -18,12 +21,9 @@ export interface ISkill {
|
|||
sid?: string;
|
||||
SkillPackage: ISkillPackage;
|
||||
Module: IModule;
|
||||
Launch: ILaunch;
|
||||
ROS2: IRos2;
|
||||
BTAction: IBTAction[];
|
||||
Interface: IInterface;
|
||||
Settings: ISetting[];
|
||||
xxx: IXxx;
|
||||
// Interface: IInterface;
|
||||
// Settings: ISetting[];
|
||||
}
|
||||
export interface IWeightsDependency {
|
||||
weights_name: string;
|
||||
|
@ -39,16 +39,55 @@ export interface IParam {
|
|||
type: string;
|
||||
dependency: Object;
|
||||
}
|
||||
export class ParamViewModel implements IParam {
|
||||
type: string;
|
||||
dependency: Object;
|
||||
constructor(type: string, dependency: Object) {
|
||||
this.type = type;
|
||||
this.dependency = dependency;
|
||||
}
|
||||
static empty = () => new ParamViewModel("", {});
|
||||
}
|
||||
export interface IBTAction {
|
||||
name: string;
|
||||
format: string;
|
||||
// TODO: Нужно выпилить его отсюда
|
||||
// sid?: string;
|
||||
type: string;
|
||||
param: IParam[];
|
||||
result: string[];
|
||||
}
|
||||
|
||||
export enum BtAction {
|
||||
ACTION = "ACTION",
|
||||
CONDITION = "CONDITION",
|
||||
}
|
||||
export class BtActionViewModel extends ValidationModel implements IBTAction {
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
name: string;
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
format: string;
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
type: string;
|
||||
param: IParam[];
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
result: string[];
|
||||
@IsNotEmpty()
|
||||
@IsEnum(BtAction)
|
||||
typeAction: BtAction;
|
||||
constructor(name: string, format: string, type: string, param: IParam[], result: string[], typeAction: BtAction) {
|
||||
super();
|
||||
this.name = name;
|
||||
this.format = format;
|
||||
this.type = type;
|
||||
this.param = param;
|
||||
this.result = result;
|
||||
this.typeAction = typeAction;
|
||||
}
|
||||
static empty = () => new BtActionViewModel("", "", "", [], [], BtAction.ACTION);
|
||||
public validParam = (type: string) => this.param.someR((param) => param.type === type);
|
||||
}
|
||||
export interface IInterface {
|
||||
Input: IPut[];
|
||||
Output: IPut[];
|
||||
|
@ -83,19 +122,22 @@ export interface ISkillPackage {
|
|||
format: string;
|
||||
}
|
||||
|
||||
export interface IXxx {
|
||||
cameraLink: string;
|
||||
topicImage: string;
|
||||
topicCameraInfo: string;
|
||||
}
|
||||
|
||||
export class SkillPackage implements ISkillPackage {
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
name: string;
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
version: string;
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
format: string;
|
||||
constructor(name: string, version: string, format: string) {
|
||||
this.name = name;
|
||||
this.format = format;
|
||||
this.version = version;
|
||||
}
|
||||
static empty = () => new SkillPackage("", "", "");
|
||||
}
|
||||
export class Module implements IModule {
|
||||
@IsString()
|
||||
|
@ -142,12 +184,12 @@ export class Setting implements ISetting {
|
|||
name: string;
|
||||
value: string | number;
|
||||
}
|
||||
export class Xxx implements IXxx {
|
||||
cameraLink: string;
|
||||
topicImage: string;
|
||||
topicCameraInfo: string;
|
||||
}
|
||||
export class SkillModel implements ISkill {
|
||||
|
||||
export class SkillModel extends ValidationModel implements ISkill {
|
||||
constructor() {
|
||||
super();
|
||||
makeAutoObservable(this);
|
||||
}
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
sid?: string;
|
||||
|
@ -157,29 +199,12 @@ export class SkillModel implements ISkill {
|
|||
@ValidateNested()
|
||||
@Type(() => Module)
|
||||
Module: IModule;
|
||||
@ValidateNested()
|
||||
@Type(() => Launch)
|
||||
Launch: ILaunch;
|
||||
@ValidateNested()
|
||||
@Type(() => Ros2)
|
||||
ROS2: IRos2;
|
||||
@ValidateNested()
|
||||
@IsArray()
|
||||
@Type(() => BTAction)
|
||||
BTAction: IBTAction[];
|
||||
@ValidateNested()
|
||||
@Type(() => Interface)
|
||||
Interface: IInterface;
|
||||
@ValidateNested()
|
||||
@IsArray()
|
||||
@Type(() => Setting)
|
||||
Settings: ISetting[];
|
||||
@ValidateNested()
|
||||
@Type(() => Xxx)
|
||||
xxx: IXxx;
|
||||
BTAction: BtActionViewModel[];
|
||||
topicsOut: TopicViewModel[] = [];
|
||||
static empty() {
|
||||
const skillModel = new SkillModel();
|
||||
skillModel.BTAction = [];
|
||||
skillModel.SkillPackage = SkillPackage.empty();
|
||||
return skillModel;
|
||||
}
|
||||
public static isEmpty(skill: SkillModel): Result<void, SkillModel> {
|
||||
|
@ -350,12 +375,6 @@ export class Skills {
|
|||
skill.BTAction.forEach((action) => {
|
||||
action.param.forEach((param) => {
|
||||
if (param.type.isEqual(skillType)) {
|
||||
// console.log('SKILL TYPE')
|
||||
// console.log(skillType);
|
||||
// console.log("SID")
|
||||
// console.log(sid)
|
||||
// console.log("DEPENDENCY")
|
||||
// console.log(param.dependency)
|
||||
acc = Object.keys(param.dependency).isNotEmpty();
|
||||
}
|
||||
});
|
||||
|
|
|
@ -14,6 +14,8 @@ export class ValidationModel {
|
|||
if (error.constraints) return Object.values(error.constraints).join(", ");
|
||||
return "";
|
||||
});
|
||||
console.log(errors)
|
||||
console.log(this)
|
||||
return Result.error(message.join(","));
|
||||
} else {
|
||||
return Result.ok(this as unknown as T);
|
||||
|
|
|
@ -71,6 +71,36 @@ export class HttpRepository {
|
|||
}
|
||||
return Result.ok(response.text as T);
|
||||
}
|
||||
public async _arrayJsonToClassInstanceRequest<T>(
|
||||
method: HttpMethod,
|
||||
url: string,
|
||||
instance: ClassConstructor<T>,
|
||||
data?: any
|
||||
): Promise<Result<HttpError, T[]>> {
|
||||
try {
|
||||
const reqInit = {
|
||||
body: data,
|
||||
method: method,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
};
|
||||
if (data !== undefined) {
|
||||
reqInit["body"] = JSON.stringify(data);
|
||||
}
|
||||
const response = await fetch(this.server + url, reqInit);
|
||||
|
||||
if (response.status !== 200) {
|
||||
return Result.error(new HttpError(this.server + url, response.status));
|
||||
}
|
||||
const array: any[] = await response.json();
|
||||
return Result.ok(
|
||||
array.map((el) => {
|
||||
return plainToInstance(instance, el) as T;
|
||||
})
|
||||
);
|
||||
} catch (error) {
|
||||
return Result.error(new HttpError(error, 0));
|
||||
}
|
||||
}
|
||||
public async _jsonToClassInstanceRequest<T>(
|
||||
method: HttpMethod,
|
||||
url: string,
|
||||
|
|
|
@ -9,10 +9,12 @@ import { DataSetScreen, DatasetsScreenPath } from "../../features/dataset/datase
|
|||
import { AssemblesScreen, AssemblesScreenPath } from "../../features/assembles/assembles_screen";
|
||||
import { SimulationScreen, SimulationScreenPath } from "../../features/simulations/simulations_screen";
|
||||
import { EstimateScreen, EstimateScreenPath } from "../../features/estimate/estimate_screen";
|
||||
import { CalculationScreenPath, CalculationScreen } from "../../features/calculation/presentation/calculation_screen";
|
||||
import { CalculationInstanceScreenPath, CalculationInstanceScreen } from "../../features/calculation_instance/presentation/calculation_instance_screen";
|
||||
import { DetailsScreenPath, DetailsScreen } from "../../features/details/details_screen";
|
||||
import { DigitalTwinsScreen, DigitalTwinsScreenPath } from "../../features/digital_twins/digital_twins_screen";
|
||||
import { TopicsScreen, TopicsScreenPath } from "../../features/topics/topics_screen";
|
||||
import { SkillsScreen, SkillsScreenPath } from "../../features/skills/skills_screen";
|
||||
import { CalculationsTemplateScreenPath } from "../../features/calculations_template/calculations_template_screen";
|
||||
|
||||
const idURL = ":id";
|
||||
|
||||
|
@ -58,8 +60,8 @@ export const router = createBrowserRouter([
|
|||
element: <EstimateScreen />,
|
||||
},
|
||||
{
|
||||
path: CalculationScreenPath,
|
||||
element: <CalculationScreen />,
|
||||
path: CalculationInstanceScreenPath,
|
||||
element: <CalculationInstanceScreen />,
|
||||
},
|
||||
{
|
||||
path: DigitalTwinsScreenPath,
|
||||
|
@ -69,4 +71,12 @@ export const router = createBrowserRouter([
|
|||
path: TopicsScreenPath,
|
||||
element: <TopicsScreen />,
|
||||
},
|
||||
{
|
||||
path: SkillsScreenPath,
|
||||
element: <SkillsScreen />,
|
||||
},
|
||||
{
|
||||
path: CalculationsTemplateScreenPath,
|
||||
element: <CalculationInstanceScreen />,
|
||||
},
|
||||
]);
|
||||
|
|
|
@ -9,7 +9,7 @@ import { AssemblesScreenPath } from "../../../features/assembles/assembles_scree
|
|||
import { SimulationScreenPath } from "../../../features/simulations/simulations_screen";
|
||||
import { EstimateScreenPath } from "../../../features/estimate/estimate_screen";
|
||||
import { BehaviorTreeBuilderPath } from "../../../features/behavior_tree_builder/presentation/behavior_tree_builder_screen";
|
||||
import { CalculationScreenPath as SkillScreenPath } from "../../../features/calculation/presentation/calculation_screen";
|
||||
import { CalculationInstanceScreenPath as SkillScreenPath } from "../../../features/calculation_instance/presentation/calculation_instance_screen";
|
||||
import { UiBaseError } from "../../model/ui_base_error";
|
||||
import { DetailsScreenPath } from "../../../features/details/details_screen";
|
||||
export interface IBlockProps {
|
||||
|
|
|
@ -9,6 +9,9 @@ import { CoreButton } from "../../../core/ui/button/button";
|
|||
import { CoreInput } from "../../../core/ui/input/input";
|
||||
import { DetailsScreenPath } from "../../details/details_screen";
|
||||
import { DigitalTwinsScreenPath } from "../../digital_twins/digital_twins_screen";
|
||||
import { TopicsScreenPath } from "../../topics/topics_screen";
|
||||
import { SkillsScreenPath } from "../../skills/skills_screen";
|
||||
import { CalculationsTemplateScreenPath } from "../../calculations_template/calculations_template_screen";
|
||||
|
||||
export const AllProjectScreenPath = "/";
|
||||
export const AllProjectScreen: React.FunctionComponent = observer(() => {
|
||||
|
@ -30,6 +33,10 @@ export const AllProjectScreen: React.FunctionComponent = observer(() => {
|
|||
children={
|
||||
<>
|
||||
<div onClick={() => navigate(DigitalTwinsScreenPath)}>Digital twins</div>
|
||||
<div onClick={() => navigate(TopicsScreenPath)}>Topics</div>
|
||||
<div onClick={() => navigate(SkillsScreenPath)}>Skills</div>
|
||||
<div onClick={() => navigate(CalculationsTemplateScreenPath)}>Calculation template</div>
|
||||
|
||||
{store.projectsModels?.map((el) => {
|
||||
return (
|
||||
<div
|
||||
|
|
|
@ -143,6 +143,7 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState<BehaviorTreeView
|
|||
});
|
||||
(await this.behaviorTreeBuilderHttpRepository.getBtSkills()).fold(
|
||||
(model) => {
|
||||
|
||||
this.skillTemplates = model;
|
||||
this.skillTree.children = this.skillTree.children?.map((el) => {
|
||||
if (el.name === "Действия") {
|
||||
|
|
|
@ -2,12 +2,13 @@ import { BehaviorTreeBuilderStore } from "../../behavior_tree_builder_store";
|
|||
import { CameraDeviceForm } from "./camera_device_form/camera_device_form_form";
|
||||
import { MoveToPose } from "./move_to_pose/move_to_pose_form";
|
||||
import { RobotDeviceForm } from "./robot_device_form/robot_device_form_form";
|
||||
import { TopicDependencyViewModel } from "./topics_form/topic_dependency_view_model";
|
||||
import { TopicsForm } from "./topics_form/topics_form";
|
||||
import { WeightsForm } from "./weights_form/weights_form";
|
||||
|
||||
export interface IPropsForm<T> {
|
||||
dependency: T;
|
||||
store: BehaviorTreeBuilderStore;
|
||||
store?: BehaviorTreeBuilderStore;
|
||||
onChange: (dependency: Object) => void;
|
||||
}
|
||||
|
||||
|
@ -22,7 +23,18 @@ export enum Form {
|
|||
topic = "topic",
|
||||
moveToPose = "move_to_pose",
|
||||
}
|
||||
export interface BtDependencyFormBuilder {
|
||||
form: Form;
|
||||
component?: React.ReactNode;
|
||||
}
|
||||
|
||||
export const btDependencyFormBuilder = (onChange: (dependency: Object) => void) => [
|
||||
{ form: Form.weights, component: null },
|
||||
{
|
||||
form: Form.topic,
|
||||
component: <TopicsForm store={undefined} dependency={TopicDependencyViewModel.empty()} onChange={onChange} />,
|
||||
},
|
||||
];
|
||||
export const forms = (
|
||||
props: any,
|
||||
onChange: (dependency: Object) => void,
|
||||
|
|
|
@ -8,9 +8,11 @@ import { PointModel } from "../../../../../../core/model/point_model";
|
|||
import { ObjectIsNotEmpty } from "../../../../../../core/extensions/object";
|
||||
import { CoreButton } from "../../../../../../core/ui/button/button";
|
||||
import { message } from "antd";
|
||||
import { BehaviorTreeBuilderStore } from "../../../behavior_tree_builder_store";
|
||||
|
||||
export const MoveToPose = observer((props: IPropsForm<MoveToPoseRobotModel | undefined>) => {
|
||||
const [store] = React.useState(() => new MoveToPoseStore(props.store));
|
||||
const propStore = props.store as BehaviorTreeBuilderStore;
|
||||
const [store] = React.useState(() => new MoveToPoseStore(propStore));
|
||||
React.useEffect(() => {
|
||||
if (ObjectIsNotEmpty(props.dependency)) store.loadDependency(props.dependency);
|
||||
store.init();
|
||||
|
@ -18,16 +20,16 @@ export const MoveToPose = observer((props: IPropsForm<MoveToPoseRobotModel | und
|
|||
return (
|
||||
<div>
|
||||
<CoreSelect
|
||||
items={props.store.sceneAsset?.getAllRobotsTopics() ?? ["ERROR"]}
|
||||
items={propStore.sceneAsset?.getAllRobotsTopics() ?? ["ERROR"]}
|
||||
value={store.viewModel.robot_name ?? ""}
|
||||
label={"Робот"}
|
||||
onChange={(text) => store.updateForm({ robot_name: text })}
|
||||
/>
|
||||
<CoreSelect
|
||||
items={props.store.sceneAsset?.getAllPoints() ?? ["ERROR"]}
|
||||
items={propStore.sceneAsset?.getAllPoints() ?? ["ERROR"]}
|
||||
label={"Точка"}
|
||||
onChange={(text) =>
|
||||
store.updateForm({ pose: props.store.sceneAsset?.getElementByName<PointModel>(text).toDependency() })
|
||||
store.updateForm({ pose: propStore.sceneAsset?.getElementByName<PointModel>(text).toDependency() })
|
||||
}
|
||||
value={store.viewModel?.pose?.name ?? ""}
|
||||
/>
|
||||
|
|
|
@ -1,18 +1,21 @@
|
|||
import makeAutoObservable from "mobx-store-inheritance";
|
||||
import { Result } from "../../../../../../core/helper/result";
|
||||
import { SidViewModel } from "../../../../../../core/model/device_dependency_view_model";
|
||||
import { ValidationModel } from "../../../../../../core/model/validation_model";
|
||||
import { IsNotEmpty, IsString } from "class-validator";
|
||||
import { StoreTopicType } from "./topics_form_store";
|
||||
|
||||
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();
|
||||
export class TopicDependencyViewModel extends ValidationModel {
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
type: string;
|
||||
mode: StoreTopicType;
|
||||
constructor(type: string, mode: StoreTopicType) {
|
||||
super();
|
||||
makeAutoObservable(this);
|
||||
this.type = type;
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
static empty() {
|
||||
return new TopicDependencyViewModel('', false);
|
||||
return new TopicDependencyViewModel("", StoreTopicType.specifiesDependencies);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import React from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { TopicsFormStore } from "./topics_form_store";
|
||||
import { StoreTopicType, 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 { match } from "ts-pattern";
|
||||
import { CoreInput } from "../../../../../../core/ui/input/input";
|
||||
import { CoreButton } from "../../../../../../core/ui/button/button";
|
||||
import { message } from "antd";
|
||||
|
||||
|
@ -14,40 +14,38 @@ export const TopicsForm = observer((props: IPropsForm<Partial<TopicDependencyVie
|
|||
|
||||
React.useEffect(() => {
|
||||
store.init();
|
||||
}, [store, props]);
|
||||
store.loadClassInstance(TopicDependencyViewModel, props.dependency as TopicDependencyViewModel);
|
||||
console.log(store.viewModel);
|
||||
}, [props]);
|
||||
|
||||
return (
|
||||
<div style={{ border: "1px solid", margin: 10, padding: 10 }}>
|
||||
<div>
|
||||
<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) => {
|
||||
store.updateForm({ axis: !status });
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<CoreButton
|
||||
style={{ margin: 10, width: 100 }}
|
||||
text="OK"
|
||||
onClick={async () => {
|
||||
(await store.viewModel.valid<TopicDependencyViewModel>()).fold(
|
||||
(s) => {
|
||||
props.onChange(s);
|
||||
},
|
||||
(e) => message.error(e)
|
||||
);
|
||||
}}
|
||||
/>
|
||||
{match(store.viewModel.mode)
|
||||
.with(StoreTopicType.btExecute, () => (
|
||||
<>
|
||||
<CoreText text={"Выберите точку размещения"} type={CoreTextType.header} />
|
||||
</>
|
||||
))
|
||||
.with(StoreTopicType.specifiesDependencies, () => (
|
||||
<>
|
||||
<CoreInput label={"Тип топика"} onChange={(text) => store.updateForm({ type: text })} />
|
||||
<div style={{ height: 10 }} />
|
||||
<CoreButton
|
||||
text="OK"
|
||||
style={{ width: 100 }}
|
||||
onClick={async () => {
|
||||
(await store.viewModel.valid<TopicDependencyViewModel>()).fold(
|
||||
(s) => props.onChange(s),
|
||||
(e) => message.error(e)
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
))
|
||||
.otherwise(() => (
|
||||
<></>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
|
|
@ -4,7 +4,10 @@ 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 enum StoreTopicType {
|
||||
specifiesDependencies = "specifiesDependencies",
|
||||
btExecute = "btExecute",
|
||||
}
|
||||
export class TopicsFormStore extends FormState<TopicDependencyViewModel, CoreError> {
|
||||
constructor() {
|
||||
super();
|
||||
|
@ -15,13 +18,6 @@ export class TopicsFormStore extends FormState<TopicDependencyViewModel, CoreErr
|
|||
cameraDeviceHttpRepository: TopicsFormHttpRepository = new TopicsFormHttpRepository();
|
||||
errorHandingStrategy = (error: CoreError) => {};
|
||||
init = async (navigate?: NavigateFunction | undefined) => {
|
||||
try {
|
||||
throw new Error('213')
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
// await this.mapOk('topics', this.cameraDeviceHttpRepository.getAllTopics())
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import makeAutoObservable from "mobx-store-inheritance";
|
||||
import { FormState, CoreError } from "../../../../../../core/store/base_store";
|
||||
import { DataSetHttpRepository } from "../../../../../dataset/dataset_http_repository";
|
||||
import { ISkils, CalculationHttpRepository } from "../../../../../calculation/data/calculation_http_repository";
|
||||
import { ISkils, CalculationHttpRepository } from "../../../../../calculation_instance/data/calculation_http_repository";
|
||||
import { WeightsViewModel } from "./weights_view_model";
|
||||
import { Parts } from "../../../../../details/details_http_repository";
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ import { Drawer, Modal } from "antd";
|
|||
import { observer } from "mobx-react-lite";
|
||||
import { MainPage } from "../../../core/ui/pages/main_page";
|
||||
import { CoreText, CoreTextType } from "../../../core/ui/text/text";
|
||||
import { DrawersSkill, CalculationStore, StoreTypes } from "./calculation_store";
|
||||
import { DrawersSkill, CalculationInstanceStore, StoreTypes } from "./calculation_instance_store";
|
||||
import { CoreInput } from "../../../core/ui/input/input";
|
||||
import { CoreButton } from "../../../core/ui/button/button";
|
||||
import { CoreSelect } from "../../../core/ui/select/select";
|
||||
|
@ -22,10 +22,10 @@ interface IItem {
|
|||
|
||||
const skills: IItem[] = [{ name: "ML", isActive: true }];
|
||||
|
||||
export const CalculationScreenPath = "/calculation";
|
||||
export const CalculationInstanceScreenPath = "/calculation";
|
||||
|
||||
export const CalculationScreen = observer(() => {
|
||||
const [store] = React.useState(() => new CalculationStore());
|
||||
export const CalculationInstanceScreen = observer(() => {
|
||||
const [store] = React.useState(() => new CalculationInstanceStore());
|
||||
|
||||
React.useEffect(() => {
|
||||
store.init();
|
|
@ -20,11 +20,7 @@ export enum StoreTypes {
|
|||
empty = "empty",
|
||||
}
|
||||
|
||||
export class CalculationStore extends UiDrawerFormState<CalculationModel, HttpError> {
|
||||
deleteInstance = async (id: string) => {
|
||||
await this.calculationHttpRepository.deleteInstance(id);
|
||||
await this.init();
|
||||
};
|
||||
export class CalculationInstanceStore extends UiDrawerFormState<CalculationModel, HttpError> {
|
||||
calculationHttpRepository: CalculationHttpRepository = new CalculationHttpRepository();
|
||||
calculationSocketRepository: CalculationSocketRepository = new CalculationSocketRepository();
|
||||
activeProjectId?: UUID;
|
||||
|
@ -49,6 +45,10 @@ export class CalculationStore extends UiDrawerFormState<CalculationModel, HttpEr
|
|||
this.calculationSocketRepository.on(this.socketUpdate);
|
||||
makeAutoObservable(this);
|
||||
}
|
||||
deleteInstance = async (id: string) => {
|
||||
await this.calculationHttpRepository.deleteInstance(id);
|
||||
await this.init();
|
||||
};
|
||||
socketUpdate = (data: ProcessUpdate) => {
|
||||
this.calculationInstances?.map((el) => {
|
||||
if (el?._id && el._id.isEqual(data.id)) {
|
|
@ -1,6 +1,5 @@
|
|||
import { match } from "ts-pattern";
|
||||
import { PoseEstimateCard } from "./pose_estimate_card/model_card";
|
||||
import { FormBuilderValidationModel } from "../../../../dataset/dataset_model";
|
||||
import { Dropdown, MenuProps, message } from "antd";
|
||||
import { CoreText, CoreTextType } from "../../../../../core/ui/text/text";
|
||||
import { IMenuItem } from "../../../../dataset/card_dataset";
|
|
@ -1,11 +1,4 @@
|
|||
import { CoreButton } from "../../../../../../core/ui/button/button";
|
||||
import { Icon } from "../../../../../../core/ui/icons/icons";
|
||||
import { Dropdown } from "antd";
|
||||
import { CoreText, CoreTextType } from "../../../../../../core/ui/text/text";
|
||||
import { ProcessStatus } from "../../../../../dataset/dataset_model";
|
||||
import { IMenuItem } from "../../../../../dataset/card_dataset";
|
||||
import type { MenuProps } from "antd";
|
||||
import { match } from "ts-pattern";
|
||||
|
||||
|
||||
export interface IModelCardProps {
|
||||
dependency?: Object;
|
|
@ -0,0 +1,3 @@
|
|||
import { CoreHttpRepository } from "../../core/repository/core_http_repository";
|
||||
|
||||
export class CalculationsTemplateHttpRepository extends CoreHttpRepository {}
|
|
@ -0,0 +1,13 @@
|
|||
import { observer } from "mobx-react-lite";
|
||||
import { CalculationsTemplateStore } from "./calculations_template_store";
|
||||
import React from "react";
|
||||
|
||||
export const CalculationsTemplateScreenPath = "/calculations/template";
|
||||
|
||||
export const CalculationsTemplateScreen = observer(() => {
|
||||
const [store] = React.useState(() => new CalculationsTemplateStore());
|
||||
React.useEffect(() => {
|
||||
store.init();
|
||||
}, []);
|
||||
return <></>;
|
||||
});
|
|
@ -0,0 +1,16 @@
|
|||
import makeAutoObservable from "mobx-store-inheritance";
|
||||
import { UiDrawerFormState } from "../../core/store/base_store";
|
||||
import { HttpError } from "../../core/repository/core_http_repository";
|
||||
import { CalculationsTemplateViewModel } from "./calculations_template_view_model";
|
||||
import { NavigateFunction } from "react-router-dom";
|
||||
export enum CalculationTemplateDrawer {
|
||||
newSkill = "Новый навык",
|
||||
}
|
||||
export class CalculationsTemplateStore extends UiDrawerFormState<CalculationsTemplateViewModel, HttpError> {
|
||||
viewModel: CalculationsTemplateViewModel = CalculationsTemplateViewModel.empty();
|
||||
constructor() {
|
||||
super(CalculationTemplateDrawer);
|
||||
makeAutoObservable(this);
|
||||
}
|
||||
init = async (navigate?: NavigateFunction | undefined): Promise<any> => {};
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
import { ValidationModel } from "../../core/model/validation_model";
|
||||
|
||||
export class CalculationsTemplateViewModel extends ValidationModel {
|
||||
static empty() {
|
||||
return new CalculationsTemplateViewModel();
|
||||
}
|
||||
}
|
8
ui/src/features/skills/skills_http_repository.ts
Normal file
8
ui/src/features/skills/skills_http_repository.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
import { SkillModel } from "../../core/model/skill_model";
|
||||
import { HttpMethod, HttpRepository } from "../../core/repository/core_http_repository";
|
||||
|
||||
export class SkillsHttpRepository extends HttpRepository {
|
||||
featureApi = "/skills";
|
||||
createNewSkill = (model: SkillModel) => this._jsonRequest(HttpMethod.POST, this.featureApi, model);
|
||||
getAllSkills = () => this._arrayJsonToClassInstanceRequest(HttpMethod.GET, this.featureApi, SkillModel);
|
||||
}
|
184
ui/src/features/skills/skills_screen.tsx
Normal file
184
ui/src/features/skills/skills_screen.tsx
Normal file
|
@ -0,0 +1,184 @@
|
|||
import React from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { DrawersSkills, SkillsStore } from "./skills_store";
|
||||
import { Drawer, Modal } from "antd";
|
||||
import { CoreInput } from "../../core/ui/input/input";
|
||||
import { CoreText, CoreTextType } from "../../core/ui/text/text";
|
||||
import { TopicViewModel } from "../topics/topic_view_model";
|
||||
import { BtAction, BtActionViewModel } from "../../core/model/skill_model";
|
||||
import { btDependencyFormBuilder } from "../behavior_tree_builder/presentation/ui/forms/forms";
|
||||
import { CoreButton } from "../../core/ui/button/button";
|
||||
import { CoreSelect } from "../../core/ui/select/select";
|
||||
|
||||
export const SkillsScreenPath = "/skills";
|
||||
|
||||
export const SkillsScreen = observer(() => {
|
||||
const [store] = React.useState(() => new SkillsStore());
|
||||
React.useEffect(() => {
|
||||
store.init();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<CoreButton
|
||||
style={{ width: 100 }}
|
||||
text="new skill"
|
||||
onClick={() => store.editDrawer(DrawersSkills.newSkill, true)}
|
||||
/>
|
||||
{store.skills?.map((el) => (
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: "coral",
|
||||
width: "max-content",
|
||||
margin: 10,
|
||||
padding: 10,
|
||||
borderRadius: 20,
|
||||
}}
|
||||
>
|
||||
<CoreText text={`skill server name:${el.SkillPackage.name}`} type={CoreTextType.header} />
|
||||
<div>
|
||||
<CoreText text={"actions"} type={CoreTextType.header} />
|
||||
|
||||
{el.BTAction.map((el) => (
|
||||
<div>
|
||||
<CoreText text={el.name} type={CoreTextType.medium} />
|
||||
{el.param.map((element) => (
|
||||
<div>
|
||||
<div>{element.type}</div>
|
||||
<div>{JSON.stringify(element.dependency)}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<Drawer
|
||||
width={(window.innerWidth / 100) * 50}
|
||||
title={store.titleDrawer}
|
||||
destroyOnClose={true}
|
||||
onClose={() => store.editDrawer(DrawersSkills.newSkill, false)}
|
||||
open={store.drawers.find((el) => el.name === DrawersSkills.newSkill)?.status}
|
||||
>
|
||||
<div style={{ display: "flex", flexDirection: "column", justifyContent: "space-between", height: "100%" }}>
|
||||
<div>
|
||||
<CoreInput
|
||||
label={"name"}
|
||||
onChange={(text) =>
|
||||
store.updateForm({ SkillPackage: Object.assign(store.viewModel.SkillPackage, { name: text }) })
|
||||
}
|
||||
/>
|
||||
<CoreInput
|
||||
label={"format"}
|
||||
onChange={(text) =>
|
||||
store.updateForm({ SkillPackage: Object.assign(store.viewModel.SkillPackage, { format: text }) })
|
||||
}
|
||||
/>
|
||||
<CoreInput
|
||||
label="version"
|
||||
onChange={(text) =>
|
||||
store.updateForm({ SkillPackage: Object.assign(store.viewModel.SkillPackage, { version: text }) })
|
||||
}
|
||||
/>
|
||||
<CoreText
|
||||
text={`Topics ${store.viewModel.topicsOut.length}`}
|
||||
type={CoreTextType.large}
|
||||
onClick={() => store.updateForm({ topicsOut: store.viewModel.topicsOut.add(TopicViewModel.empty()) })}
|
||||
/>
|
||||
<div>
|
||||
{store.viewModel.topicsOut.map((el, index) => (
|
||||
<div key={index} style={{ marginTop: 10 }}>
|
||||
<CoreInput
|
||||
value={el.name}
|
||||
label={"name"}
|
||||
onChange={(text) =>
|
||||
store.updateForm({ topicsOut: store.viewModel.topicsOut.replacePropIndex({ name: text }, index) })
|
||||
}
|
||||
/>
|
||||
<CoreInput
|
||||
value={el.type}
|
||||
label={"type"}
|
||||
onChange={(text) =>
|
||||
store.updateForm({ topicsOut: store.viewModel.topicsOut.replacePropIndex({ type: text }, index) })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<CoreText
|
||||
text={`BTAction ${store.viewModel.BTAction.length}`}
|
||||
type={CoreTextType.large}
|
||||
onClick={() => store.updateForm({ BTAction: store.viewModel.BTAction.add(BtActionViewModel.empty()) })}
|
||||
/>
|
||||
{store.viewModel.BTAction.map((el, index) => (
|
||||
<div key={index} style={{ marginTop: 10 }}>
|
||||
<CoreInput
|
||||
value={el.name}
|
||||
label={"name"}
|
||||
onChange={(text) =>
|
||||
store.updateForm({ BTAction: store.viewModel.BTAction.replacePropIndex({ name: text }, index) })
|
||||
}
|
||||
/>
|
||||
<CoreInput
|
||||
value={el.type}
|
||||
label={"type"}
|
||||
onChange={(text) =>
|
||||
store.updateForm({ BTAction: store.viewModel.BTAction.replacePropIndex({ type: text }, index) })
|
||||
}
|
||||
/>
|
||||
<CoreInput
|
||||
value={el.format}
|
||||
label={"format"}
|
||||
onChange={(text) =>
|
||||
store.updateForm({ BTAction: store.viewModel.BTAction.replacePropIndex({ format: text }, index) })
|
||||
}
|
||||
/>
|
||||
<CoreSelect
|
||||
items={Object.keys(BtAction)}
|
||||
value={el.typeAction}
|
||||
label={"type action"}
|
||||
onChange={(value: string) =>
|
||||
store.updateForm({
|
||||
BTAction: store.viewModel.BTAction.replacePropIndex({ typeAction: value as BtAction }, index),
|
||||
})
|
||||
}
|
||||
/>
|
||||
<CoreText
|
||||
text={`Param ${el.param.length}`}
|
||||
type={CoreTextType.large}
|
||||
onClick={() => store.addNewParam(index)}
|
||||
/>
|
||||
{el.param.map((param) => (
|
||||
<div style={{ border: "1px solid", padding: 10, margin: 1 }} onClick={() => store.showModal()}>
|
||||
<div>{param.type}</div>
|
||||
<div> {JSON.stringify(param.dependency)}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<CoreButton text="Save" style={{ width: 100 }} onClick={() => store.saveNewSkill()} />
|
||||
</div>
|
||||
</Drawer>
|
||||
<Modal
|
||||
destroyOnClose={true}
|
||||
afterClose={() => (store.selectParam = undefined)}
|
||||
open={store.isModalOpen}
|
||||
footer={null}
|
||||
closable={false}
|
||||
closeIcon={null}
|
||||
onCancel={store.handleCancel}
|
||||
>
|
||||
<CoreText text={"Выберите параметр"} type={CoreTextType.large} />
|
||||
{store.selectParam !== undefined
|
||||
? store.selectParam.component
|
||||
: btDependencyFormBuilder((dependency) => store.onChangeBtDependency(dependency)).map((el) => (
|
||||
<div onClick={() => store.clickParam(el)}>{el.form}</div>
|
||||
))}
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
});
|
78
ui/src/features/skills/skills_store.ts
Normal file
78
ui/src/features/skills/skills_store.ts
Normal file
|
@ -0,0 +1,78 @@
|
|||
import makeAutoObservable from "mobx-store-inheritance";
|
||||
import { UiDrawerFormState } from "../../core/store/base_store";
|
||||
import { NavigateFunction } from "react-router-dom";
|
||||
import { HttpError } from "../../core/repository/core_http_repository";
|
||||
import { ParamViewModel, SkillModel } from "../../core/model/skill_model";
|
||||
import { Form } from "../behavior_tree_builder/presentation/ui/forms/forms";
|
||||
import { message } from "antd";
|
||||
import { SkillsHttpRepository } from "./skills_http_repository";
|
||||
export enum DrawersSkills {
|
||||
newSkill = "Новый навык",
|
||||
}
|
||||
export class SkillsStore extends UiDrawerFormState<SkillModel, HttpError> {
|
||||
isModalOpen: boolean = false;
|
||||
activeIndex?: number;
|
||||
viewModel: SkillModel = SkillModel.empty();
|
||||
selectParam?: {
|
||||
form: Form;
|
||||
component?: React.ReactNode;
|
||||
};
|
||||
skills: SkillModel[];
|
||||
skillsHttpRepository: SkillsHttpRepository = new SkillsHttpRepository();
|
||||
|
||||
constructor() {
|
||||
super(DrawersSkills);
|
||||
makeAutoObservable(this);
|
||||
}
|
||||
init = async (navigate?: NavigateFunction | undefined) => {
|
||||
this.mapOk("skills", this.skillsHttpRepository.getAllSkills());
|
||||
};
|
||||
showModal = () => {
|
||||
this.isModalOpen = true;
|
||||
};
|
||||
|
||||
handleOk = () => {
|
||||
this.isModalOpen = false;
|
||||
};
|
||||
|
||||
handleCancel = () => {
|
||||
this.isModalOpen = false;
|
||||
};
|
||||
addNewParam = (index: number) => {
|
||||
this.activeIndex = index;
|
||||
this.showModal();
|
||||
};
|
||||
onChangeBtDependency = (dependency: Object) =>
|
||||
this.viewModel.BTAction.at(this.activeIndex ?? 0)
|
||||
?.validParam(this.selectParam?.form ?? "")
|
||||
.fold(
|
||||
() => {
|
||||
this.updateForm({
|
||||
BTAction: this.viewModel.BTAction.replacePropIndex(
|
||||
{
|
||||
param: this.viewModel.BTAction.at(this.activeIndex ?? 0)?.param.add(
|
||||
new ParamViewModel(this.selectParam?.form ?? "", dependency)
|
||||
),
|
||||
},
|
||||
this.activeIndex ?? 0
|
||||
),
|
||||
});
|
||||
this.handleCancel();
|
||||
},
|
||||
() => {
|
||||
message.error(`${this.selectParam?.form} is filled`);
|
||||
this.handleCancel();
|
||||
}
|
||||
);
|
||||
|
||||
clickParam(el: { form: Form; component: null } | { form: Form; component: React.JSX.Element }): void {
|
||||
this.selectParam = el;
|
||||
if (el.component === null) this.onChangeBtDependency({});
|
||||
}
|
||||
saveNewSkill = async () => {
|
||||
(await this.viewModel.valid<SkillModel>()).fold(
|
||||
async (model) => this.skillsHttpRepository.createNewSkill(model),
|
||||
async (e) => message.error(e)
|
||||
);
|
||||
};
|
||||
}
|
|
@ -1,15 +1,15 @@
|
|||
import * as React from "react";
|
||||
import { socketListerStore } from "./socket_lister_store";
|
||||
import { socketListenerStore } from "./socket_listener_store";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { CoreText, CoreTextType } from "../../core/ui/text/text";
|
||||
|
||||
export interface ISocketListerProps {
|
||||
export interface ISocketListenerProps {
|
||||
children?: JSX.Element;
|
||||
}
|
||||
|
||||
export const SocketLister = observer((props: ISocketListerProps) => {
|
||||
export const SocketListener = observer((props: ISocketListenerProps) => {
|
||||
React.useEffect(() => {
|
||||
socketListerStore.init();
|
||||
socketListenerStore.init();
|
||||
}, []);
|
||||
return (
|
||||
<>
|
|
@ -1,7 +1,7 @@
|
|||
import { makeAutoObservable } from "mobx";
|
||||
import { SocketRepository, socketRepository } from "../../core/repository/core_socket_repository";
|
||||
|
||||
class SocketListerStore {
|
||||
class SocketListenerStore {
|
||||
socketRepository: SocketRepository;
|
||||
socketHasDisconnect = false;
|
||||
|
||||
|
@ -28,4 +28,4 @@ class SocketListerStore {
|
|||
}
|
||||
}
|
||||
|
||||
export const socketListerStore = new SocketListerStore(socketRepository);
|
||||
export const socketListenerStore = new SocketListenerStore(socketRepository);
|
|
@ -1,8 +1,20 @@
|
|||
import { IsNotEmpty, IsString } from "class-validator";
|
||||
import { ValidationModel } from "../../core/model/validation_model";
|
||||
|
||||
export class TopicViewModel extends ValidationModel {
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
name: string;
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
type: string;
|
||||
constructor(name: string, type: string) {
|
||||
super();
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
}
|
||||
static empty() {
|
||||
return new TopicViewModel();
|
||||
return new TopicViewModel("", "");
|
||||
}
|
||||
}
|
||||
export interface ITopicModel {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { HttpMethod, HttpRepository } from "../../core/repository/core_http_repository";
|
||||
|
||||
export class TopicsHttpRepository extends HttpRepository {
|
||||
getAllTopics = () => this._jsonRequest(HttpMethod.GET, "/topics");
|
||||
featureApi = "/topics";
|
||||
getAllTopics = () => this._jsonRequest(HttpMethod.GET, this.featureApi);
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ export const TopicsScreen = observer(() => {
|
|||
return (
|
||||
<>
|
||||
{store.topics?.map((el) => (
|
||||
<div>
|
||||
<div style={{ margin: 10, border: "1px solid red" }}>
|
||||
<div>{el.name}</div>
|
||||
<div>{el.type}</div>
|
||||
</div>
|
||||
|
|
|
@ -2,7 +2,7 @@ import "reflect-metadata";
|
|||
import "antd/dist/antd.min.css";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import { extensions } from "./core/extensions/extensions";
|
||||
import { SocketLister } from "./features/socket_lister/socket_lister";
|
||||
import { SocketListener } from "./features/socket_listener/socket_listener";
|
||||
import { RouterProvider } from "react-router-dom";
|
||||
import { router } from "./core/routers/routers";
|
||||
import { configure } from "mobx";
|
||||
|
@ -17,8 +17,9 @@ const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement)
|
|||
|
||||
root.render(
|
||||
<>
|
||||
<SocketLister>
|
||||
<SocketListener>
|
||||
<RouterProvider router={router} />
|
||||
</SocketLister>
|
||||
</SocketListener>
|
||||
</>
|
||||
);
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue