Compare commits
136 commits
Author | SHA1 | Date | |
---|---|---|---|
4ceae093f3 | |||
f0e09812ee | |||
ccc1e2da4b | |||
c85784f3dc | |||
![]() |
7d3b8ff0cb | ||
![]() |
d7e8c825cb | ||
![]() |
40eebf9dd8 | ||
![]() |
564866393f | ||
![]() |
2afed3a4f0 | ||
470a7ef43e | |||
7c01c61194 | |||
![]() |
3044540814 | ||
![]() |
dfe7b20f8d | ||
![]() |
c2b91ee4d7 | ||
![]() |
c2da1d8f4c | ||
![]() |
d7ea79935e | ||
![]() |
a94d84ba69 | ||
![]() |
d3a07a5c11 | ||
![]() |
12c09c4461 | ||
![]() |
8696d99194 | ||
bad5c4d8a0 | |||
![]() |
88e40f7bdb | ||
![]() |
58b22d294d | ||
![]() |
f39f69a440 | ||
![]() |
ea1ebe0e95 | ||
![]() |
2024834d06 | ||
![]() |
300a5005ba | ||
![]() |
e163f0698b | ||
cad230eee9 | |||
![]() |
aaa4257920 | ||
![]() |
3accb5af61 | ||
![]() |
6190869d8d | ||
![]() |
28c36ae710 | ||
![]() |
3f951d1c09 | ||
![]() |
29dcfb6ac1 | ||
![]() |
00c553ecd7 | ||
![]() |
3f88ceebe4 | ||
![]() |
2d59388494 | ||
![]() |
23c39cbae2 | ||
![]() |
059b2e3e64 | ||
![]() |
f019c3a1c4 | ||
![]() |
2f15b86a42 | ||
![]() |
15cb712c3d | ||
![]() |
1ec2a3ff7a | ||
![]() |
7693377ad1 | ||
![]() |
1cc489fc72 | ||
![]() |
139f012803 | ||
![]() |
0dbc04f09b | ||
![]() |
c19e4c684e | ||
![]() |
2b4795dfc2 | ||
![]() |
04b1c2bfc7 | ||
![]() |
3369306451 | ||
![]() |
15930c5f85 | ||
![]() |
da883edb64 | ||
![]() |
c135bca77a | ||
![]() |
4657652dd0 | ||
![]() |
314e070bee | ||
![]() |
d47d555061 | ||
![]() |
50d239a4eb | ||
![]() |
5eb588a709 | ||
![]() |
ba5394107a | ||
![]() |
fb4fa52c14 | ||
![]() |
4e6132a872 | ||
![]() |
d804aa2edf | ||
![]() |
e2cd7af032 | ||
![]() |
c0a23c31f7 | ||
![]() |
193e884e40 | ||
![]() |
401080d78e | ||
![]() |
3b8d9e4298 | ||
![]() |
25e57c1a56 | ||
![]() |
a920f5fb45 | ||
![]() |
cac5fad8ce | ||
![]() |
d6702185f0 | ||
![]() |
1287625107 | ||
![]() |
e748debd2f | ||
![]() |
7063e93c75 | ||
![]() |
cf75b4220a | ||
![]() |
ca3a1cfed9 | ||
![]() |
48be3e6d33 | ||
![]() |
d41310196f | ||
![]() |
0b3eefec2d | ||
![]() |
17ed2905c2 | ||
![]() |
6e6a1c0c1d | ||
![]() |
6003cfde04 | ||
![]() |
9120729d41 | ||
![]() |
318d0a7893 | ||
![]() |
d8b5018cb2 | ||
![]() |
50822a031d | ||
![]() |
559262db34 | ||
![]() |
b5750b12ef | ||
![]() |
50d0c4c12b | ||
![]() |
0a4eea19c5 | ||
![]() |
81238c5182 | ||
![]() |
53fe9bf51a | ||
![]() |
4e726be376 | ||
![]() |
814eb485eb | ||
![]() |
8f8fc3107d | ||
![]() |
61118a7423 | ||
![]() |
c5ae89d18a | ||
![]() |
5162612a77 | ||
![]() |
4585d079f6 | ||
![]() |
89d4226ea6 | ||
![]() |
a0cee0b394 | ||
![]() |
e0a6cd0af1 | ||
![]() |
c49beb8218 | ||
![]() |
8bc943de2c | ||
![]() |
f0ed478b36 | ||
![]() |
c4ddb3dc8c | ||
![]() |
e155b4a2a1 | ||
![]() |
e005a42254 | ||
![]() |
c628cdb9af | ||
![]() |
7e25cc216a | ||
![]() |
810eb5926e | ||
![]() |
94b40bf24c | ||
![]() |
38ffde1d77 | ||
![]() |
e85dc14fc8 | ||
![]() |
f11dfa7e57 | ||
![]() |
2b8d0fa88b | ||
![]() |
a2066ce5cd | ||
![]() |
776b6e540e | ||
![]() |
78ebb748ea | ||
![]() |
c10cdb8158 | ||
![]() |
27763fc8d2 | ||
![]() |
0c03906518 | ||
![]() |
c17515d571 | ||
![]() |
6840402b1f | ||
![]() |
dffc73c30f | ||
![]() |
c43c192a3e | ||
![]() |
0d825f7f63 | ||
![]() |
de5b493c77 | ||
![]() |
acc79df97d | ||
![]() |
5dec799002 | ||
![]() |
9028186a74 | ||
![]() |
2adb939f37 | ||
![]() |
ae9842d5e1 | ||
![]() |
7ff6165882 |
1
.vscode/settings.json
vendored
|
@ -20,6 +20,7 @@
|
||||||
"GLTF",
|
"GLTF",
|
||||||
"grau",
|
"grau",
|
||||||
"idontsudo",
|
"idontsudo",
|
||||||
|
"Pids",
|
||||||
"raycaster",
|
"raycaster",
|
||||||
"skils",
|
"skils",
|
||||||
"typedataset",
|
"typedataset",
|
||||||
|
|
61
flake.lock
generated
|
@ -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
|
|
||||||
}
|
|
37
flake.nix
|
@ -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
|
@ -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
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
```
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -65,12 +65,12 @@ interface ISubSetFeatureRouter<A> {
|
||||||
method: HttpMethodType;
|
method: HttpMethodType;
|
||||||
subUrl: string;
|
subUrl: string;
|
||||||
fn:
|
fn:
|
||||||
| CallbackStrategyWithValidationModel<A>
|
| CallbackStrategyWithValidationModel<A>
|
||||||
| CallbackStrategyWithEmpty
|
| CallbackStrategyWithEmpty
|
||||||
| CallbackStrategyWithIdQuery
|
| CallbackStrategyWithIdQuery
|
||||||
| CallBackStrategyWithQueryPage
|
| CallBackStrategyWithQueryPage
|
||||||
| CallbackStrategyWithFileUpload
|
| CallbackStrategyWithFileUpload
|
||||||
| CallbackStrategyWithFilesUploads;
|
| CallbackStrategyWithFilesUploads;
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class ICoreHttpController {
|
abstract class ICoreHttpController {
|
||||||
|
@ -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) });
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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)
|
||||||
);
|
);
|
||||||
|
|
72
server/src/core/scenarios/exec_process_scenario_v2.ts
Normal 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)));
|
||||||
|
}
|
||||||
|
}
|
|
@ -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) {
|
||||||
|
|
|
@ -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();
|
||||||
|
|
5
server/src/core/usecases/get_root_dir_usecase.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import * as os from 'os';
|
||||||
|
|
||||||
|
export class GetRootDirUseCase {
|
||||||
|
call = (): string => os.homedir()
|
||||||
|
}
|
|
@ -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(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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))));
|
||||||
|
}
|
|
@ -2,12 +2,13 @@ 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()
|
||||||
public project:string;
|
public project: string;
|
||||||
public skills:any;
|
public skills: any;
|
||||||
public xml:string;
|
public xml: string;
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
public unixTime: number
|
public unixTime: number
|
||||||
}
|
}
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 }),
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -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) => );
|
||||||
|
|
||||||
|
// }
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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 }));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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`;
|
||||||
|
|
|
@ -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
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -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("");
|
|
||||||
}
|
|
51
server/src/features/runtime/domain/stop_process_usecase.ts
Normal 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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(),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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"}
|
||||||
|
// }
|
|
@ -36,7 +36,7 @@ export interface IWeightsDependency {
|
||||||
export interface IDeviceDependency {
|
export interface IDeviceDependency {
|
||||||
sid: string;
|
sid: string;
|
||||||
}
|
}
|
||||||
export interface IDependency {}
|
export interface IDependency { }
|
||||||
export interface IParam {
|
export interface IParam {
|
||||||
isFilled: boolean;
|
isFilled: boolean;
|
||||||
type: string;
|
type: string;
|
||||||
|
@ -276,7 +276,7 @@ export class SkillModel extends ValidationModel implements ISkill {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SkillDependency implements IDependency {
|
export class SkillDependency implements IDependency {
|
||||||
constructor(public skills: ISkillDependency[]) {}
|
constructor(public skills: ISkillDependency[]) { }
|
||||||
static empty() {
|
static empty() {
|
||||||
return new SkillDependency([]);
|
return new SkillDependency([]);
|
||||||
}
|
}
|
||||||
|
@ -424,24 +424,27 @@ 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)) {
|
|
||||||
acc.push(param?.dependency ?? DependencyViewModel.empty());
|
|
||||||
}
|
|
||||||
return param;
|
|
||||||
});
|
|
||||||
return action;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return acc;
|
if (param.type.isEqual(skillType)) {
|
||||||
}, [])
|
|
||||||
.at(0) ?? DependencyViewModel.empty();
|
|
||||||
|
acc.push(param?.dependency ?? DependencyViewModel.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
return param;
|
||||||
|
});
|
||||||
|
return action;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, [])
|
||||||
|
.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);
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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`);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -1,23 +1,27 @@
|
||||||
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";
|
||||||
socket: Socket | undefined;
|
socket: Socket | undefined;
|
||||||
|
|
||||||
async connect():Promise<Result<boolean, boolean>> {
|
async connect(): Promise<Result<boolean, boolean>> {
|
||||||
const socket = io(this.serverURL);
|
const socket = io(this.serverURL);
|
||||||
|
|
||||||
this.socket = socket;
|
this.socket = socket;
|
||||||
socket.connect();
|
socket.connect();
|
||||||
socket.on('realtime', (d) =>{
|
socketCoreInstances.forEach((el) => {
|
||||||
|
socket.on(el.event, (event) => el.emit(event))
|
||||||
|
})
|
||||||
|
socket.on('realtime', (d) => {
|
||||||
this.emit({
|
this.emit({
|
||||||
event:"realtime",
|
event: "realtime",
|
||||||
payload:d
|
payload: d
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
if(socket.connected){
|
if (socket.connected) {
|
||||||
return Result.ok(true)
|
return Result.ok(true)
|
||||||
}
|
}
|
||||||
return Result.error(false)
|
return Result.error(false)
|
||||||
|
@ -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]
|
|
@ -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;
|
||||||
|
@ -75,7 +76,7 @@ export abstract class UiErrorState<T> extends UiLoader {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
};
|
};
|
||||||
abstract init(navigate?: NavigateFunction): Promise<any>;
|
abstract init(navigate?: NavigateFunction): Promise<any>;
|
||||||
dispose() {}
|
dispose() { }
|
||||||
errors: UiBaseError[] = [];
|
errors: UiBaseError[] = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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%" }}>
|
||||||
|
|
|
@ -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,219 +9,162 @@ 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;
|
const [store] = React.useState(() => new FormBuilderStore());
|
||||||
}
|
|
||||||
|
|
||||||
export const FormBuilder = observer((props: IFormBuilder) => {
|
React.useEffect(() => {
|
||||||
const [store] = React.useState(() => new FormBuilderStore());
|
store.init(props.formBuilder.context, props.formBuilder.result);
|
||||||
|
if (props.formBuilder.form.isNotEmpty()) {
|
||||||
|
store.formViewModel = new FormViewModel(
|
||||||
|
props.formBuilder.form.map((el) => InputBuilderViewModel.fromJSON(el)),
|
||||||
|
props.formBuilder.result,
|
||||||
|
props.formBuilder.context
|
||||||
|
);
|
||||||
|
props.formBuilder.form.map((el) => InputBuilderViewModel.fromJSON(el));
|
||||||
|
}
|
||||||
|
store.changerForm.on((event) => {
|
||||||
|
if (event) props.onChange(event);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
React.useEffect(() => {
|
return (
|
||||||
store.init(props.formBuilder.context, props.formBuilder.result);
|
<div>
|
||||||
if (props.formBuilder.form.isNotEmpty()) {
|
{store.isError ? (
|
||||||
store.formViewModel = new FormViewModel(
|
<>Error</>
|
||||||
props.formBuilder.form.map((el) => InputBuilderViewModel.fromJSON(el)),
|
) : (
|
||||||
props.formBuilder.result,
|
<div>
|
||||||
props.formBuilder.context
|
{store.formViewModel?.inputs?.map((element, index) => {
|
||||||
);
|
if (element.type?.isEqual(InputType.ENUM)) {
|
||||||
props.formBuilder.form.map((el) => InputBuilderViewModel.fromJSON(el));
|
const values = element.values as string[];
|
||||||
}
|
return (
|
||||||
store.changerForm.on((event) => {
|
<CoreSelect
|
||||||
if (event) props.onChange(event);
|
key={index}
|
||||||
});
|
items={values}
|
||||||
}, []);
|
value={element.totalValue ?? element.defaultValue}
|
||||||
|
onChange={(value) => store.changeTotalValue(element.id, value)}
|
||||||
|
label={element.name}
|
||||||
|
style={{ margin: 20 }}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (element.type?.isEqual(InputType.ARRAY)) {
|
||||||
|
return (
|
||||||
|
<div key={index} style={{ border: "1px black solid", margin: 20 }}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
margin: 20,
|
||||||
|
alignItems: "center",
|
||||||
|
paddingRight: 20,
|
||||||
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
store.open(element.id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CoreText text={element.name} type={CoreTextType.large} />
|
||||||
|
<Icon type="PlusCircle" style={{ width: 33 }} />
|
||||||
|
</div>
|
||||||
|
|
||||||
return (
|
{element.isOpen ? (
|
||||||
<div>
|
<div style={{ margin: 20 }}>
|
||||||
{store.isError ? (
|
{element.totalValue instanceof Array
|
||||||
<>Error</>
|
? element.totalValue?.map((subArray, index) => {
|
||||||
) : (
|
return (
|
||||||
<div>
|
<div style={{ margin: 20 }}>
|
||||||
{store.formViewModel?.inputs?.map((element, index) => {
|
<div style={{ display: "flex" }}>
|
||||||
if (element.type?.isEqual(InputType.ENUM)) {
|
<CoreText text={(element.subType ?? "") + ` ${index}`} type={CoreTextType.medium} />
|
||||||
const values = element.values as string[];
|
<Icon
|
||||||
return (
|
style={{ paddingLeft: 20 }}
|
||||||
<CoreSelect
|
type="DeleteCircle"
|
||||||
key={index}
|
onClick={() => store.deleteTotalValueSubItem(element.id, index)}
|
||||||
items={values}
|
/>
|
||||||
value={element.totalValue ?? element.defaultValue}
|
</div>
|
||||||
onChange={(value) =>
|
|
||||||
store.changeTotalValue(element.id, value)
|
|
||||||
}
|
|
||||||
label={element.name}
|
|
||||||
style={{ margin: 20 }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (element.type?.isEqual(InputType.ARRAY)) {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
key={index}
|
|
||||||
style={{ border: "1px black solid", margin: 20 }}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
margin: 20,
|
|
||||||
alignItems: "center",
|
|
||||||
paddingRight: 20,
|
|
||||||
}}
|
|
||||||
onClick={() => {
|
|
||||||
store.open(element.id);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<CoreText text={element.name} type={CoreTextType.large} />
|
|
||||||
<Icon type="PlusCircle" style={{ width: 33 }} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{element.isOpen ? (
|
{subArray.map((subSubArrayItem: InputBuilderViewModel, subIndex: number) => {
|
||||||
<div style={{ margin: 20 }}>
|
if (subSubArrayItem.type.isEqual(InputType.ENUM)) {
|
||||||
{element.totalValue instanceof Array
|
|
||||||
? element.totalValue?.map((subArray, index) => {
|
|
||||||
return (
|
|
||||||
<div style={{ margin: 20 }}>
|
|
||||||
<div style={{ display: "flex" }}>
|
|
||||||
<CoreText
|
|
||||||
text={(element.subType ?? "") + ` ${index}`}
|
|
||||||
type={CoreTextType.medium}
|
|
||||||
/>
|
|
||||||
<Icon
|
|
||||||
style={{ paddingLeft: 20 }}
|
|
||||||
type="DeleteCircle"
|
|
||||||
onClick={() =>
|
|
||||||
store.deleteTotalValueSubItem(
|
|
||||||
element.id,
|
|
||||||
index
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{subArray.map(
|
|
||||||
(
|
|
||||||
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>
|
);
|
||||||
);
|
})
|
||||||
})
|
: null}
|
||||||
: null}
|
</div>
|
||||||
</div>
|
) : null}
|
||||||
) : null}
|
</div>
|
||||||
</div>
|
);
|
||||||
);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (element.type?.isEqualMany([InputType.NUMBER, InputType.STRING]))
|
if (element.type?.isEqualMany([InputType.NUMBER, InputType.STRING]))
|
||||||
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)
|
onChange={(e) => {
|
||||||
? (el) => Number().isValid(el)
|
store.changeTotalValue(element.id, e);
|
||||||
: undefined
|
}}
|
||||||
}
|
value={element.totalValue ?? element.defaultValue}
|
||||||
onChange={(e) => {
|
error="только числа"
|
||||||
store.changeTotalValue(element.id, e);
|
label={element.name}
|
||||||
}}
|
style={{ margin: 20 }}
|
||||||
value={element.totalValue ?? element.defaultValue}
|
/>
|
||||||
error="только числа"
|
</div>
|
||||||
label={element.name}
|
);
|
||||||
style={{ margin: 20 }}
|
if (element.type?.isEqual(InputType.OBJECT))
|
||||||
/>
|
return (
|
||||||
</div>
|
<>
|
||||||
);
|
{getFormBuilderComponents(
|
||||||
if (element.type?.isEqual(InputType.OBJECT))
|
element.name.replace(">", "").replace("<", "").replace("/", ""),
|
||||||
return (
|
element.totalValue ?? element.defaultValue,
|
||||||
<>
|
(text) => store.changeTotalValue(element.id, text)
|
||||||
{getFormBuilderComponents(
|
).fold(
|
||||||
element.name
|
(s) => (
|
||||||
.replace(">", "")
|
<>{s}</>
|
||||||
.replace("<", "")
|
),
|
||||||
.replace("/", ""),
|
(error) => (
|
||||||
element.totalValue ?? element.defaultValue,
|
<>{error}</>
|
||||||
(text) => store.changeTotalValue(element.id, text)
|
)
|
||||||
).fold(
|
)}
|
||||||
(s) => (
|
</>
|
||||||
<>{s}</>
|
);
|
||||||
),
|
|
||||||
(error) => (
|
|
||||||
<>{error}</>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
return <div>Error {String(element)}</div>;
|
return <div>Error {String(element)}</div>;
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
|
@ -95,7 +95,7 @@ export class FormViewModel {
|
||||||
.replace(/[^\x00-\x7F]/g, "")
|
.replace(/[^\x00-\x7F]/g, "")
|
||||||
.replaceAll("\n", "")
|
.replaceAll("\n", "")
|
||||||
.replaceAll("\\", "")
|
.replaceAll("\\", "")
|
||||||
// .replaceAll("/", "")
|
// .replaceAll("/", "")
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log("ERROR: FormViewModel json() " + result);
|
console.log("ERROR: FormViewModel json() " + result);
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
|
@ -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 })
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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</>;
|
|
||||||
});
|
|
|
@ -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>
|
||||||
|
);
|
||||||
|
});
|
|
@ -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;
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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>
|
||||||
<FormBuilder
|
<div style={{height:50}}/>
|
||||||
formBuilder={store.viewModel}
|
<FormBuilder
|
||||||
onChange={(e) => {
|
formBuilder={store.viewModel}
|
||||||
console.log(e.output);
|
onChange={(e) => {
|
||||||
// console.log(JSON.stringify(e.output))
|
console.log(e)
|
||||||
}}
|
// console.log(e.output);
|
||||||
/>
|
console.log(JSON.stringify(e.output))
|
||||||
</Modal>
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -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">
|
||||||
|
|
|
@ -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 (
|
||||||
|
|
|
@ -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={{
|
||||||
|
|
|
@ -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>
|
||||||
|
|
46
ui/src/core/ui/popover/popover.tsx
Normal 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;
|
|
@ -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)",
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,54 +63,36 @@ export const BehaviorTreeBuilderScreen = observer(() => {
|
||||||
}, []);
|
}, []);
|
||||||
return (
|
return (
|
||||||
<MainPageV2
|
<MainPageV2
|
||||||
rightChild={
|
|
||||||
<>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
width: "100%",
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "row",
|
|
||||||
justifyContent: "end",
|
|
||||||
paddingRight: 30,
|
|
||||||
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
|
|
||||||
style={{
|
|
||||||
backgroundColor: store.isNeedSaveBtn ? themeStore.theme.greenWhite : undefined,
|
|
||||||
height: 40,
|
|
||||||
textAlign: "center",
|
|
||||||
alignContent: "center",
|
|
||||||
width: 40,
|
|
||||||
borderRadius: 100,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{store.isNeedSaveBtn ? (
|
|
||||||
<Icon style={{ height: 21 }} onClick={() => store.onClickSaveBehaviorTree()} type="Floppy" />
|
|
||||||
) : undefined}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<></>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
style={{ position: "absolute", height: "100%", overflow: "hidden" }}
|
style={{ position: "absolute", height: "100%", overflow: "hidden" }}
|
||||||
bgColor={themeStore.theme.black}
|
bgColor={themeStore.theme.black}
|
||||||
children={
|
children={
|
||||||
<>
|
<>
|
||||||
<>
|
<>
|
||||||
|
<div style={{ position: "absolute", zIndex: 100 }}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: "100%",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "row",
|
||||||
|
justifyContent: "end",
|
||||||
|
paddingRight: 30,
|
||||||
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
backgroundColor: themeStore.theme.greenWhite,
|
||||||
|
height: 40,
|
||||||
|
textAlign: "center",
|
||||||
|
alignContent: "center",
|
||||||
|
width: 40,
|
||||||
|
borderRadius: 100,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon style={{ height: 21 }} onClick={() => store.onClickSaveBehaviorTree()} type="Floppy" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
height: "100%",
|
height: "100%",
|
||||||
|
@ -160,10 +139,45 @@ export const BehaviorTreeBuilderScreen = observer(() => {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Panel>
|
</Panel>
|
||||||
<PanelResizeHandle>
|
{store.panels.map((el, index) => (
|
||||||
<div style={{ width: 10, height: "100%", backgroundColor: "beige" }}></div>
|
<>
|
||||||
</PanelResizeHandle>
|
<PanelResizeHandle
|
||||||
<Panel defaultSize={0}> </Panel>
|
children={
|
||||||
|
<>
|
||||||
|
<div style={{ width: 10, height: "100%", backgroundColor: "beige" }}></div>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
></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%" }}>
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
height: "100%",
|
||||||
|
overflow: "auto",
|
||||||
|
}}
|
||||||
|
>
|
||||||
<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 (
|
||||||
{s.component}
|
<div key={index} style={{ flex: 1 }}>
|
||||||
</div>
|
{s.component}
|
||||||
),
|
</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}
|
||||||
|
|
|
@ -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);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
);
|
||||||
|
});
|
|
@ -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>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 }} />}
|
||||||
|
@ -65,9 +65,13 @@ 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 })) ?? []}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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`;
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -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)
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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(() => {
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -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())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 /> */}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
6
web_p/camera_info_topic.json
Normal 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
|
@ -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
|
@ -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
|
@ -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
|
@ -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)
|
|
@ -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]
|
||||||
rec["cuboid"] = t[0]
|
if len(t) > 0:
|
||||||
|
rec["cuboid"] = t[0]
|
||||||
|
else: # object name does not match file name
|
||||||
|
rec["Error"] = "!!! object name does not match file name: cuboid is zero"
|
||||||
|
rec["cuboid"] = np.zeros((8, 3)).tolist()
|
||||||
|
|
||||||
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:
|
||||||
|
|
Before Width: | Height: | Size: 81 KiB |
Before Width: | Height: | Size: 73 KiB |
Before Width: | Height: | Size: 71 KiB |
Before Width: | Height: | Size: 86 KiB |
|
@ -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
|
|
Before Width: | Height: | Size: 82 KiB |
Before Width: | Height: | Size: 85 KiB |
Before Width: | Height: | Size: 124 KiB |
Before Width: | Height: | Size: 141 KiB |
|
@ -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
|
|
|
Before Width: | Height: | Size: 280 KiB |