diff --git a/.vscode/settings.json b/.vscode/settings.json index 58a804b..2793cf8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -20,7 +20,6 @@ "GLTF", "grau", "idontsudo", - "Pids", "raycaster", "skils", "typedataset", diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..5444c1a --- /dev/null +++ b/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1733935512, + "narHash": "sha256-beDnPaHubnwcsbVPC3rIVtQM3QVFDMmc0dtfHCW5UrA=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "d55cc4608dae1b42a0ef5c0cf701b501fc7bae58", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "master", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..8f0a918 --- /dev/null +++ b/flake.nix @@ -0,0 +1,37 @@ +{ + description = "Robossembler Development Environments on Nix"; + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs?ref=master"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, flake-utils }: + flake-utils.lib.eachDefaultSystem + (system: + let + pkgs = import nixpkgs { inherit system; overlays = []; }; + in + { + packages = { + frontend = pkgs.writeShellApplication { + name = "frontend"; + runtimeInputs = [ pkgs.nodejs ]; + text = '' + cd ui && npm i && npm run dev + ''; + }; + backend = pkgs.writeShellApplication { + name = "frontend"; + runtimeInputs = [ pkgs.nodejs ]; + text = '' + cd server && npm i && npm run dev + ''; + }; + }; + + devShells = { + default = pkgs.mkShell { packages = with pkgs; [ nodejs ]; }; + }; + } + ); +} diff --git a/p.json b/p.json deleted file mode 100644 index 0844c5e..0000000 --- a/p.json +++ /dev/null @@ -1,82 +0,0 @@ -{ - "process": { - "type": "OBJECT_DETECTION", - "selectProcess": { - "value": { - "_id": "675db885429ef25f8d2efaa2", - "script": "ls -l -a", - "formBuilder": { - "result": "", - "context": "", - "form": [], - "output": "", - "type": "formBuilder" - }, - "type": "OBJECT_DETECTION", - "instanceName": "ls -l -a", - "name": "ls", - "isEnd": true, - "createDate": "1734195300981", - "card": "pose_estimate", - "path": "/Users/idontsudo/webservice/server/build/public//process/ls", - "instancePath": "/Users/idontsudo/webservice/server/build/public//process/ls/ls -l -a", - "project": { - "_id": "675eb125281cf9253681efa3", - "description": "e1wq", - "rootDir": "/Users/idontsudo/webservice/server/build/public/f49f8f47-5427-48aa-8aff-c5e7ae4e6efe", - "isActive": true, - "__v": 0 - }, - "__v": 0, - "lastProcessExecCommand": "ls -l -a --path /Users/idontsudo/webservice/server/build/public/process/ls/ls -l -a --form /Users/idontsudo/webservice/server/build/public/process/ls/ls -l -a/form.json", - "processStatus": "endError", - "lastProcessLogs": "ls: unrecognized option `--path'nnusage: ls [-@ABCFGHILOPRSTUWabcdefghiklmnopqrstuvwxy1%,] [--color=when] [-D format] [file ...]n" - } - } - }, - "datasetObjects": { - "details": [] - }, - "typedataset": "ObjectDetection", - "models_randomization": { - "loc_range_low": [ - -1, - -1, - 0 - ], - "loc_range_high": [ - 1, - 1, - 2 - ] - }, - "scene": { - "objects": [], - "lights": [] - }, - "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 - ] - } -} \ No newline at end of file diff --git a/server/README.md b/server/README.md index 6a3cc96..cb0043a 100644 --- a/server/README.md +++ b/server/README.md @@ -1,14 +1 @@ -Веб-сервис для отладки Robossembler Framework - -### Миграция данных веб-сервиса - -Имеется ввиду перенос данных, хранящихся в БД MongoDB. - -Для этого вначале создаётся копия БД с именем `dev` в папке `my_copy`: -```sh -mongodump --host localhost --port 27017 --db dev --out my_copy -``` -Затем папка `my_copy` переносится в нужное место (например, на другой сервер). И запускается её восстановление в новом месте (копия БД с именем `dev`): -```sh -mongorestore --host localhost --port 27017 --db dev my_copy/dev -``` +Веб-сервис для отладки Robossembler Framework \ No newline at end of file diff --git a/server/command.json b/server/command.json index 8762c7e..468f1fe 100644 --- a/server/command.json +++ b/server/command.json @@ -15,12 +15,12 @@ "checkCommand": null, "filter": null }, - "btRuntimeProcess": { - "execCommand": "cd ~/robossembler-ws && source ./install/local_setup.bash; ros2 launch rbs_bt_executor rbs_bt_web.launch.py bt_path:=${bt_path}", + "btBuilderProcess": { + "execCommand": "nix run github:nixos/nixpkgs#python312Packages.tensorboard -- --logdir ${dir_path}", "date": null, "status": null, "delay": 0, "checkCommand": null, "filter": null } -} +} \ No newline at end of file diff --git a/server/src/core/controllers/app.ts b/server/src/core/controllers/app.ts index c1dca95..41a52e7 100644 --- a/server/src/core/controllers/app.ts +++ b/server/src/core/controllers/app.ts @@ -9,9 +9,6 @@ import { dirname } from "path"; import { CheckAndCreateStaticFilesFolderUseCase } from "../usecases/check_and_create_static_files_folder_usecase"; import { DataBaseConnectUseCase } from "../usecases/database_connect_usecase"; import { TypedEvent } from "../helpers/typed_event"; -import { CalculationInstanceDBModel } from "../../features/calculations_instance/models/calculations_instance_database_model"; -import * as fs from "fs"; -import { WriteFileSystemFileUseCase } from "../usecases/write_file_system_file_usecase"; export enum ServerStatus { init = "init", @@ -88,25 +85,7 @@ export class App extends TypedEvent { this.app.use(express.json()); this.app.use(express.urlencoded({ extended: true })); this.app.use(express.static(App.staticFilesStoreDir())); - this.app.get('/logs', async (req, res) => { - const id = req.query.id; - if (id === undefined) { - return res.status(400).json('need req query id?=') - } - - const calculationInstanceDBModel = await CalculationInstanceDBModel.findById(id) - if (calculationInstanceDBModel === null) { - return res.status(400).json("calcultaion db model is null"); - } - const p = App.staticFilesStoreDir() + '/log.txt'; - - (await new WriteFileSystemFileUseCase().call(p, calculationInstanceDBModel.lastProcessLogs)).map(() => { - return res.sendFile(p); - }) - - - }) this.app.use( fileUpload({ createParentPath: true, diff --git a/server/src/core/controllers/http_controller.ts b/server/src/core/controllers/http_controller.ts index 9542de1..5b61528 100644 --- a/server/src/core/controllers/http_controller.ts +++ b/server/src/core/controllers/http_controller.ts @@ -65,12 +65,12 @@ interface ISubSetFeatureRouter { method: HttpMethodType; subUrl: string; fn: - | CallbackStrategyWithValidationModel - | CallbackStrategyWithEmpty - | CallbackStrategyWithIdQuery - | CallBackStrategyWithQueryPage - | CallbackStrategyWithFileUpload - | CallbackStrategyWithFilesUploads; + | CallbackStrategyWithValidationModel + | CallbackStrategyWithEmpty + | CallbackStrategyWithIdQuery + | CallBackStrategyWithQueryPage + | CallbackStrategyWithFileUpload + | CallbackStrategyWithFilesUploads; } abstract class ICoreHttpController { @@ -234,7 +234,6 @@ export class CoreHttpController implements ICoreHttpController { return res.json(ok); }, (err) => { - return res.status(400).json({ error: String(err) }); } ); diff --git a/server/src/core/models/skill_model.ts b/server/src/core/models/skill_model.ts index 57baf8c..c22ab7d 100644 --- a/server/src/core/models/skill_model.ts +++ b/server/src/core/models/skill_model.ts @@ -18,7 +18,7 @@ export class TopicViewModel { export interface IParam { type: string; - dependency: object; + dependency: Object; } export interface Skill { SkillPackage: ISkillPackage; diff --git a/server/src/core/scenarios/create_intance_scenario.ts b/server/src/core/scenarios/create_intance_scenario.ts index 9df5453..53b98fe 100644 --- a/server/src/core/scenarios/create_intance_scenario.ts +++ b/server/src/core/scenarios/create_intance_scenario.ts @@ -7,8 +7,9 @@ export abstract class CreateInstanceScenario extends Callbac abstract validationModel: V; abstract databaseModel: any; call = async (model: V): ResponseBase => { - model.instancePath = `${model.path.pathNormalize()}/${model.instanceName}`.pathNormalize(); - model.path = model.path.pathNormalize(); + model.instancePath = `${model.path}/${model.instanceName}`; + console.log("INSTANCE PATh"); + console.log(model.instancePath) return (await new CreateFolderUseCase().call(model.instancePath)).map( async () => await new CreateDataBaseModelUseCase(this.databaseModel).call(model) ); diff --git a/server/src/core/scenarios/exec_process_scenario_v2.ts b/server/src/core/scenarios/exec_process_scenario_v2.ts deleted file mode 100644 index cd846d4..0000000 --- a/server/src/core/scenarios/exec_process_scenario_v2.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { Disposable, Listener, TypedEvent } from "../helpers/typed_event"; -import { GetRootDirUseCase } from "../usecases/get_root_dir_usecase" -import { exec } from 'child_process'; - -export const activeProcessPids: { [name: string]: { pid: number, status: ProcessStatus } } = {} - -export enum ProcessStatus { - run = 'run', - endError = 'endError', - endOk = 'endOk', - userDelete = 'userDelete', -} - -class ExecutorProgramServiceV2 extends TypedEvent<{ [name: string]: { pid: number, status: ProcessStatus } }> {} - -export const executorProgramServiceV2 = new ExecutorProgramServiceV2(); -class ProcessData { - log: string | undefined; - status: ProcessStatus; - constructor(log: string | undefined, status: ProcessStatus) { - - if (log !== undefined) { - this.log = log; - } - this.status = status; - - } -} -abstract class Watcher { -} -export abstract class ExecProcessWatcher extends TypedEvent implements Watcher { - logs: string[] = []; - status: ProcessStatus; - constructor() { - super(); - this.on((event) => { - if (event.log !== undefined) { - this.logs.push(event.log); - } - this.status = event.status; - if (event.status.isEqualMany([ProcessStatus.endError, ProcessStatus.endOk])) { - this.result() - } - }) - } - abstract result(): Promise - -} -export class ExecProcessScenarioV2 { - call = (command: string, watcher: ExecProcessWatcher, name?: string): void => { - const process = exec(command, { cwd: new GetRootDirUseCase().call() }); - if (process.pid) { - activeProcessPids[name ?? command] = { pid: process.pid, status: ProcessStatus.run }; - executorProgramServiceV2.emit(activeProcessPids); - } - process.stdout.on('data', (data) => watcher.emit(new ProcessData(data, ProcessStatus.run))) - process.stderr.on('data', (data) => watcher.emit(new ProcessData(data, ProcessStatus.run))); - process.on('close', (code) => { - if (code === 0) { - watcher.emit(new ProcessData(undefined, ProcessStatus.endOk)) - activeProcessPids[name ?? command] = { pid: process.pid ?? 0, status: ProcessStatus.endOk } - executorProgramServiceV2.emit(activeProcessPids); - } else { - watcher.emit(new ProcessData(undefined, ProcessStatus.endError)) - activeProcessPids[name ?? command] = { pid: process.pid ?? 0, status: ProcessStatus.endError }; - executorProgramServiceV2.emit(activeProcessPids); - } - }); - - process.on('error', (err) => watcher.emit(new ProcessData(err.message, ProcessStatus.endError))); - } -} \ No newline at end of file diff --git a/server/src/core/services/executor_program_service.ts b/server/src/core/services/executor_program_service.ts index 3b1a609..2f5dc7b 100644 --- a/server/src/core/services/executor_program_service.ts +++ b/server/src/core/services/executor_program_service.ts @@ -50,6 +50,7 @@ export class ExecutorProgramService }; worker.send(workerDataExec); worker.on("message", (e) => { + console.log(JSON.stringify(e)); const spawnError = SpawnError.isError(e); if (spawnError instanceof SpawnError) { diff --git a/server/src/core/usecases/exec_process_usecase.ts b/server/src/core/usecases/exec_process_usecase.ts index faa140e..a6899dc 100644 --- a/server/src/core/usecases/exec_process_usecase.ts +++ b/server/src/core/usecases/exec_process_usecase.ts @@ -7,7 +7,6 @@ import { ExecutorResult } from "../models/executor_result"; import { ExecutorProgramService } from "../services/executor_program_service"; export const executorProgramService = new ExecutorProgramService(""); - export class KillLastProcessUseCase extends CallbackStrategyWithEmpty { call = async (): Promise> => { executorProgramService.deleteWorker(); diff --git a/server/src/core/usecases/get_root_dir_usecase.ts b/server/src/core/usecases/get_root_dir_usecase.ts deleted file mode 100644 index c134d6f..0000000 --- a/server/src/core/usecases/get_root_dir_usecase.ts +++ /dev/null @@ -1,5 +0,0 @@ -import * as os from 'os'; - -export class GetRootDirUseCase { - call = (): string => os.homedir() -} \ No newline at end of file diff --git a/server/src/features/behavior_trees/behavior_trees_presentation.ts b/server/src/features/behavior_trees/behavior_trees_presentation.ts index cff6a73..1ea112a 100644 --- a/server/src/features/behavior_trees/behavior_trees_presentation.ts +++ b/server/src/features/behavior_trees/behavior_trees_presentation.ts @@ -8,9 +8,6 @@ import { SaveBtScenario as FillBtScenario } from "./domain/save_bt_scenario"; import { GetCameraUseCase } from "./domain/get_cameras_usecase"; import { GetRobotsUseCase } from "./domain/get_robots_usecase"; import { GetTopicsUseCase } from "./domain/get_topics_usecase"; -import { DeleteBehaviorTreeScenario } from "./domain/delete_behavior_tree_scenario"; - - export class BehaviorTreesPresentation extends CrudController { constructor() { @@ -46,11 +43,6 @@ export class BehaviorTreesPresentation extends CrudController ( - await new SearchOneDataBaseModelUseCase(ProjectDBModel).call({ isActive: true }, "no active projects") - ).map(async (model) => (await (new DeleteRecursiveFolderUseCase().call(`${model.rootDir}/${StaticFilesProject.behaviorTrees}`))).map(async () => (await new DeleteDataBaseModelUseCase(BehaviorTreeDBModel).call(id)))); -} \ No newline at end of file diff --git a/server/src/features/behavior_trees/models/behavior_tree_validation_model.ts b/server/src/features/behavior_trees/models/behavior_tree_validation_model.ts index 06f103c..e729b67 100644 --- a/server/src/features/behavior_trees/models/behavior_tree_validation_model.ts +++ b/server/src/features/behavior_trees/models/behavior_tree_validation_model.ts @@ -2,13 +2,12 @@ import { IsMongoId, IsNumber, IsString } from "class-validator"; import { IBehaviorTreeModel } from "./behavior_tree_database_model"; export class BehaviorTreeValidationModel implements IBehaviorTreeModel { - _id: string; @IsString() public name: string; @IsMongoId() - public project: string; - public skills: any; - public xml: string; + public project:string; + public skills:any; + public xml:string; @IsNumber() public unixTime: number } diff --git a/server/src/features/calculations_instance/calculations_instance_presentation.ts b/server/src/features/calculations_instance/calculations_instance_presentation.ts index d89766d..42f8793 100644 --- a/server/src/features/calculations_instance/calculations_instance_presentation.ts +++ b/server/src/features/calculations_instance/calculations_instance_presentation.ts @@ -4,8 +4,6 @@ import { CalculationInstanceValidationModel } from "./models/calculations_instan import { CalculationInstanceDBModel } from "./models/calculations_instance_database_model"; import { CreateCalculationInstanceScenario } from "./domain/create_calculation_instance_scenario"; import { DeleteCalculationsInstanceScenario } from "./domain/delete_calculations_instance_scenario"; -import { GetAllEndCalculationsInstanceActiveProjectScenarios } from "./domain/get_all_end_calculations_instance_active_project_scenarios"; - export class CalculationsInstancesPresentation extends CrudController< CalculationInstanceValidationModel, @@ -20,12 +18,7 @@ export class CalculationsInstancesPresentation extends CrudController< super.delete(new DeleteCalculationsInstanceScenario().call); super.post(new CreateCalculationInstanceScenario().call); - this.subRoutes.push({ - method: "POST", - subUrl: "get/all/end/calculations", - //@ts-expect-error - fn: new GetAllEndCalculationsInstanceActiveProjectScenarios(), - }) + this.subRoutes.push({ method: "GET", subUrl: "exec", diff --git a/server/src/features/calculations_instance/domain/exec_calculations_instance_process_scenario.ts b/server/src/features/calculations_instance/domain/exec_calculations_instance_process_scenario.ts index ec28885..e98afc1 100644 --- a/server/src/features/calculations_instance/domain/exec_calculations_instance_process_scenario.ts +++ b/server/src/features/calculations_instance/domain/exec_calculations_instance_process_scenario.ts @@ -7,21 +7,7 @@ import { ProcessWatcherAndDatabaseUpdateService } from "../../datasets/domain/cr import { CalculationInstanceDBModel, ICalculationInstance } from "../models/calculations_instance_database_model"; import { Result } from "../../../core/helpers/result"; import { CreateFileUseCase } from "../../../core/usecases/create_file_usecase"; -import { ExecProcessScenarioV2, ExecProcessWatcher } from "../../../core/scenarios/exec_process_scenario_v2"; -class ExecProcess extends ExecProcessWatcher { - id: string; - constructor(id: string) { - super() - this.id = id; - } - result = async (): Promise => (await new ReadByIdDataBaseModelUseCase(CalculationInstanceDBModel).call(this.id)).map(async (model) => { - model.lastProcessLogs = this.logs.join('\n') - model.processStatus = this.status; - // @ts-ignore - await model.save() - }); -} export class ExecCalculationInstanceProcessScenario extends CallbackStrategyWithIdQuery { idValidationExpression = new MongoIdValidation(); call = async (id: string): ResponseBase => @@ -31,15 +17,19 @@ export class ExecCalculationInstanceProcessScenario extends CallbackStrategyWith return (await new IsHaveActiveProcessUseCase().call()).map(async () => { const execCommand = `${model.script } --path ${model.instancePath.pathNormalize()} --form ${fileOutPath}`.replace("\n", ""); - + await new CreateFileUseCase().call(fileOutPath, Buffer.from(JSON.stringify(model.formBuilder))); await CalculationInstanceDBModel.findById(id).updateOne({ processStatus: "RUN", lastProcessExecCommand: execCommand, }); - - new ExecProcessScenarioV2().call(execCommand, new ExecProcess(model._id)) - + new ExecProcessUseCase().call( + // @ts-expect-error + `${model.project.rootDir}/`, + execCommand, + id, + new ProcessWatcherAndDatabaseUpdateService(id as unknown as ObjectId, CalculationInstanceDBModel) + ); return Result.ok("OK"); }); } diff --git a/server/src/features/calculations_instance/domain/get_all_end_calculations_instance_active_project_scenarios.ts b/server/src/features/calculations_instance/domain/get_all_end_calculations_instance_active_project_scenarios.ts deleted file mode 100644 index b9eb5f3..0000000 --- a/server/src/features/calculations_instance/domain/get_all_end_calculations_instance_active_project_scenarios.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { IsString } from "class-validator"; -import { CallbackStrategyWithValidationModel, ResponseBase } from "../../../core/controllers/http_controller"; -import { SearchManyDataBaseModelUseCase } from "../../../core/usecases/search_many_database_model_usecase"; -import { GetActiveProjectIdScenario } from "../../projects/domain/get_active_project_id_scenario"; -import { ICalculationInstance, CalculationInstanceDBModel } from "../models/calculations_instance_database_model"; - - -export class CalculationInstanceType { - @IsString() - type: string; -} - -export class GetAllEndCalculationsInstanceActiveProjectScenarios extends CallbackStrategyWithValidationModel { - validationModel: CalculationInstanceType = new CalculationInstanceType(); - call = async (model: CalculationInstanceType): ResponseBase => (await new GetActiveProjectIdScenario().call()).map( - async (activeProjectModel) => await new SearchManyDataBaseModelUseCase(CalculationInstanceDBModel).call({ project: activeProjectModel.id, isEnd: true, type: model.type }), - ); - - -} diff --git a/server/src/features/calculations_instance/domain/log_to_text_proces_usecase.ts b/server/src/features/calculations_instance/domain/log_to_text_proces_usecase.ts deleted file mode 100644 index bed8aa0..0000000 --- a/server/src/features/calculations_instance/domain/log_to_text_proces_usecase.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { CallbackStrategyWithIdQuery, ResponseBase } from "../../../core/controllers/http_controller"; -import { ReadByIdDataBaseModelUseCase } from "../../../core/usecases/read_database_model_usecase"; -import { CoreValidation } from "../../../core/validations/core_validation"; -import { MongoIdValidation } from "../../../core/validations/mongo_id_validation"; -import { ICalculationInstance, CalculationInstanceDBModel } from "../models/calculations_instance_database_model"; - -// export class LogToProcessUseCase extends CallbackStrategyWithIdQuery { -// idValidationExpression: CoreValidation = new MongoIdValidation(); -// call = async (id: string): ResponseBase => (await new ReadByIdDataBaseModelUseCase(CalculationInstanceDBModel).call(id)).map((model) => ); - -// } \ No newline at end of file diff --git a/server/src/features/calculations_instance/models/calculations_instance_database_model.ts b/server/src/features/calculations_instance/models/calculations_instance_database_model.ts index 43a091d..43e13d9 100644 --- a/server/src/features/calculations_instance/models/calculations_instance_database_model.ts +++ b/server/src/features/calculations_instance/models/calculations_instance_database_model.ts @@ -3,7 +3,6 @@ import { FormBuilderValidationModel } from "../../datasets/models/dataset_valida import { IProjectModel, projectSchema } from "../../projects/models/project_model_database_model"; export interface ICalculationInstance { - _id: string; script: string; instancePath: string; formBuilder: FormBuilderValidationModel; @@ -62,7 +61,6 @@ export const CalculationInstanceSchema = new Schema({ instancePath: { type: String, }, - project: { type: Schema.Types.ObjectId, ref: projectSchema, diff --git a/server/src/features/calculations_instance/models/calculations_instance_validation_model.ts b/server/src/features/calculations_instance/models/calculations_instance_validation_model.ts index eb8e6e2..46b993c 100644 --- a/server/src/features/calculations_instance/models/calculations_instance_validation_model.ts +++ b/server/src/features/calculations_instance/models/calculations_instance_validation_model.ts @@ -4,7 +4,6 @@ import { FormBuilderValidationModel } from "../../datasets/models/dataset_valida import { IProjectModel } from "../../projects/models/project_model_database_model"; export class CalculationInstanceValidationModel implements ICalculationInstance { - _id: string; @IsNotEmpty() @IsString() instanceName: string; diff --git a/server/src/features/datasets/models/dataset_validation_model.ts b/server/src/features/datasets/models/dataset_validation_model.ts index abd6e84..5486ace 100644 --- a/server/src/features/datasets/models/dataset_validation_model.ts +++ b/server/src/features/datasets/models/dataset_validation_model.ts @@ -15,7 +15,6 @@ export enum ProcessStatus { END = "END", ERROR = "ERROR", NEW = "NEW", - } export interface IDatasetModel { _id?: string; diff --git a/server/src/features/digital_twins_instance/domain/exec_instance_scenario.ts b/server/src/features/digital_twins_instance/domain/exec_instance_scenario.ts index 5171e07..f4ec799 100644 --- a/server/src/features/digital_twins_instance/domain/exec_instance_scenario.ts +++ b/server/src/features/digital_twins_instance/domain/exec_instance_scenario.ts @@ -21,7 +21,8 @@ export class ExecInstanceScenario extends CallbackStrategyWithIdQuery { await new ReadByIdDataBaseModelUseCase(DigitalTwinsInstanceDatabaseModel).call(id) ).map( (document) => ( - + console.log('DOCUMeNT PATH'), + console.log(document.instancePath.pathNormalize()), new ExecProcessUseCase().call( document.instancePath, `python3 $GET_INTERFACES --path ${document.instancePath.pathNormalize()} --package '${JSON.stringify( diff --git a/server/src/features/projects/domain/get_active_project_id_scenario.ts b/server/src/features/projects/domain/get_active_project_id_scenario.ts index 74267b3..0e06a58 100644 --- a/server/src/features/projects/domain/get_active_project_id_scenario.ts +++ b/server/src/features/projects/domain/get_active_project_id_scenario.ts @@ -4,8 +4,9 @@ import { SearchOneDataBaseModelUseCase } from "../../../core/usecases/search_dat import { IProjectModel, ProjectDBModel } from "../models/project_model_database_model"; export class GetActiveProjectIdScenario extends CallbackStrategyWithEmpty { - call = async (): Promise> => ( - await new SearchOneDataBaseModelUseCase(ProjectDBModel).call({ isActive: true }, "no active projects") - ).map((model) => Result.ok({ id: model._id })); + async call(): Promise> { + return ( + await new SearchOneDataBaseModelUseCase(ProjectDBModel).call({ isActive: true }, "no active projects") + ).map((model) => Result.ok({ id: model._id })); + } } - diff --git a/server/src/features/projects/domain/robossembler_assets_network_mapper_scenario.ts b/server/src/features/projects/domain/robossembler_assets_network_mapper_scenario.ts index 368b00b..8b24b30 100644 --- a/server/src/features/projects/domain/robossembler_assets_network_mapper_scenario.ts +++ b/server/src/features/projects/domain/robossembler_assets_network_mapper_scenario.ts @@ -35,6 +35,7 @@ export class RobossemblerAssetsNetworkMapperScenario extends CallbackStrategyWit "/assets/"; model.map((el) => { const assetLibsAddress = assetAddress + "/libs/objects/" + el.name; + console.log(assetLibsAddress); el.stlUrl = `${assetAddress}${el.part_path}`; el.glUrl = `${assetLibsAddress}.glb`; el.daeUrl = `${assetLibsAddress}.dae`; diff --git a/server/src/features/runtime/domain/exec_bt_builder_usecase.ts b/server/src/features/runtime/domain/exec_bt_builder_usecase.ts index dd243ec..66ea474 100644 --- a/server/src/features/runtime/domain/exec_bt_builder_usecase.ts +++ b/server/src/features/runtime/domain/exec_bt_builder_usecase.ts @@ -1,53 +1,7 @@ -import { - CallbackStrategyWithIdQuery, - CallbackStrategyWithValidationModel, - ResponseBase, -} from "../../../core/controllers/http_controller"; -import { StaticFilesProject } from "../../../core/models/static_files"; -import { - ExecProcessScenarioV2, - ExecProcessWatcher, - activeProcessPids, -} from "../../../core/scenarios/exec_process_scenario_v2"; -import { ReadByIdDataBaseModelScenario } from "../../../core/scenarios/read_by_id_database_model_scenario"; -import { SearchOneDataBaseModelUseCase } from "../../../core/usecases/search_database_model_usecase"; -import { CoreValidation } from "../../../core/validations/core_validation"; -import { MongoIdValidation } from "../../../core/validations/mongo_id_validation"; -import { BehaviorTreeDBModel } from "../../behavior_trees/models/behavior_tree_database_model"; -import { BehaviorTreeValidationModel } from "../../behavior_trees/models/behavior_tree_validation_model"; -import { IProjectModel, ProjectDBModel } from "../../projects/models/project_model_database_model"; -import { GetCommandScenario } from "./get_command_scenario"; +import { CallbackStrategyWithEmpty, ResponseBase } from "../../../core/controllers/http_controller"; -class ProcessWatcher extends ExecProcessWatcher { - async result(): Promise { - console.log(this); +export class ExecBtBuilderUseCase extends CallbackStrategyWithEmpty { + call(): ResponseBase { + throw new Error("Method not implemented."); } } - -export enum Proceed { - EXEC_BT = "EXEC_BT", -} -export class ExecBtScenario extends CallbackStrategyWithIdQuery { - idValidationExpression: CoreValidation = new MongoIdValidation(); - call = async (id: string): ResponseBase => - (await new ReadByIdDataBaseModelScenario(BehaviorTreeDBModel).call(id)).map( - async (behaviorTreeValidationModel) => - ( - await new SearchOneDataBaseModelUseCase(ProjectDBModel).call( - { isActive: true }, - "no active projects" - ) - ).map(async (model) => - (await new GetCommandScenario().call("btRuntimeProcess")).map((execProcessData) => - new ExecProcessScenarioV2().call( - execProcessData.execCommand.replace( - "${bt_path}", - `${model.rootDir}/${StaticFilesProject.behaviorTrees}/${behaviorTreeValidationModel.name}/bt.xml` - ), - new ProcessWatcher(), - Proceed.EXEC_BT - ) - ) - ) - ); -} diff --git a/server/src/features/runtime/domain/get_run_time_statuses_usecase.ts b/server/src/features/runtime/domain/get_run_time_statuses_usecase.ts deleted file mode 100644 index 2967c31..0000000 --- a/server/src/features/runtime/domain/get_run_time_statuses_usecase.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { CallbackStrategyWithEmpty, ResponseBase } from "../../../core/controllers/http_controller"; -import { Result } from "../../../core/helpers/result"; -import { activeProcessPids } from "../../../core/scenarios/exec_process_scenario_v2"; - -export class GetRunTimeStatuses extends CallbackStrategyWithEmpty { - call = async (): ResponseBase => Result.ok(activeProcessPids); -} \ No newline at end of file diff --git a/server/src/features/runtime/domain/get_simulation_state_usecase.ts b/server/src/features/runtime/domain/get_simulation_state_usecase.ts new file mode 100644 index 0000000..4658739 --- /dev/null +++ b/server/src/features/runtime/domain/get_simulation_state_usecase.ts @@ -0,0 +1,10 @@ +import { CallbackStrategyWithEmpty, ResponseBase } from "../../../core/controllers/http_controller"; +import { Result } from "../../../core/helpers/result"; +import { SpawnProcessUseCase } from "../../../core/usecases/exec_process_usecase"; +import { GetCommandScenario } from "./get_command_scenario"; +import { ProcessWatcher } from "../service/process_watcher"; +import { App } from "../../../core/controllers/app"; + +export class GetSimulationStateScenario extends CallbackStrategyWithEmpty { + call = async (): ResponseBase => Result.ok(""); +} diff --git a/server/src/features/runtime/domain/stop_process_usecase.ts b/server/src/features/runtime/domain/stop_process_usecase.ts deleted file mode 100644 index 4e52d90..0000000 --- a/server/src/features/runtime/domain/stop_process_usecase.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { IsString } from "class-validator"; -import { CallbackStrategyWithValidationModel, ResponseBase } from "../../../core/controllers/http_controller"; -import { ProcessStatus, activeProcessPids } from "../../../core/scenarios/exec_process_scenario_v2"; -import { Result } from "../../../core/helpers/result"; -import { GetRootDirUseCase } from "../../../core/usecases/get_root_dir_usecase"; -import { exec } from 'child_process'; - - -export class StopProcessModel { - @IsString() - pid: string -} - -export class StopProcessUseCase extends CallbackStrategyWithValidationModel { - validationModel: StopProcessModel = new StopProcessModel(); - call = async (model: StopProcessModel): ResponseBase => { - try { - if (activeProcessPids[model.pid] === undefined) { - return Result.error('missing pid'); - } - const processKillStatus = await new Promise((resolve, reject) => { - try { - exec(`kill ${activeProcessPids[model.pid].pid}`, { cwd: new GetRootDirUseCase().call() }, (error, stdout, stderr) => { - if (error) { - reject(`Ошибка: ${stderr}`); return; - } - if (stderr) { - reject(`Ошибка: ${stderr}`); - return; - } - - resolve('Process kill') - - }); - } catch (e) { - resolve('Process kill') - } - - }) - if (processKillStatus == 'Process kill') { - activeProcessPids[model.pid].status = ProcessStatus.userDelete; - } - return Result.ok(processKillStatus); - } catch (error) { - return Result.ok(error) - } - - - } - -} \ No newline at end of file diff --git a/server/src/features/runtime/model/command.ts b/server/src/features/runtime/model/command.ts index 3334f20..863d14e 100644 --- a/server/src/features/runtime/model/command.ts +++ b/server/src/features/runtime/model/command.ts @@ -11,4 +11,4 @@ export interface ExecProcess { delay: number; checkCommand: null; filter: null; -} \ No newline at end of file +} diff --git a/server/src/features/runtime/runtime_presentation.ts b/server/src/features/runtime/runtime_presentation.ts index 9804b39..335deee 100644 --- a/server/src/features/runtime/runtime_presentation.ts +++ b/server/src/features/runtime/runtime_presentation.ts @@ -1,19 +1,42 @@ - +import { CrudController } from "../../core/controllers/crud_controller"; import { ExecRunTimeCommandValidationModel } from "./model/run_time_validation_model"; -import { CoreHttpController, SubRouter, CallbackStrategyWithIdQuery } from "../../core/controllers/http_controller"; -import { ExecBtScenario } from "./domain/exec_bt_builder_usecase"; -import { GetRunTimeStatuses } from "./domain/get_run_time_statuses_usecase"; -import { StopProcessUseCase } from "./domain/stop_process_usecase"; +import { ExecRuntimeDatabaseModel } from "./model/run_time_database_model"; +import { CoreHttpController, SubRouter, HttpMethodType, CallbackStrategyWithIdQuery, ResponseBase } from "../../core/controllers/http_controller"; +import { ExecBtBuilderUseCase } from "./domain/exec_bt_builder_usecase"; +import { ExecSimulationUseCase } from "./domain/exec_simulation_usecase"; +import { GetBtBuilderStateUseCase } from "./domain/get_bt_builder_status_usecase"; +import { GetSimulationStateScenario } from "./domain/get_simulation_state_usecase"; +import { MongoIdValidation } from "../../core/validations/mongo_id_validation"; +import { CoreValidation } from "../../core/validations/core_validation"; +import { ReadByIdDataBaseModelUseCase } from "../../core/usecases/read_by_id_database_model_usecase"; +import { ICalculationInstance, CalculationInstanceDBModel } from "../calculations_instance/models/calculations_instance_database_model"; +import { Result } from "../../core/helpers/result"; +import { SpawnProcessUseCase } from "../../core/usecases/exec_process_usecase"; +import { ProcessWatcher } from "./service/process_watcher"; +class ExecAnalyzeScenario extends CallbackStrategyWithIdQuery { -export class RunTimePresentation extends CoreHttpController { + idValidationExpression: CoreValidation = new MongoIdValidation() + call = async (id: string) => + (await new ReadByIdDataBaseModelUseCase(CalculationInstanceDBModel).call(id)).map(async (model) => + (await new SpawnProcessUseCase().call('/Users/idontsudo/webservice', `nix run github:nixos/nixpkgs#python312Packages.tensorboard -- --logdir ${model.instancePath}`, "", new ProcessWatcher())).map(() => Result.ok('ok'),), + ) +} + +export class RunTimePresentation extends CrudController { constructor() { super({ url: "run_time", }); - this.subRoutes.push(new SubRouter("POST", "exec/bt", new ExecBtScenario())); - this.subRoutes.push(new SubRouter("GET", "status", new GetRunTimeStatuses())); - this.subRoutes.push(new SubRouter("POST", "kill", new StopProcessUseCase())) + this.subRoutes.push(new SubRouter("POST", "/exec/bt/builder", new ExecBtBuilderUseCase())); + this.subRoutes.push(new SubRouter("POST", "/get/bt/builder/state", new GetBtBuilderStateUseCase())); + this.subRoutes.push(new SubRouter("POST", "/get/simulator/state", new GetSimulationStateScenario())); + this.subRoutes.push(new SubRouter('POST', "exec/analyze", new ExecAnalyzeScenario())) + this.subRoutes.push({ + method: "POST", + subUrl: "/exec/simulation/", + fn: new ExecSimulationUseCase(), + }); } } diff --git a/server/src/main.ts b/server/src/main.ts index d88e058..31d168e 100644 --- a/server/src/main.ts +++ b/server/src/main.ts @@ -3,11 +3,11 @@ import { App } from "./core/controllers/app"; import { SocketSubscriber } from "./core/controllers/socket_controller"; import { extensions } from "./core/extensions/extensions"; import { httpRoutes } from "./core/controllers/routes"; -import { executorProgramService } from "./core/usecases/exec_process_usecase"; -import { executorProgramServiceV2 } from "./core/scenarios/exec_process_scenario_v2"; +import { SpawnProcessUseCase, executorProgramService } from "./core/usecases/exec_process_usecase"; +import { ProcessWatcher } from "./features/runtime/service/process_watcher"; extensions(); -const socketSubscribers = [new SocketSubscriber(executorProgramService, "realtime"), new SocketSubscriber(executorProgramServiceV2, 'realtimeV2',)]; +const socketSubscribers = [new SocketSubscriber(executorProgramService, "realtime")]; new App(httpRoutes, socketSubscribers).listen(); diff --git a/ui/src/core/extensions/array.ts b/ui/src/core/extensions/array.ts index 2c4cf2a..33ec3d8 100644 --- a/ui/src/core/extensions/array.ts +++ b/ui/src/core/extensions/array.ts @@ -1,14 +1,7 @@ -/* eslint-disable no-extend-native */ import { Result } from "../helper/result"; /* eslint-disable @typescript-eslint/no-this-alias */ export const ArrayExtensions = () => { - if (Array.prototype.plus === undefined) { - Array.prototype.plus = function (array) { - array.forEach((el) => this.push(el)) - return this; - } - } if ([].indexOfR === undefined) { Array.prototype.indexOfR = function (element) { if (this.indexOf(element) === -1) { @@ -17,11 +10,6 @@ export const ArrayExtensions = () => { return Result.ok(this); }; } - if ([].whereOne === undefined) { - Array.prototype.whereOne = function (predicate) { - return this.filter(predicate).atR(0); - } - } if ([].atR === undefined) { Array.prototype.atR = function (index) { if (index === undefined) { diff --git a/ui/src/core/extensions/extensions.ts b/ui/src/core/extensions/extensions.ts index 2eb2ff7..5afcbed 100644 --- a/ui/src/core/extensions/extensions.ts +++ b/ui/src/core/extensions/extensions.ts @@ -30,8 +30,6 @@ declare global { someR(predicate: (value: T) => boolean): Result>; updateAll(value: Partial): Array; atR(index: number | undefined): Result; - whereOne(predicate: (value: T) => boolean): Result; - plus(array: any[]): Array } interface Date { formatDate(): string; diff --git a/ui/src/core/model/form_builder_validation_model.tsx b/ui/src/core/model/form_builder_validation_model.tsx index 4f1837b..f858f40 100644 --- a/ui/src/core/model/form_builder_validation_model.tsx +++ b/ui/src/core/model/form_builder_validation_model.tsx @@ -1,15 +1,23 @@ import { IsNotEmpty, IsString } from "class-validator"; import { BehaviorTreeBuilderStore } from "../../features/behavior_tree_builder/presentation/behavior_tree_builder_store"; -import { datasetFormMockContext, datasetFormMockResult, defaultFormValue } from "../../features/dataset/dataset_model"; +import { + datasetFormMockContext, + datasetFormMockResult, + defaultFormValue, +} from "../../features/dataset/dataset_model"; import { DependencyViewModel } from "./skill_model"; import { ValidationModel } from "./validation_model"; import { FormType } from "./form"; import makeAutoObservable from "mobx-store-inheritance"; -export class FormBuilderValidationModel extends ValidationModel implements DependencyViewModel { +export class FormBuilderValidationModel + extends ValidationModel + implements DependencyViewModel +{ @IsNotEmpty() @IsString() public result: string; + @IsNotEmpty() @IsString() public context: string; public form: string[]; @@ -28,26 +36,22 @@ export class FormBuilderValidationModel extends ValidationModel implements Depen formBuilderValidationModel.context.isEmpty() && formBuilderValidationModel.result.isEmpty() && formBuilderValidationModel.form.isEmpty(); - static test = () => new FormBuilderValidationModel(ffContext, ff1Result, [], ""); + static test = () => + new FormBuilderValidationModel(ffContext, ff1Result, [], ""); static datasetEmpty = () => - new FormBuilderValidationModel(datasetFormMockContext, datasetFormMockResult, [], defaultFormValue); - static empty = () => new FormBuilderValidationModel("", "", [], ""); - static emptyTest = () => new FormBuilderValidationModel(``, ``, [], defaultFormValue); - static creteDataSetTest = () => new FormBuilderValidationModel(``, scene, [], ""); - static emptySimple = () => new FormBuilderValidationModel("", simpleFormBuilder, [], ""); - static eee = () => new FormBuilderValidationModel( - ``, - `{ - "robot_name": \${ROBOT_NAME:string:rbs_arm}, - "pose": { - "position": { "x": \${X:number:0.1}, "y": \${Y:number:0.1}, "z": \${Z:number:0.7} }, - "orientation": { "x": \${X:number:0.1}, "y": \${Y:number:0.1}, "z": \${Z:number:0.7} } - } - }`, + datasetFormMockContext, + datasetFormMockResult, [], - "" + defaultFormValue ); + static empty = () => new FormBuilderValidationModel("", "", [], ""); + static emptyTest = () => + new FormBuilderValidationModel(``, ``, [], defaultFormValue); + static creteDataSetTest = () => + new FormBuilderValidationModel(``, scene, [], ""); + static emptySimple = () => + new FormBuilderValidationModel("", simpleFormBuilder, [], ""); static vision = () => new FormBuilderValidationModel( ` @@ -92,7 +96,6 @@ export class FormBuilderValidationModel extends ValidationModel implements Depen };`, ` { - "process":\${:OBJECT:{"type": "OBJECT_DETECTION"}, "datasetObjects":\${:OBJECT:{"details": []}, "typedataset": \${typedataset:Enum:ObjectDetection}, "models_randomization":{ @@ -139,6 +142,3 @@ export const ff1Result = `{ "empty":\${NAME:string:default}, "params": \${ITEM:Array:[]} }`; -// { -// "process":\${:OBJECT:{"type": "OBJECT_DETECTION"} -// } \ No newline at end of file diff --git a/ui/src/core/model/skill_model.ts b/ui/src/core/model/skill_model.ts index cbc311d..d9e3a40 100644 --- a/ui/src/core/model/skill_model.ts +++ b/ui/src/core/model/skill_model.ts @@ -36,7 +36,7 @@ export interface IWeightsDependency { export interface IDeviceDependency { sid: string; } -export interface IDependency { } +export interface IDependency {} export interface IParam { isFilled: boolean; type: string; @@ -276,7 +276,7 @@ export class SkillModel extends ValidationModel implements ISkill { } export class SkillDependency implements IDependency { - constructor(public skills: ISkillDependency[]) { } + constructor(public skills: ISkillDependency[]) {} static empty() { return new SkillDependency([]); } @@ -424,27 +424,24 @@ export class Skills { .flat(1) .filter((el) => el !== ""); - getDependencyBySkillLabelAndType = (skillType: string, sid: string): DependencyViewModel => this.skills - .reduce((acc, skill) => { - if (skill.sid?.isEqual(sid)) { - skill.BTAction.map((action) => { - action.param.map((param) => { - - if (param.type.isEqual(skillType)) { - - - acc.push(param?.dependency ?? DependencyViewModel.empty()); - } - - return param; + getDependencyBySkillLabelAndType = (skillType: string, sid: string): DependencyViewModel => + this.skills + .reduce((acc, skill) => { + if (skill.sid?.isEqual(sid)) { + skill.BTAction.map((action) => { + action.param.map((param) => { + if (param.type.isEqualR(skillType)) { + acc.push(param?.dependency ?? DependencyViewModel.empty()); + } + return param; + }); + return action; }); - return action; - }); - } + } - return acc; - }, []) - .at(0) ?? DependencyViewModel.empty() + return acc; + }, []) + .at(0) ?? DependencyViewModel.empty(); static isEmpty(model: Skills): Result { if (model.skills.isEmpty()) { return Result.error(undefined); diff --git a/ui/src/core/model/validation_model.ts b/ui/src/core/model/validation_model.ts index f28d353..7e41b5e 100644 --- a/ui/src/core/model/validation_model.ts +++ b/ui/src/core/model/validation_model.ts @@ -1,4 +1,3 @@ -import { message } from "antd"; import { Result } from "../helper/result"; import { validate, ValidationError } from "class-validator"; @@ -27,12 +26,4 @@ export class ValidationModel { return Result.ok(this as unknown as T); } }; - validMessage = async(): Promise> => { - const result = await this.valid(); - - if (result.isFailure()) { - message.error(result.error); - } - return result; - } } diff --git a/ui/src/core/repository/core_http_repository.ts b/ui/src/core/repository/core_http_repository.ts index 37ed2bb..48eb520 100644 --- a/ui/src/core/repository/core_http_repository.ts +++ b/ui/src/core/repository/core_http_repository.ts @@ -132,7 +132,7 @@ export class CoreHttpRepository extends HttpRepository { getAssetsActiveProject = async (): Promise> => { return this._jsonRequest(HttpMethod.GET, "/projects/assets"); }; - + getSceneAsset = (id: string) => this._jsonToClassInstanceRequest(HttpMethod.GET, `/scenes/by_id?id=${id}`, SceneAsset) as Promise< Result @@ -141,6 +141,5 @@ export class CoreHttpRepository extends HttpRepository { return this._jsonRequest(HttpMethod.GET, "/projects/get/active/project/id"); } getAllScenes = () => this._jsonRequest(HttpMethod.GET, "/scenes"); - getAllTopics = () => this._jsonRequest(HttpMethod.GET, `/topics`); - } + \ No newline at end of file diff --git a/ui/src/core/repository/core_socket_repository.ts b/ui/src/core/repository/core_socket_repository.ts index acd0e32..db8d3eb 100644 --- a/ui/src/core/repository/core_socket_repository.ts +++ b/ui/src/core/repository/core_socket_repository.ts @@ -1,27 +1,23 @@ import { Socket, io } from "socket.io-client"; import { Result } from "../helper/result"; import { TypedEvent } from "../helper/typed_event"; -import { RuntimeModel } from "../../features/behavior_tree_builder/presentation/ui/actions/runtime_actions"; export class SocketRepository extends TypedEvent { serverURL = "ws://localhost:4001"; socket: Socket | undefined; - - async connect(): Promise> { + + async connect():Promise> { const socket = io(this.serverURL); - + this.socket = socket; socket.connect(); - socketCoreInstances.forEach((el) => { - socket.on(el.event, (event) => el.emit(event)) - }) - socket.on('realtime', (d) => { + socket.on('realtime', (d) =>{ this.emit({ - event: "realtime", - payload: d + event:"realtime", + payload:d }) }) - if (socket.connected) { + if(socket.connected){ return Result.ok(true) } return Result.error(false) @@ -29,15 +25,4 @@ export class SocketRepository extends TypedEvent { } -export const socketRepository = new SocketRepository() - - -export abstract class SocketCore extends TypedEvent { - abstract event: string - -} -export class RunTimeSocketRepository extends SocketCore { - event = 'realtimeV2'; -} -export const runTimeSocketRepository = new RunTimeSocketRepository(); -const socketCoreInstances: SocketCore[] = [runTimeSocketRepository] \ No newline at end of file +export const socketRepository = new SocketRepository() \ No newline at end of file diff --git a/ui/src/core/store/base_store.ts b/ui/src/core/store/base_store.ts index d9eafaf..b9562f6 100644 --- a/ui/src/core/store/base_store.ts +++ b/ui/src/core/store/base_store.ts @@ -17,7 +17,6 @@ interface IMessage { errorMessage?: string; } export abstract class UiLoader { - navigate?: NavigateFunction; isLoading = false; async httpHelper(callBack: Promise>) { this.isLoading = true; @@ -76,7 +75,7 @@ export abstract class UiErrorState extends UiLoader { console.log(error); }; abstract init(navigate?: NavigateFunction): Promise; - dispose() { } + dispose() {} errors: UiBaseError[] = []; } @@ -127,17 +126,7 @@ export abstract class FormState extends UiErrorState { loadClassInstance = (instance: ClassConstructor, viewModel: V) => { this.viewModel = plainToInstance(instance, viewModel); }; - isModalOpen: boolean = false; - modalShow = () => { - this.isModalOpen = true; - }; - - modalClickOk = () => { - this.isModalOpen = false; - }; - - modalCancel = () => { - this.isModalOpen = false; - }; } + + \ No newline at end of file diff --git a/ui/src/core/ui/drawer/drawer.tsx b/ui/src/core/ui/drawer/drawer.tsx index 14f9f5d..d825d59 100644 --- a/ui/src/core/ui/drawer/drawer.tsx +++ b/ui/src/core/ui/drawer/drawer.tsx @@ -7,20 +7,20 @@ export const DrawerV2: React.FC<{ title?: string; onClose: () => void; children: React.ReactNode; - width?: number; -}> = ({ isOpen, onClose, children, title, width }) => { +}> = ({ isOpen, onClose, children, title }) => { return (
diff --git a/ui/src/core/ui/form_builder/form_builder.tsx b/ui/src/core/ui/form_builder/form_builder.tsx index 22abe72..7376f86 100644 --- a/ui/src/core/ui/form_builder/form_builder.tsx +++ b/ui/src/core/ui/form_builder/form_builder.tsx @@ -1,5 +1,9 @@ import * as React from "react"; -import { FormViewModel, InputBuilderViewModel, InputType } from "./form_view_model"; +import { + FormViewModel, + InputBuilderViewModel, + InputType, +} from "./form_view_model"; import { observer } from "mobx-react-lite"; import { FormBuilderStore } from "./form_builder_store"; import { CoreSelect } from "../select/select"; @@ -9,162 +13,219 @@ import { CoreText, CoreTextType } from "../text/text"; import { getFormBuilderComponents } from "./forms/form_builder_components"; import { FormBuilderValidationModel } from "../../model/form_builder_validation_model"; -export const FormBuilder = observer( - (props: { formBuilder: FormBuilderValidationModel; onChange: (change: FormBuilderValidationModel) => void }) => { - const [store] = React.useState(() => new FormBuilderStore()); +export interface IFormBuilder { + formBuilder: FormBuilderValidationModel; + onChange: (change: FormBuilderValidationModel) => void; +} - React.useEffect(() => { - store.init(props.formBuilder.context, props.formBuilder.result); - if (props.formBuilder.form.isNotEmpty()) { - store.formViewModel = new FormViewModel( - props.formBuilder.form.map((el) => InputBuilderViewModel.fromJSON(el)), - props.formBuilder.result, - props.formBuilder.context - ); - props.formBuilder.form.map((el) => InputBuilderViewModel.fromJSON(el)); - } - store.changerForm.on((event) => { - if (event) props.onChange(event); - }); - }, []); +export const FormBuilder = observer((props: IFormBuilder) => { + const [store] = React.useState(() => new FormBuilderStore()); - return ( -
- {store.isError ? ( - <>Error - ) : ( -
- {store.formViewModel?.inputs?.map((element, index) => { - if (element.type?.isEqual(InputType.ENUM)) { - const values = element.values as string[]; - return ( - store.changeTotalValue(element.id, value)} - label={element.name} - style={{ margin: 20 }} - /> - ); - } - if (element.type?.isEqual(InputType.ARRAY)) { - return ( -
-
{ - store.open(element.id); - }} - > - - -
+ React.useEffect(() => { + store.init(props.formBuilder.context, props.formBuilder.result); + if (props.formBuilder.form.isNotEmpty()) { + store.formViewModel = new FormViewModel( + props.formBuilder.form.map((el) => InputBuilderViewModel.fromJSON(el)), + props.formBuilder.result, + props.formBuilder.context + ); + props.formBuilder.form.map((el) => InputBuilderViewModel.fromJSON(el)); + } + store.changerForm.on((event) => { + if (event) props.onChange(event); + }); + }, []); - {element.isOpen ? ( -
- {element.totalValue instanceof Array - ? element.totalValue?.map((subArray, index) => { - return ( -
-
- - store.deleteTotalValueSubItem(element.id, index)} - /> -
+ return ( +
+ {store.isError ? ( + <>Error + ) : ( +
+ {store.formViewModel?.inputs?.map((element, index) => { + if (element.type?.isEqual(InputType.ENUM)) { + const values = element.values as string[]; + return ( + + store.changeTotalValue(element.id, value) + } + label={element.name} + style={{ margin: 20 }} + /> + ); + } + if (element.type?.isEqual(InputType.ARRAY)) { + return ( +
+
{ + store.open(element.id); + }} + > + + +
- {subArray.map((subSubArrayItem: InputBuilderViewModel, subIndex: number) => { - if (subSubArrayItem.type.isEqual(InputType.ENUM)) { + {element.isOpen ? ( +
+ {element.totalValue instanceof Array + ? element.totalValue?.map((subArray, index) => { + return ( +
+
+ + + store.deleteTotalValueSubItem( + element.id, + index + ) + } + /> +
+ + {subArray.map( + ( + subSubArrayItem: InputBuilderViewModel, + subIndex: number + ) => { + if ( + subSubArrayItem.type.isEqual( + InputType.ENUM + ) + ) { return ( <> String(el)) ?? []} - value={subSubArrayItem.totalValue ?? subSubArrayItem.defaultValue} - onChange={(value) => console.log(subSubArrayItem.id)} + items={ + subSubArrayItem.values?.map( + (el) => String(el) + ) ?? [] + } + value={ + subSubArrayItem.totalValue ?? + subSubArrayItem.defaultValue + } + onChange={(value) => console.log(subSubArrayItem.id) + } label={element.name} style={{ margin: 5 }} /> ); } - if (subSubArrayItem.type.isEqualMany([InputType.NUMBER, InputType.STRING])) + if ( + subSubArrayItem.type.isEqualMany([ + InputType.NUMBER, + InputType.STRING, + ]) + ) return (
store.changeTotalSubValue(element.id, subIndex, e, index)} + onChange={(e) => + store.changeTotalSubValue( + element.id, + subIndex, + e, + index + ) + } validation={ - subSubArrayItem.type.isEqual(InputType.NUMBER) + subSubArrayItem.type.isEqual( + InputType.NUMBER + ) ? (el) => Number().isValid(el) : undefined } error="только числа" - value={subSubArrayItem.totalValue ?? subSubArrayItem.defaultValue} + value={ + subSubArrayItem.totalValue ?? + subSubArrayItem.defaultValue + } label={subSubArrayItem.name} />
); return <>Error; - })} -
- ); - }) - : null} -
- ) : null} -
- ); - } + } + )} +
+ ); + }) + : null} +
+ ) : null} +
+ ); + } - if (element.type?.isEqualMany([InputType.NUMBER, InputType.STRING])) - return ( -
- Number().isValid(el) : undefined} - onChange={(e) => { - store.changeTotalValue(element.id, e); - }} - value={element.totalValue ?? element.defaultValue} - error="только числа" - label={element.name} - style={{ margin: 20 }} - /> -
- ); - if (element.type?.isEqual(InputType.OBJECT)) - return ( - <> - {getFormBuilderComponents( - element.name.replace(">", "").replace("<", "").replace("/", ""), - element.totalValue ?? element.defaultValue, - (text) => store.changeTotalValue(element.id, text) - ).fold( - (s) => ( - <>{s} - ), - (error) => ( - <>{error} - ) - )} - - ); + if (element.type?.isEqualMany([InputType.NUMBER, InputType.STRING])) + return ( +
+ Number().isValid(el) + : undefined + } + onChange={(e) => { + store.changeTotalValue(element.id, e); + }} + value={element.totalValue ?? element.defaultValue} + error="только числа" + label={element.name} + style={{ margin: 20 }} + /> +
+ ); + if (element.type?.isEqual(InputType.OBJECT)) + return ( + <> + {getFormBuilderComponents( + element.name + .replace(">", "") + .replace("<", "") + .replace("/", ""), + element.totalValue ?? element.defaultValue, + (text) => store.changeTotalValue(element.id, text) + ).fold( + (s) => ( + <>{s} + ), + (error) => ( + <>{error} + ) + )} + + ); return
Error {String(element)}
; - })} -
- )} -
- ); - } -); + })} +
+ )} +
+ ); +}); diff --git a/ui/src/core/ui/form_builder/form_view_model.ts b/ui/src/core/ui/form_builder/form_view_model.ts index a542e55..ec5e61b 100644 --- a/ui/src/core/ui/form_builder/form_view_model.ts +++ b/ui/src/core/ui/form_builder/form_view_model.ts @@ -95,7 +95,7 @@ export class FormViewModel { .replace(/[^\x00-\x7F]/g, "") .replaceAll("\n", "") .replaceAll("\\", "") - // .replaceAll("/", "") + // .replaceAll("/", "") ); } catch (error) { console.log("ERROR: FormViewModel json() " + result); @@ -190,9 +190,8 @@ export class FormViewModel { }); return result as unknown as string; } - static fromString(result: string = '', context: string = ''): Result { + static fromString(result: string, context: string): Result { try { - if (result.isEmpty() && context.isEmpty()) { return Result.error(undefined); } diff --git a/ui/src/core/ui/form_builder/forms/form_builder_components.tsx b/ui/src/core/ui/form_builder/forms/form_builder_components.tsx index 1e40957..ec30844 100644 --- a/ui/src/core/ui/form_builder/forms/form_builder_components.tsx +++ b/ui/src/core/ui/form_builder/forms/form_builder_components.tsx @@ -1,10 +1,10 @@ import { Result } from "../../../helper/result"; -import { SelectProcess } from "./select_dataset/presentation/select_process"; +import { SelectDatasetScreen } from "./select_dataset/presentation/select_dataset_screen"; import { SelectDetail } from "./select_detail/presentation/select_detail_screen"; export enum FormBuilderComponents { SelectDetail = "SelectDetail", - SelectProcess = "SelectProcess", + SelectDataset = "SelectDataset", } export interface IFormBuilderComponentsProps { dependency: T; @@ -18,8 +18,8 @@ export const getFormBuilderComponents = ( if (name.isEqual(FormBuilderComponents.SelectDetail)) { return Result.ok(); } - if (name.isEqual(FormBuilderComponents.SelectProcess)) { - return Result.ok(); + if (name.isEqual(FormBuilderComponents.SelectDataset)) { + return Result.ok(); } return Result.error(name); }; diff --git a/ui/src/core/ui/form_builder/forms/select_dataset/data/select_dataset_repository.tsx b/ui/src/core/ui/form_builder/forms/select_dataset/data/select_dataset_repository.tsx new file mode 100644 index 0000000..e69de29 diff --git a/ui/src/core/ui/form_builder/forms/select_dataset/data/select_process_repository.ts b/ui/src/core/ui/form_builder/forms/select_dataset/data/select_process_repository.ts deleted file mode 100644 index b511865..0000000 --- a/ui/src/core/ui/form_builder/forms/select_dataset/data/select_process_repository.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { CoreHttpRepository, HttpMethod } from "../../../../../repository/core_http_repository"; - -export class SelectProcessRepository extends CoreHttpRepository { - getAllProcessByType = (type: string) => this._jsonRequest(HttpMethod.POST, '/calculations/instances/get/all/end/calculations', { type: type }) -} \ No newline at end of file diff --git a/ui/src/core/ui/form_builder/forms/select_dataset/model/select_dataset_model.ts b/ui/src/core/ui/form_builder/forms/select_dataset/model/select_dataset_model.ts new file mode 100644 index 0000000..e69de29 diff --git a/ui/src/core/ui/form_builder/forms/select_dataset/model/select_process_model.ts b/ui/src/core/ui/form_builder/forms/select_dataset/model/select_process_model.ts deleted file mode 100644 index f6e8ca1..0000000 --- a/ui/src/core/ui/form_builder/forms/select_dataset/model/select_process_model.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { IsObject, IsString } from "class-validator"; -import { ValidationModel } from "../../../../../model/validation_model"; -import { CalculationModel } from "../../../../../../features/calculation_instance/model/calculation_model"; - - -export class SelectProcessModel extends ValidationModel { - @IsString() - type: string = ''; - @IsObject() - selectProcess?: SelectProcess; -} - - - -export interface SelectProcess { - value: CalculationModel; -} - \ No newline at end of file diff --git a/ui/src/core/ui/form_builder/forms/select_dataset/presentation/select_dataset_screen.tsx b/ui/src/core/ui/form_builder/forms/select_dataset/presentation/select_dataset_screen.tsx new file mode 100644 index 0000000..31d57a6 --- /dev/null +++ b/ui/src/core/ui/form_builder/forms/select_dataset/presentation/select_dataset_screen.tsx @@ -0,0 +1,6 @@ +import { observer } from "mobx-react-lite"; +import { IFormBuilderComponentsProps } from "../../form_builder_components"; + +export const SelectDatasetScreen = observer((props:IFormBuilderComponentsProps) => { + return <>SELECT DATASET; +}); diff --git a/ui/src/core/ui/form_builder/forms/select_dataset/presentation/select_dataset_store.ts b/ui/src/core/ui/form_builder/forms/select_dataset/presentation/select_dataset_store.ts new file mode 100644 index 0000000..e69de29 diff --git a/ui/src/core/ui/form_builder/forms/select_dataset/presentation/select_process.tsx b/ui/src/core/ui/form_builder/forms/select_dataset/presentation/select_process.tsx deleted file mode 100644 index 68a80ca..0000000 --- a/ui/src/core/ui/form_builder/forms/select_dataset/presentation/select_process.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import React, { useState } from "react"; -import { observer } from "mobx-react-lite"; -import { IFormBuilderComponentsProps } from "../../form_builder_components"; -import { useStore } from "../../../../../helper/use_store"; -import { SelectProcessStore } from "./select_process_store"; -import { useEffect } from "react"; -import { SelectProcessModel } from "../model/select_process_model"; -import { Loader } from "../../../../loader/loader"; -import { CoreSelect } from "../../../../select/select"; -import { message } from "antd"; -import { CalculationModel } from "../../../../../../features/calculation_instance/model/calculation_model"; - -export const SelectProcess = observer((props: IFormBuilderComponentsProps) => { - const [store] = useState(new SelectProcessStore()); - useEffect(() => { - if (typeof props.dependency === "string") { - store.loadClassInstance(SelectProcessModel, JSON.parse(props.dependency)); - } else { - store.loadClassInstance(SelectProcessModel, props.dependency); - } - - store.init(); - }, []); - return ( -
- {store.isLoading ? ( - - ) : ( -
- el.instanceName)} - value={store.viewModel?.selectProcess?.value.instanceName ?? ""} - label={`Процесс тип ${store?.viewModel?.type ?? ""}`} - onChange={async (value: string, index: number) => { - store.updateForm({ - selectProcess: { value: store.calculationInstances.at(index) ?? CalculationModel.empty() }, - }); - (await store.viewModel.valid()).fold( - (model) => props.onChange(model), - (error) => message.error(error) - ); - }} - /> -
- )} -
- ); -}); diff --git a/ui/src/core/ui/form_builder/forms/select_dataset/presentation/select_process_store.ts b/ui/src/core/ui/form_builder/forms/select_dataset/presentation/select_process_store.ts deleted file mode 100644 index fefca2e..0000000 --- a/ui/src/core/ui/form_builder/forms/select_dataset/presentation/select_process_store.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { NavigateFunction } from "react-router-dom"; -import { FormState } from "../../../../../store/base_store"; -import { SelectProcessModel } from "../model/select_process_model"; -import { SelectProcessRepository } from "../data/select_process_repository"; -import { CalculationModel } from "../../../../../../features/calculation_instance/model/calculation_model"; -import makeAutoObservable from "mobx-store-inheritance"; - -export class SelectProcessStore extends FormState { - selectProcessRepository = new SelectProcessRepository(); - viewModel: SelectProcessModel; - calculationInstances: CalculationModel[] = []; - constructor() { - super(); - makeAutoObservable(this); - } - async init(navigate?: NavigateFunction | undefined): Promise { - await this.mapOk('calculationInstances', this.selectProcessRepository.getAllProcessByType(this.viewModel.type)); - - this.calculationInstances = this.calculationInstances.map((el) => { - // @ts-ignore - delete el['formBuilder']; - return el; - }) - - } -} \ No newline at end of file diff --git a/ui/src/core/ui/form_builder/forms/select_detail/presentation/select_detail_screen.tsx b/ui/src/core/ui/form_builder/forms/select_detail/presentation/select_detail_screen.tsx index 145c670..5e0aa28 100644 --- a/ui/src/core/ui/form_builder/forms/select_detail/presentation/select_detail_screen.tsx +++ b/ui/src/core/ui/form_builder/forms/select_detail/presentation/select_detail_screen.tsx @@ -1,18 +1,21 @@ +// @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) => { const [store] = React.useState(() => new SelectDetailStore()); React.useEffect(() => { + console.log(props.dependency.details); store.viewModel = new SelectDetailViewModel(props.dependency.details); store.isLoading = false; store.init(); }, []); - + return (
{store.isLoading ? ( diff --git a/ui/src/core/ui/form_builder/forms/select_detail/presentation/ui/list_item.tsx b/ui/src/core/ui/form_builder/forms/select_detail/presentation/ui/list_item.tsx index 0690fbe..7263d7a 100644 --- a/ui/src/core/ui/form_builder/forms/select_detail/presentation/ui/list_item.tsx +++ b/ui/src/core/ui/form_builder/forms/select_detail/presentation/ui/list_item.tsx @@ -16,6 +16,7 @@ export const ListItem = (props: IListItemProps) => { backgroundColor: "rgba(254, 247, 255, 1)", border: "1px #6750a4 solid", width: "100%", + height: 110, display: "flex", justifyContent: "space-between", alignItems: "center", diff --git a/ui/src/core/ui/form_builder/test.tsx b/ui/src/core/ui/form_builder/test.tsx index b9735d7..5074df0 100644 --- a/ui/src/core/ui/form_builder/test.tsx +++ b/ui/src/core/ui/form_builder/test.tsx @@ -9,7 +9,7 @@ import { FormBuilder } from "./form_builder"; import makeAutoObservable from "mobx-store-inheritance"; class FormBuilderTextStore extends ModalStore { - viewModel = FormBuilderValidationModel.eee(); + viewModel = FormBuilderValidationModel.empty(); constructor() { super(); makeAutoObservable(this); @@ -21,7 +21,6 @@ export const FormBuildTest = observer(() => { return (
- (store.viewModel.result = text)} /> (store.viewModel.context = text)} /> (store.isModalOpen = true)} /> @@ -34,16 +33,15 @@ export const FormBuildTest = observer(() => { onCancel={() => { store.isModalOpen = false; }} - > -
- { - console.log(e) - // console.log(e.output); - console.log(JSON.stringify(e.output)) - }} - /> + > + { + console.log(e.output); + // console.log(JSON.stringify(e.output)) + }} + /> +
); }); diff --git a/ui/src/core/ui/icons/icons.tsx b/ui/src/core/ui/icons/icons.tsx index 823e680..33b571b 100644 --- a/ui/src/core/ui/icons/icons.tsx +++ b/ui/src/core/ui/icons/icons.tsx @@ -931,18 +931,6 @@ const getIconSvg = ( /> ); - - case "3points": - return Result.ok( - - - - ); case "Move": return Result.ok( diff --git a/ui/src/core/ui/input/input.tsx b/ui/src/core/ui/input/input.tsx index a2af4d9..1a78a4a 100644 --- a/ui/src/core/ui/input/input.tsx +++ b/ui/src/core/ui/input/input.tsx @@ -16,6 +16,7 @@ interface IInputProps extends IStyle { type?: CoreInputType; trim?: boolean; styleContentEditable?: React.CSSProperties; + isFormBuilder?: boolean; } export const CoreInput = (props: IInputProps) => { @@ -28,6 +29,15 @@ export const CoreInput = (props: IInputProps) => { setAppendInnerText(false); } }, [ref, value, isAppendInnerText, setAppendInnerText, props]); + // React.useEffect(() => { + // if (props.isFormBuilder) { + // if (ref.current && props.value) { + // ref.current.innerText = value; + // setValue(props.value); + // console.log(props.value); + // } + // } + // }, [props.value]); const isSmall = props.type !== undefined && props.type.isEqual(CoreInputType.small); return ( diff --git a/ui/src/core/ui/input/input_v2.tsx b/ui/src/core/ui/input/input_v2.tsx index 72e04aa..30bfc20 100644 --- a/ui/src/core/ui/input/input_v2.tsx +++ b/ui/src/core/ui/input/input_v2.tsx @@ -2,16 +2,15 @@ import { themeStore } from "../../.."; import { Icon } from "../icons/icons"; import { CoreText, CoreTextType, FontType } from "../text/text"; -export const InputV2: React.FC<{ - style?: React.CSSProperties; +interface InputV2Props { label: string; value?: string; trim?: boolean; - validation?: (value: string) => boolean; - error?: string; height?: number; onChange?: (text: string) => void; -}> = ({ label, height, value, onChange, trim }) => { + +} +export const InputV2: React.FC = ({ label, height, value, onChange, trim }) => { return (
- {/*
{rightChild}
-
*/} +
{children}
diff --git a/ui/src/core/ui/popover/popover.tsx b/ui/src/core/ui/popover/popover.tsx deleted file mode 100644 index 55b6901..0000000 --- a/ui/src/core/ui/popover/popover.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import React, { useEffect, useRef, useState } from "react"; -import styled from "styled-components"; - -interface PopoverProps { - content: React.ReactNode; - children: React.ReactNode; -} - -const PopoverV2: React.FC = ({ content, children }) => { - const [visible, setVisible] = useState(false); - const ref = useRef(null); - const togglePopover = () => { - setVisible((prev) => !prev); - }; - - return ( -
togglePopover()} - > - {children} -
- {content} -
-
- ); -}; -// visibility: ${({ visible }) => (visible ? 'visible' : 'hidden')}; -// opacity: ${({ visible }) => (visible ? 1 : 0)}; -// transition: opacity 0.2s ease, visibility 0.2s ease; -export default PopoverV2; diff --git a/ui/src/core/ui/select/select.tsx b/ui/src/core/ui/select/select.tsx index 47bff79..017c16f 100644 --- a/ui/src/core/ui/select/select.tsx +++ b/ui/src/core/ui/select/select.tsx @@ -6,7 +6,7 @@ interface ICoreSelectProps extends IStyle { items: string[]; value: string; label: string; - onChange: (value: string, index: number) => void; + onChange: (value: string) => void; } export const CoreSelect = (props: ICoreSelectProps) => { const ref = React.useRef(null); @@ -52,7 +52,7 @@ export const CoreSelect = (props: ICoreSelectProps) => { key={i} onClick={() => { setValue(el); - props.onChange(el, i); + props.onChange(el); }} style={{ backgroundColor: "rgba(230, 224, 233, 1)", diff --git a/ui/src/features/behavior_tree_builder/data/behavior_tree_builder_http_repository.ts b/ui/src/features/behavior_tree_builder/data/behavior_tree_builder_http_repository.ts index 9c6e264..0da5653 100644 --- a/ui/src/features/behavior_tree_builder/data/behavior_tree_builder_http_repository.ts +++ b/ui/src/features/behavior_tree_builder/data/behavior_tree_builder_http_repository.ts @@ -21,6 +21,7 @@ export class BehaviorTreeBuilderHttpRepository extends CoreHttpRepository { `${this.featureApi}/by_id?id=${id}`, BehaviorTreeModel ) as unknown as Promise>; + deleteBt = (id: string) => this._jsonRequest(HttpMethod.DELETE, `${this.featureApi}?id=${id}`); editBt = async (model: BehaviorTreeModel) => { await this._jsonRequest(HttpMethod.POST, `${this.featureApi}/fill/tree`, model); model.__v = undefined diff --git a/ui/src/features/behavior_tree_builder/presentation/behavior_tree_builder_screen.tsx b/ui/src/features/behavior_tree_builder/presentation/behavior_tree_builder_screen.tsx index 67f44a6..5a890c6 100644 --- a/ui/src/features/behavior_tree_builder/presentation/behavior_tree_builder_screen.tsx +++ b/ui/src/features/behavior_tree_builder/presentation/behavior_tree_builder_screen.tsx @@ -4,15 +4,20 @@ import { createEditor } from "./ui/editor/editor"; import { SkillTree } from "./ui/skill_tree/skill_tree"; import { BehaviorTreeBuilderStore, DrawerState } from "./behavior_tree_builder_store"; import { observer } from "mobx-react-lite"; +import { match } from "ts-pattern"; import { Icon } from "../../../core/ui/icons/icons"; import { CoreText, CoreTextType } from "../../../core/ui/text/text"; import { useNavigate, useParams } from "react-router-dom"; import { IForms, forms } from "./ui/forms/forms"; +import { ButtonV2, ButtonV2Type } from "../../../core/ui/button/button_v2"; +import { CoreCard } from "../../../core/ui/card/card"; import { themeStore } from "../../.."; +import { CoreModal } from "../../../core/ui/modal/modal"; +import { InputV2 } from "../../../core/ui/input/input_v2"; +import { SelectV2 } from "../../../core/ui/select/select_v2"; import { MainPageV2 } from "../../../core/ui/pages/main_page_v2"; import { DrawerV2 } from "../../../core/ui/drawer/drawer"; import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels"; -import PopoverV2 from "../../../core/ui/popover/popover"; export const behaviorTreeBuilderScreenPath = "behavior/tree/screen/path"; @@ -49,9 +54,7 @@ export const BehaviorTreeBuilderScreen = observer(() => { if (ref.current) { // @ts-expect-error const domReact: DOMReact = ref.current.getBoundingClientRect(); - - // УБЕРИ + 300 - store.dragZoneSetOffset(0, domReact.y, domReact.width + 300, domReact.height); + store.dragZoneSetOffset(0, domReact.y, domReact.width, domReact.height); } }, [ref.current]); @@ -63,36 +66,54 @@ export const BehaviorTreeBuilderScreen = observer(() => { }, []); return ( +
+ {/* {}} text="Запуск" textColor={themeStore.theme.black} /> +
+ {}} + text="Стоп" + type={ButtonV2Type.empty} + textColor={themeStore.theme.greenWhite} + /> +
*/} + {store.isNeedSaveBtn ? ( +
+ {store.isNeedSaveBtn ? ( + store.onClickSaveBehaviorTree()} type="Floppy" /> + ) : undefined} +
+ ) : ( + <> + )} +
+ + } style={{ position: "absolute", height: "100%", overflow: "hidden" }} bgColor={themeStore.theme.black} children={ <> <> -
-
-
- store.onClickSaveBehaviorTree()} type="Floppy" /> -
-
-
{ }} /> - {store.panels.map((el, index) => ( - <> - -
- - } - >
- - store.panelResize(size, index)} - > -
- } - content={ -
- {store.panelActions.map((el, i) => ( - el.action(index)} /> - ))} -
- } - /> -
{el.name}
-
- {el.body} -
- - ))} + +
+
+
@@ -186,17 +172,8 @@ export const BehaviorTreeBuilderScreen = observer(() => { title={store.titleDrawer} onClose={() => store.editDrawer(DrawerState.editThreadBehaviorTree, false)} isOpen={store.drawers.find((el) => el.name === DrawerState.editThreadBehaviorTree)?.status} - width={window.innerWidth / 2} > -
+
{store.skillTemplates?.getForms(store.selected ?? "").map((formType, index) => forms( @@ -209,13 +186,11 @@ export const BehaviorTreeBuilderScreen = observer(() => { ) .rFind((form) => form.name.isEqual(formType)) .fold( - (s) => { - return ( -
- {s.component} -
- ); - }, + (s) => ( +
+ {s.component} +
+ ), () => (
Error: Unknown form type {formType} diff --git a/ui/src/features/behavior_tree_builder/presentation/behavior_tree_builder_store.tsx b/ui/src/features/behavior_tree_builder/presentation/behavior_tree_builder_store.tsx index f3658d3..126d256 100644 --- a/ui/src/features/behavior_tree_builder/presentation/behavior_tree_builder_store.tsx +++ b/ui/src/features/behavior_tree_builder/presentation/behavior_tree_builder_store.tsx @@ -26,8 +26,6 @@ import { BehaviorTreeModel } from "../model/behavior_tree_model"; import { PrimitiveViewModel, SystemPrimitive } from "../model/primitive_view_model"; import { SceneAsset } from "../../../core/model/scene_asset"; import { themeStore } from "../../.."; -import { RunTimeActions } from "./ui/actions/runtime_actions"; -import { ITopicModel } from "../../topics/topic_view_model"; interface I2DArea { x: number; @@ -39,31 +37,8 @@ interface I2DArea { export enum DrawerState { editThreadBehaviorTree = "Редактирование", } -interface IActionPanel { - name: string; - selectIsClosePopover?: boolean; - action: (index: number) => void; -} - -export class PanelBody { - body?: React.ReactNode; - size: number = 0; - name: string = "выберите тип панели"; - constructor(body: React.ReactNode | undefined, name: string | undefined, size: number | undefined) { - makeAutoObservable(this); - - if (name) this.name = name; - if (body) this.body = body; - if (size) this.size = size; - } -} export class BehaviorTreeBuilderStore extends UiDrawerFormState { - panelActions: IActionPanel[] = [ - { name: "Добавить панель", action: () => this.addNewPanel() }, - { name: "Убрать панель", action: (index) => this.removePanel(index) }, - { name: "Runtime", action: (index) => this.changePanel(index, "Runtime", ) }, - ]; sceneAsset?: SceneAsset; viewModel: BehaviorTreeViewModel = BehaviorTreeViewModel.empty(); behaviorTreeModel: BehaviorTreeModel = BehaviorTreeModel.empty(); @@ -76,15 +51,15 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState; areaPlugin?: AreaPlugin; nodeUpdateObserver?: NodeRerenderObserver; primitiveViewModel: PrimitiveViewModel; - topics: ITopicModel[]; skillTree: ISkillView = { name: "", children: [ @@ -95,12 +70,7 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState this.panels.push(new PanelBody(undefined, undefined, undefined)); - removePanel = (index: number) => - this.panels.length !== 1 - ? (this.panels = this.panels.filter((_, i) => i !== index)) - : message.error("должна быть хоть одна панель"); + constructor() { super(DrawerState); makeAutoObservable(this); @@ -116,7 +86,7 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState this.filledOutTemplates.topicsStack.plus(this.topics); + getAllTopics = () => this.filledOutTemplates.topicsStack; errorHandingStrategy = (_: CoreError) => {}; dragEnd = (e: EventTarget) => { @@ -132,13 +102,6 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState { const drawPoint = { x: x, y: y, w: 1, h: 1 }; - - console.log( - drawPoint.x < this.area!.x + this.area!.w && - drawPoint.x + drawPoint.w > this.area!.x && - drawPoint.y < this.area!.y + this.area!.h && - drawPoint.y + drawPoint.h > this.area!.y - ); if ( drawPoint.x < this.area!.x + this.area!.w && drawPoint.x + drawPoint.w > this.area!.x && @@ -152,7 +115,7 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState { @@ -216,7 +179,7 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState { @@ -244,6 +207,7 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState { + console.log(xml); this.behaviorTreeModel.skills = this.filledOutTemplates; this.behaviorTreeModel.scene = NodeBehaviorTree.fromReteScene( this.editor as NodeEditor, @@ -381,11 +345,4 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState {}; - changePanel = (index: number, name: string, body?: React.ReactNode) => { - this.panels = this.panels.replacePropIndex({ name: name, body: body }, index); - }; - - panelResize = (size: number, index: number) => { - this.panels.replacePropIndex({ size: size }, index); - }; } diff --git a/ui/src/features/behavior_tree_builder/presentation/ui/actions/runtime_actions.tsx b/ui/src/features/behavior_tree_builder/presentation/ui/actions/runtime_actions.tsx deleted file mode 100644 index ea5ecde..0000000 --- a/ui/src/features/behavior_tree_builder/presentation/ui/actions/runtime_actions.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import { NavigateFunction, useParams } from "react-router-dom"; -import { CoreHttpRepository, HttpMethod } from "../../../../../core/repository/core_http_repository"; -import { CoreError, UiErrorState } from "../../../../../core/store/base_store"; -import { ProcessStatus } from "../../../../dataset/dataset_model"; -import makeAutoObservable from "mobx-store-inheritance"; -import { useStore } from "../../../../../core/helper/use_store"; -import { Loader } from "../../../../../core/ui/loader/loader"; -import { useEffect } from "react"; -import { - RunTimeSocketRepository, - runTimeSocketRepository, -} from "../../../../../core/repository/core_socket_repository"; -import { observer } from "mobx-react-lite"; -interface IPid { - pid: string; -} -export interface RuntimeModel { - [name: string]: { pid: number; status: String }; -} - -export class RunTimeHttpRepository extends CoreHttpRepository { - feature = "/run_time"; - stop = (model: IPid) => this._jsonRequest(HttpMethod.POST, this.feature + "/kill", model); - getStatuses = () => this._jsonRequest(HttpMethod.GET, this.feature + "/status"); - execBt = (id: string) => this._jsonRequest(HttpMethod.POST, this.feature + `/exec/bt?id=${id}`); -} -// export class RunTimeSocketRepository extends SockeCore {} -export class RunTimeStore extends UiErrorState { - runTimeHttpRepository: RunTimeHttpRepository = new RunTimeHttpRepository(); - runTimeSocketRepository: RunTimeSocketRepository = runTimeSocketRepository; - pageId: string; - runTime?: RuntimeModel = {}; - constructor() { - super(); - makeAutoObservable(this); - runTimeSocketRepository.on((event) => (console.log(event), (this.runTime = event))); - } - async init(navigate?: NavigateFunction | undefined): Promise { - this.mapOk("runTime", this.runTimeHttpRepository.getStatuses()); - this.navigate = navigate; - } - - executeBt = () => this.runTimeHttpRepository.execBt(this.pageId); - initParam = (id: string) => (this.pageId = id); - isExecuteBt = (): boolean => { - if (this.runTime === undefined) { - return false; - } - if ( - this.runTime["EXEC_BT"] !== undefined && - this.runTime["EXEC_BT"]["status"] !== undefined && - this.runTime["EXEC_BT"]["status"] === "endOk" - ) { - return false; - } - return true; - }; -} -export const RunTimeActions = observer(() => { - const store = useStore(RunTimeStore); - const params = useParams(); - useEffect(() => { - store.initParam(params.id as string); - }, []); - return ( -
- {store.isLoading ? ( - - ) : ( - <> - {store.isExecuteBt() ? ( - <> -
store.executeBt()} - > - Дерево запущено -
- - ) : ( - <> -
store.executeBt()} - > - Запустить дерево -
- - )} - - )} -
- ); -}); diff --git a/ui/src/features/behavior_tree_builder/presentation/ui/forms/form_builder/form_builder_form.tsx b/ui/src/features/behavior_tree_builder/presentation/ui/forms/form_builder/form_builder_form.tsx index dfffe63..7c5d2a5 100644 --- a/ui/src/features/behavior_tree_builder/presentation/ui/forms/form_builder/form_builder_form.tsx +++ b/ui/src/features/behavior_tree_builder/presentation/ui/forms/form_builder/form_builder_form.tsx @@ -19,7 +19,7 @@ export const FormBuilderForm = observer((props: IPropsForm +
FormBuilder
{store.isBtScreen ? (
@@ -27,6 +27,7 @@ export const FormBuilderForm = observer((props: IPropsForm { store.viewModel = form; + console.log(form); }} />
@@ -80,7 +81,7 @@ export const FormBuilderForm = observer((props: IPropsForm - (console.log(form), props.onChange(form))} /> + {}} /> )} diff --git a/ui/src/features/behavior_tree_manager/behavior_tree_manager_repository.ts b/ui/src/features/behavior_tree_manager/behavior_tree_manager_repository.ts index a64f732..b99cbf2 100644 --- a/ui/src/features/behavior_tree_manager/behavior_tree_manager_repository.ts +++ b/ui/src/features/behavior_tree_manager/behavior_tree_manager_repository.ts @@ -4,7 +4,7 @@ import { BehaviorTreeViewModel } from "../behavior_tree_builder/model/behavior_t export class BehaviorTreeManagerHttpRepository extends CoreHttpRepository { featureApi = `/behavior/trees`; - deleteBt = (id: string) => this._jsonRequest(HttpMethod.POST, `${this.featureApi}/delete/bt?id=${id}`); + deleteBt = (id: string) => this._jsonRequest(HttpMethod.DELETE, `${this.featureApi}?id=${id}`); saveNewBt = async (model: BehaviorTreeViewModel) => this._jsonRequest(HttpMethod.POST, this.featureApi, model); getAllBtInstances = async () => this._jsonRequest(HttpMethod.GET, this.featureApi); } diff --git a/ui/src/features/behavior_tree_manager/behavior_tree_manager_screen.tsx b/ui/src/features/behavior_tree_manager/behavior_tree_manager_screen.tsx index 482bbc2..fe8d179 100644 --- a/ui/src/features/behavior_tree_manager/behavior_tree_manager_screen.tsx +++ b/ui/src/features/behavior_tree_manager/behavior_tree_manager_screen.tsx @@ -1,6 +1,6 @@ import { observer } from "mobx-react-lite"; import { BehaviorTreeManagerStore } from "./behavior_tree_manager_store"; -import React, { useEffect } from "react"; +import React from "react"; import { useStore } from "../../core/helper/use_store"; import { ButtonV2, ButtonV2Type } from "../../core/ui/button/button_v2"; import { CoreCard } from "../../core/ui/card/card"; @@ -16,13 +16,13 @@ export const BehaviorTreeManagerScreenPath = "/behavior/tree/manager"; export const BehaviorTreeManagerScreen = observer(() => { const store = useStore(BehaviorTreeManagerStore); - useEffect(() => {}, []); + return ( <> -
+
} @@ -65,13 +65,9 @@ export const BehaviorTreeManagerScreen = observer(() => { }} />
- store.updateForm({ name: text })} /> + store.updateForm({ name: text })} />
- store.updateForm({ description: text })} - /> + store.updateForm({ description: text })} />
({ name: el.name, value: el._id })) ?? []} diff --git a/ui/src/features/behavior_tree_manager/behavior_tree_manager_store.ts b/ui/src/features/behavior_tree_manager/behavior_tree_manager_store.ts index 4d2b069..f19fc5b 100644 --- a/ui/src/features/behavior_tree_manager/behavior_tree_manager_store.ts +++ b/ui/src/features/behavior_tree_manager/behavior_tree_manager_store.ts @@ -12,6 +12,7 @@ export enum DrawerState { } export class BehaviorTreeManagerStore extends UiDrawerFormState { viewModel: BehaviorTreeViewModel = BehaviorTreeViewModel.empty(); + navigate?: NavigateFunction; btTreeModels: BehaviorTreeModel[] = []; scenes?: SceneModel[]; behaviorTreeManagerHttpRepository = new BehaviorTreeManagerHttpRepository(); diff --git a/ui/src/features/calculation_instance/data/calculation_http_repository.ts b/ui/src/features/calculation_instance/data/calculation_http_repository.ts index 2b60a10..90e4695 100644 --- a/ui/src/features/calculation_instance/data/calculation_http_repository.ts +++ b/ui/src/features/calculation_instance/data/calculation_http_repository.ts @@ -14,13 +14,7 @@ export interface ISkils { } export class CalculationHttpRepository extends CoreHttpRepository { - async getLogs(id: string) { - - await this._request(HttpMethod.GET, `/logs?id=${id}`) - window.location.href = 'http://localhost:4001/log.txt'; - - } - + featureApi = `/calculations/instances`; subFeatureApi = `/calculations/template`; diff --git a/ui/src/features/calculation_instance/model/calculation_model.ts b/ui/src/features/calculation_instance/model/calculation_model.ts index 9fd6c2d..01e1519 100644 --- a/ui/src/features/calculation_instance/model/calculation_model.ts +++ b/ui/src/features/calculation_instance/model/calculation_model.ts @@ -4,9 +4,7 @@ import { FormBuilderValidationModel } from "../../../core/model/form_builder_val export enum ModelMachineLearningTypes { OBJECT_DETECTION = "OBJECT_DETECTION", - POSE_ESTIMATION = "POSE_ESTIMATION", - BOP_DATASET = "BOP_DATASET", - WEIGHTS = "WEIGHTS", + POSE_ESTIMATE = "POSE_ESTIMATE", } export class CalculationModel extends ValidationModel { diff --git a/ui/src/features/calculation_instance/presentation/calculation_instance_screen.tsx b/ui/src/features/calculation_instance/presentation/calculation_instance_screen.tsx index 7a56975..fc9dec4 100644 --- a/ui/src/features/calculation_instance/presentation/calculation_instance_screen.tsx +++ b/ui/src/features/calculation_instance/presentation/calculation_instance_screen.tsx @@ -28,6 +28,7 @@ export const CalculationInstanceScreenPath = "/calculation"; export const CalculationInstanceScreen = observer(() => { const store = useStore(CalculationInstanceStore); + return ( <> { () => store.makeEditProcess(el), () => store.deleteInstance(el._id ?? ""), () => store.execSkill(el._id ?? ""), - () => store.execSkill(el._id ?? ""), - () => store.changeProcessStatus(el._id ?? ""), - () => store.getTxtLog(el._id ?? "") + () => store.execSkill(el._id ?? "") )} ); @@ -95,10 +94,7 @@ export const CalculationInstanceScreen = observer(() => {
{ - console.log(formBuilder); - store.updateForm({ formBuilder: formBuilder }); - }} + onChange={(formBuilder) => store.updateForm({ formBuilder: formBuilder })} />
@@ -138,7 +134,7 @@ export const CalculationInstanceScreen = observer(() => { store.updateForm({ type: text })} /> { label={"Тип карточки"} onChange={(text: string) => store.updateForm({ card: text })} /> - store.updateForm({ name: text.replaceAll("\n", "") })} - /> - store.updateForm({ script: text.replaceAll("\n", "") })} - /> + store.updateForm({ name: text })} /> + store.updateForm({ script: text })} /> (store.viewModel.formBuilder.result = text)} @@ -218,7 +206,7 @@ export const CalculationInstanceScreen = observer(() => { store.isModalOpen = false; }} > - {}} /> + {}} /> ); diff --git a/ui/src/features/calculation_instance/presentation/calculation_instance_store.tsx b/ui/src/features/calculation_instance/presentation/calculation_instance_store.tsx index bb68c7e..1ca399a 100644 --- a/ui/src/features/calculation_instance/presentation/calculation_instance_store.tsx +++ b/ui/src/features/calculation_instance/presentation/calculation_instance_store.tsx @@ -5,7 +5,7 @@ import { Drawer, UiDrawerFormState } from "../../../core/store/base_store"; import { CalculationHttpRepository } from "../data/calculation_http_repository"; import { message } from "antd"; import { UUID } from "../../all_projects/data/project_http_repository"; -import { CalculationModel as calculationModel } from "../model/calculation_model"; +import { CalculationModel } from "../model/calculation_model"; import { ProcessUpdate, CalculationSocketRepository } from "../data/calculation_socket_repository"; import { match } from "ts-pattern"; import { plainToInstance } from "class-transformer"; @@ -20,17 +20,16 @@ export enum StoreTypes { empty = "empty", } -export class CalculationInstanceStore extends UiDrawerFormState { - getTxtLog = (id: string) => this.calculationHttpRepository.getLogs(id); +export class CalculationInstanceStore extends UiDrawerFormState { calculationHttpRepository: CalculationHttpRepository = new CalculationHttpRepository(); calculationSocketRepository: CalculationSocketRepository = new CalculationSocketRepository(); activeProjectId?: UUID; storeType: StoreTypes = StoreTypes.empty; - viewModel: calculationModel = calculationModel.empty(); - modelTemplate?: calculationModel[]; - editProcess?: calculationModel; - calculationInstances?: calculationModel[]; - selectTemplate?: calculationModel; + viewModel: CalculationModel = CalculationModel.empty(); + modelTemplate?: CalculationModel[]; + editProcess?: CalculationModel; + calculationInstances?: CalculationModel[]; + selectTemplate?: CalculationModel; titleDrawer: string = DrawersSkill.NEW_SKILL; drawers: Drawer[]; isModalOpen: boolean = false; @@ -42,7 +41,7 @@ export class CalculationInstanceStore extends UiDrawerFormState { + setSelectTemplate = (el: CalculationModel) => { this.selectTemplate = el; - const instance = plainToInstance(calculationModel, el); + const instance = plainToInstance(CalculationModel, el); instance.instanceName = this?.viewModel.instanceName; this.viewModel = instance; }; @@ -85,7 +84,7 @@ export class CalculationInstanceStore extends UiDrawerFormState {}) .with(StoreTypes.newType, async () => - (await this.viewModel.valid()).fold( + (await this.viewModel.valid()).fold( async (model) => { model.project = this.activeProjectId?.id; @@ -100,7 +99,7 @@ export class CalculationInstanceStore extends UiDrawerFormState { - (await this.viewModel.valid()).fold( + (await this.viewModel.valid()).fold( async (model) => { delete model._id; model.project = this.activeProjectId?.id; @@ -129,30 +128,20 @@ export class CalculationInstanceStore extends UiDrawerFormState { this.editDrawer(DrawersSkill.EDIT_SKILL, false); - (await this.viewModel.valid()).fold( - async (model) => (await this.calculationHttpRepository.editCalculation(model), await this.init()), + (await this.viewModel.valid()).fold( + async (model) => await this.calculationHttpRepository.editCalculation(model), async (err) => message.error(err) ); }; - makeEditProcess = (el: calculationModel) => { + makeEditProcess = (el: CalculationModel) => { this.editProcess = el; - this.loadClassInstance(calculationModel, el); + this.loadClassInstance(CalculationModel, el); this.editDrawer(DrawersSkill.EDIT_SKILL, true); }; - deleteTemplate = async (el: calculationModel) => { + deleteTemplate = async (el: CalculationModel) => { await this.messageHttp(this.calculationHttpRepository.deleteTemplate(el._id ?? ""), { successMessage: "Удален", }); await this.mapOk("modelTemplate", this.calculationHttpRepository.getAllTemplates()); }; - changeProcessStatus = async (id: string) => - this! - .calculationInstances!.whereOne((el) => el._id === id) - .map( - async (calculationModel) => ( - (calculationModel.isEnd = !calculationModel.isEnd), - await this.calculationHttpRepository.editCalculation(calculationModel), - await this.init(undefined) - ) - ); } diff --git a/ui/src/features/calculation_instance/presentation/ui/cards/get_model_card.tsx b/ui/src/features/calculation_instance/presentation/ui/cards/get_model_card.tsx index c0fdb2c..901029d 100644 --- a/ui/src/features/calculation_instance/presentation/ui/cards/get_model_card.tsx +++ b/ui/src/features/calculation_instance/presentation/ui/cards/get_model_card.tsx @@ -1,6 +1,6 @@ import { match } from "ts-pattern"; import { PoseEstimateCard } from "./pose_estimate_card/model_card"; -import { Dropdown, MenuProps, } from "antd"; +import { Dropdown, MenuProps, message } from "antd"; import { CoreText, CoreTextType } from "../../../../../core/ui/text/text"; import { IMenuItem } from "../../../../dataset/card_dataset"; import { Icon } from "../../../../../core/ui/icons/icons"; @@ -15,9 +15,7 @@ export const getModelCard = ( onEdit: Function, onDelete: Function, onPlay: Function, - onPause: Function, - onChangeProcessIsEnd: Function, - onLog: Function + onPause: Function ) => { const menu: IMenuItem[] = [ { @@ -28,10 +26,6 @@ export const getModelCard = ( onClick: () => onDelete(), name: "Удалить", }, - { - onClick: () => onChangeProcessIsEnd(), - name: calculationModel.isEnd ? "Вернуть на доработку" : "Завершить", - }, ]; const items: MenuProps["items"] = menu.map((el, index) => { @@ -69,8 +63,7 @@ export const getModelCard = ( - // window.prompt("Copy to clipboard: Ctrl+C, Enter", calculationModel.lastProcessLogs ?? "Not found logs") - onLog() + window.prompt("Copy to clipboard: Ctrl+C, Enter", calculationModel.lastProcessLogs ?? "Not found logs") } /> this._jsonRequest(HttpMethod.POST, `/run_time/exec/analyze?id=${id}`); } export class PoseEstimateStore extends UiErrorState { + navigate?: NavigateFunction; poseEstimateRepository = new PoseEstimateRepository(); constructor() { super(); diff --git a/ui/src/features/skills/skills_screen.tsx b/ui/src/features/skills/skills_screen.tsx index 216604e..894fc56 100644 --- a/ui/src/features/skills/skills_screen.tsx +++ b/ui/src/features/skills/skills_screen.tsx @@ -12,6 +12,7 @@ import { useStore } from "../../core/helper/use_store"; import { FormBuilder } from "../../core/ui/form_builder/form_builder"; import { ButtonV2 } from "../../core/ui/button/button_v2"; import { Result } from "../../core/helper/result"; +import { plainToInstance } from "class-transformer"; export const isValidJson = (json: any): Result => { try { @@ -287,4 +288,3 @@ export const SkillsScreen = observer(() => { ); }); - \ No newline at end of file diff --git a/ui/src/features/topics/topic_view_model.ts b/ui/src/features/topics/topic_view_model.ts index 74551db..44c8bc2 100644 --- a/ui/src/features/topics/topic_view_model.ts +++ b/ui/src/features/topics/topic_view_model.ts @@ -21,7 +21,6 @@ export class TopicViewModel extends ValidationModel { export interface ITopicModel { digitalTwinId?: string; sid?: string; - _id?: string; name: string; type: string; } diff --git a/ui/src/features/topics/topics_repository.ts b/ui/src/features/topics/topics_repository.ts index e06412c..852926b 100644 --- a/ui/src/features/topics/topics_repository.ts +++ b/ui/src/features/topics/topics_repository.ts @@ -1,9 +1,6 @@ -import { CoreHttpRepository, HttpMethod, HttpRepository } from "../../core/repository/core_http_repository"; -import { TopicViewModel } from "./topic_view_model"; +import { HttpMethod, HttpRepository } from "../../core/repository/core_http_repository"; -export class TopicsHttpRepository extends CoreHttpRepository { - deleteTopic = (id: any) => this._jsonRequest(HttpMethod.DELETE, `${this.featureApi}?id=${id}`); +export class TopicsHttpRepository extends HttpRepository { featureApi = "/topics"; - createTopics = (model: TopicViewModel) => this._jsonRequest(HttpMethod.POST, this.featureApi, model); - + getAllTopics = () => this._jsonRequest(HttpMethod.GET, this.featureApi); } diff --git a/ui/src/features/topics/topics_screen.tsx b/ui/src/features/topics/topics_screen.tsx index f61fc7b..2fb82c8 100644 --- a/ui/src/features/topics/topics_screen.tsx +++ b/ui/src/features/topics/topics_screen.tsx @@ -2,11 +2,6 @@ import React from "react"; import { observer } from "mobx-react-lite"; import { TopicsStore } from "./topics_store"; import { useStore } from "../../core/helper/use_store"; -import { CoreButton } from "../../core/ui/button/button"; -import { CoreModal } from "../../core/ui/modal/modal"; -import { InputV2 } from "../../core/ui/input/input_v2"; -import { CoreText, CoreTextType } from "../../core/ui/text/text"; -import { CoreInput } from "../../core/ui/input/input"; export const TopicsScreenPath = "/topics"; @@ -15,43 +10,12 @@ export const TopicsScreen = observer(() => { return ( <> -
- store.modalShow()} /> - store.openTopicModal()} /> -
{store.topics?.map((el) => (
- store.deleteTopic(el._id ?? "")} /> - window.prompt("Copy to clipboard: Ctrl+C, Enter", JSON.stringify(el))} - /> -
{el.name}
{el.type}
))} - store.modalCancel()} - children={ -
- store.updateForm({ type: text })} /> - store.updateForm({ name: text })} /> - store.createTopic()} /> -
- } - /> - store.cancelImportTopicModal()} - children={ -
- store.importTopic(text)} label="import" /> -
- } - /> ); }); diff --git a/ui/src/features/topics/topics_store.ts b/ui/src/features/topics/topics_store.ts index d9e4157..eb680b1 100644 --- a/ui/src/features/topics/topics_store.ts +++ b/ui/src/features/topics/topics_store.ts @@ -6,35 +6,14 @@ import { NavigateFunction } from "react-router-dom"; import { TopicsHttpRepository } from "./topics_repository"; export class TopicsStore extends FormState { - importTopic(text: string): void { - this.loadClassInstance(TopicModel, JSON.parse(text)) - - this.createTopic() - } - - viewModel: TopicModel = TopicModel.empty(); topics?: ITopicModel[]; topicsHttpRepository: TopicsHttpRepository = new TopicsHttpRepository(); - importTopicModal = false; constructor() { super(); makeAutoObservable(this); } init = async (navigate?: NavigateFunction | undefined) => { - this.modalCancel(); - this.importTopicModal = false; await this.mapOk("topics", this.topicsHttpRepository.getAllTopics()); }; - cancelImportTopicModal = () => { - this.importTopicModal = false; - } - openTopicModal = () => { - this.importTopicModal = true; - } - deleteTopic = async (id: string) => (await (this.topicsHttpRepository.deleteTopic(id))).map(() => this.init()); - createTopic = async () => - (await this.viewModel.validMessage()).map(async (model) => - (await this.topicsHttpRepository.createTopics(model)).map(() => this.init()) - ); } diff --git a/ui/src/index.tsx b/ui/src/index.tsx index a2b72f7..a8d3fea 100644 --- a/ui/src/index.tsx +++ b/ui/src/index.tsx @@ -7,7 +7,6 @@ import { RouterProvider } from "react-router-dom"; import { router } from "./core/routers/routers"; import { configure } from "mobx"; import { ThemeStore } from "./core/store/theme_store"; -import { FormBuildTest } from "./core/ui/form_builder/test"; configure({ enforceActions: "never", @@ -23,6 +22,5 @@ root.render( - {/* */} ); diff --git a/web_p/camera_info_topic.json b/web_p/camera_info_topic.json deleted file mode 100644 index f7006fd..0000000 --- a/web_p/camera_info_topic.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "_id": "67b43b59ba78351d1a11d74a", - "name": "/rgbd_camera/camera_info", - "type": "sensor_msgs/msg/CameraInfo", - "__v": 0 -} \ No newline at end of file diff --git a/web_p/image_topic.json b/web_p/image_topic.json deleted file mode 100644 index b8a7e1a..0000000 --- a/web_p/image_topic.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "_id": "67b43b20ba78351d1a11d747", - "name": "/rgbd_camera/image", - "type": "sensor_msgs/msg/Image", - "__v": 0 -} \ No newline at end of file diff --git a/web_p/models_dope.py b/web_p/models_dope.py deleted file mode 100755 index 0c89004..0000000 --- a/web_p/models_dope.py +++ /dev/null @@ -1,196 +0,0 @@ -""" -NVIDIA from jtremblay@gmail.com -""" - -# Networks -import torch -import torch -import torch.nn as nn -import torch.nn.parallel -import torch.utils.data -import torchvision.models as models - - -class DopeNetwork(nn.Module): - def __init__( - self, - pretrained=False, - numBeliefMap=9, - numAffinity=16, - stop_at_stage=6, # number of stages to process (if less than total number of stages) - ): - super(DopeNetwork, self).__init__() - - self.stop_at_stage = stop_at_stage - - vgg_full = models.vgg19(pretrained=False).features - self.vgg = nn.Sequential() - for i_layer in range(24): - self.vgg.add_module(str(i_layer), vgg_full[i_layer]) - - # Add some layers - i_layer = 23 - self.vgg.add_module( - str(i_layer), nn.Conv2d(512, 256, kernel_size=3, stride=1, padding=1) - ) - self.vgg.add_module(str(i_layer + 1), nn.ReLU(inplace=True)) - self.vgg.add_module( - str(i_layer + 2), nn.Conv2d(256, 128, kernel_size=3, stride=1, padding=1) - ) - self.vgg.add_module(str(i_layer + 3), nn.ReLU(inplace=True)) - - # print('---Belief------------------------------------------------') - # _2 are the belief map stages - self.m1_2 = DopeNetwork.create_stage(128, numBeliefMap, True) - self.m2_2 = DopeNetwork.create_stage( - 128 + numBeliefMap + numAffinity, numBeliefMap, False - ) - self.m3_2 = DopeNetwork.create_stage( - 128 + numBeliefMap + numAffinity, numBeliefMap, False - ) - self.m4_2 = DopeNetwork.create_stage( - 128 + numBeliefMap + numAffinity, numBeliefMap, False - ) - self.m5_2 = DopeNetwork.create_stage( - 128 + numBeliefMap + numAffinity, numBeliefMap, False - ) - self.m6_2 = DopeNetwork.create_stage( - 128 + numBeliefMap + numAffinity, numBeliefMap, False - ) - - # print('---Affinity----------------------------------------------') - # _1 are the affinity map stages - self.m1_1 = DopeNetwork.create_stage(128, numAffinity, True) - self.m2_1 = DopeNetwork.create_stage( - 128 + numBeliefMap + numAffinity, numAffinity, False - ) - self.m3_1 = DopeNetwork.create_stage( - 128 + numBeliefMap + numAffinity, numAffinity, False - ) - self.m4_1 = DopeNetwork.create_stage( - 128 + numBeliefMap + numAffinity, numAffinity, False - ) - self.m5_1 = DopeNetwork.create_stage( - 128 + numBeliefMap + numAffinity, numAffinity, False - ) - self.m6_1 = DopeNetwork.create_stage( - 128 + numBeliefMap + numAffinity, numAffinity, False - ) - - def forward(self, x): - """Runs inference on the neural network""" - - out1 = self.vgg(x) - - out1_2 = self.m1_2(out1) - out1_1 = self.m1_1(out1) - - if self.stop_at_stage == 1: - return [out1_2], [out1_1] - - out2 = torch.cat([out1_2, out1_1, out1], 1) - out2_2 = self.m2_2(out2) - out2_1 = self.m2_1(out2) - - if self.stop_at_stage == 2: - return [out1_2, out2_2], [out1_1, out2_1] - - out3 = torch.cat([out2_2, out2_1, out1], 1) - out3_2 = self.m3_2(out3) - out3_1 = self.m3_1(out3) - - if self.stop_at_stage == 3: - return [out1_2, out2_2, out3_2], [out1_1, out2_1, out3_1] - - out4 = torch.cat([out3_2, out3_1, out1], 1) - out4_2 = self.m4_2(out4) - out4_1 = self.m4_1(out4) - - if self.stop_at_stage == 4: - return [out1_2, out2_2, out3_2, out4_2], [out1_1, out2_1, out3_1, out4_1] - - out5 = torch.cat([out4_2, out4_1, out1], 1) - out5_2 = self.m5_2(out5) - out5_1 = self.m5_1(out5) - - if self.stop_at_stage == 5: - return [out1_2, out2_2, out3_2, out4_2, out5_2], [ - out1_1, - out2_1, - out3_1, - out4_1, - out5_1, - ] - - out6 = torch.cat([out5_2, out5_1, out1], 1) - out6_2 = self.m6_2(out6) - out6_1 = self.m6_1(out6) - - return [out1_2, out2_2, out3_2, out4_2, out5_2, out6_2], [ - out1_1, - out2_1, - out3_1, - out4_1, - out5_1, - out6_1, - ] - - @staticmethod - def create_stage(in_channels, out_channels, first=False): - """Create the neural network layers for a single stage.""" - - model = nn.Sequential() - mid_channels = 128 - if first: - padding = 1 - kernel = 3 - count = 6 - final_channels = 512 - else: - padding = 3 - kernel = 7 - count = 10 - final_channels = mid_channels - - # First convolution - model.add_module( - "0", - nn.Conv2d( - in_channels, mid_channels, kernel_size=kernel, stride=1, padding=padding - ), - ) - - # Middle convolutions - i = 1 - while i < count - 1: - model.add_module(str(i), nn.ReLU(inplace=True)) - i += 1 - model.add_module( - str(i), - nn.Conv2d( - mid_channels, - mid_channels, - kernel_size=kernel, - stride=1, - padding=padding, - ), - ) - i += 1 - - # Penultimate convolution - model.add_module(str(i), nn.ReLU(inplace=True)) - i += 1 - model.add_module( - str(i), nn.Conv2d(mid_channels, final_channels, kernel_size=1, stride=1) - ) - i += 1 - - # Last convolution - model.add_module(str(i), nn.ReLU(inplace=True)) - i += 1 - model.add_module( - str(i), nn.Conv2d(final_channels, out_channels, kernel_size=1, stride=1) - ) - i += 1 - - return model diff --git a/web_p/od_skill.json b/web_p/od_skill.json deleted file mode 100644 index 34d2e50..0000000 --- a/web_p/od_skill.json +++ /dev/null @@ -1,87 +0,0 @@ -{ - "_id": "67b33b15f3412f3530fdb337", - "bgColor": "rgba(5, 26, 39, 1)", - "borderColor": "rgba(25, 130, 196, 1)", - "SkillPackage": { "name": "Robossembler", "version": "1", "format": "1.0" }, - "Module": { "node_name": "lc_yolo", "name": "ObjectDetection", "description": "Object detection skill with YOLOv8" }, - "BTAction": [ - { - "name": "odConfigure", - "type": "run", - "param": [ - { - "type": "topic", - "dependency": { - "type": "topic", - "topicType": "sensor_msgs/msg/CameraInfo" - }, - "isFilled": false - }, - { - "type": "topic", - "dependency": { - "type": "topic", - "topicType": "sensor_msgs/msg/Image" - }, - "isFilled": false - }, - { - "type": "formBuilder", - "dependency": { - "result": "{\"process\":\\${:OBJECT:{\"type\":\"WEIGHTS\"},\"object_name\":\\${object_name:string:}}", - "context": "", - "form": [], - "output": "", - "type": "formBuilder" - }, - "isFilled": false - } - ], - "typeAction": "ACTION" - }, - { - "name": "odStop", - "type": "stop", - "param": [], - "typeAction": "ACTION" - }, - { - "name": "isDetectionRun", - "type": "if", - "param": [], - "typeAction": "CONDITION" - }, - { - "name": "isDetection", - "type": "if", - "param": [], - "typeAction": "CONDITION" - } - ], - "topicsOut": [ - { - "name": "lc_yolo/object_detection", - "type": "rbs_skill_interfaces/msg/BoundBox" - }, - { - "name": "lc_yolo/detect_image", - "type": "sensor_msgs/msg/Image" - } - ], - "Launch": { - "executable": "od_yolo_lc.py", - "package": "rbss_objectdetection" - }, - "Settings": { - "result": "{\"params\": \\${ITEM:Array:[]}}", - "context": "type ITEM = {\"name\": \\${NAME:string:default},\"value\": \\${VALUE:string:default}};", - "form": [ - "{\"name\":\"ITEM\",\"type\":\"Array\",\"defaultValue\":\"[]\",\"values\":[{\"name\":\"NAME\",\"type\":\"string\",\"defaultValue\":\"default\",\"isOpen\":false,\"id\":\"fa8b442a-101b-448b-b5fd-55ad64f8e578\"},{\"name\":\"VALUE\",\"type\":\"string\",\"defaultValue\":\"default\",\"isOpen\":false,\"id\":\"c59b86d8-a54b-42da-b2bf-5fdfeff7f711\"}],\"totalValue\":[],\"isOpen\":true,\"subType\":\"ITEM\",\"id\":\"8be7af67-5860-4a3f-bdab-09c75a53bff7\"}" - ], - "output": { - "params": [] - }, - "type": "formBuilder" - }, - "__v": 0 -} \ No newline at end of file diff --git a/web_p/rbs_train2.py b/web_p/rbs_train2.py deleted file mode 100644 index f72257d..0000000 --- a/web_p/rbs_train2.py +++ /dev/null @@ -1,64 +0,0 @@ -""" - rbs_train2 - Общая задача: web-service pipeline - Реализуемая функция: обучение нейросетевой модели по заданному BOP-датасету - - python3 $PYTHON_EDUCATION --path /home/user/webservice/server/build/public/process/proc/inst_proc \ - --form /home/user/webservice/server/build/public/process/proc/inst_proc/form.json - - 28.01.2025 @shalenikol release 0.1 - 17.02.2025 @shalenikol release 0.2 addon_dir -""" -import argparse -import os -import json -from train_Yolo import train_YoloV8 -from train_Dope import train_Dope_i - -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument("--path", required=True, help="Output path for weights") - parser.add_argument("--form", required=True, help="Json-file with training parameters") - args = parser.parse_args() - - if not os.path.isdir(args.path): - print(f"Invalid output path '{args.path}'") - exit(-1) - wname = os.path.basename(args.path) - outpath = os.path.dirname(args.path) - - if not os.path.isfile(args.form): - print(f"Error: no such file '{args.form}'") - exit(-2) - with open(args.form, "r") as f: - j_data = f.read() - try: - cfg = json.loads(j_data) - except json.JSONDecodeError as e: - print(f"JSon error: {e}") - exit(-3) - - cfg = cfg["output"] # edited params - dataset_params = cfg["process"]["selectProcess"]["value"] - dataset_type = dataset_params["type"] - if dataset_type != "BOP_DATASET": - print(f"Error: Invalid dataset type '{dataset_type}'") - exit(-4) - dataset_name = dataset_params["instanceName"] - dataset_path = dataset_params["path"] - dataset_path = dataset_path.replace("//", "/") # !!! TODO !!! Nikita - - epoch = cfg["n_epoch"] - pretrain = (cfg["pretrain"] == "True") #False - ttype = cfg["typeWeight"] #"ObjectDetection" - - addon_dir = "" - if "addon" in cfg: - addon = cfg["addon"].strip() - if addon and os.path.isdir(addon): - addon_dir = addon - - if ttype == "ObjectDetection": - train_YoloV8(dataset_path, wname, dataset_name, outpath, epoch, pretrain, addon_dir) - else: - train_Dope_i(dataset_path, wname, dataset_name, outpath, epoch, pretrain) diff --git a/web_p/renderBOPdataset2.py b/web_p/renderBOPdataset2.py index 3a037fd..61cdf90 100755 --- a/web_p/renderBOPdataset2.py +++ b/web_p/renderBOPdataset2.py @@ -8,7 +8,6 @@ import blenderproc as bproc 02.05.2024 @shalenikol release 0.1 02.07.2024 @shalenikol release 0.2 28.10.2024 @shalenikol release 0.3 - 28.02.2025 @shalenikol release 0.4 blenderproc 2.8.0 + blender 4.2.1 LTS """ import numpy as np import argparse @@ -17,32 +16,17 @@ import os import shutil import json from pathlib import Path -import time - -########################### -# !!! чтобы избежать ошибки в версии 2.8.0 -# free(): invalid pointer -# при вызове bproc.writer.write_bop -import pyrender -from pyrender.platforms import egl -########################### - -start_time = time.time() # Запоминаем время начала import bpy VHACD_PATH = "blenderproc_resources/vhacd" DIR_MODELS = "models" -# DIR_MESH = "assets/libs/objects/" +DIR_MESH = "assets/libs/objects/" FILE_LOG_SCENE = "res.txt" FILE_RBS_INFO = "rbs_info.json" FILE_GT_COCO = "scene_gt_coco.json" -FILE_PARAMS = "form.json" -PROCEDURAL_TEXTURE = "texture_path" # key in randomization params: for texture types (Noise Textures), (Procedural Patterns) or (Tileable Textures) -EXT_MODELS = ".fbx" # for scene objects (floor ...) -DETAIL_KEY = "daeUrl" # "fbx" # key in dict 'Detail' for mesh path of model +EXT_MODELS = ".fbx" TEXTURE_TMPL = "*.jpg" -TEXTURE_IMAGE_TYPES = ["Base Color", "Metallic", "Normal", "Roughness", "Specular IOR Level"] Not_Categories_Name = True # наименование категории в COCO-аннотации отсутствует @@ -73,50 +57,19 @@ def convert2relative(height, width, bbox): y += h/2 return x/width, y/height, w/width, h/height -def convert_seconds(total_seconds): - hours = int(total_seconds // 3600) - minutes = int((total_seconds % 3600) // 60) - seconds = int(total_seconds % 60) - return f"{hours:02}:{minutes:02}:{seconds:02}" - def render() -> int: - res_dir = rnd_par.output_dir - log_dir = os.path.dirname(res_dir) - # copy file with randomization params - file_params = os.path.join(res_dir, FILE_PARAMS) - if os.path.isfile(file_params): - shutil.copy2(file_params, log_dir) - - if os.path.isdir(res_dir): - shutil.rmtree(res_dir) - i = 0 for obj in all_meshs: # Make the object actively participate in the physics simulation obj.enable_rigidbody(active=True, collision_shape="COMPOUND") # Also use convex decomposition as collision shapes obj.build_convex_decomposition_collision_shape(VHACD_PATH) - - # # это для procedural texture, но пока не правильно - # fn = (os.path.splitext(rnd_par.models.filenames[i]))[0] + ".jpg" # файл с текстурой - # if os.path.isfile(fn): - # material = bproc.material.create_material_from_texture(fn, material_name="texture_model"+str(i)) - # # Применяем текстуру к материалу - # obj.replace_materials(material) - tex = rnd_par.models.textures[i] # описание текстур - if tex["is"]: - mat = bproc.material.create("m"+str(i)) - for x in tex["t_images"]: - key = list(x.keys())[0] - mat.set_principled_shader_value(key, bpy.data.images.load(filepath=x[key])) - obj.replace_materials(mat) - i += 1 # print(f"{i} : {obj.get_name()}") objs = all_meshs + rnd_par.scene.objs - log_txt = os.path.join(log_dir, FILE_LOG_SCENE) + log_txt = os.path.join(os.path.dirname(rnd_par.output_dir), FILE_LOG_SCENE) with open(log_txt, "w") as fh: for i,o in enumerate(objs): loc = o.get_location() @@ -138,21 +91,23 @@ def render() -> int: rnd_par.image_size_wh[1], lens_unit="FOV") - # Enable transparency so the background becomes transparent - bproc.renderer.set_output_format(enable_transparency=True) # ??? # add segmentation masks (per class and per instance) bproc.renderer.enable_segmentation_output(map_by=["category_id", "instance", "name"]) # activate depth rendering bproc.renderer.enable_depth_output(activate_antialiasing=False) + # res_dir = os.path.join(rnd_par.output_dir, rnd_par.ds_name) + res_dir = rnd_par.output_dir + if os.path.isdir(res_dir): + shutil.rmtree(res_dir) # Цикл рендеринга # Do multiple times: Position the shapenet objects using the physics simulator and render X images with random camera poses for r in range(rnd_par.n_series): print(f"********** Series : {r+1}") - is_texture = True if PROCEDURAL_TEXTURE in rnd_par.models_randomization else False + is_texture = True if "texture_path" in rnd_par.models_randomization else False if is_texture: - val = rnd_par.models_randomization[PROCEDURAL_TEXTURE] + val = rnd_par.models_randomization["texture_path"] l_texture = _get_list_texture(val) image = bpy.data.images.load(filepath=str(l_texture[r % len(l_texture)])) # один случайный объект в кадре / все заданные объекты @@ -170,32 +125,16 @@ def render() -> int: for i,o in enumerate(rnd_par.scene.objs): # объекты сцены rnd_mat = rnd_par.scene.obj_data[i]["material_randomization"] - - # if PROCEDURAL_TEXTURE in rnd_mat: # путь к текстурам (*.jpg) - # mat = bproc.material.create("m"+str(i)) - # # for x in tex["t_images"]: - # # key = list(x.keys())[0] - # val = rnd_mat[PROCEDURAL_TEXTURE] - # val = _get_list_texture(val) - # image = bpy.data.images.load(filepath=str(random.choice(val))) - # mat.set_principled_shader_value("Base Color", image) - # o.replace_materials(mat) - mats = o.get_materials() #[0] for mat in mats: - - # with open(log_txt, "a") as fh: - # fh.write("************* mat\n") - # fh.write(f"{mat}\n") - val = rnd_mat["specular"] - mat.set_principled_shader_value("Specular IOR Level", random.uniform(val[0], val[1])) # для Blender < 4.2 было "Specular" + mat.set_principled_shader_value("Specular", random.uniform(val[0], val[1])) val = rnd_mat["roughness"] mat.set_principled_shader_value("Roughness", random.uniform(val[0], val[1])) val = rnd_mat["metallic"] mat.set_principled_shader_value("Metallic", random.uniform(val[0], val[1])) - if PROCEDURAL_TEXTURE in rnd_mat: # путь к текстурам (*.jpg) - val = rnd_mat[PROCEDURAL_TEXTURE] + if "texture_path" in rnd_mat: # путь к текстурам (*.jpg) + val = rnd_mat["texture_path"] val = _get_list_texture(val) image = bpy.data.images.load(filepath=str(random.choice(val))) mat.set_principled_shader_value("Base Color", image) @@ -217,7 +156,7 @@ def render() -> int: # Define a function that samples 6-DoF poses def sample_pose(obj: bproc.types.MeshObject): obj.set_location(np.random.uniform(rnd_par.loc_range_low, rnd_par.loc_range_high)) #[-1, -1, 0], [1, 1, 2])) - obj.set_rotation_euler(bproc.sampler.uniformSO3(around_x=rnd_par.around_x, around_y=rnd_par.around_y, around_z=rnd_par.around_z)) + obj.set_rotation_euler(bproc.sampler.uniformSO3()) # Sample the poses of all shapenet objects above the ground without any collisions in-between bproc.object.sample_poses(meshs, @@ -293,12 +232,7 @@ def render() -> int: rec["name"] = objn rec["model"] = os.path.join(DIR_MODELS, os.path.split(rnd_par.models.filenames[i])[1]) # путь относительный t = [obj.get_bound_box(local_coords=True).tolist() for obj in all_meshs if obj.get_name() == objn] - if len(t) > 0: - rec["cuboid"] = t[0] - else: # object name does not match file name - rec["Error"] = "!!! object name does not match file name: cuboid is zero" - rec["cuboid"] = np.zeros((8, 3)).tolist() - + rec["cuboid"] = t[0] data.append(rec) shutil.copy2(rnd_par.models.filenames[i], models_dir) f = (os.path.splitext(rnd_par.models.filenames[i]))[0] + ".mtl" # файл материала @@ -349,37 +283,9 @@ def render() -> int: if Not_Categories_Name: explore(res_dir) - - end_time = time.time() # время окончания - execution_time = end_time - start_time # время выполнения - with open(log_txt, "a") as fh: - fh.write("*****************\n") - fh.write(f"Время выполнения: {convert_seconds(execution_time)}\n") return 0 # success -def set_texture_model(name: str, textures: list, model_d) -> None: - """ - textures заполняется массивом текстур вида: - [{"is": True, "t_images": [{"Base Color":"/path/to/shkaf_d.png"}, {"Normal":"/path/to/shkaf_n.png"}] }, ... ] - """ - d = {"is": False} - if "models" in model_d: - for model in model_d["models"]: - if model["name"] == name: - path = model["texture_dir"].strip() - if path: - t_images = [] - for x in TEXTURE_IMAGE_TYPES: - if x in model: - rel_path = model[x].strip() - if rel_path: - t_images.append({x: os.path.join(path, rel_path)}) - if len(t_images): - d["is"] = True - d["t_images"] = t_images - textures.append(d) - -def _get_models(par, data, models_data) -> int: +def _get_models(par, data) -> int: global all_meshs par.models = lambda: None @@ -388,14 +294,13 @@ def _get_models(par, data, models_data) -> int: return 0 # no models # загрузим объекты - par.models.names = [] - par.models.filenames = [] - par.models.textures = [] + par.models.names = [] # obj_names + par.models.filenames = [] # obj_filenames i = 1 for f in data: nam = f["name"] par.models.names.append(nam) - ff = f[DETAIL_KEY] # _get_path_model(nam) + ff = f["fbx"] # _get_path_model(nam) par.models.filenames.append(ff) if not os.path.isfile(ff): print(f"Error: no such file '{ff}'") @@ -406,7 +311,6 @@ def _get_models(par, data, models_data) -> int: obj = bproc.loader.load_obj(ff) all_meshs += obj obj[0].set_cp("category_id", i) # начиная с 1 - set_texture_model(nam, par.models.textures, models_data) i += 1 return par.models.n_item @@ -466,6 +370,8 @@ if __name__ == "__main__": print(f"JSon error: {e}") exit(-2) + # output_dir = args.path + ds_cfg = cfg["output"] # dataset config generation = ds_cfg["generation"] cam_pos = ds_cfg["camera_position"] @@ -493,14 +399,11 @@ if __name__ == "__main__": rnd_par.models_randomization = models_randomization rnd_par.loc_range_low = models_randomization["loc_range_low"] rnd_par.loc_range_high = models_randomization["loc_range_high"] - rnd_par.around_x = (models_randomization["around_x"] == "True") - rnd_par.around_y = (models_randomization["around_y"] == "True") - rnd_par.around_z = (models_randomization["around_z"] == "True") bproc.init() all_meshs = [] - if _get_models(rnd_par, rnd_par.dataset_objs, models_randomization) <= 0: + if _get_models(rnd_par, rnd_par.dataset_objs) <= 0: print("Error: no models in config") exit(-4) if _get_scene(rnd_par, ds_cfg["scene"]) <= 0: diff --git a/web_p/train/F1_curve.png b/web_p/train/F1_curve.png new file mode 100644 index 0000000..1a4e9ca Binary files /dev/null and b/web_p/train/F1_curve.png differ diff --git a/web_p/train/PR_curve.png b/web_p/train/PR_curve.png new file mode 100644 index 0000000..c961c64 Binary files /dev/null and b/web_p/train/PR_curve.png differ diff --git a/web_p/train/P_curve.png b/web_p/train/P_curve.png new file mode 100644 index 0000000..b62292b Binary files /dev/null and b/web_p/train/P_curve.png differ diff --git a/web_p/train/R_curve.png b/web_p/train/R_curve.png new file mode 100644 index 0000000..757d47e Binary files /dev/null and b/web_p/train/R_curve.png differ diff --git a/web_p/train/args.yaml b/web_p/train/args.yaml new file mode 100644 index 0000000..f69a613 --- /dev/null +++ b/web_p/train/args.yaml @@ -0,0 +1,105 @@ +task: detect +mode: train +model: /home/shalenikol/fork_work/webservice/server/build/public/4c4f3909-74b0-4206-aec1-fc4acd3a1081/weights/od_w01/yolov8n.pt +data: /home/shalenikol/fork_work/webservice/server/build/public/4c4f3909-74b0-4206-aec1-fc4acd3a1081/weights/od_w01/rbs_train.yaml +epochs: 33 +time: null +patience: 50 +batch: 16 +imgsz: 640 +save: true +save_period: -1 +cache: false +device: null +workers: 8 +project: /home/shalenikol/fork_work/webservice/server/build/public/4c4f3909-74b0-4206-aec1-fc4acd3a1081/weights/od_w01 +name: train +exist_ok: false +pretrained: true +optimizer: auto +verbose: true +seed: 0 +deterministic: true +single_cls: false +rect: false +cos_lr: false +close_mosaic: 10 +resume: false +amp: true +fraction: 1.0 +profile: false +freeze: null +multi_scale: false +overlap_mask: true +mask_ratio: 4 +dropout: 0.0 +val: true +split: val +save_json: false +save_hybrid: false +conf: null +iou: 0.7 +max_det: 300 +half: false +dnn: false +plots: true +source: null +vid_stride: 1 +stream_buffer: false +visualize: false +augment: false +agnostic_nms: false +classes: null +retina_masks: false +embed: null +show: false +save_frames: false +save_txt: false +save_conf: false +save_crop: false +show_labels: true +show_conf: true +show_boxes: true +line_width: null +format: torchscript +keras: false +optimize: false +int8: false +dynamic: false +simplify: false +opset: null +workspace: 4 +nms: false +lr0: 0.01 +lrf: 0.01 +momentum: 0.937 +weight_decay: 0.0005 +warmup_epochs: 3.0 +warmup_momentum: 0.8 +warmup_bias_lr: 0.1 +box: 7.5 +cls: 0.5 +dfl: 1.5 +pose: 12.0 +kobj: 1.0 +label_smoothing: 0.0 +nbs: 64 +hsv_h: 0.015 +hsv_s: 0.7 +hsv_v: 0.4 +degrees: 0.0 +translate: 0.1 +scale: 0.5 +shear: 0.0 +perspective: 0.0 +flipud: 0.0 +fliplr: 0.5 +mosaic: 1.0 +mixup: 0.0 +copy_paste: 0.0 +auto_augment: randaugment +erasing: 0.4 +crop_fraction: 1.0 +cfg: null +tracker: botsort.yaml +save_dir: /home/shalenikol/fork_work/webservice/server/build/public/4c4f3909-74b0-4206-aec1-fc4acd3a1081/weights/od_w01/train diff --git a/web_p/train/confusion_matrix.png b/web_p/train/confusion_matrix.png new file mode 100644 index 0000000..24a7fb8 Binary files /dev/null and b/web_p/train/confusion_matrix.png differ diff --git a/web_p/train/confusion_matrix_normalized.png b/web_p/train/confusion_matrix_normalized.png new file mode 100644 index 0000000..29fc07a Binary files /dev/null and b/web_p/train/confusion_matrix_normalized.png differ diff --git a/web_p/train/events.out.tfevents.1732122141.shalenikol-desktop.109110.0 b/web_p/train/events.out.tfevents.1732122141.shalenikol-desktop.109110.0 new file mode 100644 index 0000000..825cfe6 Binary files /dev/null and b/web_p/train/events.out.tfevents.1732122141.shalenikol-desktop.109110.0 differ diff --git a/web_p/train/labels.jpg b/web_p/train/labels.jpg new file mode 100644 index 0000000..b9fb066 Binary files /dev/null and b/web_p/train/labels.jpg differ diff --git a/web_p/train/labels_correlogram.jpg b/web_p/train/labels_correlogram.jpg new file mode 100644 index 0000000..e634e7b Binary files /dev/null and b/web_p/train/labels_correlogram.jpg differ diff --git a/web_p/train/results.csv b/web_p/train/results.csv new file mode 100644 index 0000000..b6231f9 --- /dev/null +++ b/web_p/train/results.csv @@ -0,0 +1,34 @@ + epoch, train/box_loss, train/cls_loss, train/dfl_loss, metrics/precision(B), metrics/recall(B), metrics/mAP50(B), metrics/mAP50-95(B), val/box_loss, val/cls_loss, val/dfl_loss, lr/pg0, lr/pg1, lr/pg2 + 1, 0.62674, 1.281, 0.92555, 0.99239, 0.99448, 0.99323, 0.90966, 0.40212, 0.8264, 0.80447, 0.00066247, 0.00066247, 0.00066247 + 2, 0.60996, 0.71899, 0.93387, 0.9945, 0.99945, 0.99484, 0.91551, 0.43253, 0.60301, 0.8228, 0.0012893, 0.0012893, 0.0012893 + 3, 0.58648, 0.54879, 0.92909, 1, 0.98871, 0.99494, 0.9213, 0.40211, 0.39327, 0.81593, 0.0018761, 0.0018761, 0.0018761 + 4, 0.58195, 0.48301, 0.92375, 0.99087, 0.9337, 0.97172, 0.89393, 0.41614, 0.46785, 0.82069, 0.00182, 0.00182, 0.00182 + 5, 0.56201, 0.44926, 0.92381, 0.99447, 0.99385, 0.99494, 0.94951, 0.34807, 0.32406, 0.8013, 0.00182, 0.00182, 0.00182 + 6, 0.52696, 0.40581, 0.9068, 0.95813, 0.98343, 0.99281, 0.94494, 0.33023, 0.48053, 0.79401, 0.00176, 0.00176, 0.00176 + 7, 0.51017, 0.3952, 0.90752, 0.99889, 1, 0.995, 0.95388, 0.3192, 0.33973, 0.7992, 0.0017, 0.0017, 0.0017 + 8, 0.50772, 0.37889, 0.90238, 0.98351, 0.98842, 0.98581, 0.94918, 0.30154, 0.28504, 0.79667, 0.00164, 0.00164, 0.00164 + 9, 0.47737, 0.3576, 0.89251, 0.99946, 0.99448, 0.995, 0.97205, 0.28135, 0.23642, 0.79101, 0.00158, 0.00158, 0.00158 + 10, 0.46587, 0.34547, 0.89324, 0.99948, 1, 0.995, 0.96897, 0.28021, 0.28522, 0.78694, 0.00152, 0.00152, 0.00152 + 11, 0.45881, 0.33452, 0.89055, 0.99954, 1, 0.995, 0.97012, 0.26364, 0.21443, 0.7813, 0.00146, 0.00146, 0.00146 + 12, 0.44939, 0.32887, 0.89206, 0.9996, 1, 0.995, 0.98382, 0.24486, 0.20614, 0.78109, 0.0014, 0.0014, 0.0014 + 13, 0.44388, 0.32289, 0.88796, 0.99932, 1, 0.995, 0.97195, 0.27681, 0.21443, 0.77933, 0.00134, 0.00134, 0.00134 + 14, 0.43847, 0.31282, 0.88496, 0.99965, 1, 0.995, 0.98019, 0.25014, 0.20255, 0.7775, 0.00128, 0.00128, 0.00128 + 15, 0.41585, 0.30067, 0.8774, 0.99943, 1, 0.995, 0.97609, 0.25842, 0.21239, 0.78006, 0.00122, 0.00122, 0.00122 + 16, 0.41436, 0.29784, 0.87488, 0.99964, 1, 0.995, 0.97823, 0.25499, 0.19837, 0.78004, 0.00116, 0.00116, 0.00116 + 17, 0.414, 0.29771, 0.87575, 0.99943, 1, 0.995, 0.98746, 0.2251, 0.203, 0.77468, 0.0011, 0.0011, 0.0011 + 18, 0.39273, 0.29075, 0.86927, 0.99445, 1, 0.995, 0.98597, 0.22693, 0.19648, 0.77208, 0.00104, 0.00104, 0.00104 + 19, 0.40052, 0.28802, 0.87804, 0.99958, 1, 0.995, 0.98541, 0.22268, 0.18749, 0.77233, 0.00098, 0.00098, 0.00098 + 20, 0.38066, 0.27951, 0.86666, 0.99969, 1, 0.995, 0.98901, 0.20959, 0.1775, 0.7697, 0.00092, 0.00092, 0.00092 + 21, 0.38115, 0.27813, 0.8658, 0.99964, 1, 0.995, 0.98895, 0.20699, 0.1779, 0.77073, 0.00086, 0.00086, 0.00086 + 22, 0.37441, 0.27094, 0.87121, 0.99965, 1, 0.995, 0.98975, 0.20138, 0.17235, 0.76785, 0.0008, 0.0008, 0.0008 + 23, 0.36808, 0.26148, 0.86426, 0.99965, 1, 0.995, 0.98829, 0.19861, 0.1628, 0.76706, 0.00074, 0.00074, 0.00074 + 24, 0.25547, 0.199, 0.77555, 0.99955, 1, 0.995, 0.98791, 0.21853, 0.18063, 0.76972, 0.00068, 0.00068, 0.00068 + 25, 0.24799, 0.1969, 0.78404, 0.99958, 1, 0.995, 0.98812, 0.23069, 0.18178, 0.76985, 0.00062, 0.00062, 0.00062 + 26, 0.24232, 0.1915, 0.78022, 0.99968, 1, 0.995, 0.99024, 0.20883, 0.16788, 0.76752, 0.00056, 0.00056, 0.00056 + 27, 0.23288, 0.1839, 0.77463, 0.99968, 1, 0.995, 0.99151, 0.2026, 0.16501, 0.76809, 0.0005, 0.0005, 0.0005 + 28, 0.23066, 0.18012, 0.77547, 0.99961, 1, 0.995, 0.98912, 0.19388, 0.1534, 0.76246, 0.00044, 0.00044, 0.00044 + 29, 0.22286, 0.17062, 0.77932, 0.9997, 1, 0.995, 0.99039, 0.20566, 0.14978, 0.76601, 0.00038, 0.00038, 0.00038 + 30, 0.21427, 0.16357, 0.77529, 0.9997, 1, 0.995, 0.99215, 0.18345, 0.14148, 0.76206, 0.00032, 0.00032, 0.00032 + 31, 0.20895, 0.16067, 0.77189, 0.9997, 1, 0.995, 0.99187, 0.17027, 0.13746, 0.76124, 0.00026, 0.00026, 0.00026 + 32, 0.20248, 0.15421, 0.77526, 0.9997, 1, 0.995, 0.99246, 0.17229, 0.13828, 0.76056, 0.0002, 0.0002, 0.0002 + 33, 0.19494, 0.15005, 0.76361, 0.99971, 1, 0.995, 0.99302, 0.16442, 0.12543, 0.76043, 0.00014, 0.00014, 0.00014 diff --git a/web_p/train/results.png b/web_p/train/results.png new file mode 100644 index 0000000..1978bab Binary files /dev/null and b/web_p/train/results.png differ diff --git a/web_p/train/train_batch0.jpg b/web_p/train/train_batch0.jpg new file mode 100644 index 0000000..4cb50d7 Binary files /dev/null and b/web_p/train/train_batch0.jpg differ diff --git a/web_p/train/train_batch1.jpg b/web_p/train/train_batch1.jpg new file mode 100644 index 0000000..5a20720 Binary files /dev/null and b/web_p/train/train_batch1.jpg differ diff --git a/web_p/train/train_batch2.jpg b/web_p/train/train_batch2.jpg new file mode 100644 index 0000000..0ccd9f0 Binary files /dev/null and b/web_p/train/train_batch2.jpg differ diff --git a/web_p/train/train_batch3657.jpg b/web_p/train/train_batch3657.jpg new file mode 100644 index 0000000..0f1eac8 Binary files /dev/null and b/web_p/train/train_batch3657.jpg differ diff --git a/web_p/train/train_batch3658.jpg b/web_p/train/train_batch3658.jpg new file mode 100644 index 0000000..615376e Binary files /dev/null and b/web_p/train/train_batch3658.jpg differ diff --git a/web_p/train/train_batch3659.jpg b/web_p/train/train_batch3659.jpg new file mode 100644 index 0000000..0248e7a Binary files /dev/null and b/web_p/train/train_batch3659.jpg differ diff --git a/web_p/train/val_batch0_labels.jpg b/web_p/train/val_batch0_labels.jpg new file mode 100644 index 0000000..bd751a0 Binary files /dev/null and b/web_p/train/val_batch0_labels.jpg differ diff --git a/web_p/train/val_batch0_pred.jpg b/web_p/train/val_batch0_pred.jpg new file mode 100644 index 0000000..aac07e4 Binary files /dev/null and b/web_p/train/val_batch0_pred.jpg differ diff --git a/web_p/train/val_batch1_labels.jpg b/web_p/train/val_batch1_labels.jpg new file mode 100644 index 0000000..23b8ca3 Binary files /dev/null and b/web_p/train/val_batch1_labels.jpg differ diff --git a/web_p/train/val_batch1_pred.jpg b/web_p/train/val_batch1_pred.jpg new file mode 100644 index 0000000..b71a650 Binary files /dev/null and b/web_p/train/val_batch1_pred.jpg differ diff --git a/web_p/train/val_batch2_labels.jpg b/web_p/train/val_batch2_labels.jpg new file mode 100644 index 0000000..353ceea Binary files /dev/null and b/web_p/train/val_batch2_labels.jpg differ diff --git a/web_p/train/val_batch2_pred.jpg b/web_p/train/val_batch2_pred.jpg new file mode 100644 index 0000000..ce2e32f Binary files /dev/null and b/web_p/train/val_batch2_pred.jpg differ diff --git a/web_p/train/weights/best.pt b/web_p/train/weights/best.pt new file mode 100644 index 0000000..943e696 Binary files /dev/null and b/web_p/train/weights/best.pt differ diff --git a/web_p/train/weights/last.pt b/web_p/train/weights/last.pt new file mode 100644 index 0000000..d48d34d Binary files /dev/null and b/web_p/train/weights/last.pt differ diff --git a/web_p/train_Yolo.py b/web_p/train_Yolo.py index 6d77902..1eaf7a0 100644 --- a/web_p/train_Yolo.py +++ b/web_p/train_Yolo.py @@ -7,8 +7,6 @@ --name test123 --datasetName ds213 --outpath /Users/idontsudo/webservice/server/build/public/7065d6b6-c8a3-48c5-9679-bb8f3a690296/weights 27.04.2024 @shalenikol release 0.1 - 20.11.2024 @shalenikol release 0.2 parser.add_argument("--addon", default="", help="Folder with add-on for dataset") - 20.02.2025 @shalenikol release 0.2.1 add_on_dataset : fix """ import os import shutil @@ -16,13 +14,9 @@ import json import yaml from ultralytics import YOLO -# from ultralytics import settings # from ultralytics.utils.metrics import DetMetrics -# import torch -# import torch.profiler -# import torch.utils.data -FILE_BASEMODEL = "yolov8s.pt" #"yolov8n.pt" +FILE_BASEMODEL = "yolov8n.pt" FILE_RBS_INFO = "rbs_info.json" FILE_RBS_TRAIN = "rbs_train.yaml" FILE_GT_COCO = "scene_gt_coco.json" @@ -33,7 +27,6 @@ DIR_ROOT_DS = "datasets" DIR_COCO_DS = "rbs_coco" DIR_RGB_DS = "images" DIR_LABELS_DS = "labels" -LABELS_EXT = ".txt" SZ_SERIES = 15 # number of train images per validation images @@ -47,50 +40,6 @@ def convert2relative(height, width, bbox): y += h/2 return x/width, y/height, w/width, h/height -def add_on_dataset(source_dir, target_dir) -> dict: - global nn_image, f1, f2 - # Получаем список файлов в исходной директории - files = sorted(os.listdir(source_dir)) - - # Словарь для отслеживания порядковых номеров для каждого имени файла - file_nn = {} - - for file in files: - if os.path.isdir(os.path.join(source_dir, file)): - continue - # Получаем имя файла и его расширение - file_name, file_extension = os.path.splitext(file) - - # Запоминаем порядковый номер для данного имени файла - if file_name in file_nn: - nn = file_nn[file_name] - else: # new file name - nn = nn_image # текущий номер - file_nn[file_name] = nn_image - nn_image += 1 - - # Создаем новое имя файла - new_file_name = f"{nn:06}{file_extension}" - if file_extension == LABELS_EXT: - new_file_path = os.path.join(target_dir, DIR_LABELS_DS) - else: - new_file_path = os.path.join(target_dir, DIR_RGB_DS) - - line = os.path.join("./", DIR_RGB_DS, new_file_name) + "\n" - if nn % SZ_SERIES == 0: - f2.write(line) - else: - f1.write(line) - - # Полные пути к старому и новому файлам - old_file_path = os.path.join(source_dir, file) - new_file_path = os.path.join(new_file_path, new_file_name) - - # Копируем файл - shutil.copy2(old_file_path, new_file_path) - - return file_nn - def gt_parse(path: str, out_dir: str): global nn_image, f1, f2 with open(os.path.join(path, FILE_GT_COCO), "r") as fh: @@ -118,12 +67,12 @@ def gt_parse(path: str, out_dir: str): # формат: fh.write(f"{cat_id-1} {rel[0]} {rel[1]} {rel[2]} {rel[3]}\n") # category from 0 + nn_image += 1 line = os.path.join("./", DIR_RGB_DS, f + ext) + "\n" if nn_image % SZ_SERIES == 0: f2.write(line) else: f1.write(line) - nn_image += 1 def explore(path: str, res_dir: str): if not os.path.isdir(path): @@ -139,7 +88,7 @@ def explore(path: str, res_dir: str): else: explore(path_entry, res_dir) -def BOP2Yolo_dataset(dpath: str, out_dir: str, lname: list, addon:str) -> str: +def BOP2Yolo_dataset(dpath: str, out_dir: str, lname: list) -> str: """ Convert BOP-dataset to YOLO format for train """ cfg_yaml = os.path.join(out_dir, FILE_RBS_TRAIN) p = os.path.join(out_dir, DIR_ROOT_DS, DIR_COCO_DS) @@ -167,14 +116,12 @@ def BOP2Yolo_dataset(dpath: str, out_dir: str, lname: list, addon:str) -> str: f1 = open(os.path.join(res_dir, FILE_L_TRAIN), "w") f2 = open(os.path.join(res_dir, FILE_L_VAL), "w") explore(dpath, res_dir) - if addon: - add_on_dataset(addon, res_dir) f1.close() f2.close() return out_dir -def train_YoloV8(path:str, wname:str, dname:str, outpath:str, epochs:int, pretrain: bool, addon: str): +def train_YoloV8(path:str, wname:str, dname:str, outpath:str, epochs:int, pretrain: bool): """ Main procedure for train YOLOv8 model """ if not os.path.isdir(outpath): print(f"Invalid output path '{outpath}'") @@ -204,33 +151,21 @@ def train_YoloV8(path:str, wname:str, dname:str, outpath:str, epochs:int, pretra # список имён объектов list_name = list(map(lambda x: x["name"], y)) - dpath = BOP2Yolo_dataset(ds_path, out_dir, list_name, addon) + dpath = BOP2Yolo_dataset(ds_path, out_dir, list_name) if len(dpath) == 0: print(f"Error in convert dataset '{ds_path}' to '{outpath}'") exit(-4) model_path = os.path.join(dpath, FILE_BASEMODEL) model = YOLO(model_path) - - # # Update settings - # settings.update({"profile": True}) - - # prof = torch.profiler.profile( - # schedule=torch.profiler.schedule(wait=1, warmup=1, active=3, repeat=1), - # on_trace_ready=torch.profiler.tensorboard_trace_handler('./log/resnet18'), - # record_shapes=True, - # with_stack=True) - # prof.start() - results = model.train(data=os.path.join(dpath, FILE_RBS_TRAIN), epochs=epochs, project=out_dir) #, log_dir="runs/train") - # prof.stop() - + results = model.train(data=os.path.join(dpath, FILE_RBS_TRAIN), epochs=epochs, project=out_dir) wf = os.path.join(results.save_dir, FILE_TRAIN_RES) if not os.path.isfile(wf): print(f"Error in train: no result file '{wf}'") exit(-5) shutil.copy2(wf, os.path.join(dpath, wname + ".pt")) - # shutil.rmtree(results.save_dir) + shutil.rmtree(results.save_dir) if __name__ == "__main__": import argparse @@ -241,7 +176,6 @@ if __name__ == "__main__": parser.add_argument("--outpath", default="weights", help="Output path for weights") parser.add_argument("--epoch", default=3, type=int, help="How many training epochs") parser.add_argument('--pretrain', action="store_true", help="Use pretraining") - parser.add_argument("--addon", default="", help="Folder with add-on for dataset") args = parser.parse_args() - train_YoloV8(args.path, args.name, args.datasetName, args.outpath, args.epoch, args.pretrain, args.addon) + train_YoloV8(args.path, args.name, args.datasetName, args.outpath, args.epoch, args.pretrain) diff --git a/web_p/utils_dope.py b/web_p/utils_dope.py deleted file mode 100755 index 55ab058..0000000 --- a/web_p/utils_dope.py +++ /dev/null @@ -1,967 +0,0 @@ -""" -NVIDIA from jtremblay@gmail.com -""" -import numpy as np -import torch - -import os - -import torch -import torch.nn as nn -import torch.nn.parallel - -import torch.utils.data - -import torchvision.transforms as transforms - -import torch.utils.data as data -import glob -import os -import boto3 -import io - -from PIL import Image -from PIL import ImageDraw -from PIL import ImageEnhance - -from math import acos -from math import sqrt -from math import pi - -from os.path import exists, basename -import json -from os.path import join - -import albumentations as A - - -def default_loader(path): - return Image.open(path).convert("RGB") - - -def length(v): - return sqrt(v[0] ** 2 + v[1] ** 2) - - -def dot_product(v, w): - return v[0] * w[0] + v[1] * w[1] - - -def normalize(v): - norm = np.linalg.norm(v, ord=1) - if norm == 0: - norm = np.finfo(v.dtype).eps - return v / norm - - -def determinant(v, w): - return v[0] * w[1] - v[1] * w[0] - - -def inner_angle(v, w): - cosx = dot_product(v, w) / (length(v) * length(w)) - rad = acos(cosx) # in radians - return rad * 180 / pi # returns degrees - - -def py_ang(A, B=(1, 0)): - inner = inner_angle(A, B) - det = determinant(A, B) - if ( - det < 0 - ): # this is a property of the det. If the det < 0 then B is clockwise of A - return inner - else: # if the det > 0 then A is immediately clockwise of B - return 360 - inner - - -import colorsys, math - - -def append_dot(extensions): - res = [] - - for ext in extensions: - if not ext.startswith("."): - res.append(f".{ext}") - else: - res.append(ext) - - return res - - -def loadimages(root, extensions=["png"]): - imgs = [] - extensions = append_dot(extensions) - - def add_json_files( - path, - ): - for ext in extensions: - for file in os.listdir(path): - imgpath = os.path.join(path, file) - if ( - imgpath.endswith(ext) - and exists(imgpath) - and exists(imgpath.replace(ext, ".json")) - ): - imgs.append( - ( - imgpath, - imgpath.replace(path, "").replace("/", ""), - imgpath.replace(ext, ".json"), - ) - ) - - def explore(path): - if not os.path.isdir(path): - return - folders = [ - os.path.join(path, o) - for o in os.listdir(path) - if os.path.isdir(os.path.join(path, o)) - ] - - for path_entry in folders: - explore(path_entry) - - add_json_files(path) - - explore(root) - - return imgs - - -def loadweights(root): - if root.endswith(".pth") and os.path.isfile(root): - return [root] - else: - weights = [ - os.path.join(root, f) - for f in os.listdir(root) - if os.path.isfile(os.path.join(root, f)) and f.endswith(".pth") - ] - - weights.sort() - return weights - - -def loadimages_inference(root, extensions): - imgs, imgsname = [], [] - extensions = append_dot(extensions) - - def add_imgs( - path, - ): - for ext in extensions: - for file in os.listdir(path): - imgpath = os.path.join(path, file) - if imgpath.endswith(ext) and exists(imgpath): - imgs.append(imgpath) - imgsname.append(imgpath.replace(root, "")) - - def explore(path): - if not os.path.isdir(path): - return - folders = [ - os.path.join(path, o) - for o in os.listdir(path) - if os.path.isdir(os.path.join(path, o)) - ] - - for path_entry in folders: - explore(path_entry) - - add_imgs(path) - - explore(root) - - return imgs, imgsname - - -class CleanVisiiDopeLoader(data.Dataset): - def __init__( - self, - path_dataset, - objects=None, - sigma=1, - output_size=400, - extensions=["png"], - debug=False, - use_s3=False, - buckets=[], - endpoint_url=None, - ): - ################### - self.path_dataset = path_dataset - self.objects_interest = objects - self.sigma = sigma - self.output_size = output_size - self.extensions = append_dot(extensions) - self.debug = debug - ################### - - self.imgs = [] - self.s3_buckets = {} - self.use_s3 = use_s3 - - if self.use_s3: - self.session = boto3.Session() - self.s3 = self.session.resource( - service_name="s3", endpoint_url=endpoint_url - ) - - for bucket_name in buckets: - try: - self.s3_buckets[bucket_name] = self.s3.Bucket(bucket_name) - except Exception as e: - print( - f"Error trying to load bucket {bucket_name} for training data:", - e, - ) - - for bucket in self.s3_buckets: - bucket_objects = [ - str(obj.key) for obj in self.s3_buckets[bucket].objects.all() - ] - - jsons = set([json for json in bucket_objects if json.endswith(".json")]) - imgs = [ - img - for img in bucket_objects - if img.endswith(tuple(self.extensions)) - ] - - for ext in self.extensions: - for img in imgs: - # Only add images that have a ground truth file - if img.endswith(ext) and img.replace(ext, ".json") in jsons: - # (img key, bucket name, json key) - self.imgs.append((img, bucket, img.replace(ext, ".json"))) - - else: - for path_look in path_dataset: - self.imgs += loadimages(path_look, extensions=self.extensions) - - # np.random.shuffle(self.imgs) - print("Number of Training Images:", len(self.imgs)) - print(self.imgs) - - if debug: - print("Debuging will be save in debug/") - if os.path.isdir("debug"): - print(f'folder {"debug"}/ exists') - else: - os.mkdir("debug") - print(f'created folder {"debug"}/') - - def __len__(self): - return len(self.imgs) - - def __getitem__(self, index): - - # load the data - if self.use_s3: - img_key, bucket, json_key = self.imgs[index] - mem_img = io.BytesIO() - - object_img = self.s3_buckets[bucket].Object(img_key) - object_img.download_fileobj(mem_img) - - img = np.array(Image.open(mem_img).convert("RGB")) - - object_json = self.s3_buckets[bucket].Object(json_key) - data_json = json.load(object_json.get()["Body"]) - - img_name = img_key[:-3] - - else: - path_img, img_name, path_json = self.imgs[index] - - # load the image - img = np.array(Image.open(path_img).convert("RGB")) - - # load the json file - with open(path_json) as f: - data_json = json.load(f) - - all_projected_cuboid_keypoints = [] - - # load the projected cuboid keypoints - for obj in data_json["objects"]: - if ( - self.objects_interest is not None - and not obj["class"] in self.objects_interest - ): - continue - # load the projected_cuboid_keypoints - # 06.02.2024 @shalenikol - # if obj["visibility_image"] > 0: - if obj["visibility"] > 0: - projected_cuboid_keypoints = obj["projected_cuboid"] - # FAT dataset only has 8 corners for 'projected_cuboid' - if len(projected_cuboid_keypoints) == 8: - projected_cuboid_keypoints.append(obj["projected_cuboid_centroid"]) - else: - projected_cuboid_keypoints = [ - [-100, -100], - [-100, -100], - [-100, -100], - [-100, -100], - [-100, -100], - [-100, -100], - [-100, -100], - [-100, -100], - [-100, -100], - ] - all_projected_cuboid_keypoints.append(projected_cuboid_keypoints) - - if len(all_projected_cuboid_keypoints) == 0: - all_projected_cuboid_keypoints = [ - [ - [-100, -100], - [-100, -100], - [-100, -100], - [-100, -100], - [-100, -100], - [-100, -100], - [-100, -100], - [-100, -100], - [-100, -100], - ] - ] - - # flatten the keypoints - flatten_projected_cuboid = [] - for obj in all_projected_cuboid_keypoints: - for p in obj: - flatten_projected_cuboid.append(p) - - ####### - if self.debug: - img_to_save = Image.fromarray(img) - draw = ImageDraw.Draw(img_to_save) - - for ip, p in enumerate(flatten_projected_cuboid): - draw.ellipse( - (int(p[0]) - 2, int(p[1]) - 2, int(p[0]) + 2, int(p[1]) + 2), - fill="green", - ) - - img_to_save.save(f"debug/{img_name.replace('.png','_original.png')}") - ####### - - # data augmentation - transform = A.Compose( - [ - A.RandomCrop(width=400, height=400), - A.Rotate(limit=180), - A.RandomBrightnessContrast( - brightness_limit=0.2, contrast_limit=0.15, p=1 - ), - A.GaussNoise(p=1), - ], - keypoint_params=A.KeypointParams(format="xy", remove_invisible=False), - ) - transformed = transform(image=img, keypoints=flatten_projected_cuboid) - img_transformed = transformed["image"] - flatten_projected_cuboid_transformed = transformed["keypoints"] - - ####### - - # transform to the final output - if not self.output_size == 400: - transform = A.Compose( - [ - A.Resize(width=self.output_size, height=self.output_size), - ], - keypoint_params=A.KeypointParams(format="xy", remove_invisible=False), - ) - transformed = transform( - image=img_transformed, keypoints=flatten_projected_cuboid_transformed - ) - img_transformed_output_size = transformed["image"] - flatten_projected_cuboid_transformed_output_size = transformed["keypoints"] - - else: - img_transformed_output_size = img_transformed - flatten_projected_cuboid_transformed_output_size = ( - flatten_projected_cuboid_transformed - ) - - ####### - if self.debug: - img_transformed_saving = Image.fromarray(img_transformed) - - draw = ImageDraw.Draw(img_transformed_saving) - - for ip, p in enumerate(flatten_projected_cuboid_transformed): - draw.ellipse( - (int(p[0]) - 2, int(p[1]) - 2, int(p[0]) + 2, int(p[1]) + 2), - fill="green", - ) - - img_transformed_saving.save( - f"debug/{img_name.replace('.png','_transformed.png')}" - ) - ####### - - # update the keypoints list - # obj x keypoint_id x (x,y) - i_all = 0 - for i_obj, obj in enumerate(all_projected_cuboid_keypoints): - for i_p, point in enumerate(obj): - all_projected_cuboid_keypoints[i_obj][ - i_p - ] = flatten_projected_cuboid_transformed_output_size[i_all] - i_all += 1 - - # generate the belief maps - beliefs = CreateBeliefMap( - size=int(self.output_size), - pointsBelief=all_projected_cuboid_keypoints, - sigma=self.sigma, - nbpoints=9, - save=False, - ) - beliefs = torch.from_numpy(np.array(beliefs)) - # generate affinity fields with centroid. - affinities = GenerateMapAffinity( - size=int(self.output_size), - nb_vertex=8, - pointsInterest=all_projected_cuboid_keypoints, - objects_centroid=np.array(all_projected_cuboid_keypoints)[:, -1].tolist(), - scale=1, - ) - - # prepare for the image tensors - normalize_tensor = transforms.Compose( - [ - transforms.ToTensor(), - transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225)), - ] - ) - to_tensor = transforms.Compose( - [ - transforms.ToTensor(), - ] - ) - img_tensor = normalize_tensor(Image.fromarray(img_transformed)) - img_original = to_tensor(img_transformed) - - ######## - if self.debug: - imgs = VisualizeBeliefMap(beliefs) - img, grid = save_image( - imgs, - f"debug/{img_name.replace('.png','_beliefs.png')}", - mean=0, - std=1, - nrow=3, - save=True, - ) - imgs = VisualizeAffinityMap(affinities) - save_image( - imgs, - f"debug/{img_name.replace('.png','_affinities.png')}", - mean=0, - std=1, - nrow=3, - save=True, - ) - ######## - img_tensor[torch.isnan(img_tensor)] = 0 - affinities[torch.isnan(affinities)] = 0 - beliefs[torch.isnan(beliefs)] = 0 - - img_tensor[torch.isinf(img_tensor)] = 0 - affinities[torch.isinf(affinities)] = 0 - beliefs[torch.isinf(beliefs)] = 0 - - return { - "img": img_tensor, - "affinities": torch.clamp(affinities, -1, 1), - "beliefs": torch.clamp(beliefs, 0, 1), - "file_name": img_name, - "img_original": img_original, - } - - -def VisualizeAffinityMap( - tensor, - # tensor of (len(keypoints)*2)xwxh - threshold_norm_vector=0.4, - # how long does the vector has to be to be drawn - points=None, - # list of points to draw in white on top of the image - factor=1.0, - # by how much the image was reduced, scale factor - translation=(0, 0) - # by how much the points were moved - # return len(keypoints)x3xwxh # stack of images -): - images = torch.zeros(tensor.shape[0] // 2, 3, tensor.shape[1], tensor.shape[2]) - for i_image in range(0, tensor.shape[0], 2): # could be read as i_keypoint - - indices = ( - torch.abs(tensor[i_image, :, :]) + torch.abs(tensor[i_image + 1, :, :]) - > threshold_norm_vector - ).nonzero() - - for indice in indices: - - i, j = indice - - angle_vector = np.array([tensor[i_image, i, j], tensor[i_image + 1, i, j]]) - if length(angle_vector) > threshold_norm_vector: - angle = py_ang(angle_vector) - c = colorsys.hsv_to_rgb(angle / 360, 1, 1) - else: - c = [0, 0, 0] - for i_c in range(3): - images[i_image // 2, i_c, i, j] = c[i_c] - if not points is None: - point = points[i_image // 2] - - print( - int(point[1] * factor + translation[1]), - int(point[0] * factor + translation[0]), - ) - images[ - i_image // 2, - :, - int(point[1] * factor + translation[1]) - - 1 : int(point[1] * factor + translation[1]) - + 1, - int(point[0] * factor + translation[0]) - - 1 : int(point[0] * factor + translation[0]) - + 1, - ] = 1 - - return images - - -def VisualizeBeliefMap( - tensor, - # tensor of len(keypoints)xwxh - points=None, - # list of points to draw on top of the image - factor=1.0, - # by how much the image was reduced, scale factor - translation=(0, 0) - # by how much the points were moved - # return len(keypoints)x3xwxh # stack of images in torch tensor -): - images = torch.zeros(tensor.shape[0], 3, tensor.shape[1], tensor.shape[2]) - for i_image in range(0, tensor.shape[0]): # could be read as i_keypoint - - belief = tensor[i_image].clone() - belief -= float(torch.min(belief).item()) - belief /= float(torch.max(belief).item()) - - belief = torch.clamp(belief, 0, 1) - belief = torch.cat( - [belief.unsqueeze(0), belief.unsqueeze(0), belief.unsqueeze(0)] - ).unsqueeze(0) - - images[i_image] = belief - - return images - - -def GenerateMapAffinity( - size, nb_vertex, pointsInterest, objects_centroid, scale, save=False -): - # Apply the downscale right now, so the vectors are correct. - - img_affinity = Image.new("RGB", (int(size / scale), int(size / scale)), "black") - # create the empty tensors - totensor = transforms.Compose([transforms.ToTensor()]) - - affinities = [] - for i_points in range(nb_vertex): - affinities.append(torch.zeros(2, int(size / scale), int(size / scale))) - - for i_pointsImage in range(len(pointsInterest)): - pointsImage = pointsInterest[i_pointsImage] - center = objects_centroid[i_pointsImage] - for i_points in range(nb_vertex): - point = pointsImage[i_points] - - affinity_pair, img_affinity = getAfinityCenter( - int(size / scale), - int(size / scale), - tuple((np.array(pointsImage[i_points]) / scale).tolist()), - tuple((np.array(center) / scale).tolist()), - img_affinity=img_affinity, - radius=1, - ) - - affinities[i_points] = (affinities[i_points] + affinity_pair) / 2 - - # Normalizing - v = affinities[i_points].numpy() - - xvec = v[0] - yvec = v[1] - - norms = np.sqrt(xvec * xvec + yvec * yvec) - nonzero = norms > 0 - - xvec[nonzero] /= norms[nonzero] - yvec[nonzero] /= norms[nonzero] - - affinities[i_points] = torch.from_numpy(np.concatenate([[xvec], [yvec]])) - affinities = torch.cat(affinities, 0) - - return affinities - - -def getAfinityCenter( - width, height, point, center, radius=7, tensor=None, img_affinity=None -): - """ - Create the affinity map - """ - if tensor is None: - tensor = torch.zeros(2, height, width).float() - - # create the canvas for the afinity output - imgAffinity = Image.new("RGB", (width, height), "black") - totensor = transforms.Compose([transforms.ToTensor()]) - draw = ImageDraw.Draw(imgAffinity) - r1 = radius - p = point - draw.ellipse((p[0] - r1, p[1] - r1, p[0] + r1, p[1] + r1), (255, 255, 255)) - - del draw - - # compute the array to add the afinity - array = (np.array(imgAffinity) / 255)[:, :, 0] - - angle_vector = np.array(center) - np.array(point) - angle_vector = normalize(angle_vector) - affinity = np.concatenate([[array * angle_vector[0]], [array * angle_vector[1]]]) - - if not img_affinity is None: - # find the angle vector - if length(angle_vector) > 0: - angle = py_ang(angle_vector) - else: - angle = 0 - c = np.array(colorsys.hsv_to_rgb(angle / 360, 1, 1)) * 255 - draw = ImageDraw.Draw(img_affinity) - draw.ellipse( - (p[0] - r1, p[1] - r1, p[0] + r1, p[1] + r1), - fill=(int(c[0]), int(c[1]), int(c[2])), - ) - del draw - re = torch.from_numpy(affinity).float() + tensor - return re, img_affinity - - -def CreateBeliefMap(size, pointsBelief, nbpoints, sigma=16, save=False): - # Create the belief maps in the points - beliefsImg = [] - for numb_point in range(nbpoints): - array = np.zeros([size, size]) - out = np.zeros([size, size]) - - for point in pointsBelief: - p = [point[numb_point][1], point[numb_point][0]] - w = int(sigma * 2) - if p[0] - w >= 0 and p[0] + w < size and p[1] - w >= 0 and p[1] + w < size: - for i in range(int(p[0]) - w, int(p[0]) + w + 1): - for j in range(int(p[1]) - w, int(p[1]) + w + 1): - - # if there is already a point there. - array[i, j] = max( - np.exp( - -( - ((i - p[0]) ** 2 + (j - p[1]) ** 2) - / (2 * (sigma**2)) - ) - ), - array[i, j], - ) - - beliefsImg.append(array.copy()) - - if save: - stack = np.stack([array, array, array], axis=0).transpose(2, 1, 0) - imgBelief = Image.fromarray((stack * 255).astype("uint8")) - imgBelief.save("debug/{}.png".format(numb_point)) - return beliefsImg - - -def crop(img, i, j, h, w): - """Crop the given PIL.Image. - Args: - img (PIL.Image): Image to be cropped. - i: Upper pixel coordinate. - j: Left pixel coordinate. - h: Height of the cropped image. - w: Width of the cropped image. - Returns: - PIL.Image: Cropped image. - """ - return img.crop((j, i, j + w, i + h)) - - -class AddRandomContrast(object): - """ - Apply some random image filters from PIL - """ - - def __init__(self, sigma=0.1): - self.sigma = sigma - - def __call__(self, im): - - contrast = ImageEnhance.Contrast(im) - - im = contrast.enhance(np.random.normal(1, self.sigma)) - - return im - - -class AddRandomBrightness(object): - """ - Apply some random image filters from PIL - """ - - def __init__(self, sigma=0.1): - self.sigma = sigma - - def __call__(self, im): - - contrast = ImageEnhance.Brightness(im) - im = contrast.enhance(np.random.normal(1, self.sigma)) - return im - - -class AddNoise(object): - """Given mean: (R, G, B) and std: (R, G, B), - will normalize each channel of the torch.*Tensor, i.e. - channel = (channel - mean) / std - """ - - def __init__(self, std=0.1): - self.std = std - - def __call__(self, tensor): - # TODO: make efficient - t = torch.FloatTensor(tensor.size()).normal_(0, self.std) - - t = tensor.add(t) - t = torch.clamp(t, -1, 1) # this is expansive - return t - - -irange = range - - -def make_grid( - tensor, - nrow=8, - padding=2, - normalize=False, - range=None, - scale_each=False, - pad_value=0, -): - """Make a grid of images. - Args: - tensor (Tensor or list): 4D mini-batch Tensor of shape (B x C x H x W) - or a list of images all of the same size. - nrow (int, optional): Number of images displayed in each row of the grid. - The Final grid size is (B / nrow, nrow). Default is 8. - padding (int, optional): amount of padding. Default is 2. - normalize (bool, optional): If True, shift the image to the range (0, 1), - by subtracting the minimum and dividing by the maximum pixel value. - range (tuple, optional): tuple (min, max) where min and max are numbers, - then these numbers are used to normalize the image. By default, min and max - are computed from the tensor. - scale_each (bool, optional): If True, scale each image in the batch of - images separately rather than the (min, max) over all images. - pad_value (float, optional): Value for the padded pixels. - Example: - See this notebook `here `_ - """ - if not ( - torch.is_tensor(tensor) - or (isinstance(tensor, list) and all(torch.is_tensor(t) for t in tensor)) - ): - raise TypeError( - "tensor or list of tensors expected, got {}".format(type(tensor)) - ) - - # if list of tensors, convert to a 4D mini-batch Tensor - if isinstance(tensor, list): - tensor = torch.stack(tensor, dim=0) - - if tensor.dim() == 2: # single image H x W - tensor = tensor.view(1, tensor.size(0), tensor.size(1)) - if tensor.dim() == 3: # single image - if tensor.size(0) == 1: # if single-channel, convert to 3-channel - tensor = torch.cat((tensor, tensor, tensor), 0) - tensor = tensor.view(1, tensor.size(0), tensor.size(1), tensor.size(2)) - - if tensor.dim() == 4 and tensor.size(1) == 1: # single-channel images - tensor = torch.cat((tensor, tensor, tensor), 1) - - if normalize is True: - tensor = tensor.clone() # avoid modifying tensor in-place - if range is not None: - assert isinstance( - range, tuple - ), "range has to be a tuple (min, max) if specified. min and max are numbers" - - def norm_ip(img, min, max): - img.clamp_(min=min, max=max) - img.add_(-min).div_(max - min + 1e-5) - - def norm_range(t, range): - if range is not None: - norm_ip(t, range[0], range[1]) - else: - norm_ip(t, float(t.min()), float(t.max())) - - if scale_each is True: - for t in tensor: # loop over mini-batch dimension - norm_range(t, range) - else: - norm_range(tensor, range) - - if tensor.size(0) == 1: - return tensor.squeeze() - - # make the mini-batch of images into a grid - nmaps = tensor.size(0) - xmaps = min(nrow, nmaps) - ymaps = int(math.ceil(float(nmaps) / xmaps)) - height, width = int(tensor.size(2) + padding), int(tensor.size(3) + padding) - grid = tensor.new(3, height * ymaps + padding, width * xmaps + padding).fill_( - pad_value - ) - k = 0 - for y in irange(ymaps): - for x in irange(xmaps): - if k >= nmaps: - break - grid.narrow(1, y * height + padding, height - padding).narrow( - 2, x * width + padding, width - padding - ).copy_(tensor[k]) - k = k + 1 - return grid - - -def save_image(tensor, filename, nrow=4, padding=2, mean=None, std=None, save=True): - """ - Saves a given Tensor into an image file. - If given a mini-batch tensor, will save the tensor as a grid of images. - """ - from PIL import Image - - tensor = tensor.cpu() - grid = make_grid(tensor, nrow=nrow, padding=10, pad_value=1) - if not mean is None: - # ndarr = grid.mul(std).add(mean).mul(255).byte().transpose(0,2).transpose(0,1).numpy() - ndarr = ( - grid.mul(std) - .add(mean) - .mul(255) - .byte() - .transpose(0, 2) - .transpose(0, 1) - .numpy() - ) - else: - ndarr = ( - grid.mul(0.5) - .add(0.5) - .mul(255) - .byte() - .transpose(0, 2) - .transpose(0, 1) - .numpy() - ) - im = Image.fromarray(ndarr) - if save is True: - im.save(filename) - return im, grid - - -from PIL import ImageDraw, Image, ImageFont -import json - - -class Draw(object): - """Drawing helper class to visualize the neural network output""" - - def __init__(self, im): - """ - :param im: The image to draw in. - """ - self.draw = ImageDraw.Draw(im) - self.width = im.size[0] - - def draw_line(self, point1, point2, line_color, line_width=2): - """Draws line on image""" - if point1 is not None and point2 is not None: - self.draw.line([point1, point2], fill=line_color, width=line_width) - - def draw_dot(self, point, point_color, point_radius): - """Draws dot (filled circle) on image""" - if point is not None: - xy = [ - point[0] - point_radius, - point[1] - point_radius, - point[0] + point_radius, - point[1] + point_radius, - ] - self.draw.ellipse(xy, fill=point_color, outline=point_color) - - def draw_text(self, point, text, text_color): - """Draws text on image""" - if point is not None: - self.draw.text(point, text, fill=text_color, font=ImageFont.truetype("misc/arial.ttf", self.width // 50)) - - def draw_cube(self, points, color=(0, 255, 0)): - """ - Draws cube with a thick solid line across - the front top edge and an X on the top face. - """ - # draw front - self.draw_line(points[0], points[1], color) - self.draw_line(points[1], points[2], color) - self.draw_line(points[3], points[2], color) - self.draw_line(points[3], points[0], color) - - # draw back - self.draw_line(points[4], points[5], color) - self.draw_line(points[6], points[5], color) - self.draw_line(points[6], points[7], color) - self.draw_line(points[4], points[7], color) - - # draw sides - self.draw_line(points[0], points[4], color) - self.draw_line(points[7], points[3], color) - self.draw_line(points[5], points[1], color) - self.draw_line(points[2], points[6], color) - - # draw dots - self.draw_dot(points[0], point_color=color, point_radius=4) - self.draw_dot(points[1], point_color=color, point_radius=4) - - # draw x on the top - self.draw_line(points[0], points[5], color) - self.draw_line(points[1], points[4], color) - - # Draw center - self.draw_dot(points[8], point_color=color, point_radius=6) - - for i in range(9): - self.draw_text(points[i], str(i), (255, 0, 0)) - -