490 lines
12 KiB
TypeScript
490 lines
12 KiB
TypeScript
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 { ITopicModel, TopicViewModel } from "../../features/topics/topic_view_model";
|
|
import { BehaviorTreeBuilderStore } from "../../features/behavior_tree_builder/presentation/behavior_tree_builder_store";
|
|
import makeAutoObservable from "mobx-store-inheritance";
|
|
import clone from "just-clone";
|
|
export interface IDependency {
|
|
skills: ISkillDependency[];
|
|
}
|
|
|
|
export interface ISkillDependency {
|
|
sid: string;
|
|
dependency: Object;
|
|
}
|
|
|
|
export interface ISkill {
|
|
sid?: string;
|
|
SkillPackage: ISkillPackage;
|
|
Module: IModule;
|
|
BTAction: IBTAction[];
|
|
// Interface: IInterface;
|
|
// Settings: ISetting[];
|
|
}
|
|
export interface IWeightsDependency {
|
|
weights_name: string;
|
|
object_name: string;
|
|
weights_file: string;
|
|
dimensions: number[];
|
|
}
|
|
export interface IDeviceDependency {
|
|
sid: string;
|
|
}
|
|
export interface IDependency {}
|
|
export interface IParam {
|
|
isFilled: boolean;
|
|
type: string;
|
|
dependency?: DependencyViewModel;
|
|
}
|
|
export class DependencyViewModel {
|
|
static empty = () => new DependencyViewModel();
|
|
toView = (store: BehaviorTreeBuilderStore | undefined): React.ReactNode => "string";
|
|
}
|
|
export class ParamViewModel implements IParam {
|
|
type: string;
|
|
|
|
@Type(() => DependencyViewModel)
|
|
dependency: DependencyViewModel;
|
|
isFilled: boolean = false;
|
|
constructor(type: string, dependency: DependencyViewModel) {
|
|
this.type = type;
|
|
this.dependency = dependency;
|
|
}
|
|
static empty = () => new ParamViewModel("", DependencyViewModel.empty());
|
|
}
|
|
export interface IBTAction {
|
|
name: 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;
|
|
@Type(() => ParamViewModel)
|
|
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[];
|
|
}
|
|
|
|
export interface IPut {
|
|
name: string;
|
|
type: string;
|
|
}
|
|
|
|
export interface ILaunch {
|
|
executable: string;
|
|
}
|
|
|
|
export interface IModule {
|
|
name: string;
|
|
node_name: string;
|
|
description: string;
|
|
}
|
|
|
|
export interface IRos2 {
|
|
node_name: string;
|
|
}
|
|
|
|
export interface ISetting {
|
|
name: string;
|
|
value: number | string;
|
|
}
|
|
|
|
export interface ISkillPackage {
|
|
name: string;
|
|
version: string;
|
|
format: 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()
|
|
node_name: string;
|
|
@IsString()
|
|
name: string;
|
|
@IsString()
|
|
description: string;
|
|
constructor(name: string, description: string) {
|
|
this.name = name;
|
|
this.description = description;
|
|
}
|
|
static empty = () => new Module("", "");
|
|
}
|
|
export class BTAction implements IBTAction {
|
|
@IsString()
|
|
name: string;
|
|
@IsString()
|
|
type: string;
|
|
sid?: string;
|
|
@IsArray()
|
|
param: IParam[];
|
|
@IsArray()
|
|
result: string[];
|
|
}
|
|
export class Launch implements ILaunch {
|
|
@IsNotEmpty()
|
|
@IsString()
|
|
executable: string;
|
|
@IsNotEmpty()
|
|
@IsString()
|
|
package: string;
|
|
constructor(executable: string, plage: string) {
|
|
this.executable = executable;
|
|
this.package = plage;
|
|
}
|
|
static empty = () => new Launch("", "");
|
|
}
|
|
export class Ros2 implements IRos2 {
|
|
@IsString()
|
|
node_name: string;
|
|
}
|
|
export class Put implements IPut {
|
|
@IsString()
|
|
name: string;
|
|
@IsString()
|
|
type: string;
|
|
}
|
|
export class Interface implements IInterface {
|
|
@ValidateNested()
|
|
@Type(() => Put)
|
|
Input: IPut[];
|
|
@ValidateNested()
|
|
@Type(() => Put)
|
|
Output: IPut[];
|
|
}
|
|
export class Setting implements ISetting {
|
|
name: string;
|
|
value: string | number;
|
|
}
|
|
|
|
export class SkillModel extends ValidationModel implements ISkill {
|
|
@IsOptional()
|
|
@IsString()
|
|
_id?: string;
|
|
constructor() {
|
|
super();
|
|
makeAutoObservable(this);
|
|
}
|
|
|
|
bgColor: string = `rgba(5, 26, 39, 1)`;
|
|
borderColor: string = "rgba(25, 130, 196, 1)";
|
|
@IsOptional()
|
|
@IsString()
|
|
sid?: string;
|
|
@ValidateNested()
|
|
@Type(() => SkillPackage)
|
|
SkillPackage: ISkillPackage;
|
|
@ValidateNested()
|
|
@Type(() => Module)
|
|
Module: IModule;
|
|
@Type(() => BtActionViewModel)
|
|
BTAction: BtActionViewModel[];
|
|
topicsOut: TopicViewModel[] = [];
|
|
@Type(() => Launch)
|
|
Launch: Launch;
|
|
static empty() {
|
|
const skillModel = new SkillModel();
|
|
skillModel.BTAction = [];
|
|
skillModel.SkillPackage = SkillPackage.empty();
|
|
skillModel.Module = Module.empty();
|
|
skillModel.Launch = Launch.empty();
|
|
return skillModel;
|
|
}
|
|
public static isEmpty(skill: SkillModel): Result<void, SkillModel> {
|
|
if (skill.BTAction.isEmpty()) {
|
|
return Result.error(undefined);
|
|
}
|
|
return Result.ok(Object.assign(skill, {}));
|
|
}
|
|
|
|
public getSid = () => this.sid;
|
|
public setSid = (sid: string) => {
|
|
const result = clone(this);
|
|
result.sid = sid;
|
|
return result;
|
|
};
|
|
emptyParam = () => this.BTAction.at(0)?.param.at(0)?.dependency === undefined;
|
|
}
|
|
|
|
export class SkillDependency implements IDependency {
|
|
constructor(public skills: ISkillDependency[]) {}
|
|
static empty() {
|
|
return new SkillDependency([]);
|
|
}
|
|
static isEmpty = (skill: SkillDependency) => {
|
|
if (skill.skills.isEmpty()) {
|
|
return Result.error(undefined);
|
|
}
|
|
return Result.ok(skill);
|
|
};
|
|
}
|
|
|
|
export class Skills {
|
|
constructor() {
|
|
makeAutoObservable(this);
|
|
}
|
|
@IsOptional()
|
|
@IsArray()
|
|
@Type(() => TopicViewModel)
|
|
topicsStack: ITopicModel[] = [];
|
|
@IsArray()
|
|
@Type(() => SkillModel)
|
|
skills: SkillModel[];
|
|
static fromSkills = (skilsModel: SkillModel[]) => {
|
|
const skills = this.empty();
|
|
skills.skills = skilsModel;
|
|
return skills;
|
|
};
|
|
deleteTopic = (sid: string) => (this.topicsStack = this.topicsStack.filter((el) => !el.sid?.isEqual(sid)));
|
|
|
|
getSkillAtSid = (sid: string): Result<void, SkillModel> => {
|
|
const result = this.skills.filter((skill) => skill.sid?.isEqual(sid)).at(0);
|
|
if (result === undefined) {
|
|
return Result.error(undefined);
|
|
}
|
|
return Result.ok(result);
|
|
};
|
|
|
|
validation = (): Result<string[], void> => {
|
|
const errors: string[] = [];
|
|
this.skills.forEach((skill) => {
|
|
skill.BTAction.forEach((action) => {
|
|
if (action.param.isNotEmpty()) {
|
|
action.param.forEach((param) => {
|
|
if (Object.keys(param?.dependency ?? {}).isEmpty()) {
|
|
errors.push(param.type);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
});
|
|
if (errors.isNotEmpty()) {
|
|
return Result.error(errors);
|
|
}
|
|
return Result.ok(undefined);
|
|
};
|
|
getCssAtLabel = (label: string): React.CSSProperties =>
|
|
this.skills.reduce<React.CSSProperties>((acc, el) => {
|
|
el.BTAction.rFind<BtActionViewModel>((el) => el.name.isEqual(label)).map(() => {
|
|
acc = {
|
|
backgroundColor: el.bgColor,
|
|
border: `1px solid ${el.borderColor}`,
|
|
};
|
|
});
|
|
return acc;
|
|
}, {});
|
|
skillBySid = (sid: string) =>
|
|
SkillModel.isEmpty(
|
|
this.skills.reduce<SkillModel>((acc, el) => {
|
|
if (el.sid?.isEqual(sid)) {
|
|
acc = el;
|
|
}
|
|
return acc;
|
|
}, SkillModel.empty())
|
|
);
|
|
|
|
toSkillView = (): ISkillView[] =>
|
|
this.skills.map((el) => {
|
|
return {
|
|
name: el.SkillPackage.name,
|
|
children: el.BTAction.map((act) => {
|
|
return { name: act.name, uuid: v4() };
|
|
}),
|
|
};
|
|
});
|
|
getSkill = (name: string) =>
|
|
SkillModel.isEmpty(
|
|
this.skills.reduce<SkillModel>((acc, el) => {
|
|
if (el.BTAction.find((el) => el.name.isEqual(name))) {
|
|
el.BTAction.map((action) => {
|
|
action.param.map((param) => {
|
|
return param;
|
|
});
|
|
});
|
|
acc = el;
|
|
}
|
|
return acc;
|
|
}, SkillModel.empty())
|
|
);
|
|
|
|
getSkilsOut = (name: string) =>
|
|
this.skills
|
|
.reduce<string[][]>((acc, el) => {
|
|
if (el.BTAction.find((el) => el.name.isEqual(name))) {
|
|
acc = el.BTAction.filter((f) => f.name.isEqual(name)).map((z) => z.result);
|
|
}
|
|
return acc;
|
|
}, [])
|
|
.flat(1);
|
|
getSkillParams = (name: string) =>
|
|
this.skills
|
|
.reduce<IParam[][]>((acc, el) => {
|
|
if (el.BTAction.find((el) => el.name.isEqual(name))) {
|
|
acc = el.BTAction.filter((f) => f.name.isEqual(name)).map((z) => z.param);
|
|
}
|
|
return acc;
|
|
}, [])
|
|
.flat(1);
|
|
getSkillDo = (name: string) =>
|
|
this.skills.reduce((acc, el) => {
|
|
if (el.BTAction.find((el) => el.name.isEqual(name))) {
|
|
acc = el.Module.name;
|
|
}
|
|
return acc;
|
|
}, "error");
|
|
|
|
getSkillsNames = () =>
|
|
this.skills
|
|
.map((el) => {
|
|
return el.BTAction.map((act) => {
|
|
return { name: act.name };
|
|
});
|
|
})
|
|
.flat(1);
|
|
|
|
getForms = (skillLabel: string) =>
|
|
this.skills
|
|
.reduce<SkillModel[]>((acc, el) => {
|
|
if (el.BTAction.find((el) => el.name.isEqual(skillLabel))) {
|
|
acc.push(el);
|
|
}
|
|
return acc;
|
|
}, [])
|
|
.map((el) => el.BTAction.map((act) => act.param.map((el) => el.type).flat(1)))
|
|
.flat(1)
|
|
.flat(1)
|
|
.filter((el) => el !== "");
|
|
|
|
getDependencyBySkillLabelAndType = (skillType: string, sid: string): DependencyViewModel =>
|
|
this.skills
|
|
.reduce<DependencyViewModel[]>((acc, skill) => {
|
|
if (skill.sid?.isEqual(sid)) {
|
|
skill.BTAction.map((action) => {
|
|
action.param.map((param) => {
|
|
if (param.type.isEqualR(skillType)) {
|
|
acc.push(param?.dependency ?? DependencyViewModel.empty());
|
|
}
|
|
return param;
|
|
});
|
|
return action;
|
|
});
|
|
}
|
|
|
|
return acc;
|
|
}, [])
|
|
.at(0) ?? DependencyViewModel.empty();
|
|
static isEmpty(model: Skills): Result<void, void> {
|
|
if (model.skills.isEmpty()) {
|
|
return Result.error(undefined);
|
|
}
|
|
return Result.ok(undefined);
|
|
}
|
|
static empty() {
|
|
const skills = new Skills();
|
|
skills.skills = [];
|
|
return skills;
|
|
}
|
|
|
|
public dependencyIsFilled = (skillType: string, sid: string) =>
|
|
this.skills.reduce((acc, skill) => {
|
|
if (skill.sid?.isEqual(sid)) {
|
|
skill.BTAction.forEach((action) => {
|
|
action.param.forEach((param) => {
|
|
acc = param.isFilled;
|
|
});
|
|
});
|
|
}
|
|
return acc;
|
|
}, false);
|
|
|
|
getAllSids = () =>
|
|
this.skills.reduce((acc, skill) => {
|
|
skill.BTAction.forEach((action) =>
|
|
action.param.forEach((param) => {
|
|
return param;
|
|
})
|
|
);
|
|
return acc;
|
|
}, new Map<string, number>());
|
|
|
|
deleteSid(sid: string): SkillModel[] {
|
|
return this.skills.filter((skill) => !skill.sid?.isEqual(sid));
|
|
}
|
|
updateSkill = (skill: SkillModel) => {
|
|
this.skills = this.skills.map((el) => {
|
|
if (el.sid?.isEqual(skill.sid ?? "")) {
|
|
el = skill;
|
|
}
|
|
return el;
|
|
});
|
|
};
|
|
skillHasForm = (label: string): boolean => {
|
|
return true;
|
|
};
|
|
getSkillWidthAtLabel = (label: string): number =>
|
|
this.getSkillParams(label).reduce((acc, element) => {
|
|
if (element.type.length > acc) {
|
|
acc = element.type.length;
|
|
}
|
|
return acc;
|
|
}, 0);
|
|
skillIsDependencyFilled = (label: string): Result<boolean, boolean> =>
|
|
this.getSkill(label).fold(
|
|
(skill) => {
|
|
return Result.ok(skill.BTAction.find((el) => el.name.isEqual(label))?.param.isEmpty());
|
|
},
|
|
() => Result.error(false)
|
|
);
|
|
}
|
|
|