skills screen

This commit is contained in:
IDONTSUDO 2024-08-29 12:26:54 +03:00
parent cac5fad8ce
commit a920f5fb45
49 changed files with 681 additions and 168 deletions

View file

@ -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());

View file

@ -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;
}

View file

@ -13,3 +13,4 @@ export class CreateManyFolderScenario {
}
};
}

View file

@ -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" },

View 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);

View 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[] = [];
}

View 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,
});
}
}

View file

@ -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);

View file

@ -1 +0,0 @@
export class SkillInstanceValidationModel {}

View file

@ -1 +0,0 @@
export class SkillsInstancePresentation {}

View file

@ -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);

View file

@ -1 +0,0 @@
export class SkillTemplateValidationModel {}

View file

@ -1 +0,0 @@
export class SkillsTemplatePresentation {}

View file

@ -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;

View file

@ -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;

View file

@ -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();
}
});

View file

@ -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);

View file

@ -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,

View file

@ -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 />,
},
]);

View file

@ -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 {

View file

@ -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

View file

@ -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 === "Действия") {

View file

@ -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,

View file

@ -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 ?? ""}
/>

View file

@ -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);
}
}

View file

@ -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>
);
});

View file

@ -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())
};
}

View file

@ -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";

View file

@ -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();

View file

@ -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)) {

View file

@ -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";

View file

@ -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;

View file

@ -0,0 +1,3 @@
import { CoreHttpRepository } from "../../core/repository/core_http_repository";
export class CalculationsTemplateHttpRepository extends CoreHttpRepository {}

View file

@ -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 <></>;
});

View file

@ -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> => {};
}

View file

@ -0,0 +1,7 @@
import { ValidationModel } from "../../core/model/validation_model";
export class CalculationsTemplateViewModel extends ValidationModel {
static empty() {
return new CalculationsTemplateViewModel();
}
}

View 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);
}

View 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>
</>
);
});

View 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)
);
};
}

View file

@ -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 (
<>

View file

@ -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);

View file

@ -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 {

View file

@ -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);
}

View file

@ -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>

View file

@ -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>
</>
);