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",
|
||||
"grau",
|
||||
"idontsudo",
|
||||
"Pids",
|
||||
"raycaster",
|
||||
"skils",
|
||||
"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,12 +15,12 @@
|
|||
"checkCommand": null,
|
||||
"filter": null
|
||||
},
|
||||
"btBuilderProcess": {
|
||||
"execCommand": "nix run github:nixos/nixpkgs#python312Packages.tensorboard -- --logdir ${dir_path}",
|
||||
"btRuntimeProcess": {
|
||||
"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,
|
||||
"status": null,
|
||||
"delay": 0,
|
||||
"checkCommand": null,
|
||||
"filter": null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,9 @@ import { dirname } from "path";
|
|||
import { CheckAndCreateStaticFilesFolderUseCase } from "../usecases/check_and_create_static_files_folder_usecase";
|
||||
import { DataBaseConnectUseCase } from "../usecases/database_connect_usecase";
|
||||
import { TypedEvent } from "../helpers/typed_event";
|
||||
import { CalculationInstanceDBModel } from "../../features/calculations_instance/models/calculations_instance_database_model";
|
||||
import * as fs from "fs";
|
||||
import { WriteFileSystemFileUseCase } from "../usecases/write_file_system_file_usecase";
|
||||
|
||||
export enum ServerStatus {
|
||||
init = "init",
|
||||
|
@ -85,7 +88,25 @@ export class App extends TypedEvent<ServerStatus> {
|
|||
this.app.use(express.json());
|
||||
this.app.use(express.urlencoded({ extended: true }));
|
||||
this.app.use(express.static(App.staticFilesStoreDir()));
|
||||
this.app.get('/logs', async (req, res) => {
|
||||
const id = req.query.id;
|
||||
|
||||
if (id === undefined) {
|
||||
return res.status(400).json('need req query id?=')
|
||||
}
|
||||
|
||||
const calculationInstanceDBModel = await CalculationInstanceDBModel.findById(id)
|
||||
if (calculationInstanceDBModel === null) {
|
||||
return res.status(400).json("calcultaion db model is null");
|
||||
}
|
||||
const p = App.staticFilesStoreDir() + '/log.txt';
|
||||
|
||||
(await new WriteFileSystemFileUseCase().call(p, calculationInstanceDBModel.lastProcessLogs)).map(() => {
|
||||
return res.sendFile(p);
|
||||
})
|
||||
|
||||
|
||||
})
|
||||
this.app.use(
|
||||
fileUpload({
|
||||
createParentPath: true,
|
||||
|
|
|
@ -65,12 +65,12 @@ interface ISubSetFeatureRouter<A> {
|
|||
method: HttpMethodType;
|
||||
subUrl: string;
|
||||
fn:
|
||||
| CallbackStrategyWithValidationModel<A>
|
||||
| CallbackStrategyWithEmpty
|
||||
| CallbackStrategyWithIdQuery
|
||||
| CallBackStrategyWithQueryPage
|
||||
| CallbackStrategyWithFileUpload
|
||||
| CallbackStrategyWithFilesUploads;
|
||||
| CallbackStrategyWithValidationModel<A>
|
||||
| CallbackStrategyWithEmpty
|
||||
| CallbackStrategyWithIdQuery
|
||||
| CallBackStrategyWithQueryPage
|
||||
| CallbackStrategyWithFileUpload
|
||||
| CallbackStrategyWithFilesUploads;
|
||||
}
|
||||
|
||||
abstract class ICoreHttpController {
|
||||
|
@ -234,6 +234,7 @@ export class CoreHttpController<V> implements ICoreHttpController {
|
|||
return res.json(ok);
|
||||
},
|
||||
(err) => {
|
||||
|
||||
return res.status(400).json({ error: String(err) });
|
||||
}
|
||||
);
|
||||
|
|
|
@ -18,7 +18,7 @@ export class TopicViewModel {
|
|||
|
||||
export interface IParam {
|
||||
type: string;
|
||||
dependency: Object;
|
||||
dependency: object;
|
||||
}
|
||||
export interface Skill {
|
||||
SkillPackage: ISkillPackage;
|
||||
|
|
|
@ -7,9 +7,8 @@ export abstract class CreateInstanceScenario<V extends Instance> extends Callbac
|
|||
abstract validationModel: V;
|
||||
abstract databaseModel: any;
|
||||
call = async (model: V): ResponseBase => {
|
||||
model.instancePath = `${model.path}/${model.instanceName}`;
|
||||
console.log("INSTANCE PATh");
|
||||
console.log(model.instancePath)
|
||||
model.instancePath = `${model.path.pathNormalize()}/${model.instanceName}`.pathNormalize();
|
||||
model.path = model.path.pathNormalize();
|
||||
return (await new CreateFolderUseCase().call(model.instancePath)).map(
|
||||
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.on("message", (e) => {
|
||||
console.log(JSON.stringify(e));
|
||||
const spawnError = SpawnError.isError(e);
|
||||
|
||||
if (spawnError instanceof SpawnError) {
|
||||
|
|
|
@ -7,6 +7,7 @@ import { ExecutorResult } from "../models/executor_result";
|
|||
import { ExecutorProgramService } from "../services/executor_program_service";
|
||||
|
||||
export const executorProgramService = new ExecutorProgramService("");
|
||||
|
||||
export class KillLastProcessUseCase extends CallbackStrategyWithEmpty {
|
||||
call = async (): Promise<Result<undefined, string>> => {
|
||||
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 { GetRobotsUseCase } from "./domain/get_robots_usecase";
|
||||
import { GetTopicsUseCase } from "./domain/get_topics_usecase";
|
||||
import { DeleteBehaviorTreeScenario } from "./domain/delete_behavior_tree_scenario";
|
||||
|
||||
|
||||
|
||||
export class BehaviorTreesPresentation extends CrudController<BehaviorTreeValidationModel, typeof BehaviorTreeDBModel> {
|
||||
constructor() {
|
||||
|
@ -43,6 +46,11 @@ export class BehaviorTreesPresentation extends CrudController<BehaviorTreeValida
|
|||
subUrl: "topics",
|
||||
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";
|
||||
|
||||
export class BehaviorTreeValidationModel implements IBehaviorTreeModel {
|
||||
_id: string;
|
||||
@IsString()
|
||||
public name: string;
|
||||
@IsMongoId()
|
||||
public project:string;
|
||||
public skills:any;
|
||||
public xml:string;
|
||||
public project: string;
|
||||
public skills: any;
|
||||
public xml: string;
|
||||
@IsNumber()
|
||||
public unixTime: number
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@ import { CalculationInstanceValidationModel } from "./models/calculations_instan
|
|||
import { CalculationInstanceDBModel } from "./models/calculations_instance_database_model";
|
||||
import { CreateCalculationInstanceScenario } from "./domain/create_calculation_instance_scenario";
|
||||
import { DeleteCalculationsInstanceScenario } from "./domain/delete_calculations_instance_scenario";
|
||||
import { GetAllEndCalculationsInstanceActiveProjectScenarios } from "./domain/get_all_end_calculations_instance_active_project_scenarios";
|
||||
|
||||
|
||||
export class CalculationsInstancesPresentation extends CrudController<
|
||||
CalculationInstanceValidationModel,
|
||||
|
@ -18,7 +20,12 @@ export class CalculationsInstancesPresentation extends CrudController<
|
|||
|
||||
super.delete(new DeleteCalculationsInstanceScenario().call);
|
||||
super.post(new CreateCalculationInstanceScenario().call);
|
||||
|
||||
this.subRoutes.push({
|
||||
method: "POST",
|
||||
subUrl: "get/all/end/calculations",
|
||||
//@ts-expect-error
|
||||
fn: new GetAllEndCalculationsInstanceActiveProjectScenarios(),
|
||||
})
|
||||
this.subRoutes.push({
|
||||
method: "GET",
|
||||
subUrl: "exec",
|
||||
|
|
|
@ -7,7 +7,21 @@ import { ProcessWatcherAndDatabaseUpdateService } from "../../datasets/domain/cr
|
|||
import { CalculationInstanceDBModel, ICalculationInstance } from "../models/calculations_instance_database_model";
|
||||
import { Result } from "../../../core/helpers/result";
|
||||
import { CreateFileUseCase } from "../../../core/usecases/create_file_usecase";
|
||||
import { ExecProcessScenarioV2, ExecProcessWatcher } from "../../../core/scenarios/exec_process_scenario_v2";
|
||||
class ExecProcess extends ExecProcessWatcher {
|
||||
id: string;
|
||||
constructor(id: string) {
|
||||
super()
|
||||
this.id = id;
|
||||
}
|
||||
result = async (): Promise<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 {
|
||||
idValidationExpression = new MongoIdValidation();
|
||||
call = async (id: string): ResponseBase =>
|
||||
|
@ -17,19 +31,15 @@ export class ExecCalculationInstanceProcessScenario extends CallbackStrategyWith
|
|||
return (await new IsHaveActiveProcessUseCase().call()).map(async () => {
|
||||
const execCommand = `${model.script
|
||||
} --path ${model.instancePath.pathNormalize()} --form ${fileOutPath}`.replace("\n", "");
|
||||
|
||||
|
||||
await new CreateFileUseCase().call(fileOutPath, Buffer.from(JSON.stringify(model.formBuilder)));
|
||||
await CalculationInstanceDBModel.findById(id).updateOne({
|
||||
processStatus: "RUN",
|
||||
lastProcessExecCommand: execCommand,
|
||||
});
|
||||
new ExecProcessUseCase().call(
|
||||
// @ts-expect-error
|
||||
`${model.project.rootDir}/`,
|
||||
execCommand,
|
||||
id,
|
||||
new ProcessWatcherAndDatabaseUpdateService(id as unknown as ObjectId, CalculationInstanceDBModel)
|
||||
);
|
||||
|
||||
new ExecProcessScenarioV2().call(execCommand, new ExecProcess(model._id))
|
||||
|
||||
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";
|
||||
|
||||
export interface ICalculationInstance {
|
||||
_id: string;
|
||||
script: string;
|
||||
instancePath: string;
|
||||
formBuilder: FormBuilderValidationModel;
|
||||
|
@ -61,6 +62,7 @@ export const CalculationInstanceSchema = new Schema({
|
|||
instancePath: {
|
||||
type: String,
|
||||
},
|
||||
|
||||
project: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: projectSchema,
|
||||
|
|
|
@ -4,6 +4,7 @@ import { FormBuilderValidationModel } from "../../datasets/models/dataset_valida
|
|||
import { IProjectModel } from "../../projects/models/project_model_database_model";
|
||||
|
||||
export class CalculationInstanceValidationModel implements ICalculationInstance {
|
||||
_id: string;
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
instanceName: string;
|
||||
|
|
|
@ -15,6 +15,7 @@ export enum ProcessStatus {
|
|||
END = "END",
|
||||
ERROR = "ERROR",
|
||||
NEW = "NEW",
|
||||
|
||||
}
|
||||
export interface IDatasetModel {
|
||||
_id?: string;
|
||||
|
|
|
@ -21,8 +21,7 @@ export class ExecInstanceScenario extends CallbackStrategyWithIdQuery {
|
|||
await new ReadByIdDataBaseModelUseCase<IDigitalTwinsInstanceModel>(DigitalTwinsInstanceDatabaseModel).call(id)
|
||||
).map(
|
||||
(document) => (
|
||||
console.log('DOCUMeNT PATH'),
|
||||
console.log(document.instancePath.pathNormalize()),
|
||||
|
||||
new ExecProcessUseCase().call(
|
||||
document.instancePath,
|
||||
`python3 $GET_INTERFACES --path ${document.instancePath.pathNormalize()} --package '${JSON.stringify(
|
||||
|
|
|
@ -4,9 +4,8 @@ import { SearchOneDataBaseModelUseCase } from "../../../core/usecases/search_dat
|
|||
import { IProjectModel, ProjectDBModel } from "../models/project_model_database_model";
|
||||
|
||||
export class GetActiveProjectIdScenario extends CallbackStrategyWithEmpty {
|
||||
async call(): Promise<Result<any, { id: string }>> {
|
||||
return (
|
||||
await new SearchOneDataBaseModelUseCase<IProjectModel>(ProjectDBModel).call({ isActive: true }, "no active projects")
|
||||
).map((model) => Result.ok({ id: model._id }));
|
||||
}
|
||||
call = async (): Promise<Result<any, { id: string }>> => (
|
||||
await new SearchOneDataBaseModelUseCase<IProjectModel>(ProjectDBModel).call({ isActive: true }, "no active projects")
|
||||
).map((model) => Result.ok({ id: model._id }));
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,6 @@ export class RobossemblerAssetsNetworkMapperScenario extends CallbackStrategyWit
|
|||
"/assets/";
|
||||
model.map((el) => {
|
||||
const assetLibsAddress = assetAddress + "/libs/objects/" + el.name;
|
||||
console.log(assetLibsAddress);
|
||||
el.stlUrl = `${assetAddress}${el.part_path}`;
|
||||
el.glUrl = `${assetLibsAddress}.glb`;
|
||||
el.daeUrl = `${assetLibsAddress}.dae`;
|
||||
|
|
|
@ -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 {
|
||||
call(): ResponseBase {
|
||||
throw new Error("Method not implemented.");
|
||||
class ProcessWatcher extends ExecProcessWatcher {
|
||||
async result(): Promise<any> {
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -11,4 +11,4 @@ export interface ExecProcess {
|
|||
delay: number;
|
||||
checkCommand: null;
|
||||
filter: null;
|
||||
}
|
||||
}
|
|
@ -1,42 +1,19 @@
|
|||
import { CrudController } from "../../core/controllers/crud_controller";
|
||||
|
||||
import { ExecRunTimeCommandValidationModel } from "./model/run_time_validation_model";
|
||||
import { ExecRuntimeDatabaseModel } from "./model/run_time_database_model";
|
||||
import { CoreHttpController, SubRouter, HttpMethodType, CallbackStrategyWithIdQuery, ResponseBase } from "../../core/controllers/http_controller";
|
||||
import { ExecBtBuilderUseCase } from "./domain/exec_bt_builder_usecase";
|
||||
import { ExecSimulationUseCase } from "./domain/exec_simulation_usecase";
|
||||
import { GetBtBuilderStateUseCase } from "./domain/get_bt_builder_status_usecase";
|
||||
import { GetSimulationStateScenario } from "./domain/get_simulation_state_usecase";
|
||||
import { MongoIdValidation } from "../../core/validations/mongo_id_validation";
|
||||
import { CoreValidation } from "../../core/validations/core_validation";
|
||||
import { ReadByIdDataBaseModelUseCase } from "../../core/usecases/read_by_id_database_model_usecase";
|
||||
import { ICalculationInstance, CalculationInstanceDBModel } from "../calculations_instance/models/calculations_instance_database_model";
|
||||
import { Result } from "../../core/helpers/result";
|
||||
import { SpawnProcessUseCase } from "../../core/usecases/exec_process_usecase";
|
||||
import { ProcessWatcher } from "./service/process_watcher";
|
||||
import { CoreHttpController, SubRouter, CallbackStrategyWithIdQuery } from "../../core/controllers/http_controller";
|
||||
import { ExecBtScenario } from "./domain/exec_bt_builder_usecase";
|
||||
import { GetRunTimeStatuses } from "./domain/get_run_time_statuses_usecase";
|
||||
import { StopProcessUseCase } from "./domain/stop_process_usecase";
|
||||
|
||||
class ExecAnalyzeScenario extends CallbackStrategyWithIdQuery {
|
||||
|
||||
idValidationExpression: CoreValidation = new MongoIdValidation()
|
||||
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> {
|
||||
export class RunTimePresentation extends CoreHttpController<ExecRunTimeCommandValidationModel> {
|
||||
constructor() {
|
||||
super({
|
||||
url: "run_time",
|
||||
});
|
||||
|
||||
this.subRoutes.push(new SubRouter("POST", "/exec/bt/builder", new ExecBtBuilderUseCase()));
|
||||
this.subRoutes.push(new SubRouter("POST", "/get/bt/builder/state", new GetBtBuilderStateUseCase()));
|
||||
this.subRoutes.push(new SubRouter("POST", "/get/simulator/state", new GetSimulationStateScenario()));
|
||||
this.subRoutes.push(new SubRouter('POST', "exec/analyze", new ExecAnalyzeScenario()))
|
||||
this.subRoutes.push({
|
||||
method: "POST",
|
||||
subUrl: "/exec/simulation/",
|
||||
fn: new ExecSimulationUseCase(),
|
||||
});
|
||||
this.subRoutes.push(new SubRouter<any>("POST", "exec/bt", new ExecBtScenario()));
|
||||
this.subRoutes.push(new SubRouter("GET", "status", new GetRunTimeStatuses()));
|
||||
this.subRoutes.push(new SubRouter<any>("POST", "kill", new StopProcessUseCase()))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,11 +3,11 @@ import { App } from "./core/controllers/app";
|
|||
import { SocketSubscriber } from "./core/controllers/socket_controller";
|
||||
import { extensions } from "./core/extensions/extensions";
|
||||
import { httpRoutes } from "./core/controllers/routes";
|
||||
import { SpawnProcessUseCase, executorProgramService } from "./core/usecases/exec_process_usecase";
|
||||
import { ProcessWatcher } from "./features/runtime/service/process_watcher";
|
||||
import { executorProgramService } from "./core/usecases/exec_process_usecase";
|
||||
import { executorProgramServiceV2 } from "./core/scenarios/exec_process_scenario_v2";
|
||||
|
||||
extensions();
|
||||
|
||||
const socketSubscribers = [new SocketSubscriber(executorProgramService, "realtime")];
|
||||
const socketSubscribers = [new SocketSubscriber(executorProgramService, "realtime"), new SocketSubscriber(executorProgramServiceV2, 'realtimeV2',)];
|
||||
|
||||
new App(httpRoutes, socketSubscribers).listen();
|
||||
|
|
|
@ -1,7 +1,14 @@
|
|||
/* eslint-disable no-extend-native */
|
||||
import { Result } from "../helper/result";
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-this-alias */
|
||||
export const ArrayExtensions = () => {
|
||||
if (Array.prototype.plus === undefined) {
|
||||
Array.prototype.plus = function (array) {
|
||||
array.forEach((el) => this.push(el))
|
||||
return this;
|
||||
}
|
||||
}
|
||||
if ([].indexOfR === undefined) {
|
||||
Array.prototype.indexOfR = function (element) {
|
||||
if (this.indexOf(element) === -1) {
|
||||
|
@ -10,6 +17,11 @@ export const ArrayExtensions = () => {
|
|||
return Result.ok(this);
|
||||
};
|
||||
}
|
||||
if ([].whereOne === undefined) {
|
||||
Array.prototype.whereOne = function (predicate) {
|
||||
return this.filter(predicate).atR(0);
|
||||
}
|
||||
}
|
||||
if ([].atR === undefined) {
|
||||
Array.prototype.atR = function (index) {
|
||||
if (index === undefined) {
|
||||
|
|
|
@ -30,6 +30,8 @@ declare global {
|
|||
someR(predicate: (value: T) => boolean): Result<void, Array<T>>;
|
||||
updateAll(value: Partial<T>): Array<T>;
|
||||
atR(index: number | undefined): Result<void, T>;
|
||||
whereOne(predicate: (value: T) => boolean): Result<void, T>;
|
||||
plus(array: any[]): Array<T>
|
||||
}
|
||||
interface Date {
|
||||
formatDate(): string;
|
||||
|
|
|
@ -1,23 +1,15 @@
|
|||
import { IsNotEmpty, IsString } from "class-validator";
|
||||
import { BehaviorTreeBuilderStore } from "../../features/behavior_tree_builder/presentation/behavior_tree_builder_store";
|
||||
import {
|
||||
datasetFormMockContext,
|
||||
datasetFormMockResult,
|
||||
defaultFormValue,
|
||||
} from "../../features/dataset/dataset_model";
|
||||
import { datasetFormMockContext, datasetFormMockResult, defaultFormValue } from "../../features/dataset/dataset_model";
|
||||
import { DependencyViewModel } from "./skill_model";
|
||||
import { ValidationModel } from "./validation_model";
|
||||
import { FormType } from "./form";
|
||||
import makeAutoObservable from "mobx-store-inheritance";
|
||||
|
||||
export class FormBuilderValidationModel
|
||||
extends ValidationModel
|
||||
implements DependencyViewModel
|
||||
{
|
||||
export class FormBuilderValidationModel extends ValidationModel implements DependencyViewModel {
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
public result: string;
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
public context: string;
|
||||
public form: string[];
|
||||
|
@ -36,22 +28,26 @@ export class FormBuilderValidationModel
|
|||
formBuilderValidationModel.context.isEmpty() &&
|
||||
formBuilderValidationModel.result.isEmpty() &&
|
||||
formBuilderValidationModel.form.isEmpty();
|
||||
static test = () =>
|
||||
new FormBuilderValidationModel(ffContext, ff1Result, [], "");
|
||||
static test = () => new FormBuilderValidationModel(ffContext, ff1Result, [], "");
|
||||
static datasetEmpty = () =>
|
||||
new FormBuilderValidationModel(
|
||||
datasetFormMockContext,
|
||||
datasetFormMockResult,
|
||||
[],
|
||||
defaultFormValue
|
||||
);
|
||||
new FormBuilderValidationModel(datasetFormMockContext, datasetFormMockResult, [], defaultFormValue);
|
||||
static empty = () => new FormBuilderValidationModel("", "", [], "");
|
||||
static emptyTest = () =>
|
||||
new FormBuilderValidationModel(``, ``, [], defaultFormValue);
|
||||
static creteDataSetTest = () =>
|
||||
new FormBuilderValidationModel(``, scene, [], "");
|
||||
static emptySimple = () =>
|
||||
new FormBuilderValidationModel("", simpleFormBuilder, [], "");
|
||||
static emptyTest = () => new FormBuilderValidationModel(``, ``, [], defaultFormValue);
|
||||
static creteDataSetTest = () => new FormBuilderValidationModel(``, scene, [], "");
|
||||
static emptySimple = () => new FormBuilderValidationModel("", simpleFormBuilder, [], "");
|
||||
static eee = () =>
|
||||
new FormBuilderValidationModel(
|
||||
``,
|
||||
`{
|
||||
"robot_name": \${ROBOT_NAME:string:rbs_arm},
|
||||
"pose": {
|
||||
"position": { "x": \${X:number:0.1}, "y": \${Y:number:0.1}, "z": \${Z:number:0.7} },
|
||||
"orientation": { "x": \${X:number:0.1}, "y": \${Y:number:0.1}, "z": \${Z:number:0.7} }
|
||||
}
|
||||
}`,
|
||||
[],
|
||||
""
|
||||
);
|
||||
static vision = () =>
|
||||
new FormBuilderValidationModel(
|
||||
`
|
||||
|
@ -96,6 +92,7 @@ export class FormBuilderValidationModel
|
|||
};`,
|
||||
`
|
||||
{
|
||||
"process":\${<SelectProcess/>:OBJECT:{"type": "OBJECT_DETECTION"},
|
||||
"datasetObjects":\${<SelectDetail/>:OBJECT:{"details": []},
|
||||
"typedataset": \${typedataset:Enum<T>:ObjectDetection},
|
||||
"models_randomization":{
|
||||
|
@ -142,3 +139,6 @@ export const ff1Result = `{
|
|||
"empty":\${NAME:string:default},
|
||||
"params": \${ITEM:Array<ITEM>:[]}
|
||||
}`;
|
||||
// {
|
||||
// "process":\${<SelectProcess/>:OBJECT:{"type": "OBJECT_DETECTION"}
|
||||
// }
|
|
@ -36,7 +36,7 @@ export interface IWeightsDependency {
|
|||
export interface IDeviceDependency {
|
||||
sid: string;
|
||||
}
|
||||
export interface IDependency {}
|
||||
export interface IDependency { }
|
||||
export interface IParam {
|
||||
isFilled: boolean;
|
||||
type: string;
|
||||
|
@ -276,7 +276,7 @@ export class SkillModel extends ValidationModel implements ISkill {
|
|||
}
|
||||
|
||||
export class SkillDependency implements IDependency {
|
||||
constructor(public skills: ISkillDependency[]) {}
|
||||
constructor(public skills: ISkillDependency[]) { }
|
||||
static empty() {
|
||||
return new SkillDependency([]);
|
||||
}
|
||||
|
@ -424,24 +424,27 @@ export class Skills {
|
|||
.flat(1)
|
||||
.filter((el) => el !== "");
|
||||
|
||||
getDependencyBySkillLabelAndType = (skillType: string, sid: string): DependencyViewModel =>
|
||||
this.skills
|
||||
.reduce<DependencyViewModel[]>((acc, skill) => {
|
||||
if (skill.sid?.isEqual(sid)) {
|
||||
skill.BTAction.map((action) => {
|
||||
action.param.map((param) => {
|
||||
if (param.type.isEqualR(skillType)) {
|
||||
acc.push(param?.dependency ?? DependencyViewModel.empty());
|
||||
}
|
||||
return param;
|
||||
});
|
||||
return action;
|
||||
});
|
||||
}
|
||||
getDependencyBySkillLabelAndType = (skillType: string, sid: string): DependencyViewModel => this.skills
|
||||
.reduce<DependencyViewModel[]>((acc, skill) => {
|
||||
if (skill.sid?.isEqual(sid)) {
|
||||
skill.BTAction.map((action) => {
|
||||
action.param.map((param) => {
|
||||
|
||||
return acc;
|
||||
}, [])
|
||||
.at(0) ?? DependencyViewModel.empty();
|
||||
if (param.type.isEqual(skillType)) {
|
||||
|
||||
|
||||
acc.push(param?.dependency ?? DependencyViewModel.empty());
|
||||
}
|
||||
|
||||
return param;
|
||||
});
|
||||
return action;
|
||||
});
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, [])
|
||||
.at(0) ?? DependencyViewModel.empty()
|
||||
static isEmpty(model: Skills): Result<void, void> {
|
||||
if (model.skills.isEmpty()) {
|
||||
return Result.error(undefined);
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { message } from "antd";
|
||||
import { Result } from "../helper/result";
|
||||
import { validate, ValidationError } from "class-validator";
|
||||
|
||||
|
@ -26,4 +27,12 @@ export class ValidationModel {
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -132,7 +132,7 @@ export class CoreHttpRepository extends HttpRepository {
|
|||
getAssetsActiveProject = async (): Promise<Result<HttpError, Parts[]>> => {
|
||||
return this._jsonRequest<Parts[]>(HttpMethod.GET, "/projects/assets");
|
||||
};
|
||||
|
||||
|
||||
getSceneAsset = (id: string) =>
|
||||
this._jsonToClassInstanceRequest(HttpMethod.GET, `/scenes/by_id?id=${id}`, SceneAsset) as Promise<
|
||||
Result<HttpError, SceneAsset>
|
||||
|
@ -141,5 +141,6 @@ export class CoreHttpRepository extends HttpRepository {
|
|||
return this._jsonRequest<UUID>(HttpMethod.GET, "/projects/get/active/project/id");
|
||||
}
|
||||
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 { Result } from "../helper/result";
|
||||
import { TypedEvent } from "../helper/typed_event";
|
||||
import { RuntimeModel } from "../../features/behavior_tree_builder/presentation/ui/actions/runtime_actions";
|
||||
|
||||
export class SocketRepository extends TypedEvent<any> {
|
||||
serverURL = "ws://localhost:4001";
|
||||
socket: Socket | undefined;
|
||||
|
||||
async connect():Promise<Result<boolean, boolean>> {
|
||||
|
||||
async connect(): Promise<Result<boolean, boolean>> {
|
||||
const socket = io(this.serverURL);
|
||||
|
||||
|
||||
this.socket = socket;
|
||||
socket.connect();
|
||||
socket.on('realtime', (d) =>{
|
||||
socketCoreInstances.forEach((el) => {
|
||||
socket.on(el.event, (event) => el.emit(event))
|
||||
})
|
||||
socket.on('realtime', (d) => {
|
||||
this.emit({
|
||||
event:"realtime",
|
||||
payload:d
|
||||
event: "realtime",
|
||||
payload: d
|
||||
})
|
||||
})
|
||||
if(socket.connected){
|
||||
if (socket.connected) {
|
||||
return Result.ok(true)
|
||||
}
|
||||
return Result.error(false)
|
||||
|
@ -25,4 +29,15 @@ 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;
|
||||
}
|
||||
export abstract class UiLoader {
|
||||
navigate?: NavigateFunction;
|
||||
isLoading = false;
|
||||
async httpHelper<T>(callBack: Promise<Result<any, T>>) {
|
||||
this.isLoading = true;
|
||||
|
@ -75,7 +76,7 @@ export abstract class UiErrorState<T> extends UiLoader {
|
|||
console.log(error);
|
||||
};
|
||||
abstract init(navigate?: NavigateFunction): Promise<any>;
|
||||
dispose() {}
|
||||
dispose() { }
|
||||
errors: UiBaseError[] = [];
|
||||
}
|
||||
|
||||
|
@ -126,7 +127,17 @@ export abstract class FormState<V, E> extends UiErrorState<E> {
|
|||
loadClassInstance = (instance: ClassConstructor<V>, viewModel: V) => {
|
||||
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;
|
||||
onClose: () => void;
|
||||
children: React.ReactNode;
|
||||
}> = ({ isOpen, onClose, children, title }) => {
|
||||
width?: number;
|
||||
}> = ({ isOpen, onClose, children, title, width }) => {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
position: "fixed",
|
||||
top: 0,
|
||||
right: isOpen ? 0 : -300,
|
||||
width: 300,
|
||||
right: isOpen ? 0 : (width ?? 300) * -1,
|
||||
width: width ?? 300,
|
||||
height: "100%",
|
||||
backgroundColor: themeStore.theme.darkSurface,
|
||||
backgroundColor: "#880ef8",
|
||||
boxShadow: "-2px 0 5px rgba(0, 0, 0, 0.5)",
|
||||
transition: "right 0.3s ease",
|
||||
zIndex: 1000,
|
||||
|
||||
}}
|
||||
>
|
||||
<div style={{ height: "100%", width: "100%" }}>
|
||||
|
|
|
@ -1,9 +1,5 @@
|
|||
import * as React from "react";
|
||||
import {
|
||||
FormViewModel,
|
||||
InputBuilderViewModel,
|
||||
InputType,
|
||||
} from "./form_view_model";
|
||||
import { FormViewModel, InputBuilderViewModel, InputType } from "./form_view_model";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { FormBuilderStore } from "./form_builder_store";
|
||||
import { CoreSelect } from "../select/select";
|
||||
|
@ -13,219 +9,162 @@ import { CoreText, CoreTextType } from "../text/text";
|
|||
import { getFormBuilderComponents } from "./forms/form_builder_components";
|
||||
import { FormBuilderValidationModel } from "../../model/form_builder_validation_model";
|
||||
|
||||
export interface IFormBuilder {
|
||||
formBuilder: FormBuilderValidationModel;
|
||||
onChange: (change: FormBuilderValidationModel) => void;
|
||||
}
|
||||
export const FormBuilder = observer(
|
||||
(props: { formBuilder: FormBuilderValidationModel; onChange: (change: FormBuilderValidationModel) => void }) => {
|
||||
const [store] = React.useState(() => new FormBuilderStore());
|
||||
|
||||
export const FormBuilder = observer((props: IFormBuilder) => {
|
||||
const [store] = React.useState(() => new FormBuilderStore());
|
||||
React.useEffect(() => {
|
||||
store.init(props.formBuilder.context, props.formBuilder.result);
|
||||
if (props.formBuilder.form.isNotEmpty()) {
|
||||
store.formViewModel = new FormViewModel(
|
||||
props.formBuilder.form.map((el) => InputBuilderViewModel.fromJSON(el)),
|
||||
props.formBuilder.result,
|
||||
props.formBuilder.context
|
||||
);
|
||||
props.formBuilder.form.map((el) => InputBuilderViewModel.fromJSON(el));
|
||||
}
|
||||
store.changerForm.on((event) => {
|
||||
if (event) props.onChange(event);
|
||||
});
|
||||
}, []);
|
||||
|
||||
React.useEffect(() => {
|
||||
store.init(props.formBuilder.context, props.formBuilder.result);
|
||||
if (props.formBuilder.form.isNotEmpty()) {
|
||||
store.formViewModel = new FormViewModel(
|
||||
props.formBuilder.form.map((el) => InputBuilderViewModel.fromJSON(el)),
|
||||
props.formBuilder.result,
|
||||
props.formBuilder.context
|
||||
);
|
||||
props.formBuilder.form.map((el) => InputBuilderViewModel.fromJSON(el));
|
||||
}
|
||||
store.changerForm.on((event) => {
|
||||
if (event) props.onChange(event);
|
||||
});
|
||||
}, []);
|
||||
return (
|
||||
<div>
|
||||
{store.isError ? (
|
||||
<>Error</>
|
||||
) : (
|
||||
<div>
|
||||
{store.formViewModel?.inputs?.map((element, index) => {
|
||||
if (element.type?.isEqual(InputType.ENUM)) {
|
||||
const values = element.values as string[];
|
||||
return (
|
||||
<CoreSelect
|
||||
key={index}
|
||||
items={values}
|
||||
value={element.totalValue ?? element.defaultValue}
|
||||
onChange={(value) => store.changeTotalValue(element.id, value)}
|
||||
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 (
|
||||
<div>
|
||||
{store.isError ? (
|
||||
<>Error</>
|
||||
) : (
|
||||
<div>
|
||||
{store.formViewModel?.inputs?.map((element, index) => {
|
||||
if (element.type?.isEqual(InputType.ENUM)) {
|
||||
const values = element.values as string[];
|
||||
return (
|
||||
<CoreSelect
|
||||
key={index}
|
||||
items={values}
|
||||
value={element.totalValue ?? element.defaultValue}
|
||||
onChange={(value) =>
|
||||
store.changeTotalValue(element.id, value)
|
||||
}
|
||||
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 ? (
|
||||
<div style={{ margin: 20 }}>
|
||||
{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>
|
||||
|
||||
{element.isOpen ? (
|
||||
<div style={{ margin: 20 }}>
|
||||
{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
|
||||
)
|
||||
) {
|
||||
{subArray.map((subSubArrayItem: InputBuilderViewModel, subIndex: number) => {
|
||||
if (subSubArrayItem.type.isEqual(InputType.ENUM)) {
|
||||
return (
|
||||
<>
|
||||
<CoreSelect
|
||||
items={
|
||||
subSubArrayItem.values?.map(
|
||||
(el) => String(el)
|
||||
) ?? []
|
||||
}
|
||||
value={
|
||||
subSubArrayItem.totalValue ??
|
||||
subSubArrayItem.defaultValue
|
||||
}
|
||||
onChange={(value) => console.log(subSubArrayItem.id)
|
||||
}
|
||||
items={subSubArrayItem.values?.map((el) => String(el)) ?? []}
|
||||
value={subSubArrayItem.totalValue ?? subSubArrayItem.defaultValue}
|
||||
onChange={(value) => console.log(subSubArrayItem.id)}
|
||||
label={element.name}
|
||||
style={{ margin: 5 }}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
if (
|
||||
subSubArrayItem.type.isEqualMany([
|
||||
InputType.NUMBER,
|
||||
InputType.STRING,
|
||||
])
|
||||
)
|
||||
if (subSubArrayItem.type.isEqualMany([InputType.NUMBER, InputType.STRING]))
|
||||
return (
|
||||
<div>
|
||||
<CoreInput
|
||||
isFormBuilder={true}
|
||||
|
||||
style={{ margin: 5 }}
|
||||
onChange={(e) =>
|
||||
store.changeTotalSubValue(
|
||||
element.id,
|
||||
subIndex,
|
||||
e,
|
||||
index
|
||||
)
|
||||
}
|
||||
onChange={(e) => store.changeTotalSubValue(element.id, subIndex, e, index)}
|
||||
validation={
|
||||
subSubArrayItem.type.isEqual(
|
||||
InputType.NUMBER
|
||||
)
|
||||
subSubArrayItem.type.isEqual(InputType.NUMBER)
|
||||
? (el) => Number().isValid(el)
|
||||
: undefined
|
||||
}
|
||||
error="только числа"
|
||||
value={
|
||||
subSubArrayItem.totalValue ??
|
||||
subSubArrayItem.defaultValue
|
||||
}
|
||||
value={subSubArrayItem.totalValue ?? subSubArrayItem.defaultValue}
|
||||
label={subSubArrayItem.name}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
return <>Error</>;
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})
|
||||
: null}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
})
|
||||
: null}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (element.type?.isEqualMany([InputType.NUMBER, InputType.STRING]))
|
||||
return (
|
||||
<div>
|
||||
<CoreInput
|
||||
isFormBuilder={true}
|
||||
validation={
|
||||
element.type.isEqual(InputType.NUMBER)
|
||||
? (el) => Number().isValid(el)
|
||||
: undefined
|
||||
}
|
||||
onChange={(e) => {
|
||||
store.changeTotalValue(element.id, e);
|
||||
}}
|
||||
value={element.totalValue ?? element.defaultValue}
|
||||
error="только числа"
|
||||
label={element.name}
|
||||
style={{ margin: 20 }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
if (element.type?.isEqual(InputType.OBJECT))
|
||||
return (
|
||||
<>
|
||||
{getFormBuilderComponents(
|
||||
element.name
|
||||
.replace(">", "")
|
||||
.replace("<", "")
|
||||
.replace("/", ""),
|
||||
element.totalValue ?? element.defaultValue,
|
||||
(text) => store.changeTotalValue(element.id, text)
|
||||
).fold(
|
||||
(s) => (
|
||||
<>{s}</>
|
||||
),
|
||||
(error) => (
|
||||
<>{error}</>
|
||||
)
|
||||
)}
|
||||
</>
|
||||
);
|
||||
if (element.type?.isEqualMany([InputType.NUMBER, InputType.STRING]))
|
||||
return (
|
||||
<div>
|
||||
<CoreInput
|
||||
|
||||
validation={element.type.isEqual(InputType.NUMBER) ? (el) => Number().isValid(el) : undefined}
|
||||
onChange={(e) => {
|
||||
store.changeTotalValue(element.id, e);
|
||||
}}
|
||||
value={element.totalValue ?? element.defaultValue}
|
||||
error="только числа"
|
||||
label={element.name}
|
||||
style={{ margin: 20 }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
if (element.type?.isEqual(InputType.OBJECT))
|
||||
return (
|
||||
<>
|
||||
{getFormBuilderComponents(
|
||||
element.name.replace(">", "").replace("<", "").replace("/", ""),
|
||||
element.totalValue ?? element.defaultValue,
|
||||
(text) => store.changeTotalValue(element.id, text)
|
||||
).fold(
|
||||
(s) => (
|
||||
<>{s}</>
|
||||
),
|
||||
(error) => (
|
||||
<>{error}</>
|
||||
)
|
||||
)}
|
||||
</>
|
||||
);
|
||||
return <div>Error {String(element)}</div>;
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
|
|
@ -95,7 +95,7 @@ export class FormViewModel {
|
|||
.replace(/[^\x00-\x7F]/g, "")
|
||||
.replaceAll("\n", "")
|
||||
.replaceAll("\\", "")
|
||||
// .replaceAll("/", "")
|
||||
// .replaceAll("/", "")
|
||||
);
|
||||
} catch (error) {
|
||||
console.log("ERROR: FormViewModel json() " + result);
|
||||
|
@ -190,8 +190,9 @@ export class FormViewModel {
|
|||
});
|
||||
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 {
|
||||
|
||||
if (result.isEmpty() && context.isEmpty()) {
|
||||
return Result.error(undefined);
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
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";
|
||||
|
||||
export enum FormBuilderComponents {
|
||||
SelectDetail = "SelectDetail",
|
||||
SelectDataset = "SelectDataset",
|
||||
SelectProcess = "SelectProcess",
|
||||
}
|
||||
export interface IFormBuilderComponentsProps<T> {
|
||||
dependency: T;
|
||||
|
@ -18,8 +18,8 @@ export const getFormBuilderComponents = (
|
|||
if (name.isEqual(FormBuilderComponents.SelectDetail)) {
|
||||
return Result.ok(<SelectDetail dependency={dependency} onChange={onChange} />);
|
||||
}
|
||||
if (name.isEqual(FormBuilderComponents.SelectDataset)) {
|
||||
return Result.ok(<SelectDatasetScreen dependency={dependency} onChange={onChange} />);
|
||||
if (name.isEqual(FormBuilderComponents.SelectProcess)) {
|
||||
return Result.ok(<SelectProcess dependency={dependency} onChange={onChange} />);
|
||||
}
|
||||
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,21 +1,18 @@
|
|||
// @ts-nocheck
|
||||
import React from "react";
|
||||
import { IFormBuilderComponentsProps } from "../../form_builder_components";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { ListItem } from "./ui/list_item";
|
||||
import { SelectDetailStore } from "./select_detail_store";
|
||||
import { SelectDetailViewModel } from "../model/select_details_model";
|
||||
import { plainToInstance } from "class-transformer";
|
||||
|
||||
export const SelectDetail = observer((props: IFormBuilderComponentsProps<SelectDetailViewModel>) => {
|
||||
const [store] = React.useState(() => new SelectDetailStore());
|
||||
React.useEffect(() => {
|
||||
console.log(props.dependency.details);
|
||||
store.viewModel = new SelectDetailViewModel(props.dependency.details);
|
||||
store.isLoading = false;
|
||||
store.init();
|
||||
}, []);
|
||||
|
||||
|
||||
return (
|
||||
<div>
|
||||
{store.isLoading ? (
|
||||
|
|
|
@ -16,7 +16,6 @@ export const ListItem = (props: IListItemProps) => {
|
|||
backgroundColor: "rgba(254, 247, 255, 1)",
|
||||
border: "1px #6750a4 solid",
|
||||
width: "100%",
|
||||
height: 110,
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
|
|
|
@ -9,7 +9,7 @@ import { FormBuilder } from "./form_builder";
|
|||
import makeAutoObservable from "mobx-store-inheritance";
|
||||
|
||||
class FormBuilderTextStore extends ModalStore {
|
||||
viewModel = FormBuilderValidationModel.empty();
|
||||
viewModel = FormBuilderValidationModel.eee();
|
||||
constructor() {
|
||||
super();
|
||||
makeAutoObservable(this);
|
||||
|
@ -21,6 +21,7 @@ export const FormBuildTest = observer(() => {
|
|||
|
||||
return (
|
||||
<div>
|
||||
|
||||
<InputV2 label={"result"} onChange={(text) => (store.viewModel.result = text)} />
|
||||
<InputV2 label={"context"} onChange={(text) => (store.viewModel.context = text)} />
|
||||
<CoreButton text="click" onClick={() => (store.isModalOpen = true)} />
|
||||
|
@ -33,15 +34,16 @@ export const FormBuildTest = observer(() => {
|
|||
onCancel={() => {
|
||||
store.isModalOpen = false;
|
||||
}}
|
||||
>
|
||||
<FormBuilder
|
||||
formBuilder={store.viewModel}
|
||||
onChange={(e) => {
|
||||
console.log(e.output);
|
||||
// console.log(JSON.stringify(e.output))
|
||||
}}
|
||||
/>
|
||||
</Modal>
|
||||
></Modal>
|
||||
<div style={{height:50}}/>
|
||||
<FormBuilder
|
||||
formBuilder={store.viewModel}
|
||||
onChange={(e) => {
|
||||
console.log(e)
|
||||
// console.log(e.output);
|
||||
console.log(JSON.stringify(e.output))
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
|
|
@ -931,6 +931,18 @@ const getIconSvg = (
|
|||
/>
|
||||
</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":
|
||||
return Result.ok(
|
||||
<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;
|
||||
trim?: boolean;
|
||||
styleContentEditable?: React.CSSProperties;
|
||||
isFormBuilder?: boolean;
|
||||
}
|
||||
|
||||
export const CoreInput = (props: IInputProps) => {
|
||||
|
@ -29,15 +28,6 @@ export const CoreInput = (props: IInputProps) => {
|
|||
setAppendInnerText(false);
|
||||
}
|
||||
}, [ref, value, isAppendInnerText, setAppendInnerText, props]);
|
||||
// React.useEffect(() => {
|
||||
// if (props.isFormBuilder) {
|
||||
// if (ref.current && props.value) {
|
||||
// ref.current.innerText = value;
|
||||
// setValue(props.value);
|
||||
// console.log(props.value);
|
||||
// }
|
||||
// }
|
||||
// }, [props.value]);
|
||||
|
||||
const isSmall = props.type !== undefined && props.type.isEqual(CoreInputType.small);
|
||||
return (
|
||||
|
|
|
@ -2,15 +2,16 @@ import { themeStore } from "../../..";
|
|||
import { Icon } from "../icons/icons";
|
||||
import { CoreText, CoreTextType, FontType } from "../text/text";
|
||||
|
||||
interface InputV2Props {
|
||||
export const InputV2: React.FC<{
|
||||
style?: React.CSSProperties;
|
||||
label: string;
|
||||
value?: string;
|
||||
trim?: boolean;
|
||||
validation?: (value: string) => boolean;
|
||||
error?: string;
|
||||
height?: number;
|
||||
onChange?: (text: string) => void;
|
||||
|
||||
}
|
||||
export const InputV2: React.FC<InputV2Props> = ({ label, height, value, onChange, trim }) => {
|
||||
}> = ({ label, height, value, onChange, trim }) => {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
|
|
|
@ -107,7 +107,7 @@ export const MainPageV2: React.FC<{
|
|||
overflow: "auto",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
{/* <div
|
||||
style={{
|
||||
alignSelf: "center",
|
||||
width: 645,
|
||||
|
@ -222,7 +222,7 @@ export const MainPageV2: React.FC<{
|
|||
>
|
||||
{rightChild}
|
||||
</div>
|
||||
</div>
|
||||
</div> */}
|
||||
|
||||
<div style={Object.assign({ width: "100%" }, style)}>{children}</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[];
|
||||
value: string;
|
||||
label: string;
|
||||
onChange: (value: string) => void;
|
||||
onChange: (value: string, index: number) => void;
|
||||
}
|
||||
export const CoreSelect = (props: ICoreSelectProps) => {
|
||||
const ref = React.useRef<HTMLDivElement>(null);
|
||||
|
@ -52,7 +52,7 @@ export const CoreSelect = (props: ICoreSelectProps) => {
|
|||
key={i}
|
||||
onClick={() => {
|
||||
setValue(el);
|
||||
props.onChange(el);
|
||||
props.onChange(el, i);
|
||||
}}
|
||||
style={{
|
||||
backgroundColor: "rgba(230, 224, 233, 1)",
|
||||
|
|
|
@ -21,7 +21,6 @@ export class BehaviorTreeBuilderHttpRepository extends CoreHttpRepository {
|
|||
`${this.featureApi}/by_id?id=${id}`,
|
||||
BehaviorTreeModel
|
||||
) as unknown as Promise<Result<HttpError, BehaviorTreeModel>>;
|
||||
deleteBt = (id: string) => this._jsonRequest(HttpMethod.DELETE, `${this.featureApi}?id=${id}`);
|
||||
editBt = async (model: BehaviorTreeModel) => {
|
||||
await this._jsonRequest(HttpMethod.POST, `${this.featureApi}/fill/tree`, model);
|
||||
model.__v = undefined
|
||||
|
|
|
@ -4,20 +4,15 @@ import { createEditor } from "./ui/editor/editor";
|
|||
import { SkillTree } from "./ui/skill_tree/skill_tree";
|
||||
import { BehaviorTreeBuilderStore, DrawerState } from "./behavior_tree_builder_store";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { match } from "ts-pattern";
|
||||
import { Icon } from "../../../core/ui/icons/icons";
|
||||
import { CoreText, CoreTextType } from "../../../core/ui/text/text";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import { IForms, forms } from "./ui/forms/forms";
|
||||
import { ButtonV2, ButtonV2Type } from "../../../core/ui/button/button_v2";
|
||||
import { CoreCard } from "../../../core/ui/card/card";
|
||||
import { themeStore } from "../../..";
|
||||
import { CoreModal } from "../../../core/ui/modal/modal";
|
||||
import { InputV2 } from "../../../core/ui/input/input_v2";
|
||||
import { SelectV2 } from "../../../core/ui/select/select_v2";
|
||||
import { MainPageV2 } from "../../../core/ui/pages/main_page_v2";
|
||||
import { DrawerV2 } from "../../../core/ui/drawer/drawer";
|
||||
import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels";
|
||||
import PopoverV2 from "../../../core/ui/popover/popover";
|
||||
|
||||
export const behaviorTreeBuilderScreenPath = "behavior/tree/screen/path";
|
||||
|
||||
|
@ -54,7 +49,9 @@ export const BehaviorTreeBuilderScreen = observer(() => {
|
|||
if (ref.current) {
|
||||
// @ts-expect-error
|
||||
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]);
|
||||
|
||||
|
@ -66,54 +63,36 @@ export const BehaviorTreeBuilderScreen = observer(() => {
|
|||
}, []);
|
||||
return (
|
||||
<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" }}
|
||||
bgColor={themeStore.theme.black}
|
||||
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
|
||||
style={{
|
||||
height: "100%",
|
||||
|
@ -160,10 +139,45 @@ export const BehaviorTreeBuilderScreen = observer(() => {
|
|||
}}
|
||||
/>
|
||||
</Panel>
|
||||
<PanelResizeHandle>
|
||||
<div style={{ width: 10, height: "100%", backgroundColor: "beige" }}></div>
|
||||
</PanelResizeHandle>
|
||||
<Panel defaultSize={0}> </Panel>
|
||||
{store.panels.map((el, index) => (
|
||||
<>
|
||||
<PanelResizeHandle
|
||||
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>
|
||||
</div>
|
||||
</>
|
||||
|
@ -172,8 +186,17 @@ export const BehaviorTreeBuilderScreen = observer(() => {
|
|||
title={store.titleDrawer}
|
||||
onClose={() => store.editDrawer(DrawerState.editThreadBehaviorTree, false)}
|
||||
isOpen={store.drawers.find((el) => el.name === DrawerState.editThreadBehaviorTree)?.status}
|
||||
width={window.innerWidth / 2}
|
||||
>
|
||||
<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%" }}>
|
||||
{store.skillTemplates?.getForms(store.selected ?? "").map((formType, index) =>
|
||||
forms(
|
||||
|
@ -186,11 +209,13 @@ export const BehaviorTreeBuilderScreen = observer(() => {
|
|||
)
|
||||
.rFind<IForms>((form) => form.name.isEqual(formType))
|
||||
.fold(
|
||||
(s) => (
|
||||
<div key={index} style={{ height: "100%" }}>
|
||||
{s.component}
|
||||
</div>
|
||||
),
|
||||
(s) => {
|
||||
return (
|
||||
<div key={index} style={{ flex: 1 }}>
|
||||
{s.component}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
() => (
|
||||
<div key={index + "error"} style={{ height: "100%" }}>
|
||||
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 { SceneAsset } from "../../../core/model/scene_asset";
|
||||
import { themeStore } from "../../..";
|
||||
import { RunTimeActions } from "./ui/actions/runtime_actions";
|
||||
import { ITopicModel } from "../../topics/topic_view_model";
|
||||
|
||||
interface I2DArea {
|
||||
x: number;
|
||||
|
@ -37,8 +39,31 @@ interface I2DArea {
|
|||
export enum DrawerState {
|
||||
editThreadBehaviorTree = "Редактирование",
|
||||
}
|
||||
interface IActionPanel {
|
||||
name: string;
|
||||
selectIsClosePopover?: boolean;
|
||||
action: (index: number) => void;
|
||||
}
|
||||
|
||||
export class PanelBody {
|
||||
body?: React.ReactNode;
|
||||
size: number = 0;
|
||||
name: string = "выберите тип панели";
|
||||
constructor(body: React.ReactNode | undefined, name: string | undefined, size: number | undefined) {
|
||||
makeAutoObservable(this);
|
||||
|
||||
if (name) this.name = name;
|
||||
if (body) this.body = body;
|
||||
if (size) this.size = size;
|
||||
}
|
||||
}
|
||||
|
||||
export class BehaviorTreeBuilderStore extends UiDrawerFormState<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;
|
||||
viewModel: BehaviorTreeViewModel = BehaviorTreeViewModel.empty();
|
||||
behaviorTreeModel: BehaviorTreeModel = BehaviorTreeModel.empty();
|
||||
|
@ -51,15 +76,15 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState<BehaviorTreeView
|
|||
activeProject: string = "";
|
||||
behaviorTreeBuilderHttpRepository = new BehaviorTreeBuilderHttpRepository();
|
||||
canRun = true;
|
||||
isNeedSaveBtn = false;
|
||||
isNeedSaveBtn = true;
|
||||
selected: string = "";
|
||||
selectedSid: string = "";
|
||||
nodeBehaviorTree: NodeBehaviorTree[] = [];
|
||||
navigate?: NavigateFunction;
|
||||
editor?: NodeEditor<Schemes>;
|
||||
areaPlugin?: AreaPlugin<Schemes, AreaExtra>;
|
||||
nodeUpdateObserver?: NodeRerenderObserver;
|
||||
primitiveViewModel: PrimitiveViewModel;
|
||||
topics: ITopicModel[];
|
||||
skillTree: ISkillView = {
|
||||
name: "",
|
||||
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() {
|
||||
super(DrawerState);
|
||||
makeAutoObservable(this);
|
||||
|
@ -86,7 +116,7 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState<BehaviorTreeView
|
|||
this.editor = editor;
|
||||
this.areaPlugin = area;
|
||||
};
|
||||
getAllTopics = () => this.filledOutTemplates.topicsStack;
|
||||
getAllTopics = () => this.filledOutTemplates.topicsStack.plus(this.topics);
|
||||
errorHandingStrategy = (_: CoreError) => {};
|
||||
|
||||
dragEnd = (e: EventTarget) => {
|
||||
|
@ -102,6 +132,13 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState<BehaviorTreeView
|
|||
};
|
||||
drawSkillCheck = (x: number, y: number, name: string) => {
|
||||
const drawPoint = { x: x, y: y, w: 1, h: 1 };
|
||||
|
||||
console.log(
|
||||
drawPoint.x < this.area!.x + this.area!.w &&
|
||||
drawPoint.x + drawPoint.w > this.area!.x &&
|
||||
drawPoint.y < this.area!.y + this.area!.h &&
|
||||
drawPoint.y + drawPoint.h > this.area!.y
|
||||
);
|
||||
if (
|
||||
drawPoint.x < this.area!.x + this.area!.w &&
|
||||
drawPoint.x + drawPoint.w > this.area!.x &&
|
||||
|
@ -115,7 +152,7 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState<BehaviorTreeView
|
|||
name: name,
|
||||
id: sid,
|
||||
});
|
||||
this.isNeedSaveBtn = true;
|
||||
|
||||
if (!name.isEqualMany(Object.keys(SystemPrimitive))) {
|
||||
this.skillTemplates?.getSkill(name).fold(
|
||||
(skill) => {
|
||||
|
@ -179,7 +216,7 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState<BehaviorTreeView
|
|||
|
||||
await this.mapOk("sceneAsset", this.behaviorTreeBuilderHttpRepository.getSceneAsset(model.sceneId));
|
||||
this.isLoading = false;
|
||||
|
||||
await this.mapOk("topics", this.behaviorTreeBuilderHttpRepository.getAllTopics());
|
||||
this.reteForceUpdateObserver?.emit("");
|
||||
},
|
||||
async () => {
|
||||
|
@ -207,7 +244,6 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState<BehaviorTreeView
|
|||
)
|
||||
).fold(
|
||||
(xml) => {
|
||||
console.log(xml);
|
||||
this.behaviorTreeModel.skills = this.filledOutTemplates;
|
||||
this.behaviorTreeModel.scene = NodeBehaviorTree.fromReteScene(
|
||||
this.editor as NodeEditor<Schemes>,
|
||||
|
@ -345,4 +381,11 @@ export class BehaviorTreeBuilderStore extends UiDrawerFormState<BehaviorTreeView
|
|||
);
|
||||
}
|
||||
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 (
|
||||
<div style={{ overflowX: "scroll", height: "100%" }}>
|
||||
<div style={{ overflowX: "scroll", height: "100%", flex: 1 }}>
|
||||
<div>FormBuilder</div>
|
||||
{store.isBtScreen ? (
|
||||
<div>
|
||||
|
@ -27,7 +27,6 @@ export const FormBuilderForm = observer((props: IPropsForm<Partial<FormBuilderVa
|
|||
formBuilder={store.viewModel}
|
||||
onChange={(form) => {
|
||||
store.viewModel = form;
|
||||
console.log(form);
|
||||
}}
|
||||
/>
|
||||
<div style={{ height: 100 }} />
|
||||
|
@ -81,7 +80,7 @@ export const FormBuilderForm = observer((props: IPropsForm<Partial<FormBuilderVa
|
|||
store.isModalOpen = false;
|
||||
}}
|
||||
>
|
||||
<FormBuilder formBuilder={store.viewModel} onChange={() => {}} />
|
||||
<FormBuilder formBuilder={store.viewModel} onChange={(form) => (console.log(form), props.onChange(form))} />
|
||||
</Modal>
|
||||
</>
|
||||
)}
|
||||
|
|
|
@ -4,7 +4,7 @@ import { BehaviorTreeViewModel } from "../behavior_tree_builder/model/behavior_t
|
|||
|
||||
export class BehaviorTreeManagerHttpRepository extends CoreHttpRepository {
|
||||
featureApi = `/behavior/trees`;
|
||||
deleteBt = (id: string) => this._jsonRequest(HttpMethod.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);
|
||||
getAllBtInstances = async () => this._jsonRequest<BehaviorTreeModel[]>(HttpMethod.GET, this.featureApi);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { observer } from "mobx-react-lite";
|
||||
import { BehaviorTreeManagerStore } from "./behavior_tree_manager_store";
|
||||
import React from "react";
|
||||
import React, { useEffect } from "react";
|
||||
import { useStore } from "../../core/helper/use_store";
|
||||
import { ButtonV2, ButtonV2Type } from "../../core/ui/button/button_v2";
|
||||
import { CoreCard } from "../../core/ui/card/card";
|
||||
|
@ -16,13 +16,13 @@ export const BehaviorTreeManagerScreenPath = "/behavior/tree/manager";
|
|||
|
||||
export const BehaviorTreeManagerScreen = observer(() => {
|
||||
const store = useStore(BehaviorTreeManagerStore);
|
||||
|
||||
useEffect(() => {}, []);
|
||||
return (
|
||||
<>
|
||||
<MainPageV2
|
||||
children={
|
||||
<>
|
||||
<div style={{ height: "100%", overflowY: "auto", overflowX: "hidden" }}>
|
||||
<div style={{ height: "100%", }}>
|
||||
<div style={{ height: 20 }} />
|
||||
<ButtonV2
|
||||
icon={<Icon type={"Plus"} style={{ alignSelf: "center", marginLeft: 10, marginRight: 10 }} />}
|
||||
|
@ -65,9 +65,13 @@ export const BehaviorTreeManagerScreen = observer(() => {
|
|||
}}
|
||||
/>
|
||||
<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 }} />
|
||||
<InputV2 trim={true} label={"Описание"} onChange={(text) => store.updateForm({ description: text })} />
|
||||
<InputV2
|
||||
trim={true}
|
||||
label={"Описание"}
|
||||
onChange={(text) => store.updateForm({ description: text })}
|
||||
/>
|
||||
<div style={{ height: 20 }} />
|
||||
<SelectV2
|
||||
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> {
|
||||
viewModel: BehaviorTreeViewModel = BehaviorTreeViewModel.empty();
|
||||
navigate?: NavigateFunction;
|
||||
btTreeModels: BehaviorTreeModel[] = [];
|
||||
scenes?: SceneModel[];
|
||||
behaviorTreeManagerHttpRepository = new BehaviorTreeManagerHttpRepository();
|
||||
|
|
|
@ -14,7 +14,13 @@ export interface ISkils {
|
|||
}
|
||||
|
||||
export class CalculationHttpRepository extends CoreHttpRepository {
|
||||
|
||||
async getLogs(id: string) {
|
||||
|
||||
await this._request(HttpMethod.GET, `/logs?id=${id}`)
|
||||
window.location.href = 'http://localhost:4001/log.txt';
|
||||
|
||||
}
|
||||
|
||||
featureApi = `/calculations/instances`;
|
||||
subFeatureApi = `/calculations/template`;
|
||||
|
||||
|
|
|
@ -4,7 +4,9 @@ import { FormBuilderValidationModel } from "../../../core/model/form_builder_val
|
|||
|
||||
export enum ModelMachineLearningTypes {
|
||||
OBJECT_DETECTION = "OBJECT_DETECTION",
|
||||
POSE_ESTIMATE = "POSE_ESTIMATE",
|
||||
POSE_ESTIMATION = "POSE_ESTIMATION",
|
||||
BOP_DATASET = "BOP_DATASET",
|
||||
WEIGHTS = "WEIGHTS",
|
||||
}
|
||||
|
||||
export class CalculationModel extends ValidationModel {
|
||||
|
|
|
@ -28,7 +28,6 @@ export const CalculationInstanceScreenPath = "/calculation";
|
|||
|
||||
export const CalculationInstanceScreen = observer(() => {
|
||||
const store = useStore(CalculationInstanceStore);
|
||||
|
||||
return (
|
||||
<>
|
||||
<MainPage
|
||||
|
@ -60,7 +59,9 @@ export const CalculationInstanceScreen = observer(() => {
|
|||
() => store.makeEditProcess(el),
|
||||
() => store.deleteInstance(el._id ?? ""),
|
||||
() => store.execSkill(el._id ?? ""),
|
||||
() => store.execSkill(el._id ?? "")
|
||||
() => store.execSkill(el._id ?? ""),
|
||||
() => store.changeProcessStatus(el._id ?? ""),
|
||||
() => store.getTxtLog(el._id ?? "")
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
|
@ -94,7 +95,10 @@ export const CalculationInstanceScreen = observer(() => {
|
|||
<div style={{ display: "flex", flexDirection: "column", justifyContent: "space-between", height: "100%" }}>
|
||||
<FormBuilder
|
||||
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" }}>
|
||||
|
@ -134,7 +138,7 @@ export const CalculationInstanceScreen = observer(() => {
|
|||
<CoreSelect
|
||||
items={Object.keys(ModelMachineLearningTypes)}
|
||||
value={store.viewModel.type}
|
||||
label={"Тип навыка"}
|
||||
label={"Тип вычисления"}
|
||||
onChange={(text: string) => store.updateForm({ type: text })}
|
||||
/>
|
||||
<CoreSelect
|
||||
|
@ -143,8 +147,16 @@ export const CalculationInstanceScreen = observer(() => {
|
|||
label={"Тип карточки"}
|
||||
onChange={(text: string) => store.updateForm({ card: text })}
|
||||
/>
|
||||
<CoreInput label="Имя" onChange={(text) => store.updateForm({ name: text })} />
|
||||
<CoreInput label="Команда для запуска" onChange={(text) => store.updateForm({ script: text })} />
|
||||
<CoreInput
|
||||
trim={true}
|
||||
label="Имя"
|
||||
onChange={(text) => store.updateForm({ name: text.replaceAll("\n", "") })}
|
||||
/>
|
||||
<CoreInput
|
||||
trim={true}
|
||||
label="Команда для запуска"
|
||||
onChange={(text) => store.updateForm({ script: text.replaceAll("\n", "") })}
|
||||
/>
|
||||
<InputV2
|
||||
label="FormBuilder Result"
|
||||
onChange={(text) => (store.viewModel.formBuilder.result = text)}
|
||||
|
@ -206,7 +218,7 @@ export const CalculationInstanceScreen = observer(() => {
|
|||
store.isModalOpen = false;
|
||||
}}
|
||||
>
|
||||
<FormBuilder formBuilder={store.viewModel.formBuilder} onChange={() => {}} />
|
||||
<FormBuilder formBuilder={store.viewModel.formBuilder} onChange={(e) => {}} />
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -5,7 +5,7 @@ import { Drawer, UiDrawerFormState } from "../../../core/store/base_store";
|
|||
import { CalculationHttpRepository } from "../data/calculation_http_repository";
|
||||
import { message } from "antd";
|
||||
import { UUID } from "../../all_projects/data/project_http_repository";
|
||||
import { CalculationModel } from "../model/calculation_model";
|
||||
import { CalculationModel as calculationModel } from "../model/calculation_model";
|
||||
import { ProcessUpdate, CalculationSocketRepository } from "../data/calculation_socket_repository";
|
||||
import { match } from "ts-pattern";
|
||||
import { plainToInstance } from "class-transformer";
|
||||
|
@ -20,16 +20,17 @@ export enum StoreTypes {
|
|||
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();
|
||||
calculationSocketRepository: CalculationSocketRepository = new CalculationSocketRepository();
|
||||
activeProjectId?: UUID;
|
||||
storeType: StoreTypes = StoreTypes.empty;
|
||||
viewModel: CalculationModel = CalculationModel.empty();
|
||||
modelTemplate?: CalculationModel[];
|
||||
editProcess?: CalculationModel;
|
||||
calculationInstances?: CalculationModel[];
|
||||
selectTemplate?: CalculationModel;
|
||||
viewModel: calculationModel = calculationModel.empty();
|
||||
modelTemplate?: calculationModel[];
|
||||
editProcess?: calculationModel;
|
||||
calculationInstances?: calculationModel[];
|
||||
selectTemplate?: calculationModel;
|
||||
titleDrawer: string = DrawersSkill.NEW_SKILL;
|
||||
drawers: Drawer[];
|
||||
isModalOpen: boolean = false;
|
||||
|
@ -41,7 +42,7 @@ export class CalculationInstanceStore extends UiDrawerFormState<CalculationModel
|
|||
status: false,
|
||||
};
|
||||
});
|
||||
this.viewModel = CalculationModel.empty();
|
||||
this.viewModel = calculationModel.empty();
|
||||
this.calculationSocketRepository.on(this.socketUpdate);
|
||||
makeAutoObservable(this);
|
||||
}
|
||||
|
@ -67,9 +68,9 @@ export class CalculationInstanceStore extends UiDrawerFormState<CalculationModel
|
|||
successMessage: "Процесс запущен",
|
||||
});
|
||||
|
||||
setSelectTemplate = (el: CalculationModel) => {
|
||||
setSelectTemplate = (el: calculationModel) => {
|
||||
this.selectTemplate = el;
|
||||
const instance = plainToInstance(CalculationModel, el);
|
||||
const instance = plainToInstance(calculationModel, el);
|
||||
instance.instanceName = this?.viewModel.instanceName;
|
||||
this.viewModel = instance;
|
||||
};
|
||||
|
@ -84,7 +85,7 @@ export class CalculationInstanceStore extends UiDrawerFormState<CalculationModel
|
|||
match(this.storeType)
|
||||
.with(StoreTypes.empty, () => {})
|
||||
.with(StoreTypes.newType, async () =>
|
||||
(await this.viewModel.valid<CalculationModel>()).fold(
|
||||
(await this.viewModel.valid<calculationModel>()).fold(
|
||||
async (model) => {
|
||||
model.project = this.activeProjectId?.id;
|
||||
|
||||
|
@ -99,7 +100,7 @@ export class CalculationInstanceStore extends UiDrawerFormState<CalculationModel
|
|||
)
|
||||
)
|
||||
.with(StoreTypes.newModel, async () => {
|
||||
(await this.viewModel.valid<CalculationModel>()).fold(
|
||||
(await this.viewModel.valid<calculationModel>()).fold(
|
||||
async (model) => {
|
||||
delete model._id;
|
||||
model.project = this.activeProjectId?.id;
|
||||
|
@ -128,20 +129,30 @@ export class CalculationInstanceStore extends UiDrawerFormState<CalculationModel
|
|||
|
||||
saveEdiSkill = async () => {
|
||||
this.editDrawer(DrawersSkill.EDIT_SKILL, false);
|
||||
(await this.viewModel.valid<CalculationModel>()).fold(
|
||||
async (model) => await this.calculationHttpRepository.editCalculation(model),
|
||||
(await this.viewModel.valid<calculationModel>()).fold(
|
||||
async (model) => (await this.calculationHttpRepository.editCalculation(model), await this.init()),
|
||||
async (err) => message.error(err)
|
||||
);
|
||||
};
|
||||
makeEditProcess = (el: CalculationModel) => {
|
||||
makeEditProcess = (el: calculationModel) => {
|
||||
this.editProcess = el;
|
||||
this.loadClassInstance(CalculationModel, el);
|
||||
this.loadClassInstance(calculationModel, el);
|
||||
this.editDrawer(DrawersSkill.EDIT_SKILL, true);
|
||||
};
|
||||
deleteTemplate = async (el: CalculationModel) => {
|
||||
deleteTemplate = async (el: calculationModel) => {
|
||||
await this.messageHttp(this.calculationHttpRepository.deleteTemplate(el._id ?? ""), {
|
||||
successMessage: "Удален",
|
||||
});
|
||||
await this.mapOk("modelTemplate", this.calculationHttpRepository.getAllTemplates());
|
||||
};
|
||||
changeProcessStatus = async (id: string) =>
|
||||
this!
|
||||
.calculationInstances!.whereOne((el) => el._id === id)
|
||||
.map(
|
||||
async (calculationModel) => (
|
||||
(calculationModel.isEnd = !calculationModel.isEnd),
|
||||
await this.calculationHttpRepository.editCalculation(calculationModel),
|
||||
await this.init(undefined)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { match } from "ts-pattern";
|
||||
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 { IMenuItem } from "../../../../dataset/card_dataset";
|
||||
import { Icon } from "../../../../../core/ui/icons/icons";
|
||||
|
@ -15,7 +15,9 @@ export const getModelCard = (
|
|||
onEdit: Function,
|
||||
onDelete: Function,
|
||||
onPlay: Function,
|
||||
onPause: Function
|
||||
onPause: Function,
|
||||
onChangeProcessIsEnd: Function,
|
||||
onLog: Function
|
||||
) => {
|
||||
const menu: IMenuItem[] = [
|
||||
{
|
||||
|
@ -26,6 +28,10 @@ export const getModelCard = (
|
|||
onClick: () => onDelete(),
|
||||
name: "Удалить",
|
||||
},
|
||||
{
|
||||
onClick: () => onChangeProcessIsEnd(),
|
||||
name: calculationModel.isEnd ? "Вернуть на доработку" : "Завершить",
|
||||
},
|
||||
];
|
||||
|
||||
const items: MenuProps["items"] = menu.map((el, index) => {
|
||||
|
@ -63,7 +69,8 @@ export const getModelCard = (
|
|||
<Icon
|
||||
type="Log"
|
||||
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
|
||||
|
|
|
@ -26,7 +26,6 @@ export class PoseEstimateRepository extends HttpRepository {
|
|||
execAnalyze = (id: string) => this._jsonRequest(HttpMethod.POST, `/run_time/exec/analyze?id=${id}`);
|
||||
}
|
||||
export class PoseEstimateStore extends UiErrorState<any> {
|
||||
navigate?: NavigateFunction;
|
||||
poseEstimateRepository = new PoseEstimateRepository();
|
||||
constructor() {
|
||||
super();
|
||||
|
|
|
@ -12,7 +12,6 @@ import { useStore } from "../../core/helper/use_store";
|
|||
import { FormBuilder } from "../../core/ui/form_builder/form_builder";
|
||||
import { ButtonV2 } from "../../core/ui/button/button_v2";
|
||||
import { Result } from "../../core/helper/result";
|
||||
import { plainToInstance } from "class-transformer";
|
||||
|
||||
export const isValidJson = <T,>(json: any): Result<string, T> => {
|
||||
try {
|
||||
|
@ -288,3 +287,4 @@ export const SkillsScreen = observer(() => {
|
|||
</>
|
||||
);
|
||||
});
|
||||
|
|
@ -21,6 +21,7 @@ export class TopicViewModel extends ValidationModel {
|
|||
export interface ITopicModel {
|
||||
digitalTwinId?: string;
|
||||
sid?: string;
|
||||
_id?: string;
|
||||
name: 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";
|
||||
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 { TopicsStore } from "./topics_store";
|
||||
import { useStore } from "../../core/helper/use_store";
|
||||
import { CoreButton } from "../../core/ui/button/button";
|
||||
import { CoreModal } from "../../core/ui/modal/modal";
|
||||
import { InputV2 } from "../../core/ui/input/input_v2";
|
||||
import { CoreText, CoreTextType } from "../../core/ui/text/text";
|
||||
import { CoreInput } from "../../core/ui/input/input";
|
||||
|
||||
export const TopicsScreenPath = "/topics";
|
||||
|
||||
|
@ -10,12 +15,43 @@ export const TopicsScreen = observer(() => {
|
|||
|
||||
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) => (
|
||||
<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.type}</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";
|
||||
|
||||
export class TopicsStore extends FormState<TopicModel, HttpError> {
|
||||
importTopic(text: string): void {
|
||||
this.loadClassInstance(TopicModel, JSON.parse(text))
|
||||
|
||||
this.createTopic()
|
||||
}
|
||||
|
||||
|
||||
viewModel: TopicModel = TopicModel.empty();
|
||||
topics?: ITopicModel[];
|
||||
topicsHttpRepository: TopicsHttpRepository = new TopicsHttpRepository();
|
||||
importTopicModal = false;
|
||||
constructor() {
|
||||
super();
|
||||
makeAutoObservable(this);
|
||||
}
|
||||
init = async (navigate?: NavigateFunction | undefined) => {
|
||||
this.modalCancel();
|
||||
this.importTopicModal = false;
|
||||
await this.mapOk("topics", this.topicsHttpRepository.getAllTopics());
|
||||
};
|
||||
cancelImportTopicModal = () => {
|
||||
this.importTopicModal = false;
|
||||
}
|
||||
openTopicModal = () => {
|
||||
this.importTopicModal = true;
|
||||
}
|
||||
deleteTopic = async (id: string) => (await (this.topicsHttpRepository.deleteTopic(id))).map(() => this.init());
|
||||
createTopic = async () =>
|
||||
(await this.viewModel.validMessage<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 { configure } from "mobx";
|
||||
import { ThemeStore } from "./core/store/theme_store";
|
||||
import { FormBuildTest } from "./core/ui/form_builder/test";
|
||||
|
||||
configure({
|
||||
enforceActions: "never",
|
||||
|
@ -22,5 +23,6 @@ root.render(
|
|||
<SocketListener>
|
||||
<RouterProvider router={router} />
|
||||
</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.07.2024 @shalenikol release 0.2
|
||||
28.10.2024 @shalenikol release 0.3
|
||||
28.02.2025 @shalenikol release 0.4 blenderproc 2.8.0 + blender 4.2.1 LTS
|
||||
"""
|
||||
import numpy as np
|
||||
import argparse
|
||||
|
@ -16,17 +17,32 @@ import os
|
|||
import shutil
|
||||
import json
|
||||
from pathlib import Path
|
||||
import time
|
||||
|
||||
###########################
|
||||
# !!! чтобы избежать ошибки в версии 2.8.0
|
||||
# free(): invalid pointer
|
||||
# при вызове bproc.writer.write_bop
|
||||
import pyrender
|
||||
from pyrender.platforms import egl
|
||||
###########################
|
||||
|
||||
start_time = time.time() # Запоминаем время начала
|
||||
|
||||
import bpy
|
||||
|
||||
VHACD_PATH = "blenderproc_resources/vhacd"
|
||||
DIR_MODELS = "models"
|
||||
DIR_MESH = "assets/libs/objects/"
|
||||
# DIR_MESH = "assets/libs/objects/"
|
||||
FILE_LOG_SCENE = "res.txt"
|
||||
FILE_RBS_INFO = "rbs_info.json"
|
||||
FILE_GT_COCO = "scene_gt_coco.json"
|
||||
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_IMAGE_TYPES = ["Base Color", "Metallic", "Normal", "Roughness", "Specular IOR Level"]
|
||||
|
||||
Not_Categories_Name = True # наименование категории в COCO-аннотации отсутствует
|
||||
|
||||
|
@ -57,19 +73,50 @@ def convert2relative(height, width, bbox):
|
|||
y += h/2
|
||||
return x/width, y/height, w/width, h/height
|
||||
|
||||
def convert_seconds(total_seconds):
|
||||
hours = int(total_seconds // 3600)
|
||||
minutes = int((total_seconds % 3600) // 60)
|
||||
seconds = int(total_seconds % 60)
|
||||
return f"{hours:02}:{minutes:02}:{seconds:02}"
|
||||
|
||||
def render() -> int:
|
||||
res_dir = rnd_par.output_dir
|
||||
log_dir = os.path.dirname(res_dir)
|
||||
# copy file with randomization params
|
||||
file_params = os.path.join(res_dir, FILE_PARAMS)
|
||||
if os.path.isfile(file_params):
|
||||
shutil.copy2(file_params, log_dir)
|
||||
|
||||
if os.path.isdir(res_dir):
|
||||
shutil.rmtree(res_dir)
|
||||
|
||||
i = 0
|
||||
for obj in all_meshs:
|
||||
# Make the object actively participate in the physics simulation
|
||||
obj.enable_rigidbody(active=True, collision_shape="COMPOUND")
|
||||
# Also use convex decomposition as collision shapes
|
||||
obj.build_convex_decomposition_collision_shape(VHACD_PATH)
|
||||
|
||||
# # это для procedural texture, но пока не правильно
|
||||
# fn = (os.path.splitext(rnd_par.models.filenames[i]))[0] + ".jpg" # файл с текстурой
|
||||
# if os.path.isfile(fn):
|
||||
# material = bproc.material.create_material_from_texture(fn, material_name="texture_model"+str(i))
|
||||
# # Применяем текстуру к материалу
|
||||
# obj.replace_materials(material)
|
||||
tex = rnd_par.models.textures[i] # описание текстур
|
||||
if tex["is"]:
|
||||
mat = bproc.material.create("m"+str(i))
|
||||
for x in tex["t_images"]:
|
||||
key = list(x.keys())[0]
|
||||
mat.set_principled_shader_value(key, bpy.data.images.load(filepath=x[key]))
|
||||
obj.replace_materials(mat)
|
||||
|
||||
i += 1
|
||||
# print(f"{i} : {obj.get_name()}")
|
||||
|
||||
objs = all_meshs + rnd_par.scene.objs
|
||||
|
||||
log_txt = os.path.join(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:
|
||||
for i,o in enumerate(objs):
|
||||
loc = o.get_location()
|
||||
|
@ -91,23 +138,21 @@ def render() -> int:
|
|||
rnd_par.image_size_wh[1],
|
||||
lens_unit="FOV")
|
||||
|
||||
# Enable transparency so the background becomes transparent
|
||||
bproc.renderer.set_output_format(enable_transparency=True) # ???
|
||||
# add segmentation masks (per class and per instance)
|
||||
bproc.renderer.enable_segmentation_output(map_by=["category_id", "instance", "name"])
|
||||
|
||||
# activate depth rendering
|
||||
bproc.renderer.enable_depth_output(activate_antialiasing=False)
|
||||
|
||||
# res_dir = os.path.join(rnd_par.output_dir, rnd_par.ds_name)
|
||||
res_dir = rnd_par.output_dir
|
||||
if os.path.isdir(res_dir):
|
||||
shutil.rmtree(res_dir)
|
||||
# Цикл рендеринга
|
||||
# Do multiple times: Position the shapenet objects using the physics simulator and render X images with random camera poses
|
||||
for r in range(rnd_par.n_series):
|
||||
print(f"********** Series : {r+1}")
|
||||
is_texture = True if "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:
|
||||
val = rnd_par.models_randomization["texture_path"]
|
||||
val = rnd_par.models_randomization[PROCEDURAL_TEXTURE]
|
||||
l_texture = _get_list_texture(val)
|
||||
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): # объекты сцены
|
||||
rnd_mat = rnd_par.scene.obj_data[i]["material_randomization"]
|
||||
|
||||
# if PROCEDURAL_TEXTURE in rnd_mat: # путь к текстурам (*.jpg)
|
||||
# mat = bproc.material.create("m"+str(i))
|
||||
# # for x in tex["t_images"]:
|
||||
# # key = list(x.keys())[0]
|
||||
# val = rnd_mat[PROCEDURAL_TEXTURE]
|
||||
# val = _get_list_texture(val)
|
||||
# image = bpy.data.images.load(filepath=str(random.choice(val)))
|
||||
# mat.set_principled_shader_value("Base Color", image)
|
||||
# o.replace_materials(mat)
|
||||
|
||||
mats = o.get_materials() #[0]
|
||||
for mat in mats:
|
||||
|
||||
# with open(log_txt, "a") as fh:
|
||||
# fh.write("************* mat\n")
|
||||
# fh.write(f"{mat}\n")
|
||||
|
||||
val = rnd_mat["specular"]
|
||||
mat.set_principled_shader_value("Specular", 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"]
|
||||
mat.set_principled_shader_value("Roughness", random.uniform(val[0], val[1]))
|
||||
val = rnd_mat["metallic"]
|
||||
mat.set_principled_shader_value("Metallic", random.uniform(val[0], val[1]))
|
||||
if "texture_path" in rnd_mat: # путь к текстурам (*.jpg)
|
||||
val = rnd_mat["texture_path"]
|
||||
if PROCEDURAL_TEXTURE in rnd_mat: # путь к текстурам (*.jpg)
|
||||
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)
|
||||
|
@ -156,7 +217,7 @@ def render() -> int:
|
|||
# Define a function that samples 6-DoF poses
|
||||
def sample_pose(obj: bproc.types.MeshObject):
|
||||
obj.set_location(np.random.uniform(rnd_par.loc_range_low, rnd_par.loc_range_high)) #[-1, -1, 0], [1, 1, 2]))
|
||||
obj.set_rotation_euler(bproc.sampler.uniformSO3())
|
||||
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
|
||||
bproc.object.sample_poses(meshs,
|
||||
|
@ -232,7 +293,12 @@ def render() -> int:
|
|||
rec["name"] = objn
|
||||
rec["model"] = os.path.join(DIR_MODELS, os.path.split(rnd_par.models.filenames[i])[1]) # путь относительный
|
||||
t = [obj.get_bound_box(local_coords=True).tolist() for obj in all_meshs if obj.get_name() == objn]
|
||||
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)
|
||||
shutil.copy2(rnd_par.models.filenames[i], models_dir)
|
||||
f = (os.path.splitext(rnd_par.models.filenames[i]))[0] + ".mtl" # файл материала
|
||||
|
@ -283,9 +349,37 @@ def render() -> int:
|
|||
|
||||
if Not_Categories_Name:
|
||||
explore(res_dir)
|
||||
|
||||
end_time = time.time() # время окончания
|
||||
execution_time = end_time - start_time # время выполнения
|
||||
with open(log_txt, "a") as fh:
|
||||
fh.write("*****************\n")
|
||||
fh.write(f"Время выполнения: {convert_seconds(execution_time)}\n")
|
||||
return 0 # success
|
||||
|
||||
def _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
|
||||
|
||||
par.models = lambda: None
|
||||
|
@ -294,13 +388,14 @@ def _get_models(par, data) -> int:
|
|||
return 0 # no models
|
||||
|
||||
# загрузим объекты
|
||||
par.models.names = [] # obj_names
|
||||
par.models.filenames = [] # obj_filenames
|
||||
par.models.names = []
|
||||
par.models.filenames = []
|
||||
par.models.textures = []
|
||||
i = 1
|
||||
for f in data:
|
||||
nam = f["name"]
|
||||
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)
|
||||
if not os.path.isfile(ff):
|
||||
print(f"Error: no such file '{ff}'")
|
||||
|
@ -311,6 +406,7 @@ def _get_models(par, data) -> int:
|
|||
obj = bproc.loader.load_obj(ff)
|
||||
all_meshs += obj
|
||||
obj[0].set_cp("category_id", i) # начиная с 1
|
||||
set_texture_model(nam, par.models.textures, models_data)
|
||||
i += 1
|
||||
return par.models.n_item
|
||||
|
||||
|
@ -370,8 +466,6 @@ if __name__ == "__main__":
|
|||
print(f"JSon error: {e}")
|
||||
exit(-2)
|
||||
|
||||
# output_dir = args.path
|
||||
|
||||
ds_cfg = cfg["output"] # dataset config
|
||||
generation = ds_cfg["generation"]
|
||||
cam_pos = ds_cfg["camera_position"]
|
||||
|
@ -399,11 +493,14 @@ if __name__ == "__main__":
|
|||
rnd_par.models_randomization = models_randomization
|
||||
rnd_par.loc_range_low = models_randomization["loc_range_low"]
|
||||
rnd_par.loc_range_high = models_randomization["loc_range_high"]
|
||||
rnd_par.around_x = (models_randomization["around_x"] == "True")
|
||||
rnd_par.around_y = (models_randomization["around_y"] == "True")
|
||||
rnd_par.around_z = (models_randomization["around_z"] == "True")
|
||||
|
||||
bproc.init()
|
||||
|
||||
all_meshs = []
|
||||
if _get_models(rnd_par, rnd_par.dataset_objs) <= 0:
|
||||
if _get_models(rnd_par, rnd_par.dataset_objs, models_randomization) <= 0:
|
||||
print("Error: no models in config")
|
||||
exit(-4)
|
||||
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
|
|