deleted unnecessary files

added new features
This commit is contained in:
IDONTSUDO 2024-04-09 16:31:30 +03:00
parent 6840402b1f
commit c17515d571
47 changed files with 1039 additions and 479 deletions

14
.vscode/settings.json vendored
View file

@ -15,6 +15,18 @@
"*ui": false, "*ui": false,
"*ui.*": false "*ui.*": false
}, },
"cSpell.words": ["antd", "Collada", "Contolls", "fileupload", "lerp", "metadatas", "undici", "uuidv"], "cSpell.words": [
"запущен",
"процесс",
"antd",
"Collada",
"Contolls",
"fileupload",
"lerp",
"metadatas",
"Skils",
"undici",
"uuidv"
],
"editor.rulers": [100] "editor.rulers": [100]
} }

2
p.ts Normal file
View file

@ -0,0 +1,2 @@
console.log([1, 2, 3, 4, 5, 6].reduce((element, acc) => (acc += element)));
console.log(["1", "2", "3", "4", "5", "6"].reduce((element, acc) => (acc += element)));

View file

@ -49,18 +49,21 @@ export class ExecutorProgramService
if (spawnError instanceof SpawnError) { if (spawnError instanceof SpawnError) {
this.emit(Result.error(spawnError)); this.emit(Result.error(spawnError));
this.worker = undefined;
return; return;
} }
const execError = ExecError.isExecError(e); const execError = ExecError.isExecError(e);
if (execError instanceof ExecError) { if (execError instanceof ExecError) {
this.emit(Result.error(execError)); this.emit(Result.error(execError));
this.worker = undefined;
return; return;
} }
const executorResult = ExecutorResult.isExecutorResult(e); const executorResult = ExecutorResult.isExecutorResult(e);
if (executorResult instanceof ExecutorResult) { if (executorResult instanceof ExecutorResult) {
this.emit(Result.ok(executorResult)); this.emit(Result.ok(executorResult));
this.worker = undefined;
return; return;
} }
}); });
@ -78,7 +81,10 @@ export class ExecutorProgramService
console.log(error); console.log(error);
} }
} }
public deleteWorker() {
if (this.worker) this.worker.kill();
this.worker = undefined;
}
public async call(type: EXEC_TYPE, command: string, args: Array<string> | undefined = undefined): Promise<void> { public async call(type: EXEC_TYPE, command: string, args: Array<string> | undefined = undefined): Promise<void> {
if (type == EXEC_TYPE.EXEC) { if (type == EXEC_TYPE.EXEC) {
this.workerExecuted(command, WorkerType.EXEC); this.workerExecuted(command, WorkerType.EXEC);

View file

@ -1,9 +1,27 @@
import { CallbackStrategyWithEmpty } from "../controllers/http_controller";
import { Result } from "../helpers/result"; import { Result } from "../helpers/result";
import { TypedEvent } from "../helpers/typed_event"; import { TypedEvent } from "../helpers/typed_event";
import { EXEC_TYPE, ExecError, SpawnError } from "../models/exec_error_model"; import { EXEC_TYPE, ExecError, SpawnError } from "../models/exec_error_model";
import { ExecutorResult } from "../models/executor_result"; import { ExecutorResult } from "../models/executor_result";
import { ExecutorProgramService } from "../services/executor_program_service"; import { ExecutorProgramService } from "../services/executor_program_service";
const executorProgramService = new ExecutorProgramService("");
export class KillLastProcessUseCase extends CallbackStrategyWithEmpty {
call = async (): Promise<Result<undefined, string>> => {
executorProgramService.deleteWorker();
return Result.ok("ok");
};
}
export class IsHaveActiveProcessUseCase extends CallbackStrategyWithEmpty {
call = async (): Promise<Result<string, string>> => {
if (executorProgramService.worker === undefined) {
return Result.ok("process not work");
}
return Result.error("process is exists");
};
}
export class ExecProcessUseCase { export class ExecProcessUseCase {
call = async ( call = async (
path: string, path: string,
@ -11,11 +29,11 @@ export class ExecProcessUseCase {
watcher?: TypedEvent<Result<ExecError | SpawnError, ExecutorResult>> watcher?: TypedEvent<Result<ExecError | SpawnError, ExecutorResult>>
): Promise<Result<Error, string>> => { ): Promise<Result<Error, string>> => {
try { try {
const executorProgramService = new ExecutorProgramService(path); executorProgramService.execPath = path;
if (watcher) executorProgramService.on((event) => {
executorProgramService.on((event) => { console.log(event);
watcher.emit(event); if (watcher) watcher.emit(event);
}); });
executorProgramService.call(EXEC_TYPE.EXEC, command); executorProgramService.call(EXEC_TYPE.EXEC, command);
return Result.ok("ok"); return Result.ok("ok");

View file

@ -11,4 +11,3 @@ export class TriggerPresentation extends CrudController<TriggerModelValidationMo
}); });
} }
} }
"".isEmpty();

View file

@ -1,5 +1,7 @@
import { CrudController } from "../../core/controllers/crud_controller"; import { CrudController } from "../../core/controllers/crud_controller";
import { IsHaveActiveProcessUseCase, KillLastProcessUseCase } from "../../core/usecases/exec_process_usecase";
import { CreateDataSetScenario } from "./domain/create_dataset_scanario"; import { CreateDataSetScenario } from "./domain/create_dataset_scanario";
import { ExecDatasetProcessScenario } from "./domain/exec_process_scenario";
import { GetDatasetActiveProjectScenario } from "./domain/get_dataset_active_project_scenario"; import { GetDatasetActiveProjectScenario } from "./domain/get_dataset_active_project_scenario";
import { DatasetDBModel } from "./models/dataset_database_model"; import { DatasetDBModel } from "./models/dataset_database_model";
import { DatasetValidationModel } from "./models/dataset_validation_model"; import { DatasetValidationModel } from "./models/dataset_validation_model";
@ -13,5 +15,20 @@ export class DatasetsPresentation extends CrudController<DatasetValidationModel,
}); });
super.post(new CreateDataSetScenario().call); super.post(new CreateDataSetScenario().call);
super.get(new GetDatasetActiveProjectScenario().call); super.get(new GetDatasetActiveProjectScenario().call);
this.subRoutes.push({
method: "POST",
subUrl: "exec",
fn: new ExecDatasetProcessScenario(),
});
this.subRoutes.push({
method: "GET",
subUrl: "is/running",
fn: new IsHaveActiveProcessUseCase(),
});
this.subRoutes.push({
method: "GET",
subUrl: "delete/process",
fn: new KillLastProcessUseCase(),
});
} }
} }

View file

@ -48,45 +48,14 @@ export class CreateDataSetScenario extends CallbackStrategyWithValidationModel<D
return ( return (
await new SearchDataBaseModelUseCase<IProjectModel>(ProjectDBModel).call({ isActive: true }, "no active projects") await new SearchDataBaseModelUseCase<IProjectModel>(ProjectDBModel).call({ isActive: true }, "no active projects")
).map(async (project) => { ).map(async (project) => {
model.processStatus = "exec"; model.processStatus = "new";
model.local_path = project.rootDir; model.local_path = project.rootDir;
model.projectId = project._id; model.projectId = project._id;
const d = new DatasetDBModel(); const d = new DatasetDBModel();
Object.assign(d, model); Object.assign(d, model);
await d.save(); await d.save();
await new ExecProcessUseCase().call(
`${project.rootDir}/`,
`python3 $PYTHON_BLENDER_PROC --path '${project.rootDir}/${model.name}' --cfg '${JSON.stringify(model)}'`,
new ProcessWatcherAndDatabaseUpdateService(d._id as unknown as ObjectId)
);
return Result.ok("create dataset ok"); return Result.ok("create dataset ok");
}); });
}; };
} }
// сохрнать formbuilder result и передать его в python
// {
// "typedataset": ${typedataset:Enum<T>:"ObjectDetection"},
// "dataset_path": ${DATASET_PATH:string:""},
// "models":${models:Array<MODELS>:[]},
// "models_randomization":{
// "loc_range_low": [${LOC_RANGE_LOW_1:number:-1}, ${LOC_RANGE_LOW_2:number:-1},/${LOC_RANGE_LOW_3:number:0}],
// "loc_range_high": [${LOC_RANGE_HIGH_1:number:1}, ${LOC_RANGE_HIGH_2:number:1},/${LOC_RANGE_HIGH_3:number:2}]
// },
// "scene":{
// "objects": ${OBJECTS_SCENE:Array<OBJECTS_SCENE>:[]},
// "lights": ${LIGHTS:Array<LIGHTS>:[]},
// },
// "camera_position":{
// "center_shell": [${CENTER_SHELL_1:number:0}, ${CENTER_SHELL_2:number:0}, ${CENTER_SHELL_3:number:0}],
// "radius_range": [${RADIUS_RANGE_1:number:0.4}, ${RADIUS_RANGE_2:number:1.4}],
// "elevation_range": [${ELEVATION_RANGE_1:number:10}, ${ELEVATION_RANGE_2:number:90}]
// },
// "generation":{
// "n_cam_pose": ${N_CAM_POSE:number:5},
// "n_sample_on_pose": ${N_SAMPLE_ON_POSE:number:3},
// "n_series": ${N_SERIES:number:100},
// "image_format": ${image_format:Enum<F>:"jpg"},
// "image_size_wh": [${IMAGE_SIZE_WH_1:number:640}, ${IMAGE_SIZE_WH_2:number:480}]
// }
// }

