alexander

This commit is contained in:
IDONTSUDO 2024-08-21 16:15:54 +03:00
parent cf75b4220a
commit 7063e93c75
94 changed files with 1201 additions and 24490 deletions

View file

@ -23,5 +23,11 @@
"typedataset",
"URDF",
"usecases"
]
],
"files.exclude": {
"**/.git": false,
"**/.svn": false,
"**/.hg": false,
"**/CVS": false
}
}

View file

@ -42,7 +42,7 @@ export PYTHON_BLENDER="/путь_к_директории_сайлами_из/
export PYTHON_BLENDER_PROC="/путь_к_генераторуатасетов_/renderBOPdataset.py"
export PYTHON_EDUCATION="absolute_path/webp/education.py"
export PYTHON_ROBOT_BUILDER="/путь_к_генераторуатасетов_/robot_builder.py"
export GET_INTERFACES="/путь_к_директории_WEB_P/get_interfaces.py"
```
## Запуск сервера

View file

@ -19,7 +19,7 @@ module.exports = {
"@typescript-eslint/no-var-requires": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-unused-vars": [
"warn", // or "error"
"warn",
{
"argsIgnorePattern": "^_",
"varsIgnorePattern": "^_",

3
server/.gitignore vendored
View file

@ -8,4 +8,5 @@ coverage
build/
model_create.ts
public
p.ts
p.ts
package-lock.json

6159
server/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -106,6 +106,7 @@ export class App extends TypedEvent<ServerStatus> {
await result.fold(
async (_s) => {
await new CheckAndCreateStaticFilesFolderUseCase().call();
// await new SetLastActivePipelineToRealTimeServiceScenario().call();
},
async (_e) => {

View file

@ -6,14 +6,20 @@ import { extensions } from "../extensions/extensions";
import { Routes } from "../interfaces/router";
import { ScenesPresentation } from "../../features/scenes/scenes_presentation";
import { CalculationsInstancesPresentation } from "../../features/calculations_instance/calculations_instance_presentation";
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";
extensions();
export const httpRoutes: Routes[] = [
new ProjectsPresentation(),
new DatasetsPresentation(),
new ScenesPresentation(),
new BehaviorTreesPresentation(),
new CalculationsTemplatePresentation(),
new ScenesPresentation(),
new CalculationsInstancesPresentation(),
new DigitalTwinsTemplatePresentation(),
new DigitalTwinsInstancePresentation(),
new TopicsPresentation(),
].map((el) => el.call());

View file

@ -0,0 +1,5 @@
export interface Instance {
instanceName: string;
path: string;
instancePath: string;
}

View file

@ -1,6 +1,6 @@
import { Trigger } from "../../features/_triggers/models/trigger_database_model";
import { EXEC_TYPE } from "./exec_error_model";
export interface Trigger {}
export interface IPipeline {
process: IProcess;
trigger?: Trigger;

View file

@ -1,9 +1,12 @@
export enum StaticFiles {
export enum StaticFilesProject {
robossembler_assets = "robossembler_assets.json",
assets = "/assets/assets.json",
parts = "/assets/parts.json",
robots = "/robots/",
scenes = "/scenes/",
behaviorTrees = "behavior_trees",
process = "/process/",
}
export enum StaticFilesServer {
process = "/process/",
digitalTwins = "/digital_twins/",
}

View file

@ -0,0 +1,15 @@
import { CallbackStrategyWithValidationModel, ResponseBase } from "../controllers/http_controller";
import { Instance } from "../models/instance";
import { CreateDataBaseModelUseCase } from "../usecases/create_database_model_usecase";
import { CreateFolderUseCase } from "../usecases/create_folder_usecase";
export abstract class CreateInstanceScenario<V extends Instance> extends CallbackStrategyWithValidationModel<V> {
abstract validationModel: V;
abstract databaseModel: any;
call = async (model: V): ResponseBase => {
model.instancePath = `${model.path}/${model.instanceName}`;
return (await new CreateFolderUseCase().call(model.instancePath)).map(
async () => await new CreateDataBaseModelUseCase(this.databaseModel).call(model)
);
};
}

View file

@ -0,0 +1,22 @@
import { App } from "../controllers/app";
import { CallbackStrategyWithValidationModel, ResponseBase } from "../controllers/http_controller";
import { StaticFilesServer } from "../models/static_files";
import { CreateDataBaseModelUseCase } from "../usecases/create_database_model_usecase";
import { CreateFolderUseCase } from "../usecases/create_folder_usecase";
interface IModel {
path: string;
name: string;
}
export abstract class CreateTemplateScenario<V extends IModel> extends CallbackStrategyWithValidationModel<V> {
validationModel: V;
abstract databaseModel: any;
abstract path: string;
call = async (model: V): ResponseBase => {
model.path = App.staticFilesStoreDir() + StaticFilesServer.process + model.name;
return (await new CreateFolderUseCase().call(model.path)).map(() => {
return new CreateDataBaseModelUseCase(this.databaseModel).call(model);
});
};
}

View file

@ -0,0 +1,20 @@
import { CallbackStrategyWithIdQuery, ResponseBase } from "../controllers/http_controller";
import { Instance } from "../models/instance";
import { DeleteDataBaseModelUseCase } from "../usecases/delete_database_model_usecase";
import { DeleteRecursiveFolderUseCase } from "../usecases/delete_recursive_folder_usecase";
import { ReadByIdDataBaseModelUseCase } from "../usecases/read_by_id_database_model_usecase";
import { CoreValidation } from "../validations/core_validation";
import { MongoIdValidation } from "../validations/mongo_id_validation";
export abstract class DeleteInstanceScenario<D extends Instance> extends CallbackStrategyWithIdQuery {
abstract databaseModel: any;
idValidationExpression: CoreValidation = new MongoIdValidation();
call = async (id: string): ResponseBase =>
(await new ReadByIdDataBaseModelUseCase<D>(this.databaseModel).call(id)).map(async (model) => {
return (await new DeleteRecursiveFolderUseCase().call(model.instancePath)).map(
async () => await new DeleteDataBaseModelUseCase(this.databaseModel).call(id)
);
});
}

View file

@ -0,0 +1,20 @@
import { CalculationsTemplateValidationModel } from "../../features/calculations_templates/models/calculations_template_validation_model";
import { CallbackStrategyWithIdQuery, ResponseBase } from "../controllers/http_controller";
import { DeleteDataBaseModelUseCase } from "../usecases/delete_database_model_usecase";
import { DeleteRecursiveFolderUseCase } from "../usecases/delete_recursive_folder_usecase";
import { ReadByIdDataBaseModelUseCase } from "../usecases/read_by_id_database_model_usecase";
import { CoreValidation } from "../validations/core_validation";
import { MongoIdValidation } from "../validations/mongo_id_validation";
export abstract class DeleteTemplateScenario extends CallbackStrategyWithIdQuery {
abstract databaseModel: any;
idValidationExpression: CoreValidation = new MongoIdValidation();
call = async (id: string): ResponseBase =>
(await new ReadByIdDataBaseModelUseCase<CalculationsTemplateValidationModel>(this.databaseModel).call(id)).map(
async (model) => {
return (await new DeleteRecursiveFolderUseCase().call(model.path)).map(
async () => await new DeleteDataBaseModelUseCase(this.databaseModel).call(id)
);
}
);
}

View file

@ -1,10 +1,14 @@
import { pipelineRealTimeService } from "../../features/_realtime/realtime_presentation";
import { IProjectModel, ProjectDBModel } from "../../features/projects/models/project_model_database_model";
import { App } from "../controllers/app";
import { PipelineRealTimeService } from "../services/pipeline_real_time_service";
import { CreateFolderUseCase } from "../usecases/create_folder_usecase";
import { SearchOneDataBaseModelUseCase } from "../usecases/search_database_model_usecase";
export const pipelineRealTimeService = new PipelineRealTimeService();
export class SetLastActivePipelineToRealTimeServiceScenario {
call = async (): Promise<void> => {
return (

View file

@ -1,5 +1,5 @@
import { FilesChangeNotifierService, IHashesCache } from "./files_change_notifier_service";
import { IPipeline } from "../models/process_model";
import { IPipeline, Trigger } from "../models/process_model";
import { ExecutorProgramService } from "./executor_program_service";
import { EXEC_EVENT, ExecError, SpawnError } from "../models/exec_error_model";
import { TypedEvent } from "../helpers/typed_event";
@ -7,8 +7,7 @@ import { Result } from "../helpers/result";
import { ExecutorResult } from "../models/executor_result";
import { delay } from "../helpers/delay";
import { TriggerService } from "./trigger_service";
import { Trigger } from "../../features/_triggers/models/trigger_database_model";
export interface Iteration {
hashes: IHashesCache | null;
pipeline: IPipeline;

View file

@ -1,10 +1,11 @@
// @ts-nocheck
import * as vm from "node:vm";
import { IHashesCache } from "./files_change_notifier_service";
import { EventsFileChanger } from "../models/meta_data_file_manager_model";
import { Result } from "../helpers/result";
import { TypedEvent } from "../helpers/typed_event";
import { Trigger, TriggerType } from "../../features/_triggers/models/trigger_database_model";
import { Trigger } from "../models/process_model";
export class TriggerCallResult {
results: Array<TriggerSuccessResult | TriggerErrorReport>;

View file

@ -1,24 +1,19 @@
import { App } from "../controllers/app";
import { StaticFilesServer } from "../models/static_files";
import { FileSystemRepository } from "../repository/file_system_repository";
import { CreateFolderUseCase } from "./create_folder_usecase";
export class CheckAndCreateStaticFilesFolderUseCase {
fileSystemRepository: FileSystemRepository;
constructor() {
this.fileSystemRepository = new FileSystemRepository();
}
fileSystemRepository: FileSystemRepository = new FileSystemRepository();
call = async (): Promise<void> => {
Object.keys(StaticFilesServer).forEach(async (key) => {
if (!(await this.fileSystemRepository.dirIsExists(App.staticFilesStoreDir() + key))) {
await new CreateFolderUseCase().call(App.staticFilesStoreDir() + key);
}
});
if (await this.fileSystemRepository.dirIsExists(App.staticFilesStoreDir())) {
return;
}
const createFolderUseCase = await new CreateFolderUseCase().call(App.staticFilesStoreDir());
createFolderUseCase.fold(
(_s) => {},
(e) => {
console.log(e);
}
);
await new CreateFolderUseCase().call(App.staticFilesStoreDir());
};
}

View file

@ -1,15 +1,8 @@
import { CallbackStrategyWithValidationModel, ResponseBase } from "../../../core/controllers/http_controller";
import { CreateDataBaseModelUseCase } from "../../../core/usecases/create_database_model_usecase";
import { CreateFolderUseCase } from "../../../core/usecases/create_folder_usecase";
import { CreateInstanceScenario } from "../../../core/scenarios/create_intance_scenario";
import { CalculationInstanceDBModel } from "../models/calculations_instance_database_model";
import { CalculationInstanceValidationModel } from "../models/calculations_instance_validation_model";
export class CreateCalculationInstanceScenario extends CallbackStrategyWithValidationModel<CalculationInstanceValidationModel> {
export class CreateCalculationInstanceScenario extends CreateInstanceScenario<CalculationInstanceValidationModel> {
databaseModel = CalculationInstanceDBModel;
validationModel: CalculationInstanceValidationModel = new CalculationInstanceValidationModel();
call = async (model: CalculationInstanceValidationModel): ResponseBase => {
model.instancePath = `${model.path}/${model.instanceName}`;
return (await new CreateFolderUseCase().call(model.instancePath)).map(
async () => await new CreateDataBaseModelUseCase(CalculationInstanceDBModel).call(model)
);
};
}

View file

@ -1,21 +1,7 @@
import { CallbackStrategyWithIdQuery, ResponseBase } from "../../../core/controllers/http_controller";
import { DeleteDataBaseModelUseCase } from "../../../core/usecases/delete_database_model_usecase";
import { DeleteRecursiveFolderUseCase } from "../../../core/usecases/delete_recursive_folder_usecase";
import { ReadByIdDataBaseModelUseCase } from "../../../core/usecases/read_by_id_database_model_usecase";
import { CoreValidation } from "../../../core/validations/core_validation";
import { MongoIdValidation } from "../../../core/validations/mongo_id_validation";
import { CalculationsTemplateDBModel } from "../../calculations_templates/models/calculations_template_database_model";
import { DeleteInstanceScenario } from "../../../core/scenarios/delete_instance_scenario";
import { CalculationInstanceDBModel } from "../models/calculations_instance_database_model";
import { CalculationInstanceValidationModel } from "../models/calculations_instance_validation_model";
export class DeleteCalculationsInstanceScenario extends CallbackStrategyWithIdQuery {
idValidationExpression: CoreValidation = new MongoIdValidation();
call = async (id: string): ResponseBase =>
(
await new ReadByIdDataBaseModelUseCase<CalculationInstanceValidationModel>(CalculationInstanceDBModel).call(id)
).map(async (model) => {
return (await new DeleteRecursiveFolderUseCase().call(model.instancePath)).map(
async () => await new DeleteDataBaseModelUseCase(CalculationInstanceDBModel).call(id)
);
});
export class DeleteCalculationsInstanceScenario extends DeleteInstanceScenario<CalculationInstanceValidationModel> {
databaseModel = CalculationInstanceDBModel;
}

View file

@ -1,8 +1,8 @@
import { CrudController } from "../../core/controllers/crud_controller";
import { CreateCalculationsTemplateScenario } from "./domain/create_calculations_template_scenario";
import { DeleteCalculationsTemplateScenario } from "./domain/delete_calculations_template_scenario";
import { CalculationsTemplateValidationModel as CalculationsTemplateValidationModel } from "./models/calculations_template_validation_model";
import { CalculationsTemplateDBModel } from "./models/calculations_template_database_model";
import { CreateCalculationsTemplateScenario } from "./domain/create_calculations_template_scenario";
export class CalculationsTemplatePresentation extends CrudController<
CalculationsTemplateValidationModel,

View file

@ -1,27 +1,10 @@
import { CallbackStrategyWithValidationModel, ResponseBase } from "../../../core/controllers/http_controller";
import { Result } from "../../../core/helpers/result";
import { StaticFiles } from "../../../core/models/static_files";
import { CreateDataBaseModelUseCase } from "../../../core/usecases/create_database_model_usecase";
import { CreateFolderUseCase } from "../../../core/usecases/create_folder_usecase";
import { SearchOneDataBaseModelUseCase } from "../../../core/usecases/search_database_model_usecase";
import { IProjectModel, ProjectDBModel } from "../../projects/models/project_model_database_model";
import { CalculationsTemplateValidationModel } from "../models/calculations_template_validation_model";
import { StaticFilesServer } from "../../../core/models/static_files";
import { CreateTemplateScenario } from "../../../core/scenarios/create_template_scenario";
import { CalculationsTemplateDBModel } from "../models/calculations_template_database_model";
import { CalculationsTemplateValidationModel } from "../models/calculations_template_validation_model";
export class CreateCalculationsTemplateScenario extends CallbackStrategyWithValidationModel<CalculationsTemplateValidationModel> {
export class CreateCalculationsTemplateScenario extends CreateTemplateScenario<CalculationsTemplateValidationModel> {
validationModel: CalculationsTemplateValidationModel = new CalculationsTemplateValidationModel();
call = async (model: CalculationsTemplateValidationModel): ResponseBase => {
return (
await new SearchOneDataBaseModelUseCase<IProjectModel>(ProjectDBModel).call(
{ isActive: true },
"no active projects"
)
).map(async (project) => {
model.path = project.rootDir + StaticFiles.process + model.name;
return (await new CreateFolderUseCase().call(model.path)).map(() => {
return new CreateDataBaseModelUseCase(CalculationsTemplateDBModel).call(model);
});
});
};
databaseModel = CalculationsTemplateDBModel;
path: string = StaticFilesServer.process;
}

View file

@ -1,20 +1,6 @@
import { CallbackStrategyWithIdQuery, ResponseBase } from "../../../core/controllers/http_controller";
import { DeleteDataBaseModelUseCase } from "../../../core/usecases/delete_database_model_usecase";
import { DeleteRecursiveFolderUseCase } from "../../../core/usecases/delete_recursive_folder_usecase";
import { ReadByIdDataBaseModelUseCase } from "../../../core/usecases/read_by_id_database_model_usecase";
import { CoreValidation } from "../../../core/validations/core_validation";
import { MongoIdValidation } from "../../../core/validations/mongo_id_validation";
import { DeleteTemplateScenario } from "../../../core/scenarios/delete_template_scenario";
import { CalculationsTemplateDBModel } from "../models/calculations_template_database_model";
import { CalculationsTemplateValidationModel } from "../models/calculations_template_validation_model";
export class DeleteCalculationsTemplateScenario extends CallbackStrategyWithIdQuery {
idValidationExpression: CoreValidation = new MongoIdValidation();
call = async (id: string): ResponseBase =>
(
await new ReadByIdDataBaseModelUseCase<CalculationsTemplateValidationModel>(CalculationsTemplateDBModel).call(id)
).map(async (model) => {
return (await new DeleteRecursiveFolderUseCase().call(model.path)).map(
async () => await new DeleteDataBaseModelUseCase(CalculationsTemplateDBModel).call(id)
);
});
export class DeleteCalculationsTemplateScenario extends DeleteTemplateScenario {
databaseModel = CalculationsTemplateDBModel;
}

View file

@ -0,0 +1,26 @@
import { CrudController } from "../../core/controllers/crud_controller";
import { CreateDigitalTwinsInstanceScenario } from "./domain/create_digital_twins_instance";
import { DeleteDigitalTwinsInstanceScenario } from "./domain/delete_digital_twins_instance";
import { ExecInstanceScenario } from "./domain/exec_instance_scenario";
import { DigitalTwinsInstanceDatabaseModel } from "./model/digital_twins_instance_database_model";
import { DigitalTwinsInstanceValidationModel } from "./model/digital_twins_instance_validation_model";
export class DigitalTwinsInstancePresentation extends CrudController<
DigitalTwinsInstanceValidationModel,
typeof DigitalTwinsInstanceDatabaseModel
> {
constructor() {
super({
url: "digital/twins/instance",
validationModel: DigitalTwinsInstanceValidationModel,
databaseModel: DigitalTwinsInstanceDatabaseModel,
});
super.post(new CreateDigitalTwinsInstanceScenario().call);
super.delete(new DeleteDigitalTwinsInstanceScenario().call);
this.subRoutes.push({
method: "POST",
fn: new ExecInstanceScenario(),
subUrl: "exec/instance",
});
}
}

View file

@ -0,0 +1,10 @@
import { CreateInstanceScenario } from "../../../core/scenarios/create_intance_scenario";
import { DigitalTwinsInstanceDatabaseModel } from "../model/digital_twins_instance_database_model";
import { DigitalTwinsInstanceValidationModel } from "../model/digital_twins_instance_validation_model";
export class CreateDigitalTwinsInstanceScenario extends CreateInstanceScenario<DigitalTwinsInstanceValidationModel> {
databaseModel = DigitalTwinsInstanceDatabaseModel;
validationModel: DigitalTwinsInstanceValidationModel = new DigitalTwinsInstanceValidationModel();
}

View file

@ -0,0 +1,7 @@
import { DeleteInstanceScenario } from "../../../core/scenarios/delete_instance_scenario";
import { DigitalTwinsInstanceDatabaseModel } from "../model/digital_twins_instance_database_model";
import { DigitalTwinsInstanceValidationModel } from "../model/digital_twins_instance_validation_model";
export class DeleteDigitalTwinsInstanceScenario extends DeleteInstanceScenario<DigitalTwinsInstanceValidationModel> {
databaseModel = DigitalTwinsInstanceDatabaseModel;
}

View file

@ -0,0 +1,32 @@
import { CallbackStrategyWithIdQuery, ResponseBase } from "../../../core/controllers/http_controller";
import { ExecProcessUseCase } from "../../../core/usecases/exec_process_usecase";
import { ReadByIdDataBaseModelUseCase } from "../../../core/usecases/read_by_id_database_model_usecase";
import { CoreValidation } from "../../../core/validations/core_validation";
import { MongoIdValidation } from "../../../core/validations/mongo_id_validation";
import {
DigitalTwinsInstanceDatabaseModel,
IDigitalTwinsInstanceModel,
} from "../model/digital_twins_instance_database_model";
import { ITopicModel } from "../../topics/topic_database_model";
import { ExecInstanceTwinsProcessService } from "../services/exec_instance_twins_process_service";
export interface Topics {
topics: ITopicModel[];
}
export class ExecInstanceScenario extends CallbackStrategyWithIdQuery {
idValidationExpression: CoreValidation = new MongoIdValidation();
call = async (id: string): ResponseBase =>
(
await new ReadByIdDataBaseModelUseCase<IDigitalTwinsInstanceModel>(DigitalTwinsInstanceDatabaseModel).call(id)
).map((document) =>
new ExecProcessUseCase().call(
document.instancePath,
`python3 $GET_INTERFACES --path ${document.instancePath}`,
"",
new ExecInstanceTwinsProcessService(document.instancePath, document)
)
);
}

View file

@ -0,0 +1,33 @@
import { Schema, model } from "mongoose";
export interface IDigitalTwinsInstanceModel {
instancePath: string;
status?: string;
}
export const DigitalTwinsInstanceSchema = new Schema({
path: String,
name: String,
entity: String,
description: String,
command: String,
status: {
type: String,
},
interfaces: {
cmd: String,
},
instancePath: {
type: String,
},
formBuilder: {
type: Schema.Types.Mixed,
},
}).plugin(require("mongoose-autopopulate"));
export const digitalTwinsInstanceSchema = "digital_twins_instance";
export const DigitalTwinsInstanceDatabaseModel = model<IDigitalTwinsInstanceModel>(
digitalTwinsInstanceSchema,
DigitalTwinsInstanceSchema
);

View file

@ -0,0 +1,26 @@
import { Type } from "class-transformer";
import { IsEnum, IsString } from "class-validator";
import { Instance } from "../../../core/models/instance";
enum DigitalTwinsTypes {
CAMERA = "CAMERA",
ROBOT = "ROBOT",
}
export class Interfaces {
@IsString()
cmd: string;
}
export class DigitalTwinsInstanceValidationModel implements Instance {
instanceName: string;
instancePath: string;
path: string;
name: string;
@IsEnum(DigitalTwinsTypes)
entity: DigitalTwinsTypes;
@IsString()
description: string;
@IsString()
command: string;
@Type(() => Interfaces)
interfaces: Interfaces;
}

View file

@ -0,0 +1,49 @@
import { Result } from "../../../core/helpers/result";
import { TypedEvent } from "../../../core/helpers/typed_event";
import { ExecError, SpawnError, EXEC_EVENT } from "../../../core/models/exec_error_model";
import { ExecutorResult } from "../../../core/models/executor_result";
import { CreateDataBaseModelUseCase } from "../../../core/usecases/create_database_model_usecase";
import { ReadFileAndParseJsonUseCase } from "../../../core/usecases/read_file_and_parse_json";
import { ITopicModel, TopicDatabaseModel } from "../../topics/topic_database_model";
import { Topics } from "../domain/exec_instance_scenario";
export class ExecInstanceTwinsProcessService extends TypedEvent<
Result<ExecError | SpawnError, ExecutorResult>
> {
path: string;
databaseModel: any;
constructor(path: string, databaseModel: any) {
super();
this.path = path;
this.databaseModel = databaseModel;
this.on((event) => this.lister(event));
}
lister = (event: Result<ExecError | SpawnError, ExecutorResult>) =>
event.fold(
async (success) => {
if (success.event == EXEC_EVENT.END) {
(await new ReadFileAndParseJsonUseCase().call<Topics>(this.path + "/topics.json")).fold(
async (model) => {
await model.topics.forEach(async (el) => {
el.digitalTwinId = this.databaseModel._id;
await new CreateDataBaseModelUseCase<ITopicModel>(TopicDatabaseModel).call(el);
});
this.databaseModel.status = "END";
await this.databaseModel.save();
},
async (e) => {
this.databaseModel.status = "ERROR";
await this.databaseModel.save();
}
);
}
},
async (error) => {
this.databaseModel.status = "ERROR";
await this.databaseModel.save();
}
);
}

View file

@ -0,0 +1,20 @@
import { CrudController } from "../../core/controllers/crud_controller";
import { CreateDigitalTwinsTemplateScenario } from "./domain/create_digital_twins_template";
import { DeleteDigitalTwinsTemplateScenario } from "./domain/delete_digital_twins_template";
import { DigitalTwinsTemplateDBModel } from "./models/digital_twins_template_database_model";
import { DigitalTwinsTemplateValidationModel } from "./models/digital_twins_template_validation_model";
export class DigitalTwinsTemplatePresentation extends CrudController<
DigitalTwinsTemplateValidationModel,
typeof DigitalTwinsTemplateDBModel
> {
constructor() {
super({
url: "digital/twins/template",
validationModel: DigitalTwinsTemplateValidationModel,
databaseModel: DigitalTwinsTemplateDBModel,
});
super.post(new CreateDigitalTwinsTemplateScenario().call);
super.delete(new DeleteDigitalTwinsTemplateScenario().call);
}
}

View file

@ -0,0 +1,10 @@
import { StaticFilesServer } from "../../../core/models/static_files";
import { CreateTemplateScenario } from "../../../core/scenarios/create_template_scenario";
import { DigitalTwinsTemplateDBModel } from "../models/digital_twins_template_database_model";
import { DigitalTwinsTemplateValidationModel } from "../models/digital_twins_template_validation_model";
export class CreateDigitalTwinsTemplateScenario extends CreateTemplateScenario<DigitalTwinsTemplateValidationModel> {
validationModel: DigitalTwinsTemplateValidationModel = new DigitalTwinsTemplateValidationModel();
databaseModel = DigitalTwinsTemplateDBModel;
path: string = StaticFilesServer.digitalTwins;
}

View file

@ -0,0 +1,6 @@
import { DeleteTemplateScenario } from "../../../core/scenarios/delete_template_scenario";
import { DigitalTwinsTemplateDBModel } from "../models/digital_twins_template_database_model";
export class DeleteDigitalTwinsTemplateScenario extends DeleteTemplateScenario {
databaseModel = DigitalTwinsTemplateDBModel;
}

View file

@ -0,0 +1,24 @@
import { Schema, model } from "mongoose";
export interface IDigitalTwinsTemplateTemplateModel {}
export const DigitalTwinsTemplateSchema = new Schema({
path: String,
name: String,
entity: String,
description: String,
command: String,
interfaces: {
cmd: String,
},
formBuilder:{
type: Schema.Types.Mixed,
}
}).plugin(require("mongoose-autopopulate"));
export const digitalTwinsTemplateSchema = "digital_twins_templates";
export const DigitalTwinsTemplateDBModel = model<IDigitalTwinsTemplateTemplateModel>(
digitalTwinsTemplateSchema,
DigitalTwinsTemplateSchema
);

View file

@ -0,0 +1,23 @@
import { Type } from "class-transformer";
import { IsEnum, IsString } from "class-validator";
enum DigitalTwinsTypes {
CAMERA = "CAMERA",
ROBOT = "ROBOT",
}
export class Interfaces {
@IsString()
cmd: string;
}
export class DigitalTwinsTemplateValidationModel {
path: string;
name: string;
@IsEnum(DigitalTwinsTypes)
entity: DigitalTwinsTypes;
@IsString()
description: string;
@IsString()
command: string;
@Type(() => Interfaces)
interfaces: Interfaces;
}

View file

@ -0,0 +1,21 @@
import { Result } from "../../../core/helpers/result";
import { ActivePipeline } from "../../../core/models/active_pipeline_model";
import { pipelineRealTimeService } from "../../../core/scenarios/set_active_pipeline_to_realtime_service_scenario";
export class PipelineStatusUseCase {
async call(): Promise<Result<Error, ActivePipeline>> {
try {
const status = pipelineRealTimeService.status;
if (status.projectId !== null) {
return Result.ok(status);
}
if (status.projectId === null) {
return Result.error(new Error("pipelineRealTimeService does not have an active project instance"));
}
} catch (error) {
return Result.error(error as Error);
}
}
}

View file

@ -1,6 +1,6 @@
import { CallbackStrategyWithEmpty, ResponseBase } from "../../../core/controllers/http_controller";
import { Result } from "../../../core/helpers/result";
import { StaticFiles } from "../../../core/models/static_files";
import { StaticFilesProject } from "../../../core/models/static_files";
import { GetServerAddressUseCase } from "../../../core/usecases/get_server_address_usecase";
import { ReadFileAndParseJsonUseCase } from "../../../core/usecases/read_file_and_parse_json";
import { SearchManyDataBaseModelUseCase } from "../../../core/usecases/search_many_database_model_usecase";
@ -27,7 +27,7 @@ export class RobossemblerAssetsNetworkMapperScenario extends CallbackStrategyWit
).map((projectModel) => {
const { rootDir } = projectModel[0];
return new GetServerAddressUseCase().call().map(async (serverAddress) =>
(await new ReadFileAndParseJsonUseCase().call<Parts[]>(rootDir + StaticFiles.parts)).map((model) => {
(await new ReadFileAndParseJsonUseCase().call<Parts[]>(rootDir + StaticFilesProject.parts)).map((model) => {
const assetAddress =
serverAddress +
"/" +

View file

@ -1,24 +1,24 @@
import { CallbackStrategyWithValidationModel, ResponseBase } from "../../../core/controllers/http_controller";
import { Result } from "../../../core/helpers/result";
import { RobossemblerAssets } from "../../../core/models/robossembler_assets";
import { StaticFiles } from "../../../core/models/static_files";
import { StaticFilesProject } from "../../../core/models/static_files";
import { ReadingJsonFileAndConvertingToInstanceClassScenario } from "../../../core/scenarios/read_file_and_json_to_plain_instance_class_scenario";
import { WriteFileSystemFileUseCase } from "../../../core/usecases/write_file_system_file_usecase";
import { PipelineStatusUseCase } from "../../_realtime/domain/pipeline_status_usecase";
import { PipelineStatusUseCase } from "./pipeline_status_usecase";
export class SaveActiveSceneScenario extends CallbackStrategyWithValidationModel<RobossemblerAssets> {
validationModel: RobossemblerAssets = new RobossemblerAssets();
async call(model: RobossemblerAssets): ResponseBase {
return (await new PipelineStatusUseCase().call()).map(async (activeInstanceModel) =>
(
await new ReadingJsonFileAndConvertingToInstanceClassScenario(RobossemblerAssets).call(
`${activeInstanceModel.path}${StaticFiles.robossembler_assets}`.pathNormalize()
`${activeInstanceModel.path}${StaticFilesProject.robossembler_assets}`.pathNormalize()
)
).map(async (prevModel) => {
model.assets = prevModel.assets;
return (
await new WriteFileSystemFileUseCase().call(
`${activeInstanceModel.path}${StaticFiles.robossembler_assets}`.pathNormalize(),
`${activeInstanceModel.path}${StaticFilesProject.robossembler_assets}`.pathNormalize(),
JSON.stringify(model)
)
).map(() => Result.ok("assets is rewrite"));

View file

@ -4,7 +4,7 @@ import { Result } from "../../../core/helpers/result";
import { CreateFileUseCase } from "../../../core/usecases/create_file_usecase";
import { SearchManyDataBaseModelUseCase } from "../../../core/usecases/search_many_database_model_usecase";
import { IProjectModel, ProjectDBModel } from "../../projects/models/project_model_database_model";
import { StaticFiles } from "../../../core/models/static_files";
import { StaticFilesProject } from "../../../core/models/static_files";
import { CreateDataBaseModelUseCase } from "../../../core/usecases/create_database_model_usecase";
import { SceneDBModel } from "../model/scene_database_model";
export class SceneModel {
@ -33,7 +33,7 @@ export class CreateNewSceneScenario extends CallbackStrategyWithValidationModel<
return (
await new CreateFileUseCase().call(
`${rootDir}${StaticFiles.scenes}${model.name}.json`,
`${rootDir}${StaticFilesProject.scenes}${model.name}.json`,
Buffer.from(JSON.stringify(new SceneAssets(model.name)))
)
).map(async () =>

View file

@ -1,7 +1,7 @@
import { CallbackStrategyWithValidationModel, ResponseBase } from "../../../core/controllers/http_controller";
import { Result } from "../../../core/helpers/result";
import { RobotModel } from "../model/robot_model";
import { StaticFiles } from "../../../core/models/static_files";
import { StaticFilesProject } from "../../../core/models/static_files";
import { GetServerAddressUseCase } from "../../../core/usecases/get_server_address_usecase";
import { SearchManyDataBaseModelUseCase } from "../../../core/usecases/search_many_database_model_usecase";
import { IProjectModel, ProjectDBModel } from "../../projects/models/project_model_database_model";
@ -9,31 +9,7 @@ import { ExecProcessUseCase } from "../../../core/usecases/exec_process_usecase"
export class CreateRobotScenario extends CallbackStrategyWithValidationModel<RobotModel> {
validationModel: RobotModel = new RobotModel();
call = async (model: RobotModel): ResponseBase =>
(
await new SearchManyDataBaseModelUseCase<IProjectModel>(ProjectDBModel).call(
{ isActive: true },
"is dont active projects"
)
).map((projectModel) => {
const { rootDir } = projectModel[0];
return new GetServerAddressUseCase().call().map(async (serverAddress) =>
(
await new ExecProcessUseCase().call(
rootDir,
`python3 $PYTHON_ROBOT_BUILDER --path ${projectModel[0].rootDir + StaticFiles.robots} --name ${
model.name
} --nDOF ${model.nDof} --toolType ${model.toolType}`,
""
)
).map(() =>
Result.ok({
robotUrl: `${serverAddress}/${
rootDir.match(new RegExp(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gm))[0]
}${StaticFiles.robots}${model.name}/robot.xml`,
})
)
);
});
call = async (model: RobotModel): ResponseBase => {
return Result.ok([]);
};
}

View file

@ -1,6 +1,6 @@
import { CallbackStrategyWithIdQuery, ResponseBase } from "../../../core/controllers/http_controller";
import { Result } from "../../../core/helpers/result";
import { StaticFiles } from "../../../core/models/static_files";
import { StaticFilesProject } from "../../../core/models/static_files";
import { DeleteDataBaseModelUseCase } from "../../../core/usecases/delete_database_model_usecase";
import { DeleteFileUseCase } from "../../../core/usecases/delete_file_usecase";
import { ReadByIdDataBaseModelUseCase } from "../../../core/usecases/read_by_id_database_model_usecase";
@ -23,7 +23,7 @@ export class DeleteSceneScenario extends CallbackStrategyWithIdQuery {
).map(async (projectModel) => {
const { rootDir } = projectModel[0];
(await new DeleteFileUseCase()).call(`${rootDir}${StaticFiles.scenes}${sceneModel.name}.json`);
(await new DeleteFileUseCase()).call(`${rootDir}${StaticFilesProject.scenes}${sceneModel.name}.json`);
return Result.ok("Delete scene");
})
)

View file

@ -1,6 +1,6 @@
import { CallbackStrategyWithValidationModel, ResponseBase } from "../../../core/controllers/http_controller";
import { Result } from "../../../core/helpers/result";
import { StaticFiles } from "../../../core/models/static_files";
import { StaticFilesProject } from "../../../core/models/static_files";
import { CreateFileUseCase } from "../../../core/usecases/create_file_usecase";
import { SearchManyDataBaseModelUseCase } from "../../../core/usecases/search_many_database_model_usecase";
import { IProjectModel, ProjectDBModel } from "../../projects/models/project_model_database_model";
@ -19,7 +19,7 @@ export class EditSceneScenario extends CallbackStrategyWithValidationModel<Scene
return (
await new CreateFileUseCase().call(
`${rootDir}${StaticFiles.scenes}${model.name}.json`,
`${rootDir}${StaticFilesProject.scenes}${model.name}.json`,
Buffer.from(JSON.stringify(model))
)
).map(async () => Result.ok("Edit"));

View file

@ -1,6 +1,6 @@
import { CallbackStrategyWithIdQuery, ResponseBase } from "../../../core/controllers/http_controller";
import { Result } from "../../../core/helpers/result";
import { StaticFiles } from "../../../core/models/static_files";
import { StaticFilesProject } from "../../../core/models/static_files";
import { ReadByIdDataBaseModelUseCase } from "../../../core/usecases/read_by_id_database_model_usecase";
import { ReadFileAndParseJsonUseCase } from "../../../core/usecases/read_file_and_parse_json";
import { SearchManyDataBaseModelUseCase } from "../../../core/usecases/search_many_database_model_usecase";
@ -22,7 +22,7 @@ export class ReadSceneScenario extends CallbackStrategyWithIdQuery {
).map(async (projectModel) => {
const { rootDir } = projectModel[0];
return (await new ReadFileAndParseJsonUseCase().call(`${rootDir}${StaticFiles.scenes}${model.name}.json`)).map(
return (await new ReadFileAndParseJsonUseCase().call(`${rootDir}${StaticFilesProject.scenes}${model.name}.json`)).map(
async (sceneAsset) => Result.ok(sceneAsset)
);
})

View file

@ -0,0 +1,9 @@
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

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

View file

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

View file

@ -0,0 +1,9 @@
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

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

View file

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

View file

@ -0,0 +1,26 @@
import { Schema, model } from "mongoose";
import { digitalTwinsInstanceSchema } from "../digital_twins_instance/model/digital_twins_instance_database_model";
export interface ITopicModel {
digitalTwinId?: string;
name: string;
type: string;
}
export const TopicSchema = new Schema({
name: {
type: String,
},
type: {
type: String,
},
digitalTwinId: {
type: Schema.Types.ObjectId,
ref: digitalTwinsInstanceSchema,
autopopulate: true,
},
}).plugin(require("mongoose-autopopulate"));
export const topicSchema = "topics";
export const TopicDatabaseModel = model<ITopicModel>(topicSchema, TopicSchema);

View file

@ -0,0 +1,10 @@
import { Type } from "class-transformer";
import { IsEnum, IsString } from "class-validator";
import { ITopicModel } from "./topic_database_model";
export class TopicValidationModel implements ITopicModel {
@IsString()
name: string;
@IsString()
type: string;
}

View file

@ -0,0 +1,13 @@
import { CrudController } from "../../core/controllers/crud_controller";
import { TopicDatabaseModel } from "./topic_database_model";
import { TopicValidationModel } from "./topic_validation_model";
export class TopicsPresentation extends CrudController<TopicValidationModel, typeof TopicDatabaseModel> {
constructor() {
super({
url: "topics",
validationModel: TopicValidationModel,
databaseModel: TopicDatabaseModel,
});
}
}

View file

@ -1,43 +0,0 @@
import { EXEC_TYPE } from "../../src/core/models/exec_error_model";
import { IPipeline, IssueType, StackGenerateType } from "../../src/core/models/process_model";
import { TriggerType } from "../../src/features/_triggers/models/trigger_database_model";
export const mockSimplePipeline: IPipeline[] = [
{
process: {
type: EXEC_TYPE.EXEC,
command: `nix run gitlab:robossembler/nix-robossembler-overlay#test-script '{
"filesMeta":[
{"type":"folder","name":"example", "path": null,"rewrite":true}
],
"path":"$PATH"
}'`,
isGenerating: true,
isLocaleCode: false,
issueType: IssueType.WARNING,
},
trigger: {
type: TriggerType.PROCESS,
value: [""],
},
env: null,
stackGenerateType: StackGenerateType.SINGLETON,
},
{
process: {
type: EXEC_TYPE.EXEC,
command: `nix run gitlab:robossembler/nix-robossembler-overlay#test-script '{
"filesMeta":[
{"type":"file","name":"1.txt", "path":"example","rewrite":true}
],
"path":"$PATH"
}'`,
isGenerating: true,
isLocaleCode: false,
issueType: IssueType.WARNING,
},
trigger: null,
env: null,
stackGenerateType: StackGenerateType.SINGLETON,
},
];

