Compare commits

..

136 commits
main ... dev

Author SHA1 Message Date
4ceae093f3 update run command for BT 2025-04-18 10:29:26 +03:00
f0e09812ee input topics for ObjectDetection skill + OD skill 2025-04-18 10:27:34 +03:00
ccc1e2da4b clean and update scripts in web_p 2025-03-12 11:45:34 +03:00
c85784f3dc update scripts in web_p (dataset, train) 2025-02-28 20:21:33 +03:00
IDONTSUDO
7d3b8ff0cb topics 2025-02-17 21:30:09 +03:00
IDONTSUDO
d7e8c825cb progress 2025-02-15 17:23:27 +03:00
IDONTSUDO
40eebf9dd8 fix 2025-02-07 00:15:57 +03:00
IDONTSUDO
564866393f progress 2025-02-02 20:28:05 +03:00
IDONTSUDO
2afed3a4f0 fix 2025-01-26 18:40:29 +03:00
470a7ef43e add process type: WEIGHTS = "WEIGHTS" 2025-01-24 17:48:37 +03:00
7c01c61194 readme on migrate DB + 2025-01-23 11:30:39 +03:00
IDONTSUDO
3044540814 progress 2025-01-20 10:29:39 +03:00
IDONTSUDO
dfe7b20f8d progress 2025-01-19 23:36:04 +03:00
IDONTSUDO
c2b91ee4d7 progress 2025-01-19 21:37:27 +03:00
IDONTSUDO
c2da1d8f4c progress 2025-01-18 15:29:07 +03:00
IDONTSUDO
d7ea79935e fix 2025-01-01 19:11:23 +03:00
IDONTSUDO
a94d84ba69 progress 2024-12-31 15:06:14 +03:00
IDONTSUDO
d3a07a5c11 progress 2024-12-31 14:47:17 +03:00
IDONTSUDO
12c09c4461 add new feature 2024-12-31 01:27:46 +03:00
IDONTSUDO
8696d99194 progress 2024-12-14 20:09:42 +03:00
bad5c4d8a0 checking install in readme 2024-12-11 14:16:04 +03:00
IDONTSUDO
88e40f7bdb fix 2024-12-11 14:13:46 +03:00
IDONTSUDO
58b22d294d fix 2024-12-11 14:12:54 +03:00
IDONTSUDO
f39f69a440 Merge branch 'main' into dev 2024-12-01 19:09:45 +03:00
IDONTSUDO
ea1ebe0e95 progress 2024-12-01 18:53:28 +03:00
IDONTSUDO
2024834d06 progress 2024-11-28 17:51:45 +03:00
IDONTSUDO
300a5005ba fixed 2024-11-28 17:46:34 +03:00
IDONTSUDO
e163f0698b progress 2024-11-28 17:23:29 +03:00
cad230eee9 add GetInterfaces + renderBOPdataset2 2024-11-28 16:28:16 +03:00
IDONTSUDO
aaa4257920 fixed form builder test 2024-11-27 19:38:00 +03:00
IDONTSUDO
3accb5af61 fix 2024-11-27 19:30:31 +03:00
IDONTSUDO
6190869d8d progress 2024-11-27 19:29:59 +03:00
IDONTSUDO
28c36ae710 progress 2024-11-27 19:29:49 +03:00
IDONTSUDO
3f951d1c09 fixed replace "/" error 2024-11-17 14:57:16 +03:00
IDONTSUDO
29dcfb6ac1 fix 2024-10-28 11:52:53 +03:00
IDONTSUDO
00c553ecd7 fix 2024-10-28 10:33:26 +03:00
IDONTSUDO
3f88ceebe4 fixed exec comand 2024-10-27 12:21:09 +03:00
IDONTSUDO
2d59388494 fixed alex 2024-10-25 17:19:19 +03:00
IDONTSUDO
23c39cbae2 skill import 2024-10-20 15:58:14 +03:00
IDONTSUDO
059b2e3e64 fixed form builder 2024-10-20 14:44:24 +03:00
IDONTSUDO
f019c3a1c4 fixed select detail 2024-10-20 14:20:07 +03:00
IDONTSUDO
2f15b86a42 fixed bug 2024-10-20 13:09:48 +03:00
IDONTSUDO
15cb712c3d fixed errors 2024-10-14 11:41:35 +03:00
IDONTSUDO
1ec2a3ff7a fixed alex error 2024-10-14 11:10:26 +03:00
IDONTSUDO
7693377ad1 delete scene itm 2024-10-13 22:00:05 +03:00
IDONTSUDO
1cc489fc72 everything but light 2024-10-13 21:52:55 +03:00
IDONTSUDO
139f012803 alex form 2024-10-05 14:33:09 +03:00
IDONTSUDO
0dbc04f09b fixe form builder 2024-10-05 14:16:28 +03:00
IDONTSUDO
c19e4c684e fixed form builder object bug 2024-10-05 12:59:11 +03:00
IDONTSUDO
2b4795dfc2 fix 2024-10-01 15:04:28 +03:00
IDONTSUDO
04b1c2bfc7 fix 2024-10-01 14:40:13 +03:00
IDONTSUDO
3369306451 fixed condtion action bug 2024-10-01 13:55:31 +03:00
IDONTSUDO
15930c5f85 scene manger separated scene builder 2024-10-01 13:43:07 +03:00
IDONTSUDO
da883edb64 fix 2024-10-01 11:27:57 +03:00
IDONTSUDO
c135bca77a update dep 2024-10-01 09:57:21 +03:00
IDONTSUDO
4657652dd0 condition fixed 2024-09-30 18:34:59 +03:00
IDONTSUDO
314e070bee Alexander changes 2024-09-30 17:19:16 +03:00
IDONTSUDO
d47d555061 bt builder 2024-09-30 15:32:30 +03:00
IDONTSUDO
50d239a4eb fixed bugs 2024-09-26 18:41:51 +03:00
IDONTSUDO
5eb588a709 fixed 2024-09-25 21:27:26 +03:00
IDONTSUDO
ba5394107a fixed errors 2024-09-25 19:12:02 +03:00
IDONTSUDO
fb4fa52c14 fix path 2024-09-24 17:21:05 +03:00
IDONTSUDO
4e6132a872 alex find bugs 2024-09-24 17:18:56 +03:00
IDONTSUDO
d804aa2edf fixed 2024-09-23 14:56:07 +03:00
IDONTSUDO
e2cd7af032 fixed launch 2024-09-23 14:18:48 +03:00
IDONTSUDO
c0a23c31f7 fixed bugs 2024-09-23 13:19:23 +03:00
IDONTSUDO
193e884e40 fixed errors 2024-09-20 18:12:00 +03:00
IDONTSUDO
401080d78e alexander test 2024-09-20 13:56:33 +03:00
IDONTSUDO
3b8d9e4298 sync 2024-09-10 19:58:09 +03:00
IDONTSUDO
25e57c1a56 remove missing files 2024-08-29 12:34:23 +03:00
IDONTSUDO
a920f5fb45 skills screen 2024-08-29 12:26:54 +03:00
IDONTSUDO
cac5fad8ce fix 2024-08-21 18:25:43 +03:00
IDONTSUDO
d6702185f0 fixed path 2024-08-21 18:14:04 +03:00
IDONTSUDO
1287625107 fix error 2024-08-21 16:57:28 +03:00
IDONTSUDO
e748debd2f diference 2024-08-21 16:31:41 +03:00
IDONTSUDO
7063e93c75 alexander 2024-08-21 16:15:54 +03:00
IDONTSUDO
cf75b4220a missing files 2024-08-13 16:36:06 +03:00
IDONTSUDO
ca3a1cfed9 progress 2024-08-13 16:35:32 +03:00
IDONTSUDO
48be3e6d33 progress 2024-08-13 13:03:28 +03:00
IDONTSUDO
d41310196f progress machine learning screen 2024-07-26 18:50:36 +03:00
IDONTSUDO
0b3eefec2d fixed coords foorm 2024-07-25 13:14:45 +03:00
IDONTSUDO
17ed2905c2 fixed xyz 2024-07-25 12:08:09 +03:00
IDONTSUDO
6e6a1c0c1d overflow 2024-07-25 12:00:03 +03:00
IDONTSUDO
6003cfde04 scenes 2024-07-25 11:50:43 +03:00
IDONTSUDO
9120729d41 progress 2024-07-23 15:57:22 +03:00
IDONTSUDO
318d0a7893 progress 2024-07-23 13:33:19 +03:00
IDONTSUDO
d8b5018cb2 progress 2024-07-13 18:18:14 +03:00
IDONTSUDO
50822a031d progress 2024-07-03 15:25:51 +03:00
IDONTSUDO
559262db34 alex 2024-06-27 11:34:42 +03:00
IDONTSUDO
b5750b12ef alexander add env 2024-06-26 18:19:06 +03:00
IDONTSUDO
50d0c4c12b progress scene builder 2024-06-25 12:43:41 +03:00
IDONTSUDO
0a4eea19c5 details preview 2024-06-20 21:45:57 +03:00
IDONTSUDO
81238c5182 debug 2024-06-19 15:23:01 +03:00
IDONTSUDO
53fe9bf51a update dependency 2024-06-14 16:08:12 +03:00
IDONTSUDO
4e726be376 set active project 2024-06-10 17:28:13 +03:00
IDONTSUDO
814eb485eb change readme 2024-06-10 15:18:13 +03:00
IDONTSUDO
8f8fc3107d alexander bt fixed 2024-06-10 15:16:30 +03:00
Your Name
61118a7423 simultaion 2024-06-04 13:19:22 +03:00
IDONTSUDO
c5ae89d18a bt builder 2024-05-29 15:38:36 +03:00
IDONTSUDO
5162612a77 progress 2024-05-13 20:43:11 +03:00
IDONTSUDO
4585d079f6 progress 2024-05-03 21:30:18 +03:00
IDONTSUDO
89d4226ea6 progress 2024-05-02 17:57:58 +03:00
IDONTSUDO
a0cee0b394 change blender file 2024-05-02 17:41:42 +03:00
IDONTSUDO
e0a6cd0af1 alexander 2024-05-02 17:36:44 +03:00
IDONTSUDO
c49beb8218 progress 2024-05-02 17:30:59 +03:00
IDONTSUDO
8bc943de2c progress 2024-05-02 12:40:20 +03:00
IDONTSUDO
f0ed478b36 progress 2024-04-26 15:51:20 +03:00
IDONTSUDO
c4ddb3dc8c progress 2024-04-26 15:44:09 +03:00
IDONTSUDO
e155b4a2a1 progress 2024-04-25 12:22:21 +03:00
IDONTSUDO
e005a42254 Merge branch 'main' of https://gitlab.com/robossembler/webservice into alexander-1 2024-04-23 16:01:11 +03:00
IDONTSUDO
c628cdb9af progress 2024-04-23 15:56:52 +03:00
IDONTSUDO
7e25cc216a Merge branch 'main' of https://gitlab.com/robossembler/webservice into alexander 2024-04-19 13:52:07 +03:00
IDONTSUDO
810eb5926e fix 2024-04-19 13:30:03 +03:00
IDONTSUDO
94b40bf24c progress 2024-04-18 20:44:26 +03:00
IDONTSUDO
38ffde1d77 form view model array objects map edit 2024-04-18 17:03:02 +03:00
IDONTSUDO
e85dc14fc8 progress 2024-04-18 15:22:24 +03:00
IDONTSUDO
f11dfa7e57 formbuilder fix 2024-04-17 15:21:40 +03:00
IDONTSUDO
2b8d0fa88b icons add 2024-04-16 15:44:31 +03:00
IDONTSUDO
a2066ce5cd adding socket listner dataset screen 2024-04-16 15:20:24 +03:00
IDONTSUDO
776b6e540e miss 2024-04-15 18:30:41 +03:00
IDONTSUDO
78ebb748ea progress 2024-04-15 18:24:44 +03:00
IDONTSUDO
c10cdb8158 CoreInput form error adding 2024-04-12 20:43:36 +03:00
IDONTSUDO
27763fc8d2 progress 2024-04-12 17:02:49 +03:00
IDONTSUDO
0c03906518 progress 2024-04-11 22:02:57 +03:00
IDONTSUDO
c17515d571 deleted unnecessary files
added new features
2024-04-09 16:31:30 +03:00
IDONTSUDO
6840402b1f deleted unnecessary files
added new features
2024-04-09 16:31:25 +03:00
IDONTSUDO
dffc73c30f base bt 2024-02-27 18:48:35 +03:00
IDONTSUDO
c43c192a3e Merge branch 'main' of https://gitlab.com/robossembler/webservice into 14-fix/sticky-objects 2024-02-19 14:40:55 +03:00
IDONTSUDO
0d825f7f63 sticky 2024-02-19 14:36:27 +03:00
IDONTSUDO
de5b493c77 mark debug 2024-02-16 14:16:35 +03:00
IDONTSUDO
acc79df97d bt builder progress 2024-02-05 15:28:09 +03:00
IDONTSUDO
5dec799002 corrected typos, fixed a bug with object transfer 2024-01-24 15:08:52 +03:00
IDONTSUDO
9028186a74 added scene manager 2024-01-23 17:26:59 +03:00
IDONTSUDO
2adb939f37 added scene manager 2024-01-23 17:23:10 +03:00
IDONTSUDO
ae9842d5e1 process 2023-12-28 17:18:12 +03:00
IDONTSUDO
7ff6165882 Refactoring 2023-12-22 21:07:55 +03:00
117 changed files with 2746 additions and 761 deletions

View file

@ -20,6 +20,7 @@
"GLTF", "GLTF",
"grau", "grau",
"idontsudo", "idontsudo",
"Pids",
"raycaster", "raycaster",
"skils", "skils",
"typedataset", "typedataset",

61
flake.lock generated
View file

@ -1,61 +0,0 @@
{
"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
}

View file

@ -1,37 +0,0 @@
{
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 ]; };
};
}
);
}

82
p.json Normal file
View file

@ -0,0 +1,82 @@
{
"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
]
}
}

View file