View file

@ -0,0 +1,24 @@
import { ObjectId } from "mongoose";
import { CallbackStrategyWithIdQuery, ResponseBase } from "../../../core/controllers/http_controller";
import { ExecProcessUseCase, IsHaveActiveProcessUseCase } from "../../../core/usecases/exec_process_usecase";
import { ReadByIdDataBaseModelUseCase } from "../../../core/usecases/read_by_id_database_model_usecase";
import { MongoIdValidation } from "../../../core/validations/mongo_id_validation";
import { DatasetDBModel } from "../models/dataset_database_model";
import { IDatasetModel } from "../models/dataset_validation_model";
import { ProcessWatcherAndDatabaseUpdateService } from "./create_dataset_scanario";
export class ExecDatasetProcessScenario extends CallbackStrategyWithIdQuery {
idValidationExpression = new MongoIdValidation();
call = async (id: string): ResponseBase => {
return (await new ReadByIdDataBaseModelUseCase<IDatasetModel>(DatasetDBModel).call(id)).map(async (model) => {
return (await new IsHaveActiveProcessUseCase().call()).map(() => {
return new ExecProcessUseCase().call(
`${model.project.rootDir}/`,
`python3 $PYTHON_BLENDER_PROC --cfg '${JSON.stringify(model)}' `,
new ProcessWatcherAndDatabaseUpdateService(id as unknown as ObjectId)
);
});
});
};
}

View file

@ -9,7 +9,7 @@ export class GetDatasetActiveProjectScenario extends CallbackStrategyWithEmpty {
return ( return (
await new SearchDataBaseModelUseCase<IProjectModel>(ProjectDBModel).call({ isActive: true }, "no active projects") await new SearchDataBaseModelUseCase<IProjectModel>(ProjectDBModel).call({ isActive: true }, "no active projects")
).map(async (project) => { ).map(async (project) => {
return Result.ok(await DatasetDBModel.find({ projectId: project._id })); return Result.ok(await DatasetDBModel.find({ project: project._id }));
}); });
}; };
} }

View file

@ -17,19 +17,32 @@ export const DatasetSchema = new Schema({
type: Schema.Types.Mixed, type: Schema.Types.Mixed,
of: String, of: String,
}, },
unixTime: {
type: Number,
default: Date.now(),
},
neuralNetworkAction: {
type: String,
},
neuralNetworkName: {
type: String,
},
processStatus: { processStatus: {
type: String, type: String,
default: "none", default: "none",
}, },
projectId: { project: {
type: Schema.Types.ObjectId, type: Schema.Types.ObjectId,
ref: projectSchema, ref: projectSchema,
autopopulate: false, autopopulate: true,
default: null, require: true,
}, },
processLogs: { processLogs: {
type: String, type: String,
}, },
datasetType: {
type: String,
},
}).plugin(require("mongoose-autopopulate")); }).plugin(require("mongoose-autopopulate"));
export const datasetSchema = "Dataset"; export const datasetSchema = "Dataset";

View file

@ -1,5 +1,6 @@
import { Type } from "class-transformer"; import { Type } from "class-transformer";
import { IsArray, IsString, ValidateNested } from "class-validator"; import { IsArray, IsString, ValidateNested } from "class-validator";
import { IProjectModel } from "../../_projects/models/project_database_model";
export class FormBuilderValidationModel { export class FormBuilderValidationModel {
@IsString() @IsString()
@ -17,6 +18,7 @@ export interface IDatasetModel {
formBuilder: FormBuilderValidationModel; formBuilder: FormBuilderValidationModel;
processLogs: string; processLogs: string;
processStatus: string; processStatus: string;
project?: IProjectModel;
} }
export class DatasetValidationModel implements IDatasetModel { export class DatasetValidationModel implements IDatasetModel {

View file

@ -1,15 +1,8 @@
import { App } from "../../../core/controllers/app";
import { CallbackStrategyWithFileUpload, ResponseBase } from "../../../core/controllers/http_controller"; import { CallbackStrategyWithFileUpload, ResponseBase } from "../../../core/controllers/http_controller";
import { Result } from "../../../core/helpers/result";
import { IFile } from "../../../core/interfaces/file"; import { IFile } from "../../../core/interfaces/file";
import { CreateDataBaseModelUseCase } from "../../../core/usecases/create_database_model_usecase";
import { CreateFileUseCase } from "../../../core/usecases/create_file_usecase"; import { CreateFileUseCase } from "../../../core/usecases/create_file_usecase";
import { CreateFolderUseCase } from "../../../core/usecases/create_folder_usecase";
import { MongoIdValidation } from "../../../core/validations/mongo_id_validation"; import { MongoIdValidation } from "../../../core/validations/mongo_id_validation";
import { IProjectInstanceModel, ProjectInstanceDbModel } from "../models/project_instance_database_model"; import { IProjectInstanceModel } from "../models/project_instance_database_model";
import { ProjectInstanceValidationModel } from "../models/project_instance_validation_model";
import { v4 as uuidv4 } from "uuid";
import { SetActiveProjectScenario } from "./set_active_project_use_scenario";
import { ReadByIdDataBaseModelUseCase } from "../../../core/usecases/read_by_id_database_model_usecase"; import { ReadByIdDataBaseModelUseCase } from "../../../core/usecases/read_by_id_database_model_usecase";
import { ProjectDBModel } from "../../_projects/models/project_database_model"; import { ProjectDBModel } from "../../_projects/models/project_database_model";
import { ExecProcessUseCase } from "../../../core/usecases/exec_process_usecase"; import { ExecProcessUseCase } from "../../../core/usecases/exec_process_usecase";

View file

@ -17,5 +17,8 @@
<div id="root"></div> <div id="root"></div>
</body> </body>
<style>
@import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap')
</style>
</html> </html>

View file

@ -1,12 +0,0 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
<svg width="64px" height="64px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>
<g id="SVGRepo_iconCarrier"> <path fill-rule="evenodd" clip-rule="evenodd" d="M2 5.75C2 5.33579 2.33579 5 2.75 5H20.75C21.1642 5 21.5 5.33579 21.5 5.75C21.5 6.16421 21.1642 6.5 20.75 6.5H2.75C2.33579 6.5 2 6.16421 2 5.75ZM2 9.75C2 9.33579 2.33579 9 2.75 9H20.75C21.1642 9 21.5 9.33579 21.5 9.75C21.5 10.1642 21.1642 10.5 20.75 10.5H2.75C2.33579 10.5 2 10.1642 2 9.75ZM20.2113 12.6586C20.5379 12.9134 20.5961 13.3847 20.3414 13.7113L16.4414 18.7113C16.3022 18.8897 16.0899 18.9958 15.8636 18.9999C15.6373 19.004 15.4213 18.9057 15.2757 18.7324L13.1757 16.2324C12.9093 15.9152 12.9504 15.4421 13.2676 15.1757C13.5848 14.9093 14.0579 14.9504 14.3243 15.2676L15.8284 17.0582L19.1586 12.7887C19.4134 12.4621 19.8847 12.4039 20.2113 12.6586ZM2 13.75C2 13.3358 2.33579 13 2.75 13H9.75C10.1642 13 10.5 13.3358 10.5 13.75C10.5 14.1642 10.1642 14.5 9.75 14.5H2.75C2.33579 14.5 2 14.1642 2 13.75ZM2 17.75C2 17.3358 2.33579 17 2.75 17H9.75C10.1642 17 10.5 17.3358 10.5 17.75C10.5 18.1642 10.1642 18.5 9.75 18.5H2.75C2.33579 18.5 2 18.1642 2 17.75Z" fill="#f46036"/> </g>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View file

@ -22,7 +22,10 @@ declare global {
} }
interface Number { interface Number {
fromArray(): number[]; fromArray(): number[];
toPx(): string;
unixFromDate(): string;
} }
interface String { interface String {
isEmpty(): boolean; isEmpty(): boolean;
isNotEmpty(): boolean; isNotEmpty(): boolean;

View file

@ -5,4 +5,17 @@ export const NumberExtensions = () => {
return Array.from(this.toString()).map((el) => Number(el)); return Array.from(this.toString()).map((el) => Number(el));
}; };
} }
if (Number().toPx === undefined) {
// eslint-disable-next-line no-extend-native
Number.prototype.toPx = function () {
return String(this) + "px";
};
}
if (Number().unixFromDate === undefined) {
// eslint-disable-next-line no-extend-native
Number.prototype.unixFromDate = function () {
const date = new Date(Number(this) * 1000);
return `${date.getUTCFullYear()}.${date.getMonth()}.${date.getDay()} ${date.getHours()}:${date.getMinutes()}`;
};
}
}; };

View file

