webstudio/ui/src/core/model/skill_model.ts
2024-09-23 14:56:07 +03:00

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