@ -1 +1,14 @@
Веб-сервис для отладки Robossembler Framework Веб-сервис для отладки 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
```

View file

@ -15,8 +15,8 @@
"checkCommand": null, "checkCommand": null,
"filter": null "filter": null
}, },
"btBuilderProcess": { "btRuntimeProcess": {
"execCommand": "nix run github:nixos/nixpkgs#python312Packages.tensorboard -- --logdir ${dir_path}", "execCommand": "cd ~/robossembler-ws && source ./install/local_setup.bash; ros2 launch rbs_bt_executor rbs_bt_web.launch.py bt_path:=${bt_path}",
"date": null, "date": null,
"status": null, "status": null,
"delay": 0, "delay": 0,

View file

@ -9,6 +9,9 @@ import { dirname } from "path";
import { CheckAndCreateStaticFilesFolderUseCase } from "../usecases/check_and_create_static_files_folder_usecase"; import { CheckAndCreateStaticFilesFolderUseCase } from "../usecases/check_and_create_static_files_folder_usecase";
import { DataBaseConnectUseCase } from "../usecases/database_connect_usecase"; import { DataBaseConnectUseCase } from "../usecases/database_connect_usecase";
import { TypedEvent } from "../helpers/typed_event"; 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 { export enum ServerStatus {
init = "init", init = "init",
@ -85,7 +88,25 @@ export class App extends TypedEvent<ServerStatus> {
this.app.use(express.json()); this.app.use(express.json());
this.app.use(express.urlencoded({ extended: true })); this.app.use(express.urlencoded({ extended: true }));
this.app.use(express.static(App.staticFilesStoreDir())); 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( this.app.use(
fileUpload({ fileUpload({
createParentPath: true, createParentPath: true,

View file

@ -234,6 +234,7 @@ export class CoreHttpController<V> implements ICoreHttpController {
return res.json(ok); return res.json(ok);
}, },
(err) => { (err) => {
return res.status(400).json({ error: String(err) }); return res.status(400).json({ error: String(err) });
} }
); );

View file

@ -18,7 +18,7 @@ export class TopicViewModel {
export interface IParam { export interface IParam {
type: string; type: string;
dependency: Object; dependency: object;
} }
export interface Skill { export interface Skill {
SkillPackage: ISkillPackage; SkillPackage: ISkillPackage;

View file

@ -7,9 +7,8 @@ export abstract class CreateInstanceScenario<V extends Instance> extends Callbac
abstract validationModel: V; abstract validationModel: V;
abstract databaseModel: any; abstract databaseModel: any;
call = async (model: V): ResponseBase => { call = async (model: V): ResponseBase => {
model.instancePath = `${model.path}/${model.instanceName}`; model.instancePath = `${model.path.pathNormalize()}/${model.instanceName}`.pathNormalize();
console.log("INSTANCE PATh"); model.path = model.path.pathNormalize();
console.log(model.instancePath)
return (await new CreateFolderUseCase().call(model.instancePath)).map( return (await new CreateFolderUseCase().call(model.instancePath)).map(
async () => await new CreateDataBaseModelUseCase(this.databaseModel).call(model) async () => await new CreateDataBaseModelUseCase(this.databaseModel).call(model)
); );

View file

@ -0,0 +1,72 @@
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<ProcessData> 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<any>
}
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)));
}
}

View file

@ -50,7 +50,6 @@ export class ExecutorProgramService
}; };
worker.send(workerDataExec); worker.send(workerDataExec);
worker.on("message", (e) => { worker.on("message", (e) => {
console.log(JSON.stringify(e));
const spawnError = SpawnError.isError(e); const spawnError = SpawnError.isError(e);
if (spawnError instanceof SpawnError) { if (spawnError instanceof SpawnError) {

View file

@ -7,6 +7,7 @@ import { ExecutorResult } from "../models/executor_result";
import { ExecutorProgramService } from "../services/executor_program_service"; import { ExecutorProgramService } from "../services/executor_program_service";
export const executorProgramService = new ExecutorProgramService(""); export const executorProgramService = new ExecutorProgramService("");
export class KillLastProcessUseCase extends CallbackStrategyWithEmpty { export class KillLastProcessUseCase extends CallbackStrategyWithEmpty {
call = async (): Promise<Result<undefined, string>> => { call = async (): Promise<Result<undefined, string>> => {
executorProgramService.deleteWorker(); executorProgramService.deleteWorker();

View file

@ -0,0 +1,5 @@
import * as os from 'os';
export class GetRootDirUseCase {
call = (): string => os.homedir()
}

View file

@ -8,6 +8,9 @@ import { SaveBtScenario as FillBtScenario } from "./domain/save_bt_scenario";
import { GetCameraUseCase } from "./domain/get_cameras_usecase"; import { GetCameraUseCase } from "./domain/get_cameras_usecase";
import { GetRobotsUseCase } from "./domain/get_robots_usecase"; import { GetRobotsUseCase } from "./domain/get_robots_usecase";
import { GetTopicsUseCase } from "./domain/get_topics_usecase"; import { GetTopicsUseCase } from "./domain/get_topics_usecase";
import { DeleteBehaviorTreeScenario } from "./domain/delete_behavior_tree_scenario";
export class BehaviorTreesPresentation extends CrudController<BehaviorTreeValidationModel, typeof BehaviorTreeDBModel> { export class BehaviorTreesPresentation extends CrudController<BehaviorTreeValidationModel, typeof BehaviorTreeDBModel> {
constructor() { constructor() {
@ -43,6 +46,11 @@ export class BehaviorTreesPresentation extends CrudController<BehaviorTreeValida
subUrl: "topics", subUrl: "topics",
fn: new GetTopicsUseCase() fn: new GetTopicsUseCase()
}) })
this.subRoutes.push({
method: "POST",
subUrl: "delete/bt",
fn: new DeleteBehaviorTreeScenario(),
})
} }
} }

View file

@ -0,0 +1,16 @@
import { CallbackStrategyWithIdQuery, ResponseBase } from "../../../core/controllers/http_controller";
import { StaticFilesProject } from "../../../core/models/static_files";
import { DeleteDataBaseModelUseCase } from "../../../core/usecases/delete_database_model_usecase";
import { DeleteRecursiveFolderUseCase } from "../../../core/usecases/delete_recursive_folder_usecase";
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 { IProjectModel, ProjectDBModel } from "../../projects/models/project_model_database_model";
import { BehaviorTreeDBModel } from "../models/behavior_tree_database_model";
export class DeleteBehaviorTreeScenario extends CallbackStrategyWithIdQuery {
idValidationExpression: CoreValidation = new MongoIdValidation();
call = async (id: string): ResponseBase => (
await new SearchOneDataBaseModelUseCase<IProjectModel>(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))));
}

View file

@ -2,6 +2,7 @@ import { IsMongoId, IsNumber, IsString } from "class-validator";
import { IBehaviorTreeModel } from "./behavior_tree_database_model"; import { IBehaviorTreeModel } from "./behavior_tree_database_model";
export class BehaviorTreeValidationModel implements IBehaviorTreeModel { export class BehaviorTreeValidationModel implements IBehaviorTreeModel {
_id: string;
@IsString() @IsString()
public name: string; public name: string;
@IsMongoId() @IsMongoId()

View file

@ -4,6 +4,8 @@ import { CalculationInstanceValidationModel } from "./models/calculations_instan
import { CalculationInstanceDBModel } from "./models/calculations_instance_database_model"; import { CalculationInstanceDBModel } from "./models/calculations_instance_database_model";
import { CreateCalculationInstanceScenario } from "./domain/create_calculation_instance_scenario"; import { CreateCalculationInstanceScenario } from "./domain/create_calculation_instance_scenario";
import { DeleteCalculationsInstanceScenario } from "./domain/delete_calculations_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< export class CalculationsInstancesPresentation extends CrudController<
CalculationInstanceValidationModel, CalculationInstanceValidationModel,
@ -18,7 +20,12 @@ export class CalculationsInstancesPresentation extends CrudController<
super.delete(new DeleteCalculationsInstanceScenario().call); super.delete(new DeleteCalculationsInstanceScenario().call);
super.post(new CreateCalculationInstanceScenario().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({ this.subRoutes.push({
method: "GET", method: "GET",
subUrl: "exec", subUrl: "exec",

View file

@ -7,7 +7,21 @@ import { ProcessWatcherAndDatabaseUpdateService } from "../../datasets/domain/cr
import { CalculationInstanceDBModel, ICalculationInstance } from "../models/calculations_instance_database_model"; import { CalculationInstanceDBModel, ICalculationInstance } from "../models/calculations_instance_database_model";
import { Result } from "../../../core/helpers/result"; import { Result } from "../../../core/helpers/result";
import { CreateFileUseCase } from "../../../core/usecases/create_file_usecase"; 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<any> => (await new ReadByIdDataBaseModelUseCase<ICalculationInstance>(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 { export class ExecCalculationInstanceProcessScenario extends CallbackStrategyWithIdQuery {
idValidationExpression = new MongoIdValidation(); idValidationExpression = new MongoIdValidation();
call = async (id: string): ResponseBase => call = async (id: string): ResponseBase =>
@ -23,13 +37,9 @@ export class ExecCalculationInstanceProcessScenario extends CallbackStrategyWith
processStatus: "RUN", processStatus: "RUN",
lastProcessExecCommand: execCommand, lastProcessExecCommand: execCommand,
}); });
new ExecProcessUseCase().call(
// @ts-expect-error new ExecProcessScenarioV2().call(execCommand, new ExecProcess(model._id))
`${model.project.rootDir}/`,
execCommand,
id,
new ProcessWatcherAndDatabaseUpdateService(id as unknown as ObjectId, CalculationInstanceDBModel)
);
return Result.ok("OK"); return Result.ok("OK");
}); });
} }

View file

@ -0,0 +1,20 @@
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<CalculationInstanceType> {
validationModel: CalculationInstanceType = new CalculationInstanceType();
call = async (model: CalculationInstanceType): ResponseBase => (await new GetActiveProjectIdScenario().call()).map(
async (activeProjectModel) => await new SearchManyDataBaseModelUseCase<ICalculationInstance>(CalculationInstanceDBModel).call({ project: activeProjectModel.id, isEnd: true, type: model.type }),
);
}

View file

@ -0,0 +1,11 @@
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<ICalculationInstance>(CalculationInstanceDBModel).call(id)).map((model) => );
// }

View file

@ -3,6 +3,7 @@ import { FormBuilderValidationModel } from "../../datasets/models/dataset_valida
import { IProjectModel, projectSchema } from "../../projects/models/project_model_database_model"; import { IProjectModel, projectSchema } from "../../projects/models/project_model_database_model";
export interface ICalculationInstance { export interface ICalculationInstance {
_id: string;
script: string; script: string;
instancePath: string; instancePath: string;
formBuilder: FormBuilderValidationModel; formBuilder: FormBuilderValidationModel;
@ -61,6 +62,7 @@ export const CalculationInstanceSchema = new Schema({
instancePath: { instancePath: {
type: String, type: String,
}, },
project: { project: {
type: Schema.Types.ObjectId, type: Schema.Types.ObjectId,
ref: projectSchema, ref: projectSchema,

View file

@ -4,6 +4,7 @@ import { FormBuilderValidationModel } from "../../datasets/models/dataset_valida
import { IProjectModel } from "../../projects/models/project_model_database_model"; import { IProjectModel } from "../../projects/models/project_model_database_model";
export class CalculationInstanceValidationModel implements ICalculationInstance { export class CalculationInstanceValidationModel implements ICalculationInstance {
_id: string;
@IsNotEmpty() @IsNotEmpty()
@IsString() @IsString()
instanceName: string; instanceName: string;

View file

@ -15,6 +15,7 @@ export enum ProcessStatus {
END = "END", END = "END",
ERROR = "ERROR", ERROR = "ERROR",
NEW = "NEW", NEW = "NEW",
} }
export interface IDatasetModel { export interface IDatasetModel {
_id?: string; _id?: string;

View file

@ -21,8 +21,7 @@ export class ExecInstanceScenario extends CallbackStrategyWithIdQuery {
await new ReadByIdDataBaseModelUseCase<IDigitalTwinsInstanceModel>(DigitalTwinsInstanceDatabaseModel).call(id) await new ReadByIdDataBaseModelUseCase<IDigitalTwinsInstanceModel>(DigitalTwinsInstanceDatabaseModel).call(id)
).map( ).map(
(document) => ( (document) => (
console.log('DOCUMeNT PATH'),
console.log(document.instancePath.pathNormalize()),
new ExecProcessUseCase().call( new ExecProcessUseCase().call(
document.instancePath, document.instancePath,
`python3 $GET_INTERFACES --path ${document.instancePath.pathNormalize()} --package '${JSON.stringify( `python3 $GET_INTERFACES --path ${document.instancePath.pathNormalize()} --package '${JSON.stringify(

View file

@ -4,9 +4,8 @@ import { SearchOneDataBaseModelUseCase } from "../../../core/usecases/search_dat
import { IProjectModel, ProjectDBModel } from "../models/project_model_database_model"; import { IProjectModel, ProjectDBModel } from "../models/project_model_database_model";
export class GetActiveProjectIdScenario extends CallbackStrategyWithEmpty { export class GetActiveProjectIdScenario extends CallbackStrategyWithEmpty {
async call(): Promise<Result<any, { id: string }>> { call = async (): Promise<Result<any, { id: string }>> => (
return (
await new SearchOneDataBaseModelUseCase<IProjectModel>(ProjectDBModel).call({ isActive: true }, "no active projects") await new SearchOneDataBaseModelUseCase<IProjectModel>(ProjectDBModel).call({ isActive: true }, "no active projects")
).map((model) => Result.ok({ id: model._id })); ).map((model) => Result.ok({ id: model._id }));
} }
}

View file

@ -35,7 +35,6 @@ export class RobossemblerAssetsNetworkMapperScenario extends CallbackStrategyWit
"/assets/"; "/assets/";
model.map((el) => { model.map((el) => {
const assetLibsAddress = assetAddress + "/libs/objects/" + el.name; const assetLibsAddress = assetAddress + "/libs/objects/" + el.name;
console.log(assetLibsAddress);
el.stlUrl = `${assetAddress}${el.part_path}`; el.stlUrl = `${assetAddress}${el.part_path}`;
el.glUrl = `${assetLibsAddress}.glb`; el.glUrl = `${assetLibsAddress}.glb`;
el.daeUrl = `${assetLibsAddress}.dae`; el.daeUrl = `${assetLibsAddress}.dae`;

View file

@ -1,7 +1,53 @@
import { CallbackStrategyWithEmpty, ResponseBase } from "../../../core/controllers/http_controller"; 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";
export class ExecBtBuilderUseCase extends CallbackStrategyWithEmpty { class ProcessWatcher extends ExecProcessWatcher {
call(): ResponseBase { async result(): Promise<any> {
throw new Error("Method not implemented."); console.log(this);
} }
} }
export enum Proceed {
EXEC_BT = "EXEC_BT",
}
export class ExecBtScenario extends CallbackStrategyWithIdQuery {
idValidationExpression: CoreValidation = new MongoIdValidation();
call = async (id: string): ResponseBase =>
(await new ReadByIdDataBaseModelScenario<BehaviorTreeValidationModel>(BehaviorTreeDBModel).call(id)).map(
async (behaviorTreeValidationModel) =>
(
await new SearchOneDataBaseModelUseCase<IProjectModel>(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
)
)
)
);
}

View file

@ -0,0 +1,7 @@
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);
}

View file

@ -1,10 +0,0 @@
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("");
}

View file

@ -0,0 +1,51 @@
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<StopProcessModel> {
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<string>((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)
}
}
}

View file

@ -1,42 +1,19 @@
import { CrudController } from "../../core/controllers/crud_controller";
import { ExecRunTimeCommandValidationModel } from "./model/run_time_validation_model"; import { ExecRunTimeCommandValidationModel } from "./model/run_time_validation_model";
import { ExecRuntimeDatabaseModel } from "./model/run_time_database_model"; import { CoreHttpController, SubRouter, CallbackStrategyWithIdQuery } from "../../core/controllers/http_controller";
import { CoreHttpController, SubRouter, HttpMethodType, CallbackStrategyWithIdQuery, ResponseBase } from "../../core/controllers/http_controller"; import { ExecBtScenario } from "./domain/exec_bt_builder_usecase";
import { ExecBtBuilderUseCase } from "./domain/exec_bt_builder_usecase"; import { GetRunTimeStatuses } from "./domain/get_run_time_statuses_usecase";
import { ExecSimulationUseCase } from "./domain/exec_simulation_usecase"; import { StopProcessUseCase } from "./domain/stop_process_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 {
idValidationExpression: CoreValidation = new MongoIdValidation() export class RunTimePresentation extends CoreHttpController<ExecRunTimeCommandValidationModel> {
call = async (id: string) =>
(await new ReadByIdDataBaseModelUseCase<ICalculationInstance>(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<ExecRunTimeCommandValidationModel, any> {
constructor() { constructor() {
super({ super({
url: "run_time", url: "run_time",
}); });
this.subRoutes.push(new SubRouter("POST", "/exec/bt/builder", new ExecBtBuilderUseCase())); this.subRoutes.push(new SubRouter<any>("POST", "exec/bt", new ExecBtScenario()));
this.subRoutes.push(new SubRouter("POST", "/get/bt/builder/state", new GetBtBuilderStateUseCase())); this.subRoutes.push(new SubRouter("GET", "status", new GetRunTimeStatuses()));
this.subRoutes.push(new SubRouter("POST", "/get/simulator/state", new GetSimulationStateScenario())); this.subRoutes.push(new SubRouter<any>("POST", "kill", new StopProcessUseCase()))
this.subRoutes.push(new SubRouter('POST', "exec/analyze", new ExecAnalyzeScenario()))
this.subRoutes.push({
method: "POST",
subUrl: "/exec/simulation/",
fn: new ExecSimulationUseCase(),
});
} }
} }

View file

@ -3,11 +3,11 @@ import { App } from "./core/controllers/app";
import { SocketSubscriber } from "./core/controllers/socket_controller"; import { SocketSubscriber } from "./core/controllers/socket_controller";
import { extensions } from "./core/extensions/extensions"; import { extensions } from "./core/extensions/extensions";
import { httpRoutes } from "./core/controllers/routes"; import { httpRoutes } from "./core/controllers/routes";
import { SpawnProcessUseCase, executorProgramService } from "./core/usecases/exec_process_usecase"; import { executorProgramService } from "./core/usecases/exec_process_usecase";
import { ProcessWatcher } from "./features/runtime/service/process_watcher"; import { executorProgramServiceV2 } from "./core/scenarios/exec_process_scenario_v2";
extensions(); extensions();
const socketSubscribers = [new SocketSubscriber(executorProgramService, "realtime")]; const socketSubscribers = [new SocketSubscriber(executorProgramService, "realtime"), new SocketSubscriber(executorProgramServiceV2, 'realtimeV2',)];
new App(httpRoutes, socketSubscribers).listen(); new App(httpRoutes, socketSubscribers).listen();

View file

@ -1,7 +1,14 @@
/* eslint-disable no-extend-native */
import { Result } from "../helper/result"; import { Result } from "../helper/result";
/* eslint-disable @typescript-eslint/no-this-alias */ /* eslint-disable @typescript-eslint/no-this-alias */
export const ArrayExtensions = () => { export const ArrayExtensions = () => {
if (Array.prototype.plus === undefined) {
Array.prototype.plus = function (array) {
array.forEach((el) => this.push(el))
return this;
}
}
if ([].indexOfR === undefined) { if ([].indexOfR === undefined) {
Array.prototype.indexOfR = function (element) { Array.prototype.indexOfR = function (element) {
if (this.indexOf(element) === -1) { if (this.indexOf(element) === -1) {
@ -10,6 +17,11 @@ export const ArrayExtensions = () => {
return Result.ok(this); return Result.ok(this);
}; };
} }
if ([].whereOne === undefined) {
Array.prototype.whereOne = function (predicate) {
return this.filter(predicate).atR(0);
}
}
if ([].atR === undefined) { if ([].atR === undefined) {
Array.prototype.atR = function (index) { Array.prototype.atR = function (index) {
if (index === undefined) { if (index === undefined) {

View file

@ -30,6 +30,8 @@ declare global {
someR(predicate: (value: T) => boolean): Result<void, Array<T>>; someR(predicate: (value: T) => boolean): Result<void, Array<T>>;
updateAll(value: Partial<T>): Array<T>; updateAll(value: Partial<T>): Array<T>;
atR(index: number | undefined): Result<void, T>; atR(index: number | undefined): Result<void, T>;
whereOne(predicate: (value: T) => boolean): Result<void, T>;
plus(array: any[]): Array<T>
} }
interface Date { interface Date {
formatDate(): string; formatDate(): string;

View file

@ -1,23 +1,15 @@
import { IsNotEmpty, IsString } from "class-validator"; import { IsNotEmpty, IsString } from "class-validator";
import { BehaviorTreeBuilderStore } from "../../features/behavior_tree_builder/presentation/behavior_tree_builder_store"; import { BehaviorTreeBuilderStore } from "../../features/behavior_tree_builder/presentation/behavior_tree_builder_store";
import { import { datasetFormMockContext, datasetFormMockResult, defaultFormValue } from "../../features/dataset/dataset_model";
datasetFormMockContext,
datasetFormMockResult,
defaultFormValue,
} from "../../features/dataset/dataset_model";
import { DependencyViewModel } from "./skill_model"; import { DependencyViewModel } from "./skill_model";
import { ValidationModel } from "./validation_model"; import { ValidationModel } from "./validation_model";
import { FormType } from "./form"; import { FormType } from "./form";
import makeAutoObservable from "mobx-store-inheritance"; import makeAutoObservable from "mobx-store-inheritance";
export class FormBuilderValidationModel export class FormBuilderValidationModel extends ValidationModel implements DependencyViewModel {
extends ValidationModel
implements DependencyViewModel
{
@IsNotEmpty() @IsNotEmpty()
@IsString() @IsString()
public result: string; public result: string;
@IsNotEmpty()
@IsString() @IsString()
public context: string; public context: string;
public form: string[]; public form: string[];
@ -36,22 +28,26 @@ export class FormBuilderValidationModel
formBuilderValidationModel.context.isEmpty() && formBuilderValidationModel.context.isEmpty() &&
formBuilderValidationModel.result.isEmpty() && formBuilderValidationModel.result.isEmpty() &&
formBuilderValidationModel.form.isEmpty(); formBuilderValidationModel.form.isEmpty();
static test = () => static test = () => new FormBuilderValidationModel(ffContext, ff1Result, [], "");
new FormBuilderValidationModel(ffContext, ff1Result, [], "");
static datasetEmpty = () => static datasetEmpty = () =>
new FormBuilderValidationModel( new FormBuilderValidationModel(datasetFormMockContext, datasetFormMockResult, [], defaultFormValue);
datasetFormMockContext,
datasetFormMockResult,
[],
defaultFormValue
);
static empty = () => new FormBuilderValidationModel("", "", [], ""); static empty = () => new FormBuilderValidationModel("", "", [], "");
static emptyTest = () => static emptyTest = () => new FormBuilderValidationModel(``, ``, [], defaultFormValue);
new FormBuilderValidationModel(``, ``, [], defaultFormValue); static creteDataSetTest = () => new FormBuilderValidationModel(``, scene, [], "");
static creteDataSetTest = () => static emptySimple = () => new FormBuilderValidationModel("", simpleFormBuilder, [], "");
new FormBuilderValidationModel(``, scene, [], ""); static eee = () =>
static emptySimple = () => new FormBuilderValidationModel(
new FormBuilderValidationModel("", simpleFormBuilder, [], ""); ``,
`{
"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} }
}
}`,
[],
""
);
static vision = () => static vision = () =>
new FormBuilderValidationModel( new FormBuilderValidationModel(
` `
@ -96,6 +92,7 @@ export class FormBuilderValidationModel
};`, };`,
` `
{ {
"process":\${<SelectProcess/>:OBJECT:{"type": "OBJECT_DETECTION"},
"datasetObjects":\${<SelectDetail/>:OBJECT:{"details": []}, "datasetObjects":\${<SelectDetail/>:OBJECT:{"details": []},
"typedataset": \${typedataset:Enum<T>:ObjectDetection}, "typedataset": \${typedataset:Enum<T>:ObjectDetection},
"models_randomization":{ "models_randomization":{
@ -142,3 +139,6 @@ export const ff1Result = `{
"empty":\${NAME:string:default}, "empty":\${NAME:string:default},
"params": \${ITEM:Array<ITEM>:[]} "params": \${ITEM:Array<ITEM>:[]}
}`; }`;
// {
// "process":\${<SelectProcess/>:OBJECT:{"type": "OBJECT_DETECTION"}
// }

View file

@ -424,15 +424,18 @@ export class Skills {
.flat(1) .flat(1)
.filter((el) => el !== ""); .filter((el) => el !== "");
getDependencyBySkillLabelAndType = (skillType: string, sid: string): DependencyViewModel => getDependencyBySkillLabelAndType = (skillType: string, sid: string): DependencyViewModel => this.skills
this.skills
.reduce<DependencyViewModel[]>((acc, skill) => { .reduce<DependencyViewModel[]>((acc, skill) => {
if (skill.sid?.isEqual(sid)) { if (skill.sid?.isEqual(sid)) {
skill.BTAction.map((action) => { skill.BTAction.map((action) => {
action.param.map((param) => { action.param.map((param) => {
if (param.type.isEqualR(skillType)) {
if (param.type.isEqual(skillType)) {
acc.push(param?.dependency ?? DependencyViewModel.empty()); acc.push(param?.dependency ?? DependencyViewModel.empty());
} }
return param; return param;
}); });
return action; return action;
@ -441,7 +444,7 @@ export class Skills {
return acc; return acc;
}, []) }, [])
.at(0) ?? DependencyViewModel.empty(); .at(0) ?? DependencyViewModel.empty()
static isEmpty(model: Skills): Result<void, void> { static isEmpty(model: Skills): Result<void, void> {
if (model.skills.isEmpty()) { if (model.skills.isEmpty()) {
return Result.error(undefined); return Result.error(undefined);

View file

@ -1,3 +1,4 @@
import { message } from "antd";
import { Result } from "../helper/result"; import { Result } from "../helper/result";
import { validate, ValidationError } from "class-validator"; import { validate, ValidationError } from "class-validator";
@ -26,4 +27,12 @@ export class ValidationModel {
return Result.ok(this as unknown as T); return Result.ok(this as unknown as T);
} }
}; };
validMessage = async<T>(): Promise<Result<string, T>> => {
const result = await this.valid<T>();
if (result.isFailure()) {
message.error(result.error);
}
return result;
}
} }

View file

@ -141,5 +141,6 @@ export class CoreHttpRepository extends HttpRepository {
return this._jsonRequest<UUID>(HttpMethod.GET, "/projects/get/active/project/id"); return this._jsonRequest<UUID>(HttpMethod.GET, "/projects/get/active/project/id");
} }
getAllScenes = () => this._jsonRequest<SceneModel[]>(HttpMethod.GET, "/scenes"); getAllScenes = () => this._jsonRequest<SceneModel[]>(HttpMethod.GET, "/scenes");
} getAllTopics = () => this._jsonRequest(HttpMethod.GET, `/topics`);
}

View file

@ -1,6 +1,7 @@
import { Socket, io } from "socket.io-client"; import { Socket, io } from "socket.io-client";
import { Result } from "../helper/result"; import { Result } from "../helper/result";
import { TypedEvent } from "../helper/typed_event"; import { TypedEvent } from "../helper/typed_event";
import { RuntimeModel } from "../../features/behavior_tree_builder/presentation/ui/actions/runtime_actions";
export class SocketRepository extends TypedEvent<any> { export class SocketRepository extends TypedEvent<any> {
serverURL = "ws://localhost:4001"; serverURL = "ws://localhost:4001";
@ -11,6 +12,9 @@ export class SocketRepository extends TypedEvent<any> {
this.socket = socket; this.socket = socket;
socket.connect(); socket.connect();
socketCoreInstances.forEach((el) => {
socket.on(el.event, (event) => el.emit(event))
})
socket.on('realtime', (d) => { socket.on('realtime', (d) => {
this.emit({ this.emit({
event: "realtime", event: "realtime",
@ -26,3 +30,14 @@ export class SocketRepository extends TypedEvent<any> {
} }
export const socketRepository = new SocketRepository() export const socketRepository = new SocketRepository()
export abstract class SocketCore<T> extends TypedEvent<T> {
abstract event: string
}
export class RunTimeSocketRepository extends SocketCore<RuntimeModel> {
event = 'realtimeV2';
}
export const runTimeSocketRepository = new RunTimeSocketRepository();
const socketCoreInstances: SocketCore<any>[] = [runTimeSocketRepository]

View file

@ -17,6 +17,7 @@ interface IMessage {
errorMessage?: string; errorMessage?: string;
} }
export abstract class UiLoader { export abstract class UiLoader {
navigate?: NavigateFunction;
isLoading = false; isLoading = false;
async httpHelper<T>(callBack: Promise<Result<any, T>>) { async httpHelper<T>(callBack: Promise<Result<any, T>>) {
this.isLoading = true; this.isLoading = true;
@ -126,7 +127,17 @@ export abstract class FormState<V, E> extends UiErrorState<E> {
loadClassInstance = (instance: ClassConstructor<V>, viewModel: V) => { loadClassInstance = (instance: ClassConstructor<V>, viewModel: V) => {
this.viewModel = plainToInstance(instance, viewModel); this.viewModel = plainToInstance(instance, viewModel);
}; };
isModalOpen: boolean = false;
modalShow = () => {
this.isModalOpen = true;
};
modalClickOk = () => {
this.isModalOpen = false;
};
modalCancel = () => {
this.isModalOpen = false;
};
} }

View file

@ -7,20 +7,20 @@ export const DrawerV2: React.FC<{
title?: string; title?: string;
onClose: () => void; onClose: () => void;
children: React.ReactNode; children: React.ReactNode;
}> = ({ isOpen, onClose, children, title }) => { width?: number;
}> = ({ isOpen, onClose, children, title, width }) => {
return ( return (
<div <div
style={{ style={{
position: "fixed", position: "fixed",
top: 0, top: 0,
right: isOpen ? 0 : -300, right: isOpen ? 0 : (width ?? 300) * -1,
width: 300, width: width ?? 300,
height: "100%", height: "100%",
backgroundColor: themeStore.theme.darkSurface, backgroundColor: "#880ef8",
boxShadow: "-2px 0 5px rgba(0, 0, 0, 0.5)", boxShadow: "-2px 0 5px rgba(0, 0, 0, 0.5)",
transition: "right 0.3s ease", transition: "right 0.3s ease",
zIndex: 1000, zIndex: 1000,
}} }}
> >
<div style={{ height: "100%", width: "100%" }}> <div style={{ height: "100%", width: "100%" }}>

View file

@ -1,9 +1,5 @@
import * as React from "react"; import * as React from "react";
import { import { FormViewModel, InputBuilderViewModel, InputType } from "./form_view_model";
FormViewModel,
InputBuilderViewModel,
InputType,
} from "./form_view_model";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { FormBuilderStore } from "./form_builder_store"; import { FormBuilderStore } from "./form_builder_store";
import { CoreSelect } from "../select/select"; import { CoreSelect } from "../select/select";
@ -13,12 +9,8 @@ import { CoreText, CoreTextType } from "../text/text";
import { getFormBuilderComponents } from "./forms/form_builder_components"; import { getFormBuilderComponents } from "./forms/form_builder_components";
import { FormBuilderValidationModel } from "../../model/form_builder_validation_model"; import { FormBuilderValidationModel } from "../../model/form_builder_validation_model";
export interface IFormBuilder { export const FormBuilder = observer(
formBuilder: FormBuilderValidationModel; (props: { formBuilder: FormBuilderValidationModel; onChange: (change: FormBuilderValidationModel) => void }) => {
onChange: (change: FormBuilderValidationModel) => void;
}
export const FormBuilder = observer((props: IFormBuilder) => {
const [store] = React.useState(() => new FormBuilderStore()); const [store] = React.useState(() => new FormBuilderStore());
React.useEffect(() => { React.useEffect(() => {
@ -50,9 +42,7 @@ export const FormBuilder = observer((props: IFormBuilder) => {
key={index} key={index}
items={values} items={values}
value={element.totalValue ?? element.defaultValue} value={element.totalValue ?? element.defaultValue}
onChange={(value) => onChange={(value) => store.changeTotalValue(element.id, value)}
store.changeTotalValue(element.id, value)
}
label={element.name} label={element.name}
style={{ margin: 20 }} style={{ margin: 20 }}
/> />
@ -60,10 +50,7 @@ export const FormBuilder = observer((props: IFormBuilder) => {
} }
if (element.type?.isEqual(InputType.ARRAY)) { if (element.type?.isEqual(InputType.ARRAY)) {
return ( return (
<div <div key={index} style={{ border: "1px black solid", margin: 20 }}>
key={index}
style={{ border: "1px black solid", margin: 20 }}
>
<div <div
style={{ style={{
display: "flex", display: "flex",
@ -87,91 +74,49 @@ export const FormBuilder = observer((props: IFormBuilder) => {
return ( return (
<div style={{ margin: 20 }}> <div style={{ margin: 20 }}>
<div style={{ display: "flex" }}> <div style={{ display: "flex" }}>
<CoreText <CoreText text={(element.subType ?? "") + ` ${index}`} type={CoreTextType.medium} />
text={(element.subType ?? "") + ` ${index}`}
type={CoreTextType.medium}
/>
<Icon <Icon
style={{ paddingLeft: 20 }} style={{ paddingLeft: 20 }}
type="DeleteCircle" type="DeleteCircle"
onClick={() => onClick={() => store.deleteTotalValueSubItem(element.id, index)}
store.deleteTotalValueSubItem(
element.id,
index
)
}
/> />
</div> </div>
{subArray.map( {subArray.map((subSubArrayItem: InputBuilderViewModel, subIndex: number) => {
( if (subSubArrayItem.type.isEqual(InputType.ENUM)) {
subSubArrayItem: InputBuilderViewModel,
subIndex: number
) => {
if (
subSubArrayItem.type.isEqual(
InputType.ENUM
)
) {
return ( return (
<> <>
<CoreSelect <CoreSelect
items={ items={subSubArrayItem.values?.map((el) => String(el)) ?? []}
subSubArrayItem.values?.map( value={subSubArrayItem.totalValue ?? subSubArrayItem.defaultValue}
(el) => String(el) onChange={(value) => console.log(subSubArrayItem.id)}
) ?? []
}
value={
subSubArrayItem.totalValue ??
subSubArrayItem.defaultValue
}
onChange={(value) => console.log(subSubArrayItem.id)
}
label={element.name} label={element.name}
style={{ margin: 5 }} style={{ margin: 5 }}
/> />
</> </>
); );
} }
if ( if (subSubArrayItem.type.isEqualMany([InputType.NUMBER, InputType.STRING]))
subSubArrayItem.type.isEqualMany([
InputType.NUMBER,
InputType.STRING,
])
)
return ( return (
<div> <div>
<CoreInput <CoreInput
isFormBuilder={true}
style={{ margin: 5 }} style={{ margin: 5 }}
onChange={(e) => onChange={(e) => store.changeTotalSubValue(element.id, subIndex, e, index)}
store.changeTotalSubValue(
element.id,
subIndex,
e,
index
)
}
validation={ validation={
subSubArrayItem.type.isEqual( subSubArrayItem.type.isEqual(InputType.NUMBER)
InputType.NUMBER
)
? (el) => Number().isValid(el) ? (el) => Number().isValid(el)
: undefined : undefined
} }
error="только числа" error="только числа"
value={ value={subSubArrayItem.totalValue ?? subSubArrayItem.defaultValue}
subSubArrayItem.totalValue ??
subSubArrayItem.defaultValue
}
label={subSubArrayItem.name} label={subSubArrayItem.name}
/> />
</div> </div>
); );
return <>Error</>; return <>Error</>;
} })}
)}
</div> </div>
); );
}) })
@ -186,12 +131,8 @@ export const FormBuilder = observer((props: IFormBuilder) => {
return ( return (
<div> <div>
<CoreInput <CoreInput
isFormBuilder={true}
validation={ validation={element.type.isEqual(InputType.NUMBER) ? (el) => Number().isValid(el) : undefined}
element.type.isEqual(InputType.NUMBER)
? (el) => Number().isValid(el)
: undefined
}
onChange={(e) => { onChange={(e) => {
store.changeTotalValue(element.id, e); store.changeTotalValue(element.id, e);
}} }}
@ -206,10 +147,7 @@ export const FormBuilder = observer((props: IFormBuilder) => {
return ( return (
<> <>
{getFormBuilderComponents( {getFormBuilderComponents(
element.name element.name.replace(">", "").replace("<", "").replace("/", ""),
.replace(">", "")
.replace("<", "")
.replace("/", ""),
element.totalValue ?? element.defaultValue, element.totalValue ?? element.defaultValue,
(text) => store.changeTotalValue(element.id, text) (text) => store.changeTotalValue(element.id, text)
).fold( ).fold(
@ -228,4 +166,5 @@ export const FormBuilder = observer((props: IFormBuilder) => {
)} )}
</div> </div>
); );
}); }
);

View file

@ -190,8 +190,9 @@ export class FormViewModel {
}); });
return result as unknown as string; return result as unknown as string;
} }
static fromString(result: string, context: string): Result<void, FormViewModel> { static fromString(result: string = '', context: string = ''): Result<void, FormViewModel> {
try { try {
if (result.isEmpty() && context.isEmpty()) { if (result.isEmpty() && context.isEmpty()) {
return Result.error(undefined); return Result.error(undefined);
} }

View file

@ -1,10 +1,10 @@
import { Result } from "../../../helper/result"; import { Result } from "../../../helper/result";
import { SelectDatasetScreen } from "./select_dataset/presentation/select_dataset_screen"; import { SelectProcess } from "./select_dataset/presentation/select_process";
import { SelectDetail } from "./select_detail/presentation/select_detail_screen"; import { SelectDetail } from "./select_detail/presentation/select_detail_screen";
export enum FormBuilderComponents { export enum FormBuilderComponents {
SelectDetail = "SelectDetail", SelectDetail = "SelectDetail",
SelectDataset = "SelectDataset", SelectProcess = "SelectProcess",
} }
export interface IFormBuilderComponentsProps<T> { export interface IFormBuilderComponentsProps<T> {
dependency: T; dependency: T;
@ -18,8 +18,8 @@ export const getFormBuilderComponents = (
if (name.isEqual(FormBuilderComponents.SelectDetail)) { if (name.isEqual(FormBuilderComponents.SelectDetail)) {
return Result.ok(<SelectDetail dependency={dependency} onChange={onChange} />); return Result.ok(<SelectDetail dependency={dependency} onChange={onChange} />);
} }
if (name.isEqual(FormBuilderComponents.SelectDataset)) { if (name.isEqual(FormBuilderComponents.SelectProcess)) {
return Result.ok(<SelectDatasetScreen dependency={dependency} onChange={onChange} />); return Result.ok(<SelectProcess dependency={dependency} onChange={onChange} />);
} }
return Result.error(name); return Result.error(name);
}; };

View file

@ -0,0 +1,5 @@
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 })
}

View file

@ -0,0 +1,18 @@
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;
}

View file

@ -1,6 +0,0 @@
import { observer } from "mobx-react-lite";
import { IFormBuilderComponentsProps } from "../../form_builder_components";
export const SelectDatasetScreen = observer((props:IFormBuilderComponentsProps<any>) => {
return <>SELECT DATASET</>;
});

View file

@ -0,0 +1,48 @@
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<SelectProcessModel>) => {
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 (
<div>
{store.isLoading ? (
<Loader />
) : (
<div>
<CoreSelect
items={store.calculationInstances.map((el) => 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<SelectProcessModel>()).fold(
(model) => props.onChange(model),
(error) => message.error(error)
);
}}
/>
</div>
)}
</div>
);
});

View file

@ -0,0 +1,26 @@
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<SelectProcessModel, any> {
selectProcessRepository = new SelectProcessRepository();
viewModel: SelectProcessModel;
calculationInstances: CalculationModel[] = [];
constructor() {
super();
makeAutoObservable(this);
}
async init(navigate?: NavigateFunction | undefined): Promise<any> {
await this.mapOk('calculationInstances', this.selectProcessRepository.getAllProcessByType(this.viewModel.type));
this.calculationInstances = this.calculationInstances.map((el) => {
// @ts-ignore
delete el['formBuilder'];
return el;
})
}
}

View file

@ -1,16 +1,13 @@
// @ts-nocheck
import React from "react"; import React from "react";
import { IFormBuilderComponentsProps } from "../../form_builder_components"; import { IFormBuilderComponentsProps } from "../../form_builder_components";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { ListItem } from "./ui/list_item"; import { ListItem } from "./ui/list_item";
import { SelectDetailStore } from "./select_detail_store"; import { SelectDetailStore } from "./select_detail_store";
import { SelectDetailViewModel } from "../model/select_details_model"; import { SelectDetailViewModel } from "../model/select_details_model";
import { plainToInstance } from "class-transformer";
export const SelectDetail = observer((props: IFormBuilderComponentsProps<SelectDetailViewModel>) => { export const SelectDetail = observer((props: IFormBuilderComponentsProps<SelectDetailViewModel>) => {
const [store] = React.useState(() => new SelectDetailStore()); const [store] = React.useState(() => new SelectDetailStore());
React.useEffect(() => { React.useEffect(() => {
console.log(props.dependency.details);
store.viewModel = new SelectDetailViewModel(props.dependency.details); store.viewModel = new SelectDetailViewModel(props.dependency.details);
store.isLoading = false; store.isLoading = false;
store.init(); store.init();

View file

@ -16,7 +16,6 @@ export const ListItem = (props: IListItemProps) => {
backgroundColor: "rgba(254, 247, 255, 1)", backgroundColor: "rgba(254, 247, 255, 1)",
border: "1px #6750a4 solid", border: "1px #6750a4 solid",
width: "100%", width: "100%",
height: 110,
display: "flex", display: "flex",
justifyContent: "space-between", justifyContent: "space-between",
alignItems: "center", alignItems: "center",

View file

@ -9,7 +9,7 @@ import { FormBuilder } from "./form_builder";
import makeAutoObservable from "mobx-store-inheritance"; import makeAutoObservable from "mobx-store-inheritance";
class FormBuilderTextStore extends ModalStore { class FormBuilderTextStore extends ModalStore {
viewModel = FormBuilderValidationModel.empty(); viewModel = FormBuilderValidationModel.eee();
constructor() { constructor() {
super(); super();
makeAutoObservable(this); makeAutoObservable(this);
@ -21,6 +21,7 @@ export const FormBuildTest = observer(() => {
return ( return (
<div> <div>
<InputV2 label={"result"} onChange={(text) => (store.viewModel.result = text)} /> <InputV2 label={"result"} onChange={(text) => (store.viewModel.result = text)} />
<InputV2 label={"context"} onChange={(text) => (store.viewModel.context = text)} /> <InputV2 label={"context"} onChange={(text) => (store.viewModel.context = text)} />
<CoreButton text="click" onClick={() => (store.isModalOpen = true)} /> <CoreButton text="click" onClick={() => (store.isModalOpen = true)} />
@ -33,15 +34,16 @@ export const FormBuildTest = observer(() => {
onCancel={() => { onCancel={() => {
store.isModalOpen = false; store.isModalOpen = false;
}} }}
> ></Modal>
<div style={{height:50}}/>
<FormBuilder <FormBuilder
formBuilder={store.viewModel} formBuilder={store.viewModel}
onChange={(e) => { onChange={(e) => {
console.log(e.output); console.log(e)
// console.log(JSON.stringify(e.output)) // console.log(e.output);
console.log(JSON.stringify(e.output))
}} }}
/> />
</Modal>
</div> </div>
); );
}); });

View file

@ -931,6 +931,18 @@ const getIconSvg = (
/> />
</svg> </svg>
); );
case "3points":
return Result.ok(
<svg
xmlns="http://www.w3.org/2000/svg"
width={width ? width : "20"}
height={height ? height : "20"}
viewBox="0 0 128 512"
>
<path d="M64 360a56 56 0 1 0 0 112 56 56 0 1 0 0-112zm0-160a56 56 0 1 0 0 112 56 56 0 1 0 0-112zM120 96A56 56 0 1 0 8 96a56 56 0 1 0 112 0z" />
</svg>
);
case "Move": case "Move":
return Result.ok( return Result.ok(
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" xmlSpace="preserve"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" xmlSpace="preserve">

View file

@ -16,7 +16,6 @@ interface IInputProps extends IStyle {
type?: CoreInputType; type?: CoreInputType;
trim?: boolean; trim?: boolean;
styleContentEditable?: React.CSSProperties; styleContentEditable?: React.CSSProperties;
isFormBuilder?: boolean;
} }
export const CoreInput = (props: IInputProps) => { export const CoreInput = (props: IInputProps) => {
@ -29,15 +28,6 @@ export const CoreInput = (props: IInputProps) => {
setAppendInnerText(false); setAppendInnerText(false);
} }
}, [ref, value, isAppendInnerText, setAppendInnerText, props]); }, [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); const isSmall = props.type !== undefined && props.type.isEqual(CoreInputType.small);
return ( return (

View file

@ -2,15 +2,16 @@ import { themeStore } from "../../..";
import { Icon } from "../icons/icons"; import { Icon } from "../icons/icons";
import { CoreText, CoreTextType, FontType } from "../text/text"; import { CoreText, CoreTextType, FontType } from "../text/text";
interface InputV2Props { export const InputV2: React.FC<{
style?: React.CSSProperties;
label: string; label: string;
value?: string; value?: string;
trim?: boolean; trim?: boolean;
validation?: (value: string) => boolean;
error?: string;
height?: number; height?: number;
onChange?: (text: string) => void; onChange?: (text: string) => void;
}> = ({ label, height, value, onChange, trim }) => {
}
export const InputV2: React.FC<InputV2Props> = ({ label, height, value, onChange, trim }) => {
return ( return (
<div <div
style={{ style={{

View file

@ -107,7 +107,7 @@ export const MainPageV2: React.FC<{
overflow: "auto", overflow: "auto",
}} }}
> >
<div {/* <div
style={{ style={{
alignSelf: "center", alignSelf: "center",
width: 645, width: 645,
@ -222,7 +222,7 @@ export const MainPageV2: React.FC<{
> >
{rightChild} {rightChild}
</div> </div>
</div> </div> */}
<div style={Object.assign({ width: "100%" }, style)}>{children}</div> <div style={Object.assign({ width: "100%" }, style)}>{children}</div>
</div> </div>

View file

@ -0,0 +1,46 @@
import React, { useEffect, useRef, useState } from "react";
import styled from "styled-components";
interface PopoverProps {
content: React.ReactNode;
children: React.ReactNode;
}
const PopoverV2: React.FC<PopoverProps> = ({ content, children }) => {
const [visible, setVisible] = useState(false);
const ref = useRef<HTMLDivElement>(null);
const togglePopover = () => {
setVisible((prev) => !prev);
};
return (
<div
style={{
position: "relative",
display: "inline-block",
}}
onClick={() => togglePopover()}
>
{children}
<div
style={{
position: "absolute",
backgroundColor: "white",
border: "1px solid #ccc;",
borderRadius: "4px",
padding: "10px;",
zIndex: "1000",
transition: "opacity 0.2s ease, visibility 0.2s ease",
opacity: visible ? 1 : 0,
visibility: visible ? "visible" : "hidden",
}}
>
{content}
</div>
</div>
);
};
// visibility: ${({ visible }) => (visible ? 'visible' : 'hidden')};
// opacity: ${({ visible }) => (visible ? 1 : 0)};
// transition: opacity 0.2s ease, visibility 0.2s ease;
export default PopoverV2;

View file

@ -6,7 +6,7 @@ interface ICoreSelectProps extends IStyle {
items: string[]; items: string[];
value: string; value: string;
label: string; label: string;
onChange: (value: string) => void; onChange: (value: string, index: number) => void;
} }
export const CoreSelect = (props: ICoreSelectProps) => { export const CoreSelect = (props: ICoreSelectProps) => {
const ref = React.useRef<HTMLDivElement>(null); const ref = React.useRef<HTMLDivElement>(null);
@ -52,7 +52,7 @@ export const CoreSelect = (props: ICoreSelectProps) => {
key={i} key={i}
onClick={() => { onClick={() => {
setValue(el); setValue(el);
props.onChange(el); props.onChange(el, i);
}} }}
style={{ style={{
backgroundColor: "rgba(230, 224, 233, 1)", backgroundColor: "rgba(230, 224, 233, 1)",

View file

@ -21,7 +21,6 @@ export class BehaviorTreeBuilderHttpRepository extends CoreHttpRepository {
`${this.featureApi}/by_id?id=${id}`, `${this.featureApi}/by_id?id=${id}`,
BehaviorTreeModel BehaviorTreeModel
) as unknown as Promise<Result<HttpError, BehaviorTreeModel>>; ) as unknown as Promise<Result<HttpError, BehaviorTreeModel>>;
deleteBt = (id: string) => this._jsonRequest(HttpMethod.DELETE, `${this.featureApi}?id=${id}`);
editBt = async (model: BehaviorTreeModel) => { editBt = async (model: BehaviorTreeModel) => {
await this._jsonRequest(HttpMethod.POST, `${this.featureApi}/fill/tree`, model); await this._jsonRequest(HttpMethod.POST, `${this.featureApi}/fill/tree`, model);
model.__v = undefined model.__v = undefined

View file

@ -4,20 +4,15 @@ import { createEditor } from "./ui/editor/editor";
import { SkillTree } from "./ui/skill_tree/skill_tree"; import { SkillTree } from "./ui/skill_tree/skill_tree";
import { BehaviorTreeBuilderStore, DrawerState } from "./behavior_tree_builder_store"; import { BehaviorTreeBuilderStore, DrawerState } from "./behavior_tree_builder_store";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { match } from "ts-pattern";
import { Icon } from "../../../core/ui/icons/icons"; import { Icon } from "../../../core/ui/icons/icons";
import { CoreText, CoreTextType } from "../../../core/ui/text/text"; import { CoreText, CoreTextType } from "../../../core/ui/text/text";
import { useNavigate, useParams } from "react-router-dom"; import { useNavigate, useParams } from "react-router-dom";
import { IForms, forms } from "./ui/forms/forms"; 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 { 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 { MainPageV2 } from "../../../core/ui/pages/main_page_v2";
import { DrawerV2 } from "../../../core/ui/drawer/drawer"; import { DrawerV2 } from "../../../core/ui/drawer/drawer";
import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels"; import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels";
import PopoverV2 from "../../../core/ui/popover/popover";
export const behaviorTreeBuilderScreenPath = "behavior/tree/screen/path"; export const behaviorTreeBuilderScreenPath = "behavior/tree/screen/path";
@ -54,7 +49,9 @@ export const BehaviorTreeBuilderScreen = observer(() => {
if (ref.current) { if (ref.current) {
// @ts-expect-error // @ts-expect-error
const domReact: DOMReact = ref.current.getBoundingClientRect(); const domReact: DOMReact = ref.current.getBoundingClientRect();
store.dragZoneSetOffset(0, domReact.y, domReact.width, domReact.height);
// УБЕРИ + 300
store.dragZoneSetOffset(0, domReact.y, domReact.width + 300, domReact.height);
} }
}, [ref.current]); }, [ref.current]);
@ -66,8 +63,12 @@ export const BehaviorTreeBuilderScreen = observer(() => {
}, []); }, []);
return ( return (
<MainPageV2 <MainPageV2
rightChild={ style={{ position: "absolute", height: "100%", overflow: "hidden" }}
bgColor={themeStore.theme.black}
children={
<> <>
<>
<div style={{ position: "absolute", zIndex: 100 }}>
<div <div
style={{ style={{
width: "100%", width: "100%",
@ -78,20 +79,9 @@ export const BehaviorTreeBuilderScreen = observer(() => {
alignItems: "center", alignItems: "center",
}} }}
> >
{/* <ButtonV2 style={{ height: 40 }} onClick={() => {}} text="Запуск" textColor={themeStore.theme.black} />
<div style={{ width: 10 }} />
<ButtonV2
style={{ height: 40 }}
onClick={() => {}}
text="Стоп"
type={ButtonV2Type.empty}
textColor={themeStore.theme.greenWhite}
/>
<div style={{ width: 10 }} /> */}
{store.isNeedSaveBtn ? (
<div <div
style={{ style={{
backgroundColor: store.isNeedSaveBtn ? themeStore.theme.greenWhite : undefined, backgroundColor: themeStore.theme.greenWhite,
height: 40, height: 40,
textAlign: "center", textAlign: "center",
alignContent: "center", alignContent: "center",
@ -99,21 +89,10 @@ export const BehaviorTreeBuilderScreen = observer(() => {
borderRadius: 100, borderRadius: 100,
}} }}
> >
{store.isNeedSaveBtn ? (
<Icon style={{ height: 21 }} onClick={() => store.onClickSaveBehaviorTree()} type="Floppy" /> <Icon style={{ height: 21 }} onClick={() => store.onClickSaveBehaviorTree()} type="Floppy" />
) : undefined}
</div> </div>
) : (
<></>
)}
</div> </div>
</> </div>
}
style={{ position: "absolute", height: "100%", overflow: "hidden" }}
bgColor={themeStore.theme.black}
children={
<>
<>
<div <div
style={{ style={{
height: "100%", height: "100%",
@ -160,10 +139,45 @@ export const BehaviorTreeBuilderScreen = observer(() => {
}} }}
/> />
</Panel> </Panel>
<PanelResizeHandle> {store.panels.map((el, index) => (
<>
<PanelResizeHandle
children={
<>
<div style={{ width: 10, height: "100%", backgroundColor: "beige" }}></div> <div style={{ width: 10, height: "100%", backgroundColor: "beige" }}></div>
</PanelResizeHandle> </>
<Panel defaultSize={0}> </Panel> }
></PanelResizeHandle>
<Panel
style={{ backgroundColor: "rgb(29, 27, 32)" }}
defaultSize={el.size}
onResize={(size) => store.panelResize(size, index)}
>
<div
style={{
display: "flex",
width: "100%",
justifyContent: "space-between",
backgroundColor: "beige",
}}
>
<PopoverV2
children={<Icon type={"3points"} height={26} />}
content={
<div style={{ width: "max-content", padding: 10 }}>
{store.panelActions.map((el, i) => (
<CoreText type={CoreTextType.medium} text={el.name} onClick={() => el.action(index)} />
))}
</div>
}
/>
<div style={{ color: "black", alignContent: "center", paddingRight: 10 }}>{el.name}</div>
</div>
{el.body}
</Panel>
</>
))}
</PanelGroup> </PanelGroup>
</div> </div>
</> </>
@ -172,8 +186,17 @@ export const BehaviorTreeBuilderScreen = observer(() => {
title={store.titleDrawer} title={store.titleDrawer}
onClose={() => store.editDrawer(DrawerState.editThreadBehaviorTree, false)} onClose={() => store.editDrawer(DrawerState.editThreadBehaviorTree, false)}
isOpen={store.drawers.find((el) => el.name === DrawerState.editThreadBehaviorTree)?.status} isOpen={store.drawers.find((el) => el.name === DrawerState.editThreadBehaviorTree)?.status}
width={window.innerWidth / 2}
>
<div
style={{
display: "flex",
flexDirection: "column",
justifyContent: "space-between",
height: "100%",
overflow: "auto",
}}
> >
<div style={{ display: "flex", flexDirection: "column", justifyContent: "space-between", height: "100%" }}>
<div style={{ height: "100%" }}> <div style={{ height: "100%" }}>
{store.skillTemplates?.getForms(store.selected ?? "").map((formType, index) => {store.skillTemplates?.getForms(store.selected ?? "").map((formType, index) =>
forms( forms(
@ -186,11 +209,13 @@ export const BehaviorTreeBuilderScreen = observer(() => {
) )
.rFind<IForms>((form) => form.name.isEqual(formType)) .rFind<IForms>((form) => form.name.isEqual(formType))
.fold( .fold(
(s) => ( (s) => {
<div key={index} style={{ height: "100%" }}> return (
<div key={index} style={{ flex: 1 }}>
{s.component} {s.component}
</div> </div>
), );
},
() => ( () => (
<div key={index + "error"} style={{ height: "100%" }}> <div key={index + "error"} style={{ height: "100%" }}>
Error: Unknown form type {formType} Error: Unknown form type {formType}

View file

@ -26,6 +26,8 @@ import { BehaviorTreeModel } from "../model/behavior_tree_model";
import { PrimitiveViewModel, SystemPrimitive } from "../model/primitive_view_model"; import { PrimitiveViewModel, SystemPrimitive } from "../model/primitive_view_model";
import { SceneAsset } from "../../../core/model/scene_asset"; import { SceneAsset } from "../../../core/model/scene_asset";
import { themeStore } from "../../.."; import { themeStore } from "../../..";
import { RunTimeActions } from "./ui/actions/runtime_actions";
import { ITopicModel } from "../../topics/topic_view_model";
interface I2DArea { interface I2DArea {
x: number; x: number;
@ -37,8 +39,31 @@ interface I2DArea {
export enum DrawerState { export enum DrawerState {
editThreadBehaviorTree = "Редактирование", 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<BehaviorTreeViewModel, CoreError> { export class BehaviorTreeBuilderStore extends UiDrawerFormState<BehaviorTreeViewModel, CoreError> {
panelActions: IActionPanel[] = [
{ name: "Добавить панель", action: () => this.addNewPanel() },
{ name: "Убрать панель", action: (index) => this.removePanel(index) },
{ name: "Runtime", action: (index) => this.changePanel(index, "Runtime", <RunTimeActions />) },
];
sceneAsset?: SceneAsset; sceneAsset?: SceneAsset;
viewModel: BehaviorTreeViewModel = BehaviorTreeViewModel.empty(); viewModel: BehaviorTreeViewModel = BehaviorTreeViewModel.empty();
behaviorTreeModel: BehaviorTreeModel = BehaviorTreeModel.empty(); behaviorTreeModel: BehaviorTreeModel = BehaviorTreeModel.empty();
@ -51,15 +76,15 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState<BehaviorTreeView
activeProject: string = ""; activeProject: string = "";
behaviorTreeBuilderHttpRepository = new BehaviorTreeBuilderHttpRepository(); behaviorTreeBuilderHttpRepository = new BehaviorTreeBuilderHttpRepository();
canRun = true; canRun = true;
isNeedSaveBtn = false; isNeedSaveBtn = true;
selected: string = ""; selected: string = "";
selectedSid: string = ""; selectedSid: string = "";
nodeBehaviorTree: NodeBehaviorTree[] = []; nodeBehaviorTree: NodeBehaviorTree[] = [];
navigate?: NavigateFunction;
editor?: NodeEditor<Schemes>; editor?: NodeEditor<Schemes>;
areaPlugin?: AreaPlugin<Schemes, AreaExtra>; areaPlugin?: AreaPlugin<Schemes, AreaExtra>;
nodeUpdateObserver?: NodeRerenderObserver; nodeUpdateObserver?: NodeRerenderObserver;
primitiveViewModel: PrimitiveViewModel; primitiveViewModel: PrimitiveViewModel;
topics: ITopicModel[];
skillTree: ISkillView = { skillTree: ISkillView = {
name: "", name: "",
children: [ children: [
@ -70,7 +95,12 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState<BehaviorTreeView
}, },
], ],
}; };
panels: PanelBody[] = [new PanelBody(undefined, undefined, undefined)];
addNewPanel = () => 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() { constructor() {
super(DrawerState); super(DrawerState);
makeAutoObservable(this); makeAutoObservable(this);
@ -86,7 +116,7 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState<BehaviorTreeView
this.editor = editor; this.editor = editor;
this.areaPlugin = area; this.areaPlugin = area;
}; };
getAllTopics = () => this.filledOutTemplates.topicsStack; getAllTopics = () => this.filledOutTemplates.topicsStack.plus(this.topics);
errorHandingStrategy = (_: CoreError) => {}; errorHandingStrategy = (_: CoreError) => {};
dragEnd = (e: EventTarget) => { dragEnd = (e: EventTarget) => {
@ -102,6 +132,13 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState<BehaviorTreeView
}; };
drawSkillCheck = (x: number, y: number, name: string) => { drawSkillCheck = (x: number, y: number, name: string) => {
const drawPoint = { x: x, y: y, w: 1, h: 1 }; 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 ( if (
drawPoint.x < this.area!.x + this.area!.w && drawPoint.x < this.area!.x + this.area!.w &&
drawPoint.x + drawPoint.w > this.area!.x && drawPoint.x + drawPoint.w > this.area!.x &&
@ -115,7 +152,7 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState<BehaviorTreeView
name: name, name: name,
id: sid, id: sid,
}); });
this.isNeedSaveBtn = true;
if (!name.isEqualMany(Object.keys(SystemPrimitive))) { if (!name.isEqualMany(Object.keys(SystemPrimitive))) {
this.skillTemplates?.getSkill(name).fold( this.skillTemplates?.getSkill(name).fold(
(skill) => { (skill) => {
@ -179,7 +216,7 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState<BehaviorTreeView
await this.mapOk("sceneAsset", this.behaviorTreeBuilderHttpRepository.getSceneAsset(model.sceneId)); await this.mapOk("sceneAsset", this.behaviorTreeBuilderHttpRepository.getSceneAsset(model.sceneId));
this.isLoading = false; this.isLoading = false;
await this.mapOk("topics", this.behaviorTreeBuilderHttpRepository.getAllTopics());
this.reteForceUpdateObserver?.emit(""); this.reteForceUpdateObserver?.emit("");
}, },
async () => { async () => {
@ -207,7 +244,6 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState<BehaviorTreeView
) )
).fold( ).fold(
(xml) => { (xml) => {
console.log(xml);
this.behaviorTreeModel.skills = this.filledOutTemplates; this.behaviorTreeModel.skills = this.filledOutTemplates;
this.behaviorTreeModel.scene = NodeBehaviorTree.fromReteScene( this.behaviorTreeModel.scene = NodeBehaviorTree.fromReteScene(
this.editor as NodeEditor<Schemes>, this.editor as NodeEditor<Schemes>,
@ -345,4 +381,11 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState<BehaviorTreeView
); );
} }
changeSceneViewModel = (text: string) => {}; changeSceneViewModel = (text: string) => {};
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);
};
} }

View file

@ -0,0 +1,94 @@
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<RuntimeModel>(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<CoreError> {
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<any> {
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 (
<div style={{ width: "100%" }}>
{store.isLoading ? (
<Loader />
) : (
<>
{store.isExecuteBt() ? (
<>
<div
style={{ justifyContent: "center", color: "white", justifySelf: "center" }}
onClick={() => store.executeBt()}
>
Дерево запущено
</div>
</>
) : (
<>
<div
style={{ justifyContent: "center", color: "white", justifySelf: "center" }}
onClick={() => store.executeBt()}
>
Запустить дерево
</div>
</>
)}
</>
)}
</div>
);
});

View file

@ -19,7 +19,7 @@ export const FormBuilderForm = observer((props: IPropsForm<Partial<FormBuilderVa
}, []); }, []);
return ( return (
<div style={{ overflowX: "scroll", height: "100%" }}> <div style={{ overflowX: "scroll", height: "100%", flex: 1 }}>
<div>FormBuilder</div> <div>FormBuilder</div>
{store.isBtScreen ? ( {store.isBtScreen ? (
<div> <div>
@ -27,7 +27,6 @@ export const FormBuilderForm = observer((props: IPropsForm<Partial<FormBuilderVa
formBuilder={store.viewModel} formBuilder={store.viewModel}
onChange={(form) => { onChange={(form) => {
store.viewModel = form; store.viewModel = form;
console.log(form);
}} }}
/> />
<div style={{ height: 100 }} /> <div style={{ height: 100 }} />
@ -81,7 +80,7 @@ export const FormBuilderForm = observer((props: IPropsForm<Partial<FormBuilderVa
store.isModalOpen = false; store.isModalOpen = false;
}} }}
> >
<FormBuilder formBuilder={store.viewModel} onChange={() => {}} /> <FormBuilder formBuilder={store.viewModel} onChange={(form) => (console.log(form), props.onChange(form))} />
</Modal> </Modal>
</> </>
)} )}

View file

@ -4,7 +4,7 @@ import { BehaviorTreeViewModel } from "../behavior_tree_builder/model/behavior_t
export class BehaviorTreeManagerHttpRepository extends CoreHttpRepository { export class BehaviorTreeManagerHttpRepository extends CoreHttpRepository {
featureApi = `/behavior/trees`; featureApi = `/behavior/trees`;
deleteBt = (id: string) => this._jsonRequest(HttpMethod.DELETE, `${this.featureApi}?id=${id}`); deleteBt = (id: string) => this._jsonRequest(HttpMethod.POST, `${this.featureApi}/delete/bt?id=${id}`);
saveNewBt = async (model: BehaviorTreeViewModel) => this._jsonRequest(HttpMethod.POST, this.featureApi, model); saveNewBt = async (model: BehaviorTreeViewModel) => this._jsonRequest(HttpMethod.POST, this.featureApi, model);
getAllBtInstances = async () => this._jsonRequest<BehaviorTreeModel[]>(HttpMethod.GET, this.featureApi); getAllBtInstances = async () => this._jsonRequest<BehaviorTreeModel[]>(HttpMethod.GET, this.featureApi);
} }

View file

@ -1,6 +1,6 @@
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { BehaviorTreeManagerStore } from "./behavior_tree_manager_store"; import { BehaviorTreeManagerStore } from "./behavior_tree_manager_store";
import React from "react"; import React, { useEffect } from "react";
import { useStore } from "../../core/helper/use_store"; import { useStore } from "../../core/helper/use_store";
import { ButtonV2, ButtonV2Type } from "../../core/ui/button/button_v2"; import { ButtonV2, ButtonV2Type } from "../../core/ui/button/button_v2";
import { CoreCard } from "../../core/ui/card/card"; import { CoreCard } from "../../core/ui/card/card";
@ -16,13 +16,13 @@ export const BehaviorTreeManagerScreenPath = "/behavior/tree/manager";
export const BehaviorTreeManagerScreen = observer(() => { export const BehaviorTreeManagerScreen = observer(() => {
const store = useStore(BehaviorTreeManagerStore); const store = useStore(BehaviorTreeManagerStore);
useEffect(() => {}, []);
return ( return (
<> <>
<MainPageV2 <MainPageV2
children={ children={
<> <>
<div style={{ height: "100%", overflowY: "auto", overflowX: "hidden" }}> <div style={{ height: "100%", }}>
<div style={{ height: 20 }} /> <div style={{ height: 20 }} />
<ButtonV2 <ButtonV2
icon={<Icon type={"Plus"} style={{ alignSelf: "center", marginLeft: 10, marginRight: 10 }} />} icon={<Icon type={"Plus"} style={{ alignSelf: "center", marginLeft: 10, marginRight: 10 }} />}
@ -67,7 +67,11 @@ export const BehaviorTreeManagerScreen = observer(() => {
<div style={{ height: 20 }} /> <div style={{ height: 20 }} />
<InputV2 trim={true} label={"Название"} onChange={(text) => store.updateForm({ name: text })} /> <InputV2 trim={true} label={"Название"} onChange={(text) => store.updateForm({ name: text })} />
<div style={{ height: 20 }} /> <div style={{ height: 20 }} />
<InputV2 trim={true} label={"Описание"} onChange={(text) => store.updateForm({ description: text })} /> <InputV2
trim={true}
label={"Описание"}
onChange={(text) => store.updateForm({ description: text })}
/>
<div style={{ height: 20 }} /> <div style={{ height: 20 }} />
<SelectV2 <SelectV2
items={store.scenes?.map((el) => ({ name: el.name, value: el._id })) ?? []} items={store.scenes?.map((el) => ({ name: el.name, value: el._id })) ?? []}

View file

@ -12,7 +12,6 @@ export enum DrawerState {
} }
export class BehaviorTreeManagerStore extends UiDrawerFormState<BehaviorTreeViewModel, CoreError> { export class BehaviorTreeManagerStore extends UiDrawerFormState<BehaviorTreeViewModel, CoreError> {
viewModel: BehaviorTreeViewModel = BehaviorTreeViewModel.empty(); viewModel: BehaviorTreeViewModel = BehaviorTreeViewModel.empty();
navigate?: NavigateFunction;
btTreeModels: BehaviorTreeModel[] = []; btTreeModels: BehaviorTreeModel[] = [];
scenes?: SceneModel[]; scenes?: SceneModel[];
behaviorTreeManagerHttpRepository = new BehaviorTreeManagerHttpRepository(); behaviorTreeManagerHttpRepository = new BehaviorTreeManagerHttpRepository();

View file

@ -14,6 +14,12 @@ export interface ISkils {
} }
export class CalculationHttpRepository extends CoreHttpRepository { 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`; featureApi = `/calculations/instances`;
subFeatureApi = `/calculations/template`; subFeatureApi = `/calculations/template`;

View file

@ -4,7 +4,9 @@ import { FormBuilderValidationModel } from "../../../core/model/form_builder_val
export enum ModelMachineLearningTypes { export enum ModelMachineLearningTypes {
OBJECT_DETECTION = "OBJECT_DETECTION", OBJECT_DETECTION = "OBJECT_DETECTION",
POSE_ESTIMATE = "POSE_ESTIMATE", POSE_ESTIMATION = "POSE_ESTIMATION",
BOP_DATASET = "BOP_DATASET",
WEIGHTS = "WEIGHTS",
} }
export class CalculationModel extends ValidationModel { export class CalculationModel extends ValidationModel {

View file

@ -28,7 +28,6 @@ export const CalculationInstanceScreenPath = "/calculation";
export const CalculationInstanceScreen = observer(() => { export const CalculationInstanceScreen = observer(() => {
const store = useStore(CalculationInstanceStore); const store = useStore(CalculationInstanceStore);
return ( return (
<> <>
<MainPage <MainPage
@ -60,7 +59,9 @@ export const CalculationInstanceScreen = observer(() => {
() => store.makeEditProcess(el), () => store.makeEditProcess(el),
() => store.deleteInstance(el._id ?? ""), () => store.deleteInstance(el._id ?? ""),
() => store.execSkill(el._id ?? ""), () => store.execSkill(el._id ?? ""),
() => store.execSkill(el._id ?? "") () => store.execSkill(el._id ?? ""),
() => store.changeProcessStatus(el._id ?? ""),
() => store.getTxtLog(el._id ?? "")
)} )}
</span> </span>
); );
@ -94,7 +95,10 @@ export const CalculationInstanceScreen = observer(() => {
<div style={{ display: "flex", flexDirection: "column", justifyContent: "space-between", height: "100%" }}> <div style={{ display: "flex", flexDirection: "column", justifyContent: "space-between", height: "100%" }}>
<FormBuilder <FormBuilder
formBuilder={store.editProcess?.formBuilder ?? FormBuilderValidationModel.empty()} formBuilder={store.editProcess?.formBuilder ?? FormBuilderValidationModel.empty()}
onChange={(formBuilder) => store.updateForm({ formBuilder: formBuilder })} onChange={(formBuilder) => {
console.log(formBuilder);
store.updateForm({ formBuilder: formBuilder });
}}
/> />
<div style={{ display: "flex" }}> <div style={{ display: "flex" }}>
@ -134,7 +138,7 @@ export const CalculationInstanceScreen = observer(() => {
<CoreSelect <CoreSelect
items={Object.keys(ModelMachineLearningTypes)} items={Object.keys(ModelMachineLearningTypes)}
value={store.viewModel.type} value={store.viewModel.type}
label={"Тип навыка"} label={"Тип вычисления"}
onChange={(text: string) => store.updateForm({ type: text })} onChange={(text: string) => store.updateForm({ type: text })}
/> />
<CoreSelect <CoreSelect
@ -143,8 +147,16 @@ export const CalculationInstanceScreen = observer(() => {
label={"Тип карточки"} label={"Тип карточки"}
onChange={(text: string) => store.updateForm({ card: text })} onChange={(text: string) => store.updateForm({ card: text })}
/> />
<CoreInput label="Имя" onChange={(text) => store.updateForm({ name: text })} /> <CoreInput
<CoreInput label="Команда для запуска" onChange={(text) => store.updateForm({ script: text })} /> trim={true}
label="Имя"
onChange={(text) => store.updateForm({ name: text.replaceAll("\n", "") })}
/>
<CoreInput
trim={true}
label="Команда для запуска"
onChange={(text) => store.updateForm({ script: text.replaceAll("\n", "") })}
/>
<InputV2 <InputV2
label="FormBuilder Result" label="FormBuilder Result"
onChange={(text) => (store.viewModel.formBuilder.result = text)} onChange={(text) => (store.viewModel.formBuilder.result = text)}
@ -206,7 +218,7 @@ export const CalculationInstanceScreen = observer(() => {
store.isModalOpen = false; store.isModalOpen = false;
}} }}
> >
<FormBuilder formBuilder={store.viewModel.formBuilder} onChange={() => {}} /> <FormBuilder formBuilder={store.viewModel.formBuilder} onChange={(e) => {}} />
</Modal> </Modal>
</> </>
); );

View file

@ -5,7 +5,7 @@ import { Drawer, UiDrawerFormState } from "../../../core/store/base_store";
import { CalculationHttpRepository } from "../data/calculation_http_repository"; import { CalculationHttpRepository } from "../data/calculation_http_repository";
import { message } from "antd"; import { message } from "antd";
import { UUID } from "../../all_projects/data/project_http_repository"; import { UUID } from "../../all_projects/data/project_http_repository";
import { CalculationModel } from "../model/calculation_model"; import { CalculationModel as calculationModel } from "../model/calculation_model";
import { ProcessUpdate, CalculationSocketRepository } from "../data/calculation_socket_repository"; import { ProcessUpdate, CalculationSocketRepository } from "../data/calculation_socket_repository";
import { match } from "ts-pattern"; import { match } from "ts-pattern";
import { plainToInstance } from "class-transformer"; import { plainToInstance } from "class-transformer";
@ -20,16 +20,17 @@ export enum StoreTypes {
empty = "empty", empty = "empty",
} }
export class CalculationInstanceStore extends UiDrawerFormState<CalculationModel, HttpError> { export class CalculationInstanceStore extends UiDrawerFormState<calculationModel, HttpError> {
getTxtLog = (id: string) => this.calculationHttpRepository.getLogs(id);
calculationHttpRepository: CalculationHttpRepository = new CalculationHttpRepository(); calculationHttpRepository: CalculationHttpRepository = new CalculationHttpRepository();
calculationSocketRepository: CalculationSocketRepository = new CalculationSocketRepository(); calculationSocketRepository: CalculationSocketRepository = new CalculationSocketRepository();
activeProjectId?: UUID; activeProjectId?: UUID;
storeType: StoreTypes = StoreTypes.empty; storeType: StoreTypes = StoreTypes.empty;
viewModel: CalculationModel = CalculationModel.empty(); viewModel: calculationModel = calculationModel.empty();
modelTemplate?: CalculationModel[]; modelTemplate?: calculationModel[];
editProcess?: CalculationModel; editProcess?: calculationModel;
calculationInstances?: CalculationModel[]; calculationInstances?: calculationModel[];
selectTemplate?: CalculationModel; selectTemplate?: calculationModel;
titleDrawer: string = DrawersSkill.NEW_SKILL; titleDrawer: string = DrawersSkill.NEW_SKILL;
drawers: Drawer[]; drawers: Drawer[];
isModalOpen: boolean = false; isModalOpen: boolean = false;
@ -41,7 +42,7 @@ export class CalculationInstanceStore extends UiDrawerFormState<CalculationModel
status: false, status: false,
}; };
}); });
this.viewModel = CalculationModel.empty(); this.viewModel = calculationModel.empty();
this.calculationSocketRepository.on(this.socketUpdate); this.calculationSocketRepository.on(this.socketUpdate);
makeAutoObservable(this); makeAutoObservable(this);
} }
@ -67,9 +68,9 @@ export class CalculationInstanceStore extends UiDrawerFormState<CalculationModel
successMessage: "Процесс запущен", successMessage: "Процесс запущен",
}); });
setSelectTemplate = (el: CalculationModel) => { setSelectTemplate = (el: calculationModel) => {
this.selectTemplate = el; this.selectTemplate = el;
const instance = plainToInstance(CalculationModel, el); const instance = plainToInstance(calculationModel, el);
instance.instanceName = this?.viewModel.instanceName; instance.instanceName = this?.viewModel.instanceName;
this.viewModel = instance; this.viewModel = instance;
}; };
@ -84,7 +85,7 @@ export class CalculationInstanceStore extends UiDrawerFormState<CalculationModel
match(this.storeType) match(this.storeType)
.with(StoreTypes.empty, () => {}) .with(StoreTypes.empty, () => {})
.with(StoreTypes.newType, async () => .with(StoreTypes.newType, async () =>
(await this.viewModel.valid<CalculationModel>()).fold( (await this.viewModel.valid<calculationModel>()).fold(
async (model) => { async (model) => {
model.project = this.activeProjectId?.id; model.project = this.activeProjectId?.id;
@ -99,7 +100,7 @@ export class CalculationInstanceStore extends UiDrawerFormState<CalculationModel
) )
) )
.with(StoreTypes.newModel, async () => { .with(StoreTypes.newModel, async () => {
(await this.viewModel.valid<CalculationModel>()).fold( (await this.viewModel.valid<calculationModel>()).fold(
async (model) => { async (model) => {
delete model._id; delete model._id;
model.project = this.activeProjectId?.id; model.project = this.activeProjectId?.id;
@ -128,20 +129,30 @@ export class CalculationInstanceStore extends UiDrawerFormState<CalculationModel
saveEdiSkill = async () => { saveEdiSkill = async () => {
this.editDrawer(DrawersSkill.EDIT_SKILL, false); this.editDrawer(DrawersSkill.EDIT_SKILL, false);
(await this.viewModel.valid<CalculationModel>()).fold( (await this.viewModel.valid<calculationModel>()).fold(
async (model) => await this.calculationHttpRepository.editCalculation(model), async (model) => (await this.calculationHttpRepository.editCalculation(model), await this.init()),
async (err) => message.error(err) async (err) => message.error(err)
); );
}; };
makeEditProcess = (el: CalculationModel) => { makeEditProcess = (el: calculationModel) => {
this.editProcess = el; this.editProcess = el;
this.loadClassInstance(CalculationModel, el); this.loadClassInstance(calculationModel, el);
this.editDrawer(DrawersSkill.EDIT_SKILL, true); this.editDrawer(DrawersSkill.EDIT_SKILL, true);
}; };
deleteTemplate = async (el: CalculationModel) => { deleteTemplate = async (el: calculationModel) => {
await this.messageHttp(this.calculationHttpRepository.deleteTemplate(el._id ?? ""), { await this.messageHttp(this.calculationHttpRepository.deleteTemplate(el._id ?? ""), {
successMessage: "Удален", successMessage: "Удален",
}); });
await this.mapOk("modelTemplate", this.calculationHttpRepository.getAllTemplates()); 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)
)
);
} }

View file

@ -1,6 +1,6 @@
import { match } from "ts-pattern"; import { match } from "ts-pattern";
import { PoseEstimateCard } from "./pose_estimate_card/model_card"; import { PoseEstimateCard } from "./pose_estimate_card/model_card";
import { Dropdown, MenuProps, message } from "antd"; import { Dropdown, MenuProps, } from "antd";
import { CoreText, CoreTextType } from "../../../../../core/ui/text/text"; import { CoreText, CoreTextType } from "../../../../../core/ui/text/text";
import { IMenuItem } from "../../../../dataset/card_dataset"; import { IMenuItem } from "../../../../dataset/card_dataset";
import { Icon } from "../../../../../core/ui/icons/icons"; import { Icon } from "../../../../../core/ui/icons/icons";
@ -15,7 +15,9 @@ export const getModelCard = (
onEdit: Function, onEdit: Function,
onDelete: Function, onDelete: Function,
onPlay: Function, onPlay: Function,
onPause: Function onPause: Function,
onChangeProcessIsEnd: Function,
onLog: Function
) => { ) => {
const menu: IMenuItem[] = [ const menu: IMenuItem[] = [
{ {
@ -26,6 +28,10 @@ export const getModelCard = (
onClick: () => onDelete(), onClick: () => onDelete(),
name: "Удалить", name: "Удалить",
}, },
{
onClick: () => onChangeProcessIsEnd(),
name: calculationModel.isEnd ? "Вернуть на доработку" : "Завершить",
},
]; ];
const items: MenuProps["items"] = menu.map((el, index) => { const items: MenuProps["items"] = menu.map((el, index) => {
@ -63,7 +69,8 @@ export const getModelCard = (
<Icon <Icon
type="Log" type="Log"
onClick={async () => onClick={async () =>
window.prompt("Copy to clipboard: Ctrl+C, Enter", calculationModel.lastProcessLogs ?? "Not found logs") // window.prompt("Copy to clipboard: Ctrl+C, Enter", calculationModel.lastProcessLogs ?? "Not found logs")
onLog()
} }
/> />
<Icon <Icon

View file

@ -26,7 +26,6 @@ export class PoseEstimateRepository extends HttpRepository {
execAnalyze = (id: string) => this._jsonRequest(HttpMethod.POST, `/run_time/exec/analyze?id=${id}`); execAnalyze = (id: string) => this._jsonRequest(HttpMethod.POST, `/run_time/exec/analyze?id=${id}`);
} }
export class PoseEstimateStore extends UiErrorState<any> { export class PoseEstimateStore extends UiErrorState<any> {
navigate?: NavigateFunction;
poseEstimateRepository = new PoseEstimateRepository(); poseEstimateRepository = new PoseEstimateRepository();
constructor() { constructor() {
super(); super();

View file

@ -12,7 +12,6 @@ import { useStore } from "../../core/helper/use_store";
import { FormBuilder } from "../../core/ui/form_builder/form_builder"; import { FormBuilder } from "../../core/ui/form_builder/form_builder";
import { ButtonV2 } from "../../core/ui/button/button_v2"; import { ButtonV2 } from "../../core/ui/button/button_v2";
import { Result } from "../../core/helper/result"; import { Result } from "../../core/helper/result";
import { plainToInstance } from "class-transformer";
export const isValidJson = <T,>(json: any): Result<string, T> => { export const isValidJson = <T,>(json: any): Result<string, T> => {
try { try {
@ -288,3 +287,4 @@ export const SkillsScreen = observer(() => {
</> </>
); );
}); });

View file

@ -21,6 +21,7 @@ export class TopicViewModel extends ValidationModel {
export interface ITopicModel { export interface ITopicModel {
digitalTwinId?: string; digitalTwinId?: string;
sid?: string; sid?: string;
_id?: string;
name: string; name: string;
type: string; type: string;
} }

View file

@ -1,6 +1,9 @@
import { HttpMethod, HttpRepository } from "../../core/repository/core_http_repository"; import { CoreHttpRepository, HttpMethod, HttpRepository } from "../../core/repository/core_http_repository";
import { TopicViewModel } from "./topic_view_model";
export class TopicsHttpRepository extends HttpRepository { export class TopicsHttpRepository extends CoreHttpRepository {
deleteTopic = (id: any) => this._jsonRequest(HttpMethod.DELETE, `${this.featureApi}?id=${id}`);
featureApi = "/topics"; featureApi = "/topics";
getAllTopics = () => this._jsonRequest(HttpMethod.GET, this.featureApi); createTopics = (model: TopicViewModel) => this._jsonRequest(HttpMethod.POST, this.featureApi, model);
} }

View file

@ -2,6 +2,11 @@ import React from "react";
import { observer } from "mobx-react-lite"; import { observer } from "mobx-react-lite";
import { TopicsStore } from "./topics_store"; import { TopicsStore } from "./topics_store";
import { useStore } from "../../core/helper/use_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"; export const TopicsScreenPath = "/topics";
@ -10,12 +15,43 @@ export const TopicsScreen = observer(() => {
return ( return (
<> <>
<div style={{ display: "flex" }}>
<CoreButton text="добавить новый топик" style={{ width: 200 }} onClick={() => store.modalShow()} />
<CoreButton text="импорт топика" style={{ width: 200 }} onClick={() => store.openTopicModal()} />
</div>
{store.topics?.map((el) => ( {store.topics?.map((el) => (
<div style={{ margin: 10, border: "1px solid red" }}> <div style={{ margin: 10, border: "1px solid red" }}>
<CoreText text="Delete" type={CoreTextType.header} onClick={() => store.deleteTopic(el._id ?? "")} />
<CoreText
text="Copy"
type={CoreTextType.header}
onClick={() => window.prompt("Copy to clipboard: Ctrl+C, Enter", JSON.stringify(el))}
/>
<div>{el.name}</div> <div>{el.name}</div>
<div>{el.type}</div> <div>{el.type}</div>
</div> </div>
))} ))}
<CoreModal
isOpen={store.isModalOpen}
onClose={() => store.modalCancel()}
children={
<div>
<InputV2 label={"type"} onChange={(text) => store.updateForm({ type: text })} />
<InputV2 label={"name"} onChange={(text) => store.updateForm({ name: text })} />
<CoreButton text="new" onClick={() => store.createTopic()} />
</div>
}
/>
<CoreModal
isOpen={store.importTopicModal}
onClose={() => store.cancelImportTopicModal()}
children={
<div>
<InputV2 onChange={(text) => store.importTopic(text)} label="import" />
</div>
}
/>
</> </>
); );
}); });

View file

@ -6,14 +6,35 @@ import { NavigateFunction } from "react-router-dom";
import { TopicsHttpRepository } from "./topics_repository"; import { TopicsHttpRepository } from "./topics_repository";
export class TopicsStore extends FormState<TopicModel, HttpError> { export class TopicsStore extends FormState<TopicModel, HttpError> {
importTopic(text: string): void {
this.loadClassInstance(TopicModel, JSON.parse(text))
this.createTopic()
}
viewModel: TopicModel = TopicModel.empty(); viewModel: TopicModel = TopicModel.empty();
topics?: ITopicModel[]; topics?: ITopicModel[];
topicsHttpRepository: TopicsHttpRepository = new TopicsHttpRepository(); topicsHttpRepository: TopicsHttpRepository = new TopicsHttpRepository();
importTopicModal = false;
constructor() { constructor() {
super(); super();
makeAutoObservable(this); makeAutoObservable(this);
} }
init = async (navigate?: NavigateFunction | undefined) => { init = async (navigate?: NavigateFunction | undefined) => {
this.modalCancel();
this.importTopicModal = false;
await this.mapOk("topics", this.topicsHttpRepository.getAllTopics()); 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<TopicModel>()).map(async (model) =>
(await this.topicsHttpRepository.createTopics(model)).map(() => this.init())
);
} }

View file

@ -7,6 +7,7 @@ import { RouterProvider } from "react-router-dom";
import { router } from "./core/routers/routers"; import { router } from "./core/routers/routers";
import { configure } from "mobx"; import { configure } from "mobx";
import { ThemeStore } from "./core/store/theme_store"; import { ThemeStore } from "./core/store/theme_store";
import { FormBuildTest } from "./core/ui/form_builder/test";
configure({ configure({
enforceActions: "never", enforceActions: "never",
@ -22,5 +23,6 @@ root.render(
<SocketListener> <SocketListener>
<RouterProvider router={router} /> <RouterProvider router={router} />
</SocketListener> </SocketListener>
{/* <FormBuildTest /> */}
</> </>
); );

View file

@ -0,0 +1,6 @@
{
"_id": "67b43b59ba78351d1a11d74a",
"name": "/rgbd_camera/camera_info",
"type": "sensor_msgs/msg/CameraInfo",
"__v": 0
}

6
web_p/image_topic.json Normal file
View file

@ -0,0 +1,6 @@
{
"_id": "67b43b20ba78351d1a11d747",
"name": "/rgbd_camera/image",
"type": "sensor_msgs/msg/Image",
"__v": 0
}

196
web_p/models_dope.py Executable file
View file

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

87
web_p/od_skill.json Normal file
View file

@ -0,0 +1,87 @@
{
"_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\":\\${<SelectProcess/>: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<ITEM>:[]}}",
"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
}

64
web_p/rbs_train2.py Normal file
View file

@ -0,0 +1,64 @@
"""
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)

View file

@ -8,6 +8,7 @@ import blenderproc as bproc
02.05.2024 @shalenikol release 0.1 02.05.2024 @shalenikol release 0.1
02.07.2024 @shalenikol release 0.2 02.07.2024 @shalenikol release 0.2
28.10.2024 @shalenikol release 0.3 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 numpy as np
import argparse import argparse
@ -16,17 +17,32 @@ import os
import shutil import shutil
import json import json
from pathlib import Path 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 import bpy
VHACD_PATH = "blenderproc_resources/vhacd" VHACD_PATH = "blenderproc_resources/vhacd"
DIR_MODELS = "models" DIR_MODELS = "models"
DIR_MESH = "assets/libs/objects/" # DIR_MESH = "assets/libs/objects/"
FILE_LOG_SCENE = "res.txt" FILE_LOG_SCENE = "res.txt"
FILE_RBS_INFO = "rbs_info.json" FILE_RBS_INFO = "rbs_info.json"
FILE_GT_COCO = "scene_gt_coco.json" FILE_GT_COCO = "scene_gt_coco.json"
EXT_MODELS = ".fbx" 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
TEXTURE_TMPL = "*.jpg" TEXTURE_TMPL = "*.jpg"
TEXTURE_IMAGE_TYPES = ["Base Color", "Metallic", "Normal", "Roughness", "Specular IOR Level"]
Not_Categories_Name = True # наименование категории в COCO-аннотации отсутствует Not_Categories_Name = True # наименование категории в COCO-аннотации отсутствует
@ -57,19 +73,50 @@ def convert2relative(height, width, bbox):
y += h/2 y += h/2
return x/width, y/height, w/width, h/height 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: 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 i = 0
for obj in all_meshs: for obj in all_meshs:
# Make the object actively participate in the physics simulation # Make the object actively participate in the physics simulation
obj.enable_rigidbody(active=True, collision_shape="COMPOUND") obj.enable_rigidbody(active=True, collision_shape="COMPOUND")
# Also use convex decomposition as collision shapes # Also use convex decomposition as collision shapes
obj.build_convex_decomposition_collision_shape(VHACD_PATH) 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 i += 1
# print(f"{i} : {obj.get_name()}") # print(f"{i} : {obj.get_name()}")
objs = all_meshs + rnd_par.scene.objs objs = all_meshs + rnd_par.scene.objs
log_txt = os.path.join(os.path.dirname(rnd_par.output_dir), FILE_LOG_SCENE) log_txt = os.path.join(log_dir, FILE_LOG_SCENE)
with open(log_txt, "w") as fh: with open(log_txt, "w") as fh:
for i,o in enumerate(objs): for i,o in enumerate(objs):
loc = o.get_location() loc = o.get_location()
@ -91,23 +138,21 @@ def render() -> int:
rnd_par.image_size_wh[1], rnd_par.image_size_wh[1],
lens_unit="FOV") 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) # add segmentation masks (per class and per instance)
bproc.renderer.enable_segmentation_output(map_by=["category_id", "instance", "name"]) bproc.renderer.enable_segmentation_output(map_by=["category_id", "instance", "name"])
# activate depth rendering # activate depth rendering
bproc.renderer.enable_depth_output(activate_antialiasing=False) 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 # 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): for r in range(rnd_par.n_series):
print(f"********** Series : {r+1}") print(f"********** Series : {r+1}")
is_texture = True if "texture_path" in rnd_par.models_randomization else False is_texture = True if PROCEDURAL_TEXTURE in rnd_par.models_randomization else False
if is_texture: if is_texture:
val = rnd_par.models_randomization["texture_path"] val = rnd_par.models_randomization[PROCEDURAL_TEXTURE]
l_texture = _get_list_texture(val) l_texture = _get_list_texture(val)
image = bpy.data.images.load(filepath=str(l_texture[r % len(l_texture)])) image = bpy.data.images.load(filepath=str(l_texture[r % len(l_texture)]))
# один случайный объект в кадре / все заданные объекты # один случайный объект в кадре / все заданные объекты
@ -125,16 +170,32 @@ def render() -> int:
for i,o in enumerate(rnd_par.scene.objs): # объекты сцены for i,o in enumerate(rnd_par.scene.objs): # объекты сцены
rnd_mat = rnd_par.scene.obj_data[i]["material_randomization"] 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] mats = o.get_materials() #[0]
for mat in mats: for mat in mats:
# with open(log_txt, "a") as fh:
# fh.write("************* mat\n")
# fh.write(f"{mat}\n")
val = rnd_mat["specular"] val = rnd_mat["specular"]
mat.set_principled_shader_value("Specular", random.uniform(val[0], val[1])) mat.set_principled_shader_value("Specular IOR Level", random.uniform(val[0], val[1])) # для Blender < 4.2 было "Specular"
val = rnd_mat["roughness"] val = rnd_mat["roughness"]
mat.set_principled_shader_value("Roughness", random.uniform(val[0], val[1])) mat.set_principled_shader_value("Roughness", random.uniform(val[0], val[1]))
val = rnd_mat["metallic"] val = rnd_mat["metallic"]
mat.set_principled_shader_value("Metallic", random.uniform(val[0], val[1])) mat.set_principled_shader_value("Metallic", random.uniform(val[0], val[1]))
if "texture_path" in rnd_mat: # путь к текстурам (*.jpg) if PROCEDURAL_TEXTURE in rnd_mat: # путь к текстурам (*.jpg)
val = rnd_mat["texture_path"] val = rnd_mat[PROCEDURAL_TEXTURE]
val = _get_list_texture(val) val = _get_list_texture(val)
image = bpy.data.images.load(filepath=str(random.choice(val))) image = bpy.data.images.load(filepath=str(random.choice(val)))
mat.set_principled_shader_value("Base Color", image) mat.set_principled_shader_value("Base Color", image)
@ -156,7 +217,7 @@ def render() -> int:
# Define a function that samples 6-DoF poses # Define a function that samples 6-DoF poses
def sample_pose(obj: bproc.types.MeshObject): 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_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()) 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))
# Sample the poses of all shapenet objects above the ground without any collisions in-between # Sample the poses of all shapenet objects above the ground without any collisions in-between
bproc.object.sample_poses(meshs, bproc.object.sample_poses(meshs,
@ -232,7 +293,12 @@ def render() -> int:
rec["name"] = objn rec["name"] = objn
rec["model"] = os.path.join(DIR_MODELS, os.path.split(rnd_par.models.filenames[i])[1]) # путь относительный 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] 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] 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()
data.append(rec) data.append(rec)
shutil.copy2(rnd_par.models.filenames[i], models_dir) shutil.copy2(rnd_par.models.filenames[i], models_dir)
f = (os.path.splitext(rnd_par.models.filenames[i]))[0] + ".mtl" # файл материала f = (os.path.splitext(rnd_par.models.filenames[i]))[0] + ".mtl" # файл материала
@ -283,9 +349,37 @@ def render() -> int:
if Not_Categories_Name: if Not_Categories_Name:
explore(res_dir) 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 return 0 # success
def _get_models(par, data) -> int: 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:
global all_meshs global all_meshs
par.models = lambda: None par.models = lambda: None
@ -294,13 +388,14 @@ def _get_models(par, data) -> int:
return 0 # no models return 0 # no models
# загрузим объекты # загрузим объекты
par.models.names = [] # obj_names par.models.names = []
par.models.filenames = [] # obj_filenames par.models.filenames = []
par.models.textures = []
i = 1 i = 1
for f in data: for f in data:
nam = f["name"] nam = f["name"]
par.models.names.append(nam) par.models.names.append(nam)
ff = f["fbx"] # _get_path_model(nam) ff = f[DETAIL_KEY] # _get_path_model(nam)
par.models.filenames.append(ff) par.models.filenames.append(ff)
if not os.path.isfile(ff): if not os.path.isfile(ff):
print(f"Error: no such file '{ff}'") print(f"Error: no such file '{ff}'")
@ -311,6 +406,7 @@ def _get_models(par, data) -> int:
obj = bproc.loader.load_obj(ff) obj = bproc.loader.load_obj(ff)
all_meshs += obj all_meshs += obj
obj[0].set_cp("category_id", i) # начиная с 1 obj[0].set_cp("category_id", i) # начиная с 1
set_texture_model(nam, par.models.textures, models_data)
i += 1 i += 1
return par.models.n_item return par.models.n_item
@ -370,8 +466,6 @@ if __name__ == "__main__":
print(f"JSon error: {e}") print(f"JSon error: {e}")
exit(-2) exit(-2)
# output_dir = args.path
ds_cfg = cfg["output"] # dataset config ds_cfg = cfg["output"] # dataset config
generation = ds_cfg["generation"] generation = ds_cfg["generation"]
cam_pos = ds_cfg["camera_position"] cam_pos = ds_cfg["camera_position"]
@ -399,11 +493,14 @@ if __name__ == "__main__":
rnd_par.models_randomization = models_randomization rnd_par.models_randomization = models_randomization
rnd_par.loc_range_low = models_randomization["loc_range_low"] rnd_par.loc_range_low = models_randomization["loc_range_low"]
rnd_par.loc_range_high = models_randomization["loc_range_high"] 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() bproc.init()
all_meshs = [] all_meshs = []
if _get_models(rnd_par, rnd_par.dataset_objs) <= 0: if _get_models(rnd_par, rnd_par.dataset_objs, models_randomization) <= 0:
print("Error: no models in config") print("Error: no models in config")
exit(-4) exit(-4)
if _get_scene(rnd_par, ds_cfg["scene"]) <= 0: if _get_scene(rnd_par, ds_cfg["scene"]) <= 0:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

View file

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 141 KiB

View file

@ -1,34 +0,0 @@
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
1 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
2 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
3 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
4 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
5 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
6 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
7 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
8 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
9 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
10 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
11 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
12 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
13 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
14 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
15 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
16 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
17 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
18 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
19 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
20 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
21 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
22 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
23 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
24 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
25 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
26 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
27 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
28 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
29 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
30 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
31 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
32 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
33 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
34 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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 280 KiB

Some files were not shown because too many files have changed in this diff Show more