@ -15,8 +15,12 @@ import {
import { import {
StickObjectsMarkingScreen, StickObjectsMarkingScreen,
StickObjectsMarkingScreenPath, StickObjectsMarkingScreenPath,
} from "../../features/stick_objects_marking/stick_objects_marking_screen"; } from "../../features/_stick_objects_marking/stick_objects_marking_screen";
import { DataSetScreen, DatasetsScreenPath } from "../../features/dataset/dataset_screen"; import { DataSetScreen, DatasetsScreenPath } from "../../features/dataset/dataset_screen";
import DetailsScreen, { DetailsScreenPath } from "../../features/details/details_screen";
import { AssemblesScreen, AssemblesScreenPath } from "../../features/assembles/assembles_screen";
import SimulationScreen, { SimulationScreenPath } from "../../features/simulations/simulations_screen";
import { EstimateScreen, EstimateScreenPath } from "../../features/estimate/estimate_screen";
const idURL = ":id"; const idURL = ":id";
@ -51,4 +55,21 @@ export const router = createBrowserRouter([
path: DatasetsScreenPath, path: DatasetsScreenPath,
element: <DataSetScreen />, element: <DataSetScreen />,
}, },
{
path: DetailsScreenPath,
element: <DetailsScreen />,
},
{
path: AssemblesScreenPath,
element: <AssemblesScreen />,
},
{
path: SimulationScreenPath,
element: <SimulationScreen />,
},
{
path: EstimateScreenPath,
element: <EstimateScreen />,
},
]); ]);

View file

@ -0,0 +1,32 @@
import * as React from "react";
import { CoreText, CoreTextType } from "../text/text";
export interface IButtonProps {
block?: boolean;
filled?: boolean;
text?: string;
onClick?: any;
}
export function CoreButton(props: IButtonProps) {
return (
<div
onClick={() => props.onClick?.call()}
style={{
backgroundColor: props.filled ? "rgba(103, 80, 164, 1)" : "",
paddingRight: 20,
paddingLeft: 20,
paddingTop: 10,
paddingBottom: 10,
borderRadius: 24,
border: props.block ? "1px solid rgba(29, 27, 32, 0.12)" : props.filled ? "" : "1px solid black",
}}
>
<CoreText
text={props.text ?? ""}
type={CoreTextType.medium}
color={props.block ? "#1D1B20" : props.filled ? "white" : "rgba(103, 80, 164, 1)"}
/>
</div>
);
}

View file

@ -0,0 +1,97 @@
import { Spin } from "antd";
import { LoadingOutlined } from "@ant-design/icons";
import poseIMG from "../../assets/images/pose_estemation.jpg";
import { Icon } from "../icons/icons";
import { CoreText, CoreTextType } from "../text/text";
import { CoreButton } from "../button/button";
export const enum CardDataSetType {
EMPTY = "EMPTY",
COMPLETED = "COMPLETED",
}
interface ICardDataSetProps {
type: CardDataSetType;
neuralNetworkAction?: string;
neuralNetworkName?: string;
objects?: string[];
unixDate?: number;
processStatus?: string;
onClickButton?: (id: string) => void;
onClickEmptyCard?: Function;
id?: string;
}
export const CardDataSet = (props: ICardDataSetProps) => {
return (
<div
style={{
width: 272,
height: 372,
borderRadius: 12,
border: "1px solid #CAC4D0",
backgroundColor: "rgba(254, 247, 255, 1)",
cursor: "pointer",
}}
onClick={() => {
if (props.type.isEqual(CardDataSetType.EMPTY) && props.onClickEmptyCard) {
props.onClickEmptyCard();
}
}}
>
<div style={{ height: 80 }}>
{props.type.isEqual(CardDataSetType.EMPTY) ? null : (
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
<div style={{ width: 70, marginTop: 11 }}></div>
<div style={{ height: 80, alignContent: "center", marginRight: 40 }}>
<CoreText text={props.neuralNetworkName ?? ""} type={CoreTextType.header} />
<CoreText text={props.neuralNetworkAction ?? ""} type={CoreTextType.medium} />
</div>
<div>
<Icon type="Settings" />
</div>
</div>
)}
</div>
<img alt="pose" style={{ width: "100%" }} src={poseIMG}></img>
<div
style={{
textAlignLast: props.type.isEqual(CardDataSetType.EMPTY) ? "center" : "auto",
marginTop: props.type.isEqual(CardDataSetType.EMPTY) ? 80 : 10,
}}
>
{props.type === CardDataSetType.EMPTY ? (
<Icon type="PlusCircle" />
) : (
<div style={{ margin: 10 }}>
<CoreText text={`Объектов: ${props.objects?.length ?? 0}`} type={CoreTextType.large} />
<CoreText text={Number(props.unixDate).unixFromDate()} type={CoreTextType.medium} color="#49454F" />
<div style={{ height: 40 }} />
<div style={{ width: 240, overflow: "hidden", whiteSpace: "nowrap", height: 40 }}>
<CoreText text={props.objects?.join(", ") ?? ""} type={CoreTextType.medium} color="#49454F" />
</div>
<div style={{ display: "flex", flexDirection: "row", justifyContent: "flex-end", alignItems: "center" }}>
{props.processStatus === "exec" ? (
<Spin indicator={<LoadingOutlined style={{ fontSize: 34, color: "rgba(103, 80, 164, 1)" }} spin />} />
) : null}
<div style={{ width: 20 }} />
{props.processStatus === "new" ? (
<CoreButton
onClick={() => {
if (props.type.isEqual(CardDataSetType.COMPLETED) && props.onClickButton && props.id) {
props.onClickButton(props.id);
}
}}
filled={true}
text="Старт"
/>
) : (
<CoreButton text="Завершено" block={true} />
)}
</div>
</div>
)}
</div>
</div>
);
};

View file