View file

@ -1,5 +1,4 @@
import { PipelineRealTimeService } from "../../src/core/services/pipeline_real_time_service";
import { mockSimplePipeline } from "../model/mock_pipelines";
import { dirname__ } from "../test";
export class PipelineRealTimeServiceTest extends PipelineRealTimeService {

View file

@ -1,45 +0,0 @@
import { rmSync } from "fs";
import { StackService } from "../../src/core/services/stack_service";
import { delay } from "../../src/core/helpers/delay";
import { assert, dirname__ } from "../test";
import { mockSimplePipeline } from "../model/mock_pipelines";
import { FileSystemRepository } from "../../src/core/repository/file_system_repository";
import { CreateFolderUseCase } from "../../src/core/usecases/create_folder_usecase";
abstract class IStackServiceTest {
abstract test(): Promise<boolean>;
}
class SimpleTestStackServiceTest extends StackService implements IStackServiceTest {
fileSystemRepository: FileSystemRepository;
constructor() {
super(mockSimplePipeline, dirname__ + "/context/");
this.fileSystemRepository = new FileSystemRepository();
}
async test(): Promise<boolean> {
await this.call();
console.log(this.path);
const testResult = this.fileSystemRepository.readDirRecursive(this.path).equals(["1.txt", "test.txt"], true);
await delay(100);
rmSync(this.path + "example/", { recursive: true });
return testResult;
}
}
export class StackServiceTest {
dirName: string;
fileSystemRepository: FileSystemRepository;
constructor(dirName: string) {
this.dirName = dirName;
this.fileSystemRepository = new FileSystemRepository();
}
public async test() {
const tests = [new SimpleTestStackServiceTest()];
await new CreateFolderUseCase().call(this.dirName + "/context/");
for await (const el of tests) {
assert((await el.test()) === true, el.constructor.name);
await delay(3000);
}
}
}

View file

@ -1,110 +0,0 @@
import { EventsFileChanger, MetaDataFileManagerModel } from "../../src/core/models/meta_data_file_manager_model";
import { TriggerService } from "../../src/core/services/trigger_service";
import { TriggerType } from "../../src/features/_triggers/models/trigger_database_model";
import { assert } from "../test";
abstract class TriggerTest {
abstract test(): Promise<boolean>;
}
class TriggerServiceFileOkTest extends TriggerService implements TriggerTest {
constructor() {
super(
{
type: TriggerType.FILE,
value: ["context"],
},
{
"/context/": new MetaDataFileManagerModel("", "", EventsFileChanger.create),
},
""
);
}
async test(): Promise<boolean> {
const r = await this.call();
return r.isSuccess();
}
}
class TriggerServiceFileErrorTest extends TriggerService implements TriggerTest {
constructor() {
super(
{
type: TriggerType.FILE,
value: ["123"],
},
{
"/ctx/": new MetaDataFileManagerModel("", "", EventsFileChanger.create),
"/context/": new MetaDataFileManagerModel("", "", EventsFileChanger.create),
},
""
);
}
async test(): Promise<boolean> {
const r = await this.call();
return r.isFailure();
}
}
class TriggerServiceProcessOkTest extends TriggerService implements TriggerTest {
constructor() {
super(
{
type: TriggerType.PROCESS,
value: [
`function main(){
return true
}
main()`,
],
},
{
"/context/": new MetaDataFileManagerModel("", "", EventsFileChanger.create),
},
""
);
}
async test(): Promise<boolean> {
const r = await this.call();
return r.isSuccess();
}
}
class TriggerServiceProcessErrorTest extends TriggerService implements TriggerTest {
constructor() {
super(
{
type: TriggerType.PROCESS,
value: [
`function main(){
return true
}
`,
],
},
{
"/context/": new MetaDataFileManagerModel("", "", EventsFileChanger.create),
},
""
);
}
async test(): Promise<boolean> {
const r = await this.call();
return r.isFailure();
}
}
export class TriggerServiceTest {
public async test() {
const tests: TriggerTest[] = [
new TriggerServiceFileOkTest(),
new TriggerServiceFileErrorTest(),
new TriggerServiceProcessOkTest(),
new TriggerServiceProcessErrorTest(),
];
for await (const el of tests) {
assert((await el.test()) === true, el.constructor.name);
}
}
}

View file

@ -1,12 +1,8 @@
import "reflect-metadata";
import { TestCore } from "./core/test_core";
// import { UnitTestEnv } from "../src/core/di/env";
import { dirname } from "path";
// import locator from "../src/core/di/register_di";
import { ExecutorProgramServiceTest } from "./services/executor_program_service_test";
import { FilesChangerTest } from "./services/files_change_notifier_service_test";
import { TriggerServiceTest } from "./services/trigger_service_test";
import { StackServiceTest } from "./services/stack_service_test";
import { CreateDataBaseModelUseCaseTest } from "./usecases/create_database_model_usecase_test";
import { DeleteDataBaseModelUseCaseTest } from "./usecases/delete_database_model_usecase_test";
import { ReadDataBaseModelUseCaseTest } from "./usecases/read_database_model_usecase_test";
@ -39,8 +35,6 @@ const unitTest = async () => {
await init();
await new ExecutorProgramServiceTest(dirname__).test();
await new FilesChangerTest(dirname__).test();
await new StackServiceTest(dirname__).test();
await new TriggerServiceTest().test();
await new CreateDataBaseModelUseCaseTest().test();
await new CreateDataBaseModelUseCaseTest().test();
await new DeleteDataBaseModelUseCaseTest().test();

3
ui/.gitignore vendored
View file

@ -21,4 +21,5 @@
npm-debug.log*
yarn-debug.log*
yarn-error.log*
todo.md
todo.md
package-lock.json

17765
ui/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,18 +1,17 @@
import { IsNotEmpty } from "class-validator";
import { Result } from "../helper/result";
import { IDeviceDependency } from "./skill_model";
import { ValidationModel } from "./validation_model";
export class SidViewModel implements IDeviceDependency {
sid: string;
constructor(sid: string) {
this.sid = sid;
}
valid = (): Result<string, SidViewModel> => {
if (this.sid.isEmpty()) {
return Result.error('sid is empty')
}
return Result.ok(this)
}
static empty() {
return new SidViewModel('')
}
}
export class SidViewModel extends ValidationModel implements IDeviceDependency {
@IsNotEmpty()
sid: string;
constructor(sid: string) {
super();
this.sid = sid;
}
static empty() {
return new SidViewModel("");
}
}

View file

@ -186,8 +186,6 @@ export class CoreThreeRepository extends TypedEvent<any> {
);
loadUrdfRobot = (robotModel: RobotModel) =>
this.urdfLoader.load(robotModel.httpUrl, (robot) => {
console.log(robot);
// Object3D.DEFAULT_UP = new Vector3(0, 0, 1);
robot.userData[UserData.selectedObject] = true;
robot.name = robotModel.name;
if (robotModel.position) robot.position.copy(robotModel.position);

View file

@ -11,6 +11,8 @@ import { SimulationScreen, SimulationScreenPath } from "../../features/simulatio
import { EstimateScreen, EstimateScreenPath } from "../../features/estimate/estimate_screen";
import { CalculationScreenPath, CalculationScreen } from "../../features/calculation/presentation/calculation_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";
const idURL = ":id";
@ -59,4 +61,12 @@ export const router = createBrowserRouter([
path: CalculationScreenPath,
element: <CalculationScreen />,
},
{
path: DigitalTwinsScreenPath,
element: <DigitalTwinsScreen />,
},
{
path: TopicsScreenPath,
element: <TopicsScreen />,
},
]);

View file

@ -34,14 +34,10 @@ export abstract class UiLoader {
abstract errorHandingStrategy: (error?: any) => void;
mapOk = async <T>(property: string, callBack: Promise<Result<CoreError, T>>) => {
return (
(await this.httpHelper(callBack))
// eslint-disable-next-line array-callback-return
.map((el) => {
// @ts-ignore
this[property] = el;
})
);
return (await this.httpHelper(callBack)).map((el) => {
// @ts-ignore
this[property] = el;
});
};
messageHttp = async <T>(callBack: Promise<Result<CoreError, T>>, report?: IMessage) => {
return (await this.httpHelper(callBack)).fold(
@ -76,7 +72,6 @@ export class ModalStore extends SimpleErrorState {
}
export abstract class UiErrorState<T> extends UiLoader {
errorHandingStrategy = (error: T) => {
// message.error(error as any);
console.log(error);
};
abstract init(navigate?: NavigateFunction): Promise<any>;
@ -109,10 +104,8 @@ export abstract class DrawerState<E> extends UiErrorState<E> {
export abstract class UiDrawerFormState<V, E> extends DrawerState<E> {
abstract viewModel: V;
updateForm(value: Partial<V>) {
// {"id":"123","userId":"132"}
//@ts-ignore
this.viewModel = Object.assign(this.viewModel, value);
console.log(this.viewModel)
}
loadDependency = (viewModel: V) => {
this.viewModel = viewModel;
@ -120,7 +113,6 @@ export abstract class UiDrawerFormState<V, E> extends DrawerState<E> {
loadClassInstance = (instance: ClassConstructor<V>, viewModel: V) => {
this.viewModel = plainToInstance(instance, viewModel);
};
}
export abstract class FormState<V, E> extends UiErrorState<E> {
abstract viewModel: V;

View file

@ -1,80 +0,0 @@
{
"center_shell": [
0,
0,
0
],
"scene": {
"details": [
{
"name": "body_down",
"part_path": "parts/objects/body_down.stl",
"material_path": "",
"mass": 100,
"inertia": {
"ixx": 0.1,
"ixy": 0,
"ixz": 0,
"iyy": 0.1,
"iyz": 0,
"izz": 0.1
},
"visual": ".dae",
"collision": ".stl",
"stlUrl": "http://localhost:4001/d370204b-205b-44bb-9698-3bf083b4b7a7/assets/parts/objects/body_down.stl",
"glUrl": "http://localhost:4001/d370204b-205b-44bb-9698-3bf083b4b7a7/assets/libs/objects/body_down.glb",
"daeUrl": "http://localhost:4001/d370204b-205b-44bb-9698-3bf083b4b7a7/assets/libs/objects/body_down.dae",
"objUrl": "http://localhost:4001/d370204b-205b-44bb-9698-3bf083b4b7a7/assets/libs/objects/body_down.obj",
"image": "http://localhost:4001/d370204b-205b-44bb-9698-3bf083b4b7a7/assets/libs/objects/body_down.png",
"solidType": "active",
"isSelect": true
},
{
"name": "sol_gear",
"part_path": "parts/objects/sol_gear.stl",
"material_path": "",
"mass": 100,
"inertia": {
"ixx": 0.1,
"ixy": 0,
"ixz": 0,
"iyy": 0.1,
"iyz": 0,
"izz": 0.1
},
"visual": ".dae",
"collision": ".stl",
"stlUrl": "http://localhost:4001/d370204b-205b-44bb-9698-3bf083b4b7a7/assets/parts/objects/sol_gear.stl",
"glUrl": "http://localhost:4001/d370204b-205b-44bb-9698-3bf083b4b7a7/assets/libs/objects/sol_gear.glb",
"daeUrl": "http://localhost:4001/d370204b-205b-44bb-9698-3bf083b4b7a7/assets/libs/objects/sol_gear.dae",
"objUrl": "http://localhost:4001/d370204b-205b-44bb-9698-3bf083b4b7a7/assets/libs/objects/sol_gear.obj",
"image": "http://localhost:4001/d370204b-205b-44bb-9698-3bf083b4b7a7/assets/libs/objects/sol_gear.png",
"solidType": "active",
"isSelect": true
},
{
"name": "output_shaft",
"part_path": "parts/objects/output_shaft.stl",
"material_path": "",
"mass": 100,
"inertia": {
"ixx": 0.1,
"ixy": 0,
"ixz": 0,
"iyy": 0.1,
"iyz": 0,
"izz": 0.1
},
"visual": ".dae",
"collision": ".stl",
"stlUrl": "http://localhost:4001/d370204b-205b-44bb-9698-3bf083b4b7a7/assets/parts/objects/output_shaft.stl",
"glUrl": "http://localhost:4001/d370204b-205b-44bb-9698-3bf083b4b7a7/assets/libs/objects/output_shaft.glb",
"daeUrl": "http://localhost:4001/d370204b-205b-44bb-9698-3bf083b4b7a7/assets/libs/objects/output_shaft.dae",
"objUrl": "http://localhost:4001/d370204b-205b-44bb-9698-3bf083b4b7a7/assets/libs/objects/output_shaft.obj",
"image": "http://localhost:4001/d370204b-205b-44bb-9698-3bf083b4b7a7/assets/libs/objects/output_shaft.png",
"solidType": "active",
"isSelect": true
}
]
}
}

View file

@ -38,11 +38,12 @@ export const FormBuilder = observer((props: IFormBuilder) => {
<>Error</>
) : (
<div>
{store.formViewModel?.inputs?.map((element) => {
{store.formViewModel?.inputs?.map((element,index) => {
if (element.type?.isEqual(InputType.ENUM)) {
const values = element.values as string[];
return (
<CoreSelect
key={index}
items={values}
value={element.totalValue ?? element.defaultValue}
onChange={(value) => store.changeTotalValue(element.id, value)}
@ -53,7 +54,7 @@ export const FormBuilder = observer((props: IFormBuilder) => {
}
if (element.type?.isEqual(InputType.ARRAY)) {
return (
<div style={{ border: "1px black solid", margin: 20 }}>
<div key={index} style={{ border: "1px black solid", margin: 20 }}>
<div
style={{
display: "flex",

View file

@ -47,15 +47,13 @@ export class FormBuilderStore {
changeTotalValue(id: string, value: any) {
if (this.formViewModel?.inputs)
this.formViewModel.inputs = this.formViewModel?.inputs.map((el) => {
if (!el.id.isEqual(id)) {
return el;
}
if (value instanceof String) {
if (typeof value === "string") {
el.totalValue = value;
}else{
el.totalValue = JSON.stringify(value)
} else {
el.totalValue = JSON.stringify(value);
}
return el;
});

View file

@ -85,7 +85,6 @@ export class FormViewModel {
}
public json() {
const result = this.toResult();
console.log(result)
if (result.isEmpty()) {
console.log("result is Empty error");
return;
@ -109,6 +108,7 @@ export class FormViewModel {
this.inputs.forEach((element) => {
let inputResult = element.totalValue ?? element.defaultValue;
if (element.type.isEqualMany([InputType.STRING, InputType.ENUM])) {
inputResult = `"${String(inputResult)}"`;
}
@ -116,7 +116,7 @@ export class FormViewModel {
inputResult = Number(inputResult);
}
if (element.type.isEqual(InputType.OBJECT)) {
inputResult = element.totalValue ?? element.defaultValue
inputResult = element.totalValue ?? JSON.stringify(element.defaultValue);
}
if (element.type.isEqual(InputType.ARRAY)) {
if (element.totalValue === undefined) {
@ -130,7 +130,6 @@ export class FormViewModel {
if (el instanceof Array)
el.forEach((subElement) => {
let subResult = subElement.totalValue ?? subElement.defaultValue;
console.log(subResult);
if (subElement.type.isEqualMany([InputType.STRING, InputType.ENUM])) {
subResult = `"${String(subResult)}"`;
}
@ -183,8 +182,8 @@ export class FormViewModel {
}
static fromString(result: string, context: string): Result<void, FormViewModel> {
try {
if(result.isEmpty() && context.isEmpty()){
return Result.error(undefined)
if (result.isEmpty() && context.isEmpty()) {
return Result.error(undefined);
}
const enums = new Map<string, string[]>();
const types = new Map<string, InputBuilderViewModel[]>();

View file

@ -4,6 +4,7 @@ import { Parts } from "../../../../../../features/details/details_http_repositor
export class SelectDetailViewModel {
details: Parts[];
constructor(parts: Parts[]) {
console.log(parts)
this.details = parts;
makeAutoObservable(this);
}

View file

@ -1,30 +1,40 @@
// @ts-nocheck
import React from "react";
import { IFormBuilderComponentsProps } from "../../form_builder_components";
import { observer } from "mobx-react-lite";
import { ListItem } from "./ui/list_item";
import { SelectDetailStore } from "./select_detail_store";
import { SelectDetailViewModel } from "../model/select_details_model";
import { plainToInstance } from "class-transformer";
export const SelectDetail = observer((props: IFormBuilderComponentsProps<SelectDetailViewModel>) => {
const [store] = React.useState(() => new SelectDetailStore());
React.useEffect(() => {
store.loadClassInstance(SelectDetailViewModel, props.dependency);
store.viewModel = new SelectDetailViewModel(props.dependency.details);
store.isLoading = false;
console.log(store.viewModel);
store.init();
}, []);
return (
<div>
{store.parts?.map((el) => {
return (
<ListItem
status={store.viewModel.isSelected(el.name)}
name={el.name}
imgURL={el.image}
onChange={() => (store.viewModel.select(el), props.onChange(store.viewModel.toDependency()))}
/>
);
})}
{store.isLoading ? (
<></>
) : (
<>
{store.parts?.map((el) => {
return (
<ListItem
status={store.viewModel.isSelected(el.name)}
name={el.name}
imgURL={el.image}
onChange={() => (store.viewModel.select(el), props.onChange(store.viewModel.toDependency()))}
/>
);
})}
</>
)}
</div>
);
});

View file

@ -5,13 +5,26 @@ import { FormState } from "../../../../../store/base_store";
import { SelectDetailViewModel } from "../model/select_details_model";
export class SelectDetailStore extends FormState<SelectDetailViewModel, any> {
viewModel = SelectDetailViewModel.empty();
parts?: Parts[];
isLoading: boolean = true;
dataSetRepository: DataSetHttpRepository = new DataSetHttpRepository();
constructor() {
super();
makeAutoObservable(this);
}
isSelected = (name: string) => {
console.log(this.viewModel.details);
if (this.viewModel.details)
for (const el of this.viewModel.details) {
if (el.name.isEqual(name)) {
return el.isSelect as boolean;
}
}
return false;
};
init = async () => {
await this.mapOk("parts", this.dataSetRepository.getAssetsActiveProject());
};

View file

@ -47,9 +47,9 @@ export const CoreInput = (props: IInputProps) => {
/>
<div
ref={ref}
contentEditable={true}
defaultValue={props.value}
style={{
style={{
backgroundColor: "#00008000",
border: 1,
fontSize: isSmall ? 12 : 16,

View file

@ -29,7 +29,7 @@ export const CoreSlider = ({
<div
ref={refSlider}
onClick={(event) => {
console.log(event)
}}
style={{ width: "100%", height: 11, backgroundColor: "rgba(104, 80, 164, 1)" }}
>

View file

@ -8,6 +8,7 @@ import { CoreText, CoreTextType } from "../../../core/ui/text/text";
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";
export const AllProjectScreenPath = "/";
export const AllProjectScreen: React.FunctionComponent = observer(() => {
@ -18,7 +19,6 @@ export const AllProjectScreen: React.FunctionComponent = observer(() => {
store.init();
}, []);
return (
<PreviewPage
largeText={"Проекты"}
@ -29,6 +29,7 @@ export const AllProjectScreen: React.FunctionComponent = observer(() => {
isLoading={store.isLoading}
children={
<>
<div onClick={() => navigate(DigitalTwinsScreenPath)}>Digital twins</div>
{store.projectsModels?.map((el) => {
return (
<div
@ -101,4 +102,4 @@ export const AllProjectScreen: React.FunctionComponent = observer(() => {
}
/>
);
});
});

View file

@ -4,7 +4,7 @@ import { IProjectModel, IProjectView } from "../model/project_model";
import { ModalStore } from "../../../core/store/base_store";
import { message } from "antd";
export class ProjectView {
isActive: boolean;
description: string;

View file

@ -187,7 +187,6 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState<BehaviorTreeView
if (model.skills) this.filledOutTemplates = model.skills;
await this.mapOk("sceneAsset", this.behaviorTreeBuilderHttpRepository.getSceneAsset(model.sceneId));
this.isLoading = false;
this.reteForceUpdateObserver?.emit("");
},

View file

@ -1,38 +1,40 @@
import React from "react"
import { observer } from "mobx-react-lite"
import React from "react";
import { observer } from "mobx-react-lite";
import { message } from "antd";
import { CameraDeviceStore } from "./camera_device_store"
import { CameraDeviceStore } from "./camera_device_store";
import { IPropsForm } from "../forms";
import { IDeviceDependency } from "../../../../../../core/model/skill_model";
import { CoreButton } from "../../../../../../core/ui/button/button";
import { CoreSelect } from "../../../../../../core/ui/select/select";
import { CoreText, CoreTextType } from "../../../../../../core/ui/text/text";
import { SidViewModel } from "../../../../../../core/model/device_dependency_view_model";
export const CameraDeviceForm = observer((props: IPropsForm<Partial<IDeviceDependency>>) => {
const [store] = React.useState(() => new CameraDeviceStore());
React.useEffect(() => {
store.init();
}, [store]);
return (
<div style={{ border: '1px solid', margin: 10, padding: 10 }}>
<CoreText text={"Cameras"} type={CoreTextType.header} style={{ padding: 10 }} />
<CoreSelect
items={store.cameras?.camera.map((el) => el.sid) ?? []}
value={props.dependency?.sid ?? ""}
label={'Выберите камеру'}
onChange={(value: string) =>
store.updateForm({ sid: value })
} />
<CoreButton style={{ margin: 10, width: 100 }} text="OK" onClick={() => {
store.viewModel.valid().fold((s) => {
props.onChange(s);
}, (e) => message.error(e))
}} />
</div>
)
})
const [store] = React.useState(() => new CameraDeviceStore());
React.useEffect(() => {
store.init();
}, [store]);
return (
<div style={{ border: "1px solid", margin: 10, padding: 10 }}>
<CoreText text={"Cameras"} type={CoreTextType.header} style={{ padding: 10 }} />
<CoreSelect
items={store.cameras?.camera.map((el) => el.sid) ?? []}
value={props.dependency?.sid ?? ""}
label={"Выберите камеру"}
onChange={(value: string) => store.updateForm({ sid: value })}
/>
<CoreButton
style={{ margin: 10, width: 100 }}
text="OK"
onClick={async () => {
(await store.viewModel.valid<SidViewModel>()).fold(
(s) => {
props.onChange(s);
},
(e) => message.error(e)
);
}}
/>
</div>
);
});

View file

@ -16,25 +16,38 @@ export const TopicsForm = observer((props: IPropsForm<Partial<TopicDependencyVie
store.init();
}, [store, props]);
return <div style={{ border: '1px solid', margin: 10, padding: 10 }}>
<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 })
} />
return (
<div style={{ border: "1px solid", margin: 10, padding: 10 }}>
<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) => {
console.log(200)
store.updateForm({ axis: !status })
}} /></div>
<CoreButton style={{ margin: 10, width: 100 }} text="OK" onClick={() => {
store.viewModel.valid().fold((s) => {
props.onChange(s);
}, (e) => message.error(e))
}} />
</div>;
<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)
);
}}
/>
</div>
);
});

View file

@ -6,16 +6,22 @@ import { TopicsFormHttpRepository } from "./topics_form_http_repository";
import { Topics } from "../../../../../../core/model/topics";
export class TopicsFormStore extends FormState<TopicDependencyViewModel, CoreError> {
constructor() {
super();
makeAutoObservable(this);
}
topics?: Topics;
viewModel: TopicDependencyViewModel = TopicDependencyViewModel.empty();
cameraDeviceHttpRepository: TopicsFormHttpRepository = new TopicsFormHttpRepository();
errorHandingStrategy = (error: CoreError) => { }
init = async (navigate?: NavigateFunction | undefined) => {
await this.mapOk('topics', this.cameraDeviceHttpRepository.getAllTopics())
constructor() {
super();
makeAutoObservable(this);
}
topics?: Topics;
viewModel: TopicDependencyViewModel = TopicDependencyViewModel.empty();
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,6 +1,5 @@
import { observer } from "mobx-react-lite";
import React from "react";
import { Select, message } from "antd";
import { WeightsFormStore } from "./weights_store";
import { IWeightsDependency } from "../../../../../../core/model/skill_model";
@ -47,9 +46,9 @@ export const WeightsForm = observer((props: IWeightsFormProps) => {
placeholder="Выберите деталь"
optionFilterProp="children"
defaultValue={props.dependency?.weights_name}
onChange={(e) => {
store.updateForm({ weights_name: e });
store.updateWeights(e);
onChange={(text) => {
store.updateForm({ weights_name: text });
store.updateWeights(text);
}}
filterOption={(input: string, option?: { label: string; value: string }) =>
(option?.label ?? "").toLowerCase().includes(input.toLowerCase())

View file

@ -135,6 +135,7 @@ export class CalculationStore extends UiDrawerFormState<CalculationModel, HttpEr
};
makeEditProcess = (el: CalculationModel) => {
this.editProcess = el;
this.loadClassInstance(CalculationModel, el);
this.editDrawer(DrawersSkill.EDIT_SKILL, true);
};
deleteTemplate = async (el: CalculationModel) => {

View file

@ -36,19 +36,8 @@ export interface Asset {
mesh: string;
image: string;
}
export class FormBuilderValidationModel {
static vision(): FormBuilderValidationModel {
return new FormBuilderValidationModel(
`ENUM PRETRAIN = "true","false";`,
`{
"numberOfEpochs": \${numberOfEpochs:number:10},
"selectDataset": \${<SelectDataset/>:OBJECT:{"dataset": {}},
"pretrain": \${pretrain:Enum<PRETRAIN>:true}
}`,
[],
''
);
}
public result: string;
public context: string;
public form: string[];
@ -59,6 +48,11 @@ export class FormBuilderValidationModel {
this.form = form;
this.output = output;
}
static isEmpty = (formBuilderValidationModel: FormBuilderValidationModel) =>
formBuilderValidationModel.context.isEmpty() &&
formBuilderValidationModel.result.isEmpty() &&
formBuilderValidationModel.form.isEmpty();
static datasetEmpty() {
return new FormBuilderValidationModel(datasetFormMockContext, datasetFormMockResult, [], defaultFormValue);
}
@ -71,13 +65,24 @@ export class FormBuilderValidationModel {
static creteDataSetTest() {
return new FormBuilderValidationModel(``, scene, [], "");
}
static vision(): FormBuilderValidationModel {
return new FormBuilderValidationModel(
`ENUM PRETRAIN = "true","false";`,
`{
"numberOfEpochs": \${numberOfEpochs:number:10},
"selectDataset": \${<SelectDataset/>:OBJECT:{"dataset": {}},
"pretrain": \${pretrain:Enum<PRETRAIN>:true}
}`,
[],
""
);
}
}
export const scene = `{
"center_shell": [\${CENTER_SHELL_1:number:0}, \${CENTER_SHELL_2:number:0}, \${CENTER_SHELL_3:number:0}],
"scene":\${<SelectScene/>:OBJECT:{"details": []}
}`;
// {"name":"body_up","isSelect":true,"part_path":"parts/objects/body_up.stl","material_path":"","inertia":{"ixx":0.1,"ixy":0,"ixz":0,"iyy":0.1,"iyz":0,"izz":0.1},"visual":".dae","collision":".stl","stlUrl":"http://localhost:4001/d370204b-205b-44bb-9698-3bf083b4b7a7/assets/parts/objects/body_up.stl","glUrl":"http://localhost:4001/d370204b-205b-44bb-9698-3bf083b4b7a7/assets/libs/objects/body_up.glb","daeUrl":"http://localhost:4001/d370204b-205b-44bb-9698-3bf083b4b7a7/assets/libs/objects/body_up.dae","objUrl":"http://localhost:4001/d370204b-205b-44bb-9698-3bf083b4b7a7/assets/libs/objects/body_up.obj","image":"http://localhost:4001/d370204b-205b-44bb-9698-3bf083b4b7a7/assets/libs/objects/body_up.png","solidType":"active"}
export class DataSetModel {
dataSetObjects: string[];
datasetType: string;
@ -128,11 +133,12 @@ export class DataSetModel {
export const datasetTypes = ["Object Detection - YOLOv8", "Pose Estimation - DOPE"];
export const datasetFormMockResult = `{
export const datasetFormMockResult = `
{
"typedataset": \${typedataset:Enum<T>:ObjectDetection},
"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}]
"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}]
},
"selectParts":\${<SelectScene/>:OBJECT:{"details": []},
"scene":{
@ -203,3 +209,11 @@ export const defaultFormValue: any = {
camera_position: { center_shell: [0, 0, 0], radius_range: [1, 1.4], elevation_range: [10, 90] },
generation: { n_cam_pose: 5, n_sample_on_pose: 3, n_series: 100, image_format: "JPEG", image_size_wh: [640, 480] },
};
// `{
// "camera_name": \${CAMERA_NAME:string: },
// "camera_namespace": \${CAMERA_NAMESPACE:string: },
// "staticProp":{
// "e23e":""
// }
// }`;

View file

@ -3,6 +3,7 @@ import { EnvelopmentViewModel } from "./envelopment_view_model";
export interface Parts {
isSelect?: boolean;
name: string;
mass?:number;
part_path: string;
material_path: string;
stlUrl: string;

View file

@ -0,0 +1,18 @@
import { HttpMethod, HttpRepository } from "../../core/repository/core_http_repository";
import { UUID } from "../all_projects/data/project_http_repository";
import { DigitalTwinsModel } from "./digital_twins_model";
export class DigitalTwinsHttpRepository extends HttpRepository {
featureApi = `/digital/twins/template`;
subFeatureApi = `/digital/twins/instance`;
getAllDigitalTwinsTemplate = () => this._jsonRequest(HttpMethod.GET, this.featureApi);
getAllDigitalTwinsInstance = () => this._jsonRequest(HttpMethod.GET, this.subFeatureApi);
createNewDigitalTwinsTemplate = (model: DigitalTwinsModel) =>
this._jsonRequest(HttpMethod.POST, this.featureApi, model);
createNewDigitalTwinsInstance = (model: DigitalTwinsModel) =>
this._jsonRequest<UUID>(HttpMethod.POST, this.subFeatureApi, model);
deleteDigitalTwinsTemplate = (id: string) => this._jsonRequest(HttpMethod.DELETE, `${this.featureApi}?id=${id}`);
deleteDigitalTwinsInstance = (id: string) => this._jsonRequest(HttpMethod.DELETE, `${this.subFeatureApi}?id=${id}`);
execDigitalTwinsInstance = (id: UUID) =>
this._jsonRequest(HttpMethod.POST, `${this.subFeatureApi}/exec/instance?id=${id.id}`);
}

View file

@ -0,0 +1,43 @@
import { ValidationModel } from "../../core/model/validation_model";
import { Type } from "class-transformer";
import { IsEnum, IsString } from "class-validator";
import { FormBuilderValidationModel } from "../dataset/dataset_model";
export enum DigitalTwinsTypes {
CAMERA = "CAMERA",
ROBOT = "ROBOT",
}
export class Interfaces {
@IsString()
cmd: string;
}
export class DigitalTwinsModel extends ValidationModel {
_id?: string;
path: string = "";
name: string = "";
instanceName: string = "";
@IsEnum(DigitalTwinsTypes)
entity: DigitalTwinsTypes;
@IsString()
description: string;
@IsString()
command: string;
@Type(() => Interfaces)
interfaces: Interfaces;
formBuilder = FormBuilderValidationModel.empty();
static empty() {
return new DigitalTwinsModel();
}
}
export interface DigitalTwinsTemplate {
interfaces: Interfaces;
_id?: string;
path: string;
name: string;
entity: string;
description: string;
command: string;
__v: string;
formBuilder?: FormBuilderValidationModel;
}

View file

@ -0,0 +1,149 @@
import React from "react";
import { observer } from "mobx-react-lite";
import { DigitalTwinStoreType, DigitalTwinsStore, DrawersDigitalTwin } from "./digital_twins_store";
import { Drawer, Modal } from "antd";
import { CoreButton } from "../../core/ui/button/button";
import { FormBuilder } from "../../core/ui/form_builder/form_builder";
import { CoreInput } from "../../core/ui/input/input";
import { CoreSelect } from "../../core/ui/select/select";
import { DigitalTwinsModel, DigitalTwinsTypes } from "./digital_twins_model";
import { match } from "ts-pattern";
export const DigitalTwinsScreenPath = "/digital_twins";
export const DigitalTwinsScreen = observer(() => {
const [store] = React.useState(() => new DigitalTwinsStore());
React.useEffect(() => {
store.init();
}, []);
return (
<>
<div style={{ display: "flex" }}>
<CoreButton
text="Новый шаблон двойника"
filled={true}
onClick={() => store.editDrawer(DrawersDigitalTwin.newTwinTemplate, true)}
/>
<CoreButton
text="Новый двойник"
filled={true}
onClick={() => store.editDrawer(DrawersDigitalTwin.newInstanceTwinTemplate, true)}
/>
</div>
<Drawer
width={(window.innerWidth / 100) * 50}
title={store.titleDrawer}
destroyOnClose={true}
onClose={() => {
store.editDrawer(DrawersDigitalTwin.newInstanceTwinTemplate, false);
store.type = DigitalTwinStoreType.selectTwinTemplate;
store.viewModel = DigitalTwinsModel.empty();
}}
open={store.drawers.find((el) => el.name === DrawersDigitalTwin.newInstanceTwinTemplate)?.status}
>
<div style={{ display: "flex", flexDirection: "column", justifyContent: "space-between", height: "100%" }}>
{match(store.type)
.with(DigitalTwinStoreType.editInstanceTemplate, () => (
<>
<CoreInput onChange={(text) => store.updateForm({ instanceName: text })} label={"Instance name"} />
<FormBuilder
formBuilder={store.viewModel.formBuilder}
onChange={(change) => store.updateForm({ formBuilder: change })}
/>
</>
))
.with(DigitalTwinStoreType.selectTwinTemplate, () => (
<>
{store.digitalTwinsTemplates?.map((el, i) => (
<div
key={i}
style={{
backgroundColor: "rgba(104, 80, 164, 1)",
width: 180,
height: 180,
borderRadius: 10,
padding: 10,
margin: 10,
}}
>
<div onClick={() => store.deleteDigitalTwinsTemplate(el._id!)}>DELETE</div>
<div onClick={() => store.createInstanceTwins(el)}>CREATE</div>
{el.name}
</div>
))}
</>
))
.otherwise(() => (
<></>
))}
<div style={{ display: "flex" }}>
<CoreButton text="Сохранить" filled={true} onClick={() => store.saveInstanceDigitalTwin()} />
<div style={{ width: 10 }} />
<CoreButton
text="Отмена"
onClick={() => store.editDrawer(DrawersDigitalTwin.newInstanceTwinTemplate, false)}
/>
</div>
</div>
</Drawer>
<Drawer
width={(window.innerWidth / 100) * 50}
title={store.titleDrawer}
destroyOnClose={true}
onClose={() => store.editDrawer(DrawersDigitalTwin.newTwinTemplate, false)}
open={store.drawers.find((el) => el.name === DrawersDigitalTwin.newTwinTemplate)?.status}
>
<div style={{ display: "flex", flexDirection: "column", justifyContent: "space-between", height: "100%" }}>
<CoreInput onChange={(text) => store.updateForm({ name: text })} label={"Имя"} />
<CoreSelect
items={Object.keys(DigitalTwinsTypes)}
value={Object.keys(DigitalTwinsTypes)[0]}
label={"entity"}
onChange={(value: string) => store.updateForm({ entity: value as any })}
/>
<CoreInput onChange={(text) => store.updateForm({ description: text })} label={"Описание"} />
<CoreInput onChange={(text) => store.updateForm({ command: text })} label={"Команда"} />
<CoreInput onChange={(text) => store.updateForm({ interfaces: { cmd: text } })} label={"Cmd"} />
<CoreInput
label="FormBuilder Result"
onChange={(text) => (store.viewModel.formBuilder.result = text)}
style={{ height: 200, overflow: "overlay" }}
/>
<CoreInput
label="FormBuilder Context"
onChange={(text) => (store.viewModel.formBuilder.context = text)}
style={{ height: 200, overflow: "overlay" }}
/>
<div style={{ height: 10 }} />
<CoreButton
style={{ width: 206 }}
text={"Посмотреть FormBuilder "}
onClick={() => (store.isModalOpen = true)}
/>
<div style={{ height: 100 }} />
<div style={{ display: "flex" }}>
<CoreButton text="Сохранить" filled={true} onClick={() => store.saveTemplateDigitalTwin()} />
<div style={{ width: 10 }} />
<CoreButton text="Отмена" onClick={() => store.editDrawer(DrawersDigitalTwin.newTwinTemplate, false)} />
</div>
</div>
</Drawer>
<Modal
destroyOnClose={true}
open={store.isModalOpen}
footer={null}
closable={false}
closeIcon={null}
onCancel={() => {
store.isModalOpen = false;
}}
>
<FormBuilder formBuilder={store.viewModel.formBuilder} onChange={() => {}} />
</Modal>
</>
);
});

View file

@ -0,0 +1,68 @@
import makeAutoObservable from "mobx-store-inheritance";
import { UiDrawerFormState } from "../../core/store/base_store";
import { DigitalTwinsModel, DigitalTwinsTemplate as DigitalTwins } from "./digital_twins_model";
import { NavigateFunction } from "react-router-dom";
import { message } from "antd";
import { DigitalTwinsHttpRepository } from "./digital_twins_http_repository";
import { HttpError } from "../../core/repository/core_http_repository";
import { match } from "ts-pattern";
export enum DrawersDigitalTwin {
newTwinTemplate = "Новый шаблон двойника",
newInstanceTwinTemplate = "Новый инстанц шаблона двойника",
}
export enum DigitalTwinStoreType {
selectTwinTemplate = "selectTwinTemplate",
editInstanceTemplate = "editInstanceTemplate",
}
export class DigitalTwinsStore extends UiDrawerFormState<DigitalTwinsModel, HttpError> {
type: DigitalTwinStoreType = DigitalTwinStoreType.selectTwinTemplate;
digitalTwinsTemplates?: DigitalTwins[];
digitalTwinsInstances?: DigitalTwins[];
digitalTwinsHttpRepository: DigitalTwinsHttpRepository = new DigitalTwinsHttpRepository();
viewModel: DigitalTwinsModel = DigitalTwinsModel.empty();
isModalOpen: boolean = false;
init = async (_navigate?: NavigateFunction | undefined): Promise<any> => this.refresh();
constructor() {
super(DrawersDigitalTwin);
makeAutoObservable(this);
}
refresh = async () => {
this.mapOk("digitalTwinsTemplates", this.digitalTwinsHttpRepository.getAllDigitalTwinsTemplate());
this.mapOk("digitalTwinsInstances", this.digitalTwinsHttpRepository.getAllDigitalTwinsInstance());
};
saveInstanceDigitalTwin = async () =>
match(this.type)
.with(DigitalTwinStoreType.editInstanceTemplate, async () => {
this.viewModel._id = undefined;
(await this.digitalTwinsHttpRepository.createNewDigitalTwinsInstance(this.viewModel)).map(async (id) => {
await this.digitalTwinsHttpRepository.execDigitalTwinsInstance(id);
await this.refresh();
});
this.editDrawer(DrawersDigitalTwin.newInstanceTwinTemplate, false);
})
.with(DigitalTwinStoreType.selectTwinTemplate, () => {
console.log(200);
// this.editDrawer(DrawersDigitalTwin.newInstanceTwinTemplate, false),
// await (
// await this.digitalTwinsHttpRepository.createNewDigitalTwinsInstance(this.viewModel)
// ).map(() => this.refresh())
});
createInstanceTwins = (el: DigitalTwins): void => {
this.loadDependency(el as any);
this.type = DigitalTwinStoreType.editInstanceTemplate;
};
saveTemplateDigitalTwin = async () =>
(await this.viewModel.valid<DigitalTwinsModel>()).fold(
async (model) => {
(await this.digitalTwinsHttpRepository.createNewDigitalTwinsTemplate(model)).map(() => {
this.refresh();
});
},
async (e) => message.error(e)
);
deleteDigitalTwinsTemplate = async (id: string) =>
(await this.digitalTwinsHttpRepository.deleteDigitalTwinsTemplate(id)).map(() => this.refresh());
}

View file

@ -88,3 +88,4 @@ export class RobotFormStore extends FormState<RobotModel, CoreError> {
)
);
}

View file

@ -0,0 +1,12 @@
import { ValidationModel } from "../../core/model/validation_model";
export class TopicViewModel extends ValidationModel {
static empty() {
return new TopicViewModel();
}
}
export interface ITopicModel {
digitalTwinId?: string;
name: string;
type: string;
}

View file

@ -0,0 +1,5 @@
import { HttpMethod, HttpRepository } from "../../core/repository/core_http_repository";
export class TopicsHttpRepository extends HttpRepository {
getAllTopics = () => this._jsonRequest(HttpMethod.GET, "/topics");
}

View file

@ -0,0 +1,22 @@
import { observer } from "mobx-react-lite";
import { TopicsStore } from "./topics_store";
import React from "react";
export const TopicsScreenPath = "/topics";
export const TopicsScreen = observer(() => {
const [store] = React.useState(() => new TopicsStore());
React.useEffect(() => {
store.init();
}, []);
return (
<>
{store.topics?.map((el) => (
<div>
<div>{el.name}</div>
<div>{el.type}</div>
</div>
))}
</>
);
});

View file

@ -0,0 +1,19 @@
import makeAutoObservable from "mobx-store-inheritance";
import { FormState } from "../../core/store/base_store";
import { HttpError } from "../../core/repository/core_http_repository";
import { ITopicModel, TopicViewModel as TopicModel } from "./topic_view_model";
import { NavigateFunction } from "react-router-dom";
import { TopicsHttpRepository } from "./topics_repository";
export class TopicsStore extends FormState<TopicModel, HttpError> {
viewModel: TopicModel = TopicModel.empty();
topics?: ITopicModel[];
topicsHttpRepository: TopicsHttpRepository = new TopicsHttpRepository();
constructor() {
super();
makeAutoObservable(this);
}
init = async (navigate?: NavigateFunction | undefined) => {
await this.mapOk("topics", this.topicsHttpRepository.getAllTopics());
};
}

View file

@ -22,4 +22,3 @@ root.render(
</SocketLister>
</>
);

View file

@ -0,0 +1,22 @@
import shutil
import argparse
import os.path
parser = argparse.ArgumentParser()
parser.add_argument("--path")
args = parser.parse_args()
def copy_and_move_folder(src, dst):
try:
if os.path.exists(dst):
shutil.rmtree(dst)
shutil.copytree(src, dst)
print(f"Folder {src} successfully copied")
except shutil.Error as e:
print(f"Error: {e}")
source_folder = os.path.dirname(os.path.abspath(__file__)) + "/interfaces/"
copy_and_move_folder(source_folder, args.path)

View file

@ -0,0 +1,124 @@
{
"topics": [
{
"name": "/cartesian_force_controller/current_pose",
"type": "geometry_msgs/msg/PoseStamped"
},
{
"name": "/cartesian_force_controller/current_twist",
"type": "geometry_msgs/msg/TwistStamped"
},
{
"name": "/cartesian_force_controller/target_wrench",
"type": "geometry_msgs/msg/WrenchStamped"
},
{
"name": "/cartesian_force_controller/transition_event",
"type": "lifecycle_msgs/msg/TransitionEvent"
},
{
"name": "/cartesian_motion_controller/current_pose",
"type": "geometry_msgs/msg/PoseStamped"
},
{
"name": "/cartesian_motion_controller/current_twist",
"type": "geometry_msgs/msg/TwistStamped"
},
{
"name": "/cartesian_motion_controller/target_frame",
"type": "geometry_msgs/msg/PoseStamped"
},
{
"name": "/cartesian_motion_controller/transition_event",
"type": "lifecycle_msgs/msg/TransitionEvent"
},
{
"name": "/clock",
"type": "rosgraph_msgs/msg/Clock"
},
{
"name": "/dynamic_joint_states",
"type": "control_msgs/msg/DynamicJointState"
},
{
"name": "/force_torque_sensor_broadcaster/transition_event",
"type": "lifecycle_msgs/msg/TransitionEvent"
},
{
"name": "/force_torque_sensor_broadcaster/wrench",
"type": "geometry_msgs/msg/WrenchStamped"
},
{
"name": "/gripper_control_action_server/gripper_controller/commands",
"type": "std_msgs/msg/Float64MultiArray"
},
{
"name": "/gripper_controller/transition_event",
"type": "lifecycle_msgs/msg/TransitionEvent"
},
{
"name": "/gz_enviroment/transition_event",
"type": "lifecycle_msgs/msg/TransitionEvent"
},
{
"name": "/joint_effort_controller/commands",
"type": "std_msgs/msg/Float64MultiArray"
},
{
"name": "/joint_effort_controller/transition_event",
"type": "lifecycle_msgs/msg/TransitionEvent"
},
{
"name": "/joint_state_broadcaster/transition_event",
"type": "lifecycle_msgs/msg/TransitionEvent"
},
{
"name": "/joint_states",
"type": "sensor_msgs/msg/JointState"
},
{
"name": "/joint_trajectory_controller/controller_state",
"type": "control_msgs/msg/JointTrajectoryControllerState"
},
{
"name": "/joint_trajectory_controller/joint_trajectory",
"type": "trajectory_msgs/msg/JointTrajectory"
},
{
"name": "/joint_trajectory_controller/state",
"type": "control_msgs/msg/JointTrajectoryControllerState"
},
{
"name": "/joint_trajectory_controller/transition_event",
"type": "lifecycle_msgs/msg/TransitionEvent"
},
{
"name": "/rgbd_camera/camera_info",
"type": "sensor_msgs/msg/CameraInfo"
},
{
"name": "/rgbd_camera/depth_image",
"type": "sensor_msgs/msg/Image"
},
{
"name": "/rgbd_camera/image",
"type": "sensor_msgs/msg/Image"
},
{
"name": "/rgbd_camera/points",
"type": "sensor_msgs/msg/PointCloud2"
},
{
"name": "/robot_description",
"type": "std_msgs/msg/String"
},
{
"name": "/tf",
"type": "tf2_msgs/msg/TFMessage"
},
{
"name": "/tf_static",
"type": "tf2_msgs/msg/TFMessage"
}
]
}