@ -3,9 +3,7 @@ import { Input, Select, Button } from "antd";
import { InputBuilderViewModel, InputType } from "./form_view_model"; import { InputBuilderViewModel, InputType } from "./form_view_model";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { FormBuilderStore } from "./form_builder_store"; import { FormBuilderStore } from "./form_builder_store";
import { extensions } from "../../extensions/extensions"; import { FormBuilderValidationModel } from "../../../features/dataset/dataset_model";
import { FormBuilderValidationModel } from "../../../features/dataset/dataset_store";
extensions();
export interface IFormBuilder { export interface IFormBuilder {
context: string; context: string;

View file

@ -1,7 +1,7 @@
import { makeAutoObservable } from "mobx"; import { makeAutoObservable } from "mobx";
import { FormViewModel } from "./form_view_model"; import { FormViewModel } from "./form_view_model";
import { TypedEvent } from "../../helper/typed_event"; import { TypedEvent } from "../../helper/typed_event";
import { FormBuilderValidationModel } from "../../../features/dataset/dataset_store"; import { FormBuilderValidationModel } from "../../../features/dataset/dataset_model";
export class ChangerForm extends TypedEvent<FormBuilderValidationModel | undefined> {} export class ChangerForm extends TypedEvent<FormBuilderValidationModel | undefined> {}

View file

@ -1,7 +1,7 @@
import { makeAutoObservable, observable } from "mobx"; import { makeAutoObservable, observable } from "mobx";
import { Result } from "../../helper/result"; import { Result } from "../../helper/result";
import { v4 as uuidv4 } from "uuid"; import { v4 as uuidv4 } from "uuid";
import { FormBuilderValidationModel } from "../../../features/dataset/dataset_store"; import { FormBuilderValidationModel } from "../../../features/dataset/dataset_model";
export enum InputType { export enum InputType {
NUMBER = "number", NUMBER = "number",
@ -48,16 +48,16 @@ export class FormViewModel {
this.inputs = inputs; this.inputs = inputs;
makeAutoObservable(this); makeAutoObservable(this);
} }
public fromFormBuilderValidationModel() { public fromFormBuilderValidationModel() {
console.log(this.toResult());
return new FormBuilderValidationModel( return new FormBuilderValidationModel(
this.context, this.context,
this.result, this.result,
this.inputs.map((el) => el.toJson()), this.inputs.map((el) => el.toJson()),
this.toResult() JSON.parse(this.toResult().replaceAll("\n", "").replaceAll("\\", "").replaceAll("/", ""))
); );
} }
public toResult() { public toResult(): string {
let result = this.result; let result = this.result;
this.inputs.forEach((element) => { this.inputs.forEach((element) => {
@ -97,6 +97,7 @@ export class FormViewModel {
if (inputResult instanceof Array) inputResult = JSON.stringify(inputResult); if (inputResult instanceof Array) inputResult = JSON.stringify(inputResult);
result = result.replace(new RegExp("\\${" + element.name + ".*?}"), inputResult); result = result.replace(new RegExp("\\${" + element.name + ".*?}"), inputResult);
}); });
return result; return result;
} }
static fromString(result: string, context: string): Result<void, FormViewModel> { static fromString(result: string, context: string): Result<void, FormViewModel> {

View file

@ -0,0 +1,89 @@
import * as React from "react";
import { Result } from "../../helper/result";
export interface IIconsProps {
type: string;
style?: React.CSSProperties;
}
export function Icon(props: IIconsProps) {
const icon = getIconSvg(props.type);
return icon.fold(
(node) => {
return <div style={props.style}>{node}</div>;
},
() => (
<div style={props.style}>
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M6 12C9.31371 12 12 9.31371 12 6C12 2.68629 9.31371 0 6 0C2.68629 0 0 2.68629 0 6C0 9.31371 2.68629 12 6 12Z"
fill="#49454F"
/>
</svg>
</div>
)
);
}
const getIconSvg = (type: string): Result<undefined, React.JSX.Element> => {
switch (type) {
case "PlusCircle":
return Result.ok(
<svg width="33" height="33" viewBox="0 0 33 33" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M16.5003 1.32007C8.09487 1.32007 1.32031 8.09463 1.32031 16.5001C1.32031 24.9055 8.09487 31.6801 16.5003 31.6801C24.9058 31.6801 31.6803 24.9055 31.6803 16.5001C31.6803 8.09463 24.9058 1.32007 16.5003 1.32007ZM16.5003 2.64007C24.1989 2.64007 30.3603 8.80151 30.3603 16.5001C30.3603 24.1986 24.1989 30.3601 16.5003 30.3601C8.80176 30.3601 2.64031 24.1986 2.64031 16.5001C2.64031 8.80151 8.80176 2.64007 16.5003 2.64007ZM15.8403 8.58007V15.8401H8.58031V17.1601H15.8403V24.4201H17.1603V17.1601H24.4203V15.8401H17.1603V8.58007H15.8403Z"
fill="black"
/>
</svg>
);
case "Pencil":
return Result.ok(
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M16.06 0.589883L17.41 1.93988C18.2 2.71988 18.2 3.98988 17.41 4.76988L4.18 17.9999H0V13.8199L10.4 3.40988L13.23 0.589883C14.01 -0.190117 15.28 -0.190117 16.06 0.589883ZM2 15.9999L3.41 16.0599L13.23 6.22988L11.82 4.81988L2 14.6399V15.9999Z"
fill="#31111D"
/>
</svg>
);
case "MenuFab":
return Result.ok(
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_54850_114)">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M11 16V14L29 14V16L11 16ZM11 21H29V19H11V21ZM11 26H29V24H11V26Z"
fill="#49454F"
/>
</g>
<defs>
<clipPath id="clip0_54850_114">
<rect width="40" height="40" rx="20" fill="white" />
</clipPath>
</defs>
</svg>
);
case "Settings":
return Result.ok(
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_54841_7268)">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M27.3102 33.03C27.2102 33.71 26.5902 34.25 25.8502 34.25H22.1502C21.4102 34.25 20.7902 33.71 20.7002 32.98L20.4302 31.09C20.1602 30.95 19.9002 30.8 19.6402 30.63L17.8402 31.35C17.1402 31.61 16.3702 31.32 16.0302 30.7L14.2002 27.53C13.8502 26.87 14.0002 26.09 14.5602 25.65L16.0902 24.46C16.0802 24.31 16.0702 24.16 16.0702 24C16.0702 23.85 16.0802 23.69 16.0902 23.54L14.5702 22.35C13.9802 21.9 13.8302 21.09 14.2002 20.47L16.0502 17.28C16.3902 16.66 17.1602 16.38 17.8402 16.65L19.6502 17.38C19.9102 17.21 20.1702 17.06 20.4302 16.92L20.7002 15.01C20.7902 14.31 21.4102 13.76 22.1402 13.76H25.8402C26.5802 13.76 27.2002 14.3 27.2902 15.03L27.5602 16.92C27.8302 17.06 28.0902 17.21 28.3502 17.38L30.1502 16.66C30.8602 16.4 31.6302 16.69 31.9702 17.31L33.8102 20.49C34.1702 21.15 34.0102 21.93 33.4502 22.37L31.9302 23.56C31.9402 23.71 31.9502 23.86 31.9502 24.02C31.9502 24.18 31.9402 24.33 31.9302 24.48L33.4502 25.67C34.0102 26.12 34.1702 26.9 33.8202 27.53L31.9602 30.75C31.6202 31.37 30.8502 31.65 30.1602 31.38L28.3602 30.66C28.1002 30.83 27.8402 30.98 27.5802 31.12L27.3102 33.03ZM22.6202 32.25H25.3802L25.7502 29.7L26.2802 29.48C26.7202 29.3 27.1602 29.04 27.6202 28.7L28.0702 28.36L30.4502 29.32L31.8302 26.92L29.8002 25.34L29.8702 24.78L29.8733 24.7531C29.9023 24.5027 29.9302 24.2607 29.9302 24C29.9302 23.73 29.9002 23.47 29.8702 23.22L29.8002 22.66L31.8302 21.08L30.4402 18.68L28.0502 19.64L27.6002 19.29C27.1802 18.97 26.7302 18.71 26.2702 18.52L25.7502 18.3L25.3802 15.75H22.6202L22.2502 18.3L21.7202 18.51C21.2802 18.7 20.8402 18.95 20.3802 19.3L19.9302 19.63L17.5502 18.68L16.1602 21.07L18.1902 22.65L18.1202 23.21C18.0902 23.47 18.0602 23.74 18.0602 24C18.0602 24.26 18.0802 24.53 18.1202 24.78L18.1902 25.34L16.1602 26.92L17.5402 29.32L19.9302 28.36L20.3802 28.71C20.8102 29.04 21.2402 29.29 21.7102 29.48L22.2402 29.7L22.6202 32.25ZM27.5002 24C27.5002 25.933 25.9332 27.5 24.0002 27.5C22.0672 27.5 20.5002 25.933 20.5002 24C20.5002 22.067 22.0672 20.5 24.0002 20.5C25.9332 20.5 27.5002 22.067 27.5002 24Z"
fill="#49454F"
/>
</g>
<defs>
<clipPath id="clip0_54841_7268">
<rect x="4" y="4" width="40" height="40" rx="20" fill="white" />
</clipPath>
</defs>
</svg>
);
}
return Result.error(undefined);
};

View file

@ -0,0 +1,126 @@
import { DatasetsScreenPath } from "../../../features/dataset/dataset_screen";
import { Icon } from "../icons/icons";
import { useNavigate } from "react-router-dom";
import { Spin } from "antd";
import { LoadingOutlined } from "@ant-design/icons";
import React from "react";
import { SceneManagerPath } from "../../../features/scene_manager/presentation/scene_manager";
import { AssemblesScreenPath } from "../../../features/assembles/assembles_screen";
import { DetailsScreenPath } from "../../../features/details/details_screen";
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";
export interface IBlockProps {
name: string;
isActive: boolean;
path: string;
}
const Block = (props: IBlockProps) => {
const navigate = useNavigate();
return (
<div onClick={() => navigate(props.path)} style={{ height: 56, cursor: "pointer" }}>
<div
style={
props.isActive
? {
textAlignLast: "center",
height: 32,
backgroundColor: "rgba(232, 222, 248, 1)",
marginLeft: 5,
marginRight: 5,
alignContent: "center",
borderRadius: 12,
}
: { textAlignLast: "center", alignContent: "center" }
}
>
<Icon type={props.name} />
</div>
<div style={{ textAlignLast: "center" }}>{props.name}</div>
</div>
);
};
export interface IMainPageProps {
page: string;
bodyChildren?: JSX.Element;
isLoading?: boolean;
}
export const MainPage = (props: IMainPageProps) => {
const blocksNames = [
{ name: "Детали", path: DetailsScreenPath },
{ name: "Сборки", path: AssemblesScreenPath },
{ name: "Датасеты", path: DatasetsScreenPath },
{ name: "Сцена", path: SceneManagerPath },
{ name: "Навыки", path: BehaviorTreeBuilderPath },
{ name: "Симуляция", path: SimulationScreenPath },
{ name: "Оценка", path: EstimateScreenPath },
];
const blocks: IBlockProps[] = blocksNames
.map((el) => {
return { name: el.name, isActive: false, path: el.path };
})
.map((el) => {
if (el.name.isEqual(props.page)) {
el.isActive = true;
return el;
}
return el;
});
React.useEffect(() => {
document.body.style.overflow = "hidden";
return () => {
document.body.style.overflow = "scroll";
};
});
return (
<div style={{ display: "flex" }}>
<div
style={{
width: 90,
height: window.innerHeight,
display: "flex",
flexDirection: "column",
justifyContent: "space-between",
alignItems: "center",
}}
>
<div style={{ paddingTop: 43 }}>
<div style={{ textAlignLast: "center" }}>
<Icon type="MenuFab" />
</div>
<div
style={{
textAlignLast: "center",
width: 56,
height: 56,
borderRadius: 12,
backgroundColor: "#ffd9e4",
alignContent: "center",
}}
>
<Icon style={{ marginTop: 3 }} type="Pencil" />
</div>
</div>
<div>
{blocks.map((el) => (
<Block isActive={el.isActive} name={el.name} path={el.path} />
))}
</div>
<div style={{ paddingBottom: 10 }}>
<Icon type={"Settings"} />
</div>
</div>
{props.isLoading ? (
<div style={{ alignContent: "center", width: "100%", textAlign: "center" }}>
<Spin indicator={<LoadingOutlined style={{ fontSize: 68, color: "rgba(103, 80, 164, 1)" }} spin />} />
</div>
) : (
<>
<div style={{ width: 241, height: window.innerHeight, backgroundColor: "#F7F2FA", borderRadius: 16 }}> </div>
{props.bodyChildren}
</>
)}
</div>
);
};

View file

@ -2,14 +2,63 @@ import * as React from "react";
export enum CoreTextType { export enum CoreTextType {
header, header,
medium,
large,
} }
export interface ITextProps { export interface ITextProps {
text: string; text: string;
type: CoreTextType; type: CoreTextType;
color?: string;
} }
export function CoreText(props: ITextProps) { export function CoreText(props: ITextProps) {
if (props.type === CoreTextType.header) return <div style={{ color: "white", fontSize: "20px" }}>{props.text}</div>; if (props.type === CoreTextType.large) {
return (
<div
style={{
color: props.color ?? "#1D1B20",
fontSize: 16,
fontFamily: "Roboto",
fontWeight: 400,
fontSizeAdjust: 14,
textOverflow: "ellipsis",
}}
>
{props.text}
</div>
);
}
if (props.type === CoreTextType.medium)
return (
<div
style={{
color: props.color ?? "#1D1B20",
fontSize: 14,
fontFamily: "Roboto",
fontWeight: 400,
textOverflow: "ellipsis",
fontSizeAdjust: 14,
}}
>
{props.text}
</div>
);
if (props.type === CoreTextType.header)
return (
<div
style={{
color: props.color ?? "#1D1B20",
fontSize: 20,
fontFamily: "Roboto",
fontWeight: 500,
textOverflow: "ellipsis",
fontSizeAdjust: 16,
}}
>
{props.text}
</div>
);
return <div>{props.text}</div>; return <div>{props.text}</div>;
} }

View file

@ -1,4 +1,3 @@
import { ActivePipeline } from "../../../core/model/active_pipeline";
import { HttpMethod, HttpRepository } from "../../../core/repository/http_repository"; import { HttpMethod, HttpRepository } from "../../../core/repository/http_repository";
import { IProjectModel } from "../model/project_model"; import { IProjectModel } from "../model/project_model";

View file

@ -0,0 +1,8 @@
import * as React from "react";
import { MainPage } from "../../core/ui/pages/main_page";
export interface IAssemblesScreenProps {}
export const AssemblesScreenPath = "/assembles";
export function AssemblesScreen(props: IAssemblesScreenProps) {
return <MainPage page={"Сборки"}></MainPage>;
}

View file

@ -6,6 +6,7 @@ import { useRete } from "rete-react-plugin";
import { createEditor } from "./ui/editor/editor"; import { createEditor } from "./ui/editor/editor";
import { SkillTree } from "./ui/skill_tree/skill_tree"; import { SkillTree } from "./ui/skill_tree/skill_tree";
import { BehaviorTreeBuilderStore } from "./behavior_tree_builder_store"; import { BehaviorTreeBuilderStore } from "./behavior_tree_builder_store";
import { MainPage } from "../../../core/ui/pages/main_page";
export const behaviorTreeBuilderScreenPath = "behavior/tree/screen/path"; export const behaviorTreeBuilderScreenPath = "behavior/tree/screen/path";
@ -53,56 +54,24 @@ export function BehaviorTreeBuilderScreen() {
}, [store, ref]); }, [store, ref]);
return ( return (
<div> <MainPage
<div page={"Навыки"}
style={{ bodyChildren={
width: "100vw", <>
height: "86px", <div style={{ display: "flex", width: "100%" }}>
background: "#041226", {/* <SkillTree dragEnd={store.dragEnd} skills={skills} /> */}
}}
>
<CoreText text="Robossembler studio" type={CoreTextType.header} />
</div>
<div style={{ display: "flex" }}>
<div
style={{
width: "30vw",
height: "70px",
background: "#1B2E42",
}}
></div>
<div
style={{
width: "1782px",
height: "70px",
background: "rgb(27 45 66)",
}}
>
<Button onClick={() => store.saveBt()}>SAVE</Button>
</div>
</div>
<div style={{ display: "flex" }}>
<div
style={{
width: "30vw",
background: "#1B3041",
boxShadow: "inset 0px 10px 4px #16283D",
}}
>
<div style={{ overflow: "auto", height: "100%", width: "100%", padding: "10px" }}>
<SkillTree dragEnd={store.dragEnd} skills={skills} />
</div>
</div>
<div <div
ref={ref} ref={ref}
style={{ style={{
width: "1782px", width: "100%",
height: String(window.innerHeight - 86 - 70) + "px", height: window.innerHeight,
background: "#244366", background: "white",
}} }}
></div> ></div>
</div> </div>
</div> </>
}
/>
); );
} }

View file

@ -16,10 +16,10 @@ interface IRefListerProps {
} }
export const RefListener = (props: IRefListerProps) => { export const RefListener = (props: IRefListerProps) => {
const canvasRef = React.useRef<HTMLDataElement>(null); const ref = React.useRef<HTMLDataElement>(null);
React.useEffect(() => { React.useEffect(() => {
if (canvasRef.current) { if (ref.current) {
canvasRef.current.addEventListener("dragend", (e) => { ref.current.addEventListener("dragend", (e) => {
// @ts-expect-error // @ts-expect-error
if (e.target.innerHTML) { if (e.target.innerHTML) {
// @ts-expect-error // @ts-expect-error
@ -27,7 +27,7 @@ export const RefListener = (props: IRefListerProps) => {
} }
}); });
} }
}, [canvasRef, props]); }, [ref, props]);
return ( return (
<div {...props.getNodeProps({ onClick: props.handleExpand })}> <div {...props.getNodeProps({ onClick: props.handleExpand })}>
@ -36,7 +36,7 @@ export const RefListener = (props: IRefListerProps) => {
props.handleSelect(e); props.handleSelect(e);
}} }}
/> />
<span ref={canvasRef} style={{ color: "white" }} draggable="true"> <span ref={ref} style={{ color: "white" }} draggable="true">
{props.element.name} {props.element.name}
</span> </span>
</div> </div>

View file

@ -1,75 +0,0 @@
export const datasetTypes = ["Object Detection - YOLOv8", "Pose Estimation - DOPE"];
export const datasetFormMockResult = `{
"typedataset": \${typedataset:Enum<T>:"ObjectDetection"},
"dataset_path": \${ПУТЬ ДАТАСЕТА:string:""},
"models":\${models:Array<MODELS>:[]},
"models_randomization":{
"loc_range_low": [\${LOC_RANGE_LOW_1:number:-1}, \${LOC_RANGE_LOW_2:number:-1},/\${LOC_RANGE_LOW_3:number:0}],
"loc_range_high": [\${LOC_RANGE_HIGH_1:number:1}, \${LOC_RANGE_HIGH_2:number:1},/\${LOC_RANGE_HIGH_3:number:2}]
},
"scene":{
"objects": \${OBJECTS_SCENE:Array<OBJECTS_SCENE>:[]},
"lights": \${LIGHTS:Array<LIGHTS>:[]},
},
"camera_position":{
"center_shell": [\${CENTER_SHELL_1:number:0}, \${CENTER_SHELL_2:number:0}, \${CENTER_SHELL_3:number:0}],
"radius_range": [\${RADIUS_RANGE_1:number:0.4}, \${RADIUS_RANGE_2:number:1.4}],
"elevation_range": [\${ELEVATION_RANGE_1:number:10}, \${ELEVATION_RANGE_2:number:90}]
},
"generation":{
"n_cam_pose": \${N_CAM_POSE:number:5},
"n_sample_on_pose": \${N_SAMPLE_ON_POSE:number:3},
"n_series": \${N_SERIES:number:100},
"image_format": \${image_format:Enum<F>:"jpg"},
"image_size_wh": [\${IMAGE_SIZE_WH_1:number:640}, \${IMAGE_SIZE_WH_2:number:480}]
}
}
`;
export const datasetFormMockContext = `
ENUM T = "ObjectDetection","PoseEstimation";
ENUM L = "POINT","SUN";
ENUM F = "JPEG","PNG";
ENUM COLLISION_SHAPE = "SHAPE","COLLISION";
type MODELS = {
"id": \${ID:number:1},
"name": \${NAME:string:""},
"model": \${MODEL:string:"models/1.fbx"}
};
type OBJECTS_SCENE = {
"name": \${NAME:string:""},
"collision_shape": \${collision_shape:Enum<COLLISION_SHAPE>:"BOX"},
"loc_xyz": [\${LOC_XYZ_1:number:0}, \${LOC_XYZ_2:number:0}, \${LOC_XYZ_3:number:0}],
"rot_euler": [\${ROT_EULER_1:number:0},\${ROT_EULER_2:number:0}, \${ROT_EULER_3:number:0}],
"material_randomization": {
"specular": [\${SPECULAR_1:number:0}, \${SPECULAR_2:number:1}],
"roughness": [\${ROUGHNESS_1:number:0}, \${ROUGHNESS_2:number:1}],
"metallic": [\${METALLIC_1:number:0}, \${METALLIC_2:number:1}],
"base_color": [
[
\${BASE_COLOR_1:number:0},
\${BASE_COLOR_2:number:0},
\${BASE_COLOR_3:number:0},
\${BASE_COLOR_4:number:1}
],
[
\${BASE_COLOR_5:number:1},
\${BASE_COLOR_6:number:1},
\${BASE_COLOR_7:number:1},
\${BASE_COLOR_8:number:1}
]
]
}
};
type LIGHTS = {
"id": \${ID:number:1},
"type": \${type:Enum<L>:"POINT"},
"loc_xyz": [\${LOC_XYZ_1:number:5}, \${LOC_XYZ_2:number:5}, \${LOC_XYZ_3:number:5}],
"rot_euler": [\${ROT_EULER_1:number:-0.06}, \${ROT_EULER_2:number:0.61}, \${ROT_EULER_3:number:-0.19}],
"color_range_low": [\${COLOR_RANGE_LOW_1:number:0.5}, \${COLOR_RANGE_LOW_2:number:0.5}, \${COLOR_RANGE_LOW_3:number:0.5}],
"color_range_high":[\${COLOR_RANGE_HIGH_1:number:1}, \${COLOR_RANGE_HIGH_2:number:1}, $\{COLOR_RANGE_HIGH_3:number:1}],
"energy_range":[\${ENERGY_RANGE_1:number:400},\${ENERGY_RANGE_2:number:900}]
};
`;

View file

@ -1,4 +1,20 @@
import { InputBuilderViewModel } from "../../core/ui/form_builder/form_view_model"; import { InputBuilderViewModel } from "../../core/ui/form_builder/form_view_model";
import { Result } from "../../core/helper/result";
import makeAutoObservable from "mobx-store-inheritance";
export interface IDatasetModel {
_id: string;
dataSetObjects: string[];
processStatus: string;
projectId: string;
name: string;
formBuilder: FormBuilder;
unixTime: number;
datasetType: string;
local_path: string;
__v: number;
processLogs: string;
}
export interface Dataset { export interface Dataset {
name: string; name: string;
@ -23,3 +39,128 @@ export interface Asset {
mesh: string; mesh: string;
image: string; image: string;
} }
export class FormBuilderValidationModel {
public result: string;
public context: string;
public form: string[];
public output: string;
constructor(context: string, result: string, form: string[], output: string) {
this.context = context;
this.result = result;
this.form = form;
this.output = output;
}
static empty() {
return new FormBuilderValidationModel(datasetFormMockContext, datasetFormMockResult, [], "");
}
}
export class DataSetModel {
dataSetObjects: string[];
datasetType: string;
name: string;
formBuilder: FormBuilderValidationModel = FormBuilderValidationModel.empty();
project?: string;
constructor(dataSetObjects: string[], datasetType = datasetTypes[0], datasetName: string) {
this.dataSetObjects = dataSetObjects;
this.datasetType = datasetType;
this.name = datasetName;
makeAutoObservable(this);
}
static empty() {
return new DataSetModel([], "", "");
}
isValid(): Result<string, void> {
if (this.project === undefined) {
return Result.error("project is unknow");
}
if (this.dataSetObjects.isEmpty()) {
return Result.error("not selected details");
}
if (this.datasetType.isEmpty()) {
return Result.error("dataset type is empty");
}
if (this.name.isEmpty()) {
return Result.error("dataset name is empty");
}
return Result.ok();
}
}
export const datasetTypes = ["Object Detection - YOLOv8", "Pose Estimation - DOPE"];
export const datasetFormMockResult = `{
"typedataset": \${typedataset:Enum<T>:ObjectDetection},
"dataset_path": \${ПУТЬ ДАТАСЕТА:string:none},
"models":\${models:Array<MODELS>:[]},
"models_randomization":{
"loc_range_low": [\${LOC_RANGE_LOW_1:number:-1}, \${LOC_RANGE_LOW_2:number:-1},/\${LOC_RANGE_LOW_3:number:0}],
"loc_range_high": [\${LOC_RANGE_HIGH_1:number:1}, \${LOC_RANGE_HIGH_2:number:1},/\${LOC_RANGE_HIGH_3:number:2}]
},
"scene":{
"objects": \${OBJECTS_SCENE:Array<OBJECTS_SCENE>:[]},
"lights": \${LIGHTS:Array<LIGHTS>:[]}
},
"camera_position":{
"center_shell": [\${CENTER_SHELL_1:number:0}, \${CENTER_SHELL_2:number:0}, \${CENTER_SHELL_3:number:0}],
"radius_range": [\${RADIUS_RANGE_1:number:0.4}, \${RADIUS_RANGE_2:number:1.4}],
"elevation_range": [\${ELEVATION_RANGE_1:number:10}, \${ELEVATION_RANGE_2:number:90}]
},
"generation":{
"n_cam_pose": \${N_CAM_POSE:number:5},
"n_sample_on_pose": \${N_SAMPLE_ON_POSE:number:3},
"n_series": \${N_SERIES:number:100},
"image_format": \${image_format:Enum<F>:jpg},
"image_size_wh": [\${IMAGE_SIZE_WH_1:number:640}, \${IMAGE_SIZE_WH_2:number:480}]
}
}
`;
export const datasetFormMockContext = `
ENUM T = "ObjectDetection","PoseEstimation";
ENUM L = "POINT","SUN";
ENUM F = "JPEG","PNG";
ENUM COLLISION_SHAPE = "SHAPE","COLLISION";
type MODELS = {
"id": \${ID:number:1},
"name": \${NAME:string:none},
"model": \${MODEL:string:models/1.fbx}
};
type OBJECTS_SCENE = {
"name": \${NAME:string:default},
"collision_shape": \${collision_shape:Enum<COLLISION_SHAPE>:BOX},
"loc_xyz": [\${LOC_XYZ_1:number:0}, \${LOC_XYZ_2:number:0}, \${LOC_XYZ_3:number:0}],
"rot_euler": [\${ROT_EULER_1:number:0},\${ROT_EULER_2:number:0}, \${ROT_EULER_3:number:0}],
"material_randomization": {
"specular": [\${SPECULAR_1:number:0}, \${SPECULAR_2:number:1}],
"roughness": [\${ROUGHNESS_1:number:0}, \${ROUGHNESS_2:number:1}],
"metallic": [\${METALLIC_1:number:0}, \${METALLIC_2:number:1}],
"base_color": [
[
\${BASE_COLOR_1:number:0},
\${BASE_COLOR_2:number:0},
\${BASE_COLOR_3:number:0},
\${BASE_COLOR_4:number:1}
],
[
\${BASE_COLOR_5:number:1},
\${BASE_COLOR_6:number:1},
\${BASE_COLOR_7:number:1},
\${BASE_COLOR_8:number:1}
]
]
}
};
type LIGHTS = {
"id": \${ID:number:1},
"type": \${type:Enum<L>:"POINT"},
"loc_xyz": [\${LOC_XYZ_1:number:5}, \${LOC_XYZ_2:number:5}, \${LOC_XYZ_3:number:5}],
"rot_euler": [\${ROT_EULER_1:number:-0.06}, \${ROT_EULER_2:number:0.61}, \${ROT_EULER_3:number:-0.19}],
"color_range_low": [\${COLOR_RANGE_LOW_1:number:0.5}, \${COLOR_RANGE_LOW_2:number:0.5}, \${COLOR_RANGE_LOW_3:number:0.5}],
"color_range_high":[\${COLOR_RANGE_HIGH_1:number:1}, \${COLOR_RANGE_HIGH_2:number:1}, $\{COLOR_RANGE_HIGH_3:number:1}],
"energy_range":[\${ENERGY_RANGE_1:number:400},\${ENERGY_RANGE_2:number:900}]
};
`;

View file

@ -1,9 +1,12 @@
import { Result } from "../../core/helper/result"; import { Result } from "../../core/helper/result";
import { HttpError, HttpMethod, HttpRepository } from "../../core/repository/http_repository"; import { HttpError, HttpMethod, HttpRepository } from "../../core/repository/http_repository";
import { Assets, Dataset } from "./dataset_model"; import { UUID } from "../all_projects/data/project_repository";
import { DataSetModel, IDatasetModel } from "./dataset_store"; import { Assets, DataSetModel, Dataset, IDatasetModel } from "./dataset_model";
export class DataSetRepository extends HttpRepository { export class DataSetRepository extends HttpRepository {
getActiveProjectId(): Promise<Result<HttpError, UUID>> {
return this._jsonRequest<UUID>(HttpMethod.GET, "/projects/get/active/project/id");
}
getAssetsActiveProject = async (): Promise<Result<HttpError, Assets>> => { getAssetsActiveProject = async (): Promise<Result<HttpError, Assets>> => {
return this._jsonRequest<Assets>(HttpMethod.GET, "/projects/assets"); return this._jsonRequest<Assets>(HttpMethod.GET, "/projects/assets");
}; };
@ -13,4 +16,13 @@ export class DataSetRepository extends HttpRepository {
getDatasetsActiveProject = async (): Promise<Result<HttpError, IDatasetModel[]>> => { getDatasetsActiveProject = async (): Promise<Result<HttpError, IDatasetModel[]>> => {
return this._jsonRequest(HttpMethod.GET, "/datasets"); return this._jsonRequest(HttpMethod.GET, "/datasets");
}; };
execDatasetProcess = async (id: string) => {
return this._jsonRequest(HttpMethod.POST, `/datasets/exec?id=${id}`);
};
isRunningProcess = async () => {
return this._jsonRequest(HttpMethod.GET, "/datasets/is/running");
};
deleteProcess = async () => {
return this._jsonRequest(HttpMethod.GET, "/datasets/delete/process");
};
} }

View file

@ -1,9 +1,11 @@
import * as React from "react"; import * as React from "react";
import { Drawer, Button, Radio, Card, Checkbox, Input } from "antd"; import { Drawer, Button, Radio, Card, Checkbox, Input } from "antd";
import { FormBuilder } from "../../core/ui/form_builder/form_builder"; import { FormBuilder } from "../../core/ui/form_builder/form_builder";
import { datasetFormMockContext, datasetFormMockResult, datasetTypes } from "./dataset_form_mock";
import { DataSetStore } from "./dataset_store"; import { DataSetStore } from "./dataset_store";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { datasetTypes } from "./dataset_model";
import { MainPage } from "../../core/ui/pages/main_page";
import { CardDataSet, CardDataSetType } from "../../core/ui/card/card_dataset";
export const DatasetsScreenPath = "/dataset"; export const DatasetsScreenPath = "/dataset";
@ -18,7 +20,96 @@ export const DataSetScreen: React.FunctionComponent = observer(() => {
const [open1, setOpen1] = React.useState(false); const [open1, setOpen1] = React.useState(false);
return ( return (
<> <>
<Button onClick={() => setOpen(true)}>new dataset</Button> <MainPage
isLoading={false}
page="Датасеты"
bodyChildren={
<div
style={{
display: "grid",
gridTemplateColumns: "repeat(auto-fill, minmax(272px, 1fr))",
gridGap: 10,
width: "100%",
margin: 12,
overflow: "auto",
height: window.innerHeight,
}}
>
{store.datasets?.map((el) => {
console.log(el);
return (
<CardDataSet
type={CardDataSetType.COMPLETED}
objects={el.dataSetObjects}
unixDate={el.unixTime}
processStatus={el.processStatus}
neuralNetworkAction={el.datasetType.split(" - ").at(0)}
neuralNetworkName={el.datasetType.split(" - ").at(1)}
id={el._id}
onClickButton={(id) => store.runProcess(id)}
/>
);
})}
<CardDataSet
type={CardDataSetType.EMPTY}
onClickEmptyCard={() => {
setOpen(true);
}}
/>
<Drawer title="Form builder Drawer" onClose={() => setOpen1(false)} open={open1}>
<FormBuilder
context={store.dataSetModel.formBuilder.context}
result={store.dataSetModel.formBuilder.result}
onChange={(el) => {
if (el) store.dataSetModel.formBuilder = el;
}}
/>
</Drawer>
<Drawer title="New Dataset" onClose={() => setOpen(false)} open={open}>
<Input placeholder="Dataset name" onChange={(e) => (store.dataSetModel.name = e.target.value)} />
<Button onClick={() => setOpen1(true)}>edit dataset settings</Button>
<Button onClick={() => store.saveDataset()}>save dataset</Button>
<Radio.Group
value={store.dataSetModel.datasetType}
onChange={(e) => {
store.dataSetModel.datasetType = e.target.value;
}}
>
{datasetTypes.map((el) => (
<>
<Radio.Button value={el}>{el}</Radio.Button>
</>
))}
</Radio.Group>
{store.assets?.assets.map((el) => {
return (
<>
<Checkbox
onChange={() => {
store.dataSetModel.dataSetObjects.push(el.name);
}}
defaultChecked={store.dataSetModel.dataSetObjects.includes(el.name)}
>
include?
</Checkbox>
<Card title={el.name} style={{ width: 300 }}>
<img src={el.image} alt="error" />
</Card>
</>
);
})}
</Drawer>
</div>
}
/>
</>
);
});
{
/* <Button onClick={() => setOpen(true)}>new dataset</Button>
{store.datasets?.isEmpty() ? ( {store.datasets?.isEmpty() ? (
<div>empty dataset</div> <div>empty dataset</div>
) : ( ) : (
@ -42,53 +133,6 @@ export const DataSetScreen: React.FunctionComponent = observer(() => {
})} })}
</div> </div>
</div> </div>
)} )}
<Drawer title="New Dataset" onClose={() => setOpen(false)} open={open}> </> */
<Input placeholder="Dataset name" onChange={(e) => (store.dataSetModel.name = e.target.value)} /> }
<Button onClick={() => setOpen1(true)}>edit dataset settings</Button>
<Button onClick={() => store.saveDataset()}>save dataset</Button>
<Radio.Group
value={store.dataSetModel.datasetType}
onChange={(e) => {
store.dataSetModel.datasetType = e.target.value;
}}
>
{datasetTypes.map((el) => (
<>
<Radio.Button value={el}>{el}</Radio.Button>
</>
))}
</Radio.Group>
{store.assets?.assets.map((el) => {
return (
<>
<Checkbox
onChange={() => {
store.dataSetModel.dataSetObjects.push(el.name);
}}
defaultChecked={store.dataSetModel.dataSetObjects.includes(el.name)}
>
include?
</Checkbox>
<Card title={el.name} style={{ width: 300 }}>
<img src={el.image} alt="error" />
</Card>
</>
);
})}
</Drawer>
<Drawer title="Form builder Drawer" onClose={() => setOpen1(false)} open={open1}>
<FormBuilder
context={store.dataSetModel.formBuilder.context}
result={store.dataSetModel.formBuilder.result}
onChange={(el) => {
if (el) store.dataSetModel.formBuilder = el;
}}
/>
</Drawer>
</>
);
});

View file

@ -2,75 +2,46 @@ import makeAutoObservable from "mobx-store-inheritance";
import { DataSetRepository } from "./dataset_repository"; import { DataSetRepository } from "./dataset_repository";
import { UiErrorState } from "../../core/store/base_store"; import { UiErrorState } from "../../core/store/base_store";
import { HttpError } from "../../core/repository/http_repository"; import { HttpError } from "../../core/repository/http_repository";
import { Assets } from "./dataset_model"; import { Assets, DataSetModel, IDatasetModel } from "./dataset_model";
import { datasetFormMockContext, datasetFormMockResult, datasetTypes } from "./dataset_form_mock";
import { Result } from "../../core/helper/result";
import { message } from "antd"; import { message } from "antd";
import { UUID } from "../all_projects/data/project_repository";
export class FormBuilderValidationModel {
public result: string;
public context: string;
public form: string[];
public output: string;
constructor(context: string, result: string, form: string[], output: string) {
this.context = context;
this.result = result;
this.form = form;
this.output = output;
}
static empty() {
return new FormBuilderValidationModel(datasetFormMockContext, datasetFormMockResult, [], "");
}
}
export class DataSetModel {
dataSetObjects: string[];
datasetType: string;
name: string;
formBuilder: FormBuilderValidationModel = FormBuilderValidationModel.empty();
constructor(dataSetObjects: string[], datasetType = datasetTypes[0], datasetName: string) {
this.dataSetObjects = dataSetObjects;
this.datasetType = datasetType;
this.name = datasetName;
makeAutoObservable(this);
}
static empty() {
return new DataSetModel([], "", "");
}
isValid(): Result<string, void> {
if (this.dataSetObjects.isEmpty()) {
return Result.error("not selected details");
}
if (this.datasetType.isEmpty()) {
return Result.error("dataset type is empty");
}
if (this.name.isEmpty()) {
return Result.error("dataset name is empty");
}
return Result.ok();
}
}
export class DataSetStore extends UiErrorState<HttpError> { export class DataSetStore extends UiErrorState<HttpError> {
dataSetRepository: DataSetRepository; dataSetRepository: DataSetRepository;
assets?: Assets; assets?: Assets;
datasets?: IDatasetModel[]; datasets?: IDatasetModel[];
activeProject: UUID;
dataSetModel = DataSetModel.empty(); dataSetModel = DataSetModel.empty();
constructor() { constructor() {
super(); super();
this.dataSetRepository = new DataSetRepository(); this.dataSetRepository = new DataSetRepository();
makeAutoObservable(this); makeAutoObservable(this);
} }
errorHandingStrategy = (error: HttpError) => {}; runProcess = async (id: string): Promise<void> => {
(await this.dataSetRepository.isRunningProcess()).fold(
async (s) => {
(await this.dataSetRepository.execDatasetProcess(id)).fold(
() => {
message.success("процесс запущен");
},
(e) => message.error(e.message)
);
},
async (e) => message.error(e.message)
);
};
errorHandingStrategy = (error: HttpError) => {
message.error(error.message);
};
saveDataset(): void { saveDataset(): void {
this.dataSetModel.project = this.activeProject.id;
this.dataSetModel.isValid().fold( this.dataSetModel.isValid().fold(
async () => { async () => {
(await this.dataSetRepository.saveDataSet(this.dataSetModel)).fold( (await this.dataSetRepository.saveDataSet(this.dataSetModel)).fold(
() => { () => {
message.success("save dataset"); message.success("датасет сохранен");
}, },
(error) => message.error(error.message) (error) => message.error(error.message)
); );
@ -78,25 +49,10 @@ export class DataSetStore extends UiErrorState<HttpError> {
async (error) => message.error(error) async (error) => message.error(error)
); );
} }
init = async () => { init = async () => {
await this.mapOk("assets", this.dataSetRepository.getAssetsActiveProject()); await this.mapOk("assets", this.dataSetRepository.getAssetsActiveProject());
await this.mapOk("datasets", this.dataSetRepository.getDatasetsActiveProject()); await this.mapOk("datasets", this.dataSetRepository.getDatasetsActiveProject());
await this.mapOk("activeProject", this.dataSetRepository.getActiveProjectId());
}; };
} }
export interface IDatasetModel {
_id: string;
dataSetObjects: string[];
processStatus: string;
projectId: string;
name: string;
formBuilder: FormBuilder;
local_path: string;
__v: number;
processLogs: string;
}
export interface FormBuilder {
result: string;
context: string;
form: any[];
}

View file

@ -0,0 +1,8 @@
import * as React from "react";
import { MainPage } from "../../core/ui/pages/main_page";
export interface IDetailsScreenProps {}
export const DetailsScreenPath = "/detail";
export default function DetailsScreen(props: IDetailsScreenProps) {
return <MainPage page={"Детали"}></MainPage>;
}

View file

@ -0,0 +1,8 @@
import * as React from "react";
import { MainPage } from "../../core/ui/pages/main_page";
export interface IEstimateScreenProps {}
export const EstimateScreenPath = "/estimate";
export function EstimateScreen(props: IEstimateScreenProps) {
return <MainPage page={"Оценка"}></MainPage>;
}

View file

@ -1,32 +1,27 @@
import * as React from "react"; import * as React from "react";
import { LoadPage } from "../../core/ui/pages/load_page";
import { PipelineInstanceStore } from "./pipeline_instance_store"; import { PipelineInstanceStore } from "./pipeline_instance_store";
import { Button } from "antd";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { BehaviorTreeBuilderPath } from "../behavior_tree_builder/presentation/behavior_tree_builder_screen"; import { Icon } from "../../core/ui/icons/icons";
import { SceneManagerPath } from "../scene_manager/presentation/scene_manager"; import { CardDataSet, CardDataSetType } from "../../core/ui/card/card_dataset";
import { StickObjectsMarkingScreenPath } from "../stick_objects_marking/stick_objects_marking_screen"; import { MainPage } from "../../core/ui/pages/main_page";
import { DatasetsScreenPath } from "../dataset/dataset_screen";
export const PipelineInstanceScreenPath = "/pipeline_instance/"; export const PipelineInstanceScreenPath = "/pipeline_instance/";
export const PipelineInstanceScreen: React.FunctionComponent = () => { export const PipelineInstanceScreen: React.FunctionComponent = () => {
const [pipelineInstanceStore] = React.useState(() => new PipelineInstanceStore()); const [store] = React.useState(() => {
React.useEffect(() => {}, [pipelineInstanceStore]); new PipelineInstanceStore();
});
React.useEffect(() => {
document.body.style.overflow = "hidden";
return () => {
document.body.style.overflow = "scroll";
};
}, [store]);
const navigate = useNavigate(); const navigate = useNavigate();
return ( return (
<LoadPage <>
needBackButton={true} <MainPage page="Датасеты" />
largeText={"Project instance active"} </>
isError={pipelineInstanceStore.isError}
isLoading={pipelineInstanceStore.isLoading}
children={
<div>
<Button onClick={() => navigate(BehaviorTreeBuilderPath)}>Behavior tree</Button>
<Button onClick={() => navigate(SceneManagerPath)}>Scene Builder</Button>
<Button onClick={() => navigate(StickObjectsMarkingScreenPath)}>Sticky marking</Button>
<Button onClick={() => navigate(DatasetsScreenPath)}>Datasets</Button>
</div>
}
/>
); );
}; };

View file

@ -57,7 +57,6 @@ export const SceneWidget = () => {
<h1 <h1
onClick={(event) => { onClick={(event) => {
event.stopPropagation(); event.stopPropagation();
console.log(201);
}} }}
> >
HYO HYO

View file

@ -8,6 +8,7 @@ import { Button } from "antd";
import { Form, Input, ResetButton, SubmitButton } from "formik-antd"; import { Form, Input, ResetButton, SubmitButton } from "formik-antd";
import { Formik } from "formik"; import { Formik } from "formik";
import { CameraViewModel } from "../model/scene_assets"; import { CameraViewModel } from "../model/scene_assets";
import { MainPage } from "../../../core/ui/pages/main_page";
export const SceneManagerPath = "/scene/manager/"; export const SceneManagerPath = "/scene/manager/";
@ -33,141 +34,150 @@ export const SceneManger = observer(() => {
}); });
return ( return (
<div> <MainPage
<canvas ref={canvasRef} style={{ position: "absolute", overflow: "hidden" }} /> page={"Сцена"}
<div bodyChildren={
style={{ <>
display: "flex", <canvas ref={canvasRef} style={{ overflow: "hidden" }} />
flexDirection: "row",
alignContent: "center",
justifyContent: "space-between",
position: "absolute",
width: "100vw",
}}
>
<div>
{sceneIcons.map((el) => {
return (
<div
style={{
backgroundColor: store.sceneMode === el.name ? "aqua" : "ActiveBorder",
}}
onClick={() => {
el.clickHandel();
}}
>
{el.name}
</div>
);
})}
<div <div
style={{ style={{
marginTop: "10px", display: "flex",
backgroundColor: "GrayText", flexDirection: "row",
border: "solid", alignContent: "center",
borderRadius: "10px", justifyContent: "space-between",
padding: "8px", position: "absolute",
borderColor: "white", width: "100vw",
}} }}
> />
<div style={{ color: "white" }}>Scene manager</div> {/* <div>
{store.isVisibleSaveButton ? ( {sceneIcons.map((el) => {
<> return (
<Button onClick={() => store.onTapSave()}>Save</Button> <div
</> style={{
) : ( backgroundColor: store.sceneMode === el.name ? "aqua" : "ActiveBorder",
<></> }}
)} onClick={() => {
{store.isLoading ? <>Loading...</> : <></>} el.clickHandel();
{store.sceneMode === SceneMode.ADD_CAMERA ? ( }}
<div> >
<Formik {el.name}
initialValues={CameraViewModel.empty()} </div>
onSubmit={async (model, actions) => { );
store.addNewCamera(model); })}
actions.setSubmitting(false); <div
actions.resetForm(); style={{
}} marginTop: "10px",
validate={(model) => { backgroundColor: "GrayText",
return model.validate(store.getCameraLinkNames()); border: "solid",
}} borderRadius: "10px",
render={() => ( padding: "8px",
<Form> borderColor: "white",
<div }}
style={{ >
background: "white", {/* <div style={{ color: "white" }}>Scene manager</div>
flex: 1, {store.isVisibleSaveButton ? (
padding: 40, <>
width: "400px", <Button onClick={() => store.onTapSave()}>Save</Button>
}} </>
> ) : (
<Input name="cameraLink" placeholder="Camera link" /> <></>
<Input name="topicImage" placeholder="Topic Image" /> )}
<Input name="topicCameraInfo" placeholder="Topic Camera Info" /> {store.isLoading ? <>Loading...</> : <></>}
<Input name="topicDepth" placeholder="Topic Depth" /> {store.sceneMode === SceneMode.ADD_CAMERA ? (
<div>
<ResetButton>Reset</ResetButton> <Formik
<SubmitButton>Submit</SubmitButton> initialValues={CameraViewModel.empty()}
</div> onSubmit={async (model, actions) => {
</Form> store.addNewCamera(model);
)} actions.setSubmitting(false);
/> actions.resetForm();
</div> }}
) : ( validate={(model) => {
<></> return model.validate(store.getCameraLinkNames());
)} }}
{store.sceneMode === SceneMode.MOVING || SceneMode.ROTATE ? ( render={() => (
<> <Form>
{store.robossemblerAssets?.assets.map((el) => { <div
return ( style={{
<div> background: "white",
<div style={{ color: "white", marginLeft: "10px", marginRight: "10px", display: "contents" }}> flex: 1,
{el.name} padding: 40,
{store.isRenderedAsset(el.name) ? ( width: "400px",
<></>
) : (
<Button
onClick={() => {
store.loadSceneRobossemblerAsset(el.name);
}} }}
> >
add scene <Input name="cameraLink" placeholder="Camera link" />
</Button> <Input name="topicImage" placeholder="Topic Image" />
)} <Input name="topicCameraInfo" placeholder="Topic Camera Info" />
</div> <Input name="topicDepth" placeholder="Topic Depth" />
</div>
); <ResetButton>Reset</ResetButton>
})} <SubmitButton>Submit</SubmitButton>
</div>
</Form>
)}
/>
</div>
) : (
<></>
)}
{store.sceneMode === SceneMode.MOVING || SceneMode.ROTATE ? (
<>
{store.robossemblerAssets?.assets.map((el) => {
return (
<div>
<div style={{ color: "white", marginLeft: "10px", marginRight: "10px", display: "contents" }}>
{el.name}
{store.isRenderedAsset(el.name) ? (
<></>
) : (
<Button
onClick={() => {
store.loadSceneRobossemblerAsset(el.name);
}}
>
add scene
</Button>
)}
</div>
</div>
);
})}
</>
) : (
<></>
)}
</div>
</div>
<div>
{store.sceneModels.map((el) => {
return <StaticAssetModelView onTap={() => store.deleteSceneItem(el)} model={el} />;
})}
</div>
{store.sceneMode === SceneMode.MAGNETISM_MARKING ? (
<>
<div style={{ backgroundColor: "white" }}>
<div>completion of objects</div>
<div>
{store.objectMagnetism ? (
<>{store.objectMagnetism}</>
) : (
<Button>Selects an object for magnetism</Button>
)}
{store.objectForMagnetism ? (
<>{store.objectForMagnetism}</>
) : (
<Button>Selects an object magnet</Button>
)}
</div>
</div>
</> </>
) : ( ) : (
<></> <></>
)} )} */}
</div> {/* </div> */}
</div> </>
<div> }
{store.sceneModels.map((el) => { />
return <StaticAssetModelView onTap={() => store.deleteSceneItem(el)} model={el} />;
})}
</div>
{store.sceneMode === SceneMode.MAGNETISM_MARKING ? (
<>
<div style={{ backgroundColor: "white" }}>
<div>completion of objects</div>
<div>
{store.objectMagnetism ? (
<>{store.objectMagnetism}</>
) : (
<Button>Selects an object for magnetism</Button>
)}
{store.objectForMagnetism ? <>{store.objectForMagnetism}</> : <Button>Selects an object magnet</Button>}
</div>
</div>
</>
) : (
<></>
)}
</div>
</div>
); );
}); });

View file

@ -0,0 +1,11 @@
import * as React from "react";
import { MainPage } from "../../core/ui/pages/main_page";
export interface ISimulationScreenProps {}
export const SimulationScreenPath = "/simulation";
export default class SimulationScreen extends React.Component<ISimulationScreenProps> {
public render() {
return <MainPage page={"Симуляция"}></MainPage>;
}
}