Добавлены модули создания проектов сборки и подготовки датасетов
This commit is contained in:
parent
40b9b116c1
commit
f57438b404
173 changed files with 6750 additions and 1857 deletions
315
ui/package-lock.json
generated
315
ui/package-lock.json
generated
|
@ -26,15 +26,22 @@
|
|||
"mobx-react-lite": "^4.0.4",
|
||||
"mobx-store-inheritance": "^1.0.6",
|
||||
"react": "^18.2.0",
|
||||
"react-accessible-treeview": "^2.8.3",
|
||||
"react-dnd": "^16.0.1",
|
||||
"react-dnd-html5-backend": "^16.0.1",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-i18next": "^13.3.1",
|
||||
"react-infinite-scroll-component": "^6.1.0",
|
||||
"react-router-dom": "^6.18.0",
|
||||
"react-scripts": "5.0.1",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"rete-connection-plugin": "^2.0.0",
|
||||
"rete-react-plugin": "^2.0.4",
|
||||
"rete-render-utils": "^2.0.1",
|
||||
"sass": "^1.66.1",
|
||||
"serve": "^14.2.1",
|
||||
"socket.io-client": "^4.7.2",
|
||||
"styled-components": "^6.1.8",
|
||||
"three": "^0.159.0",
|
||||
"three-stdlib": "^2.28.9",
|
||||
"three-transform-controls": "^1.0.4",
|
||||
|
@ -42,7 +49,8 @@
|
|||
"typescript": "^4.9.5",
|
||||
"urdf-loader": "^0.12.1",
|
||||
"uuid": "^9.0.1",
|
||||
"web-vitals": "^2.1.4"
|
||||
"web-vitals": "^2.1.4",
|
||||
"xml-formatter": "^3.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/three": "^0.158.3"
|
||||
|
@ -2503,6 +2511,24 @@
|
|||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/is-prop-valid": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz",
|
||||
"integrity": "sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==",
|
||||
"dependencies": {
|
||||
"@emotion/memoize": "^0.8.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/memoize": {
|
||||
"version": "0.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz",
|
||||
"integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA=="
|
||||
},
|
||||
"node_modules/@emotion/unitless": {
|
||||
"version": "0.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.0.tgz",
|
||||
"integrity": "sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw=="
|
||||
},
|
||||
"node_modules/@eslint-community/eslint-utils": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
|
||||
|
@ -3232,6 +3258,21 @@
|
|||
"react-dom": ">=16.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-dnd/asap": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-5.0.2.tgz",
|
||||
"integrity": "sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A=="
|
||||
},
|
||||
"node_modules/@react-dnd/invariant": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@react-dnd/invariant/-/invariant-4.0.2.tgz",
|
||||
"integrity": "sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw=="
|
||||
},
|
||||
"node_modules/@react-dnd/shallowequal": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-4.0.2.tgz",
|
||||
"integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA=="
|
||||
},
|
||||
"node_modules/@remix-run/router": {
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.0.tgz",
|
||||
|
@ -4048,6 +4089,11 @@
|
|||
"integrity": "sha512-pXNfAD3KHOdif9EQXZ9deK82HVNaXP5ZIF5RP2QG6OQFNTaY2YIetfrE9t528vEreGQvEPRDDc8muaoYeK0SxQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/stylis": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.0.tgz",
|
||||
"integrity": "sha512-n4sx2bqL0mW1tvDf/loQ+aMX7GQD3lc3fkCMC55VFNDu/vBOabO+LTIeXKM14xK0ppk5TUGcWRjiSpIlUpghKw=="
|
||||
},
|
||||
"node_modules/@types/testing-library__jest-dom": {
|
||||
"version": "5.14.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.9.tgz",
|
||||
|
@ -5635,6 +5681,14 @@
|
|||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/camelize": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz",
|
||||
"integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-api": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz",
|
||||
|
@ -6244,6 +6298,14 @@
|
|||
"postcss": "^8.4"
|
||||
}
|
||||
},
|
||||
"node_modules/css-color-keywords": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz",
|
||||
"integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/css-declaration-sorter": {
|
||||
"version": "6.4.1",
|
||||
"resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz",
|
||||
|
@ -6434,6 +6496,16 @@
|
|||
"resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz",
|
||||
"integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w=="
|
||||
},
|
||||
"node_modules/css-to-react-native": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz",
|
||||
"integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==",
|
||||
"dependencies": {
|
||||
"camelize": "^1.0.0",
|
||||
"css-color-keywords": "^1.0.0",
|
||||
"postcss-value-parser": "^4.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/css-tree": {
|
||||
"version": "1.0.0-alpha.37",
|
||||
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz",
|
||||
|
@ -6882,9 +6954,9 @@
|
|||
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
|
||||
"integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw=="
|
||||
},
|
||||
"node_modules/diff-sequences": {
|
||||
"node_modules/diff-Sequence": {
|
||||
"version": "27.5.1",
|
||||
"resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz",
|
||||
"resolved": "https://registry.npmjs.org/diff-Sequence/-/diff-Sequence-27.5.1.tgz",
|
||||
"integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==",
|
||||
"engines": {
|
||||
"node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
|
||||
|
@ -6906,6 +6978,16 @@
|
|||
"resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
|
||||
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="
|
||||
},
|
||||
"node_modules/dnd-core": {
|
||||
"version": "16.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-16.0.1.tgz",
|
||||
"integrity": "sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng==",
|
||||
"dependencies": {
|
||||
"@react-dnd/asap": "^5.0.1",
|
||||
"@react-dnd/invariant": "^4.0.1",
|
||||
"redux": "^4.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/dns-packet": {
|
||||
"version": "5.6.1",
|
||||
"resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz",
|
||||
|
@ -8980,7 +9062,6 @@
|
|||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
|
||||
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"react-is": "^16.7.0"
|
||||
}
|
||||
|
@ -10154,7 +10235,7 @@
|
|||
"integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==",
|
||||
"dependencies": {
|
||||
"chalk": "^4.0.0",
|
||||
"diff-sequences": "^27.5.1",
|
||||
"diff-Sequence": "^27.5.1",
|
||||
"jest-get-type": "^27.5.1",
|
||||
"pretty-format": "^27.5.1"
|
||||
},
|
||||
|
@ -14375,6 +14456,17 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-accessible-treeview": {
|
||||
"version": "2.8.3",
|
||||
"resolved": "https://registry.npmjs.org/react-accessible-treeview/-/react-accessible-treeview-2.8.3.tgz",
|
||||
"integrity": "sha512-taDTIYZ6p96/zIhJBUKvyGTXcInudatP/9fwKG0BW+VRf1PmU5hOT2FkDovDKzSwj2VSOj1PRx+E6ojhOA+2xA==",
|
||||
"peerDependencies": {
|
||||
"classnames": "^2.2.6",
|
||||
"prop-types": "^15.7.2",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-app-polyfill": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/react-app-polyfill/-/react-app-polyfill-3.0.0.tgz",
|
||||
|
@ -14438,6 +14530,43 @@
|
|||
"node": ">= 12.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-dnd": {
|
||||
"version": "16.0.1",
|
||||
"resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-16.0.1.tgz",
|
||||
"integrity": "sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==",
|
||||
"dependencies": {
|
||||
"@react-dnd/invariant": "^4.0.1",
|
||||
"@react-dnd/shallowequal": "^4.0.1",
|
||||
"dnd-core": "^16.0.1",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"hoist-non-react-statics": "^3.3.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/hoist-non-react-statics": ">= 3.3.1",
|
||||
"@types/node": ">= 12",
|
||||
"@types/react": ">= 16",
|
||||
"react": ">= 16.14"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/hoist-non-react-statics": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/node": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-dnd-html5-backend": {
|
||||
"version": "16.0.1",
|
||||
"resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-16.0.1.tgz",
|
||||
"integrity": "sha512-Wu3dw5aDJmOGw8WjH1I1/yTH+vlXEL4vmjk5p+MHxP8HuHJS1lAGeIdG/hze1AvNeXWo/JgULV87LyQOr+r5jw==",
|
||||
"dependencies": {
|
||||
"dnd-core": "^16.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/react-dom": {
|
||||
"version": "18.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
|
||||
|
@ -14671,6 +14800,14 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/redux": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz",
|
||||
"integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.9.2"
|
||||
}
|
||||
},
|
||||
"node_modules/reflect-metadata": {
|
||||
"version": "0.1.14",
|
||||
"resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz",
|
||||
|
@ -14953,6 +15090,69 @@
|
|||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/rete": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/rete/-/rete-2.0.3.tgz",
|
||||
"integrity": "sha512-/xzcyEBhVXhMZVZHElnYaLKOmTEuwlnul9Wfjvxw5sdl/+6Nqn2nyqIaW4koefrFpIWZy9aitnjnP3zeCMVDuw==",
|
||||
"hasInstallScript": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/rete-area-plugin": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rete-area-plugin/-/rete-area-plugin-2.0.2.tgz",
|
||||
"integrity": "sha512-pMRNRl5jNFwsEcaIWvaHgB9QV4CrqfGipEPpxPrbS1oQXezDJ18sbELU68WceJ534OMAcu/9XiN2VLpGbRWoog==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.21.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"rete": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/rete-connection-plugin": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/rete-connection-plugin/-/rete-connection-plugin-2.0.1.tgz",
|
||||
"integrity": "sha512-KE1IcjeOQtHgkByODtWS5hgRJDGhR3Z9sZyJAEd7YMgI6o+KUIflcNjbkvhJvPeIAv6WlEAh7ZkwdLhF9bkr4w==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.21.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"rete": "^2.0.1",
|
||||
"rete-area-plugin": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/rete-react-plugin": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/rete-react-plugin/-/rete-react-plugin-2.0.5.tgz",
|
||||
"integrity": "sha512-xoui2+Mv6iqpRTxccAu3MZv3+l5LYk4AmtqGWEqlCIwZjplrsAoVeOLYq235spwf+vd3ujzapnycEzYF9aj3cA==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.21.0",
|
||||
"usehooks-ts": "^2.9.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.6 || ^17 || ^18",
|
||||
"react-dom": "^16.8.6 || ^17 || ^18",
|
||||
"rete": "^2.0.1",
|
||||
"rete-area-plugin": "^2.0.0",
|
||||
"rete-render-utils": "^2.0.0",
|
||||
"styled-components": "^5.3.6 || ^6"
|
||||
}
|
||||
},
|
||||
"node_modules/rete-render-utils": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/rete-render-utils/-/rete-render-utils-2.0.2.tgz",
|
||||
"integrity": "sha512-f4kj+dFL5QrebOkjCdwi8htHteDFbKyqrVdFDToEUvGuGod1sdLeKxOPBOhwyYDB4Zxd3Cq84I93vD2etrTL9g==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.21.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"rete": "^2.0.0",
|
||||
"rete-area-plugin": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/retry": {
|
||||
"version": "0.13.1",
|
||||
"resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
|
||||
|
@ -15535,8 +15735,7 @@
|
|||
"node_modules/shallowequal": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
|
||||
"integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==",
|
||||
"peer": true
|
||||
"integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ=="
|
||||
},
|
||||
"node_modules/shebang-command": {
|
||||
"version": "2.0.0",
|
||||
|
@ -16137,6 +16336,70 @@
|
|||
"webpack": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/styled-components": {
|
||||
"version": "6.1.8",
|
||||
"resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.8.tgz",
|
||||
"integrity": "sha512-PQ6Dn+QxlWyEGCKDS71NGsXoVLKfE1c3vApkvDYS5KAK+V8fNWGhbSUEo9Gg2iaID2tjLXegEW3bZDUGpofRWw==",
|
||||
"dependencies": {
|
||||
"@emotion/is-prop-valid": "1.2.1",
|
||||
"@emotion/unitless": "0.8.0",
|
||||
"@types/stylis": "4.2.0",
|
||||
"css-to-react-native": "3.2.0",
|
||||
"csstype": "3.1.2",
|
||||
"postcss": "8.4.31",
|
||||
"shallowequal": "1.1.0",
|
||||
"stylis": "4.3.1",
|
||||
"tslib": "2.5.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/styled-components"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">= 16.8.0",
|
||||
"react-dom": ">= 16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/styled-components/node_modules/csstype": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz",
|
||||
"integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ=="
|
||||
},
|
||||
"node_modules/styled-components/node_modules/postcss": {
|
||||
"version": "8.4.31",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
|
||||
"integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/postcss/"
|
||||
},
|
||||
{
|
||||
"type": "tidelift",
|
||||
"url": "https://tidelift.com/funding/github/npm/postcss"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.6",
|
||||
"picocolors": "^1.0.0",
|
||||
"source-map-js": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/styled-components/node_modules/tslib": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz",
|
||||
"integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg=="
|
||||
},
|
||||
"node_modules/stylehacks": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz",
|
||||
|
@ -16152,6 +16415,11 @@
|
|||
"postcss": "^8.2.15"
|
||||
}
|
||||
},
|
||||
"node_modules/stylis": {
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.1.tgz",
|
||||
"integrity": "sha512-EQepAV+wMsIaGVGX1RECzgrcqRRU/0sYOHkeLsZ3fzHaHXZy4DaOOX0vOlGQdlsjkh3mFHAIlVimpwAs4dslyQ=="
|
||||
},
|
||||
"node_modules/sucrase": {
|
||||
"version": "3.35.0",
|
||||
"resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz",
|
||||
|
@ -17104,6 +17372,20 @@
|
|||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/usehooks-ts": {
|
||||
"version": "2.16.0",
|
||||
"resolved": "https://registry.npmjs.org/usehooks-ts/-/usehooks-ts-2.16.0.tgz",
|
||||
"integrity": "sha512-bez95WqYujxp6hFdM/CpRDiVPirZPxlMzOH2QB8yopoKQMXpscyZoxOjpEdaxvV+CAWUDSM62cWnqHE0E/MZ7w==",
|
||||
"dependencies": {
|
||||
"lodash.debounce": "^4.0.8"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.15.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17 || ^18"
|
||||
}
|
||||
},
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
|
@ -18164,11 +18446,30 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/xml-formatter": {
|
||||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/xml-formatter/-/xml-formatter-3.6.2.tgz",
|
||||
"integrity": "sha512-enWhevZNOwffZFUhzl1WMcha8lFLZUgJ7NzFs5Ug4ZOFCoNheGYXz1J9Iz/e+cTn9rCkuT1GwTacz+YlmFHOGw==",
|
||||
"dependencies": {
|
||||
"xml-parser-xo": "^4.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/xml-name-validator": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz",
|
||||
"integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw=="
|
||||
},
|
||||
"node_modules/xml-parser-xo": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/xml-parser-xo/-/xml-parser-xo-4.1.1.tgz",
|
||||
"integrity": "sha512-Ggf2y90+Y6e9IK5hoPuembVHJ03PhDSdhldEmgzbihzu9k0XBo0sfcFxaSi4W1PlUSSI1ok+MJ0JCXUn+U4Ilw==",
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/xmlchars": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
|
||||
|
|
|
@ -44,7 +44,8 @@
|
|||
"typescript": "^4.9.5",
|
||||
"urdf-loader": "^0.12.1",
|
||||
"uuid": "^9.0.1",
|
||||
"web-vitals": "^2.1.4"
|
||||
"web-vitals": "^2.1.4",
|
||||
"xml-formatter": "^3.6.2"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "react-scripts start",
|
||||
|
|
|
@ -17,5 +17,13 @@
|
|||
<div id="root"></div>
|
||||
|
||||
</body>
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap')
|
||||
</style>
|
||||
<style>
|
||||
[contenteditable]:focus {
|
||||
outline: 0px solid transparent;
|
||||
}
|
||||
</style>
|
||||
|
||||
</html>
|
|
@ -1,12 +0,0 @@
|
|||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools -->
|
||||
<svg width="64px" height="64px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
|
||||
<g id="SVGRepo_bgCarrier" stroke-width="0"/>
|
||||
|
||||
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
|
||||
<g id="SVGRepo_iconCarrier"> <path fill-rule="evenodd" clip-rule="evenodd" d="M2 5.75C2 5.33579 2.33579 5 2.75 5H20.75C21.1642 5 21.5 5.33579 21.5 5.75C21.5 6.16421 21.1642 6.5 20.75 6.5H2.75C2.33579 6.5 2 6.16421 2 5.75ZM2 9.75C2 9.33579 2.33579 9 2.75 9H20.75C21.1642 9 21.5 9.33579 21.5 9.75C21.5 10.1642 21.1642 10.5 20.75 10.5H2.75C2.33579 10.5 2 10.1642 2 9.75ZM20.2113 12.6586C20.5379 12.9134 20.5961 13.3847 20.3414 13.7113L16.4414 18.7113C16.3022 18.8897 16.0899 18.9958 15.8636 18.9999C15.6373 19.004 15.4213 18.9057 15.2757 18.7324L13.1757 16.2324C12.9093 15.9152 12.9504 15.4421 13.2676 15.1757C13.5848 14.9093 14.0579 14.9504 14.3243 15.2676L15.8284 17.0582L19.1586 12.7887C19.4134 12.4621 19.8847 12.4039 20.2113 12.6586ZM2 13.75C2 13.3358 2.33579 13 2.75 13H9.75C10.1642 13 10.5 13.3358 10.5 13.75C10.5 14.1642 10.1642 14.5 9.75 14.5H2.75C2.33579 14.5 2 14.1642 2 13.75ZM2 17.75C2 17.3358 2.33579 17 2.75 17H9.75C10.1642 17 10.5 17.3358 10.5 17.75C10.5 18.1642 10.1642 18.5 9.75 18.5H2.75C2.33579 18.5 2 18.1642 2 17.75Z" fill="#f46036"/> </g>
|
||||
|
||||
</svg>
|
Before Width: | Height: | Size: 1.4 KiB |
BIN
ui/src/core/assets/images/pose_estemation.jpg
Normal file
BIN
ui/src/core/assets/images/pose_estemation.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
|
@ -51,4 +51,10 @@ export const ArrayExtensions = () => {
|
|||
return this.indexOf(element) !== -1;
|
||||
};
|
||||
}
|
||||
if ([].repeat === undefined) {
|
||||
// eslint-disable-next-line no-extend-native
|
||||
Array.prototype.repeat = function (quantity) {
|
||||
return Array(quantity).fill(this[0]);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
@ -4,8 +4,12 @@ import { NumberExtensions } from "./number";
|
|||
import { StringExtensions } from "./string";
|
||||
|
||||
export type CallBackVoidFunction = <T>(value: T) => void;
|
||||
|
||||
export type CallBackStringVoidFunction = (value: string) => void;
|
||||
export type CallBackEventTarget = (value: EventTarget) => void;
|
||||
export type OptionalProperties<T> = {
|
||||
[P in keyof T]?: T[P];
|
||||
};
|
||||
|
||||
declare global {
|
||||
interface Array<T> {
|
||||
|
@ -15,22 +19,36 @@ declare global {
|
|||
isEmpty(): boolean;
|
||||
isNotEmpty(): boolean;
|
||||
hasIncludeElement(element: T): boolean;
|
||||
repeat(quantity: number): Array<T>;
|
||||
}
|
||||
interface Number {
|
||||
fromArray(): number[];
|
||||
toPx(): string;
|
||||
unixFromDate(): string;
|
||||
isValid(str: string): boolean;
|
||||
randRange(min:number,max:number):number
|
||||
}
|
||||
|
||||
interface String {
|
||||
isEmpty(): boolean;
|
||||
isNotEmpty(): boolean;
|
||||
replaceMany(searchValues: string[], replaceValue: string): string;
|
||||
isEqual(str: string): boolean;
|
||||
isEqualMany(str: string[]): boolean;
|
||||
}
|
||||
interface Map<K, V> {
|
||||
addValueOrMakeCallback(key: K, value: V, callBack: CallBackVoidFunction): void;
|
||||
getKeyFromValueIsExists(value: V): K | undefined;
|
||||
overrideValue(key: K, value: OptionalProperties<V>): void;
|
||||
keysToJson(): string;
|
||||
toArray(): V[];
|
||||
getPredicateValue(callBack: (value: V) => boolean): K[];
|
||||
}
|
||||
interface Vector3 {}
|
||||
}
|
||||
export const extensions = () => {
|
||||
ArrayExtensions();
|
||||
StringExtensions();
|
||||
ArrayExtensions();
|
||||
NumberExtensions();
|
||||
MapExtensions();
|
||||
};
|
||||
|
|
|
@ -11,5 +11,51 @@ export const MapExtensions = () => {
|
|||
}
|
||||
};
|
||||
}
|
||||
if (Map.prototype.getKeyFromValueIsExists === undefined) {
|
||||
// eslint-disable-next-line no-extend-native
|
||||
Map.prototype.getKeyFromValueIsExists = function (value) {
|
||||
let result;
|
||||
this.forEach((el, key) => {
|
||||
if (el === value) {
|
||||
result = key;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
};
|
||||
}
|
||||
if (Map.prototype.overrideValue === undefined) {
|
||||
// eslint-disable-next-line no-extend-native
|
||||
Map.prototype.overrideValue = function (key, value) {
|
||||
const result = this.get(key);
|
||||
|
||||
this.set(key, Object.assign(result, value));
|
||||
};
|
||||
}
|
||||
if (Map.prototype.keysToJson === undefined) {
|
||||
// eslint-disable-next-line no-extend-native
|
||||
Map.prototype.keysToJson = function () {
|
||||
const result: any[] = [];
|
||||
this.forEach((el) => result.push(el));
|
||||
return JSON.stringify(result);
|
||||
};
|
||||
}
|
||||
if (Map.prototype.toArray === undefined) {
|
||||
// eslint-disable-next-line no-extend-native
|
||||
Map.prototype.toArray = function () {
|
||||
return Array.from(this.values());
|
||||
};
|
||||
}
|
||||
if (Map.prototype.getPredicateValue === undefined) {
|
||||
// eslint-disable-next-line no-extend-native
|
||||
Map.prototype.getPredicateValue = function (callBack) {
|
||||
const result: any[] = [];
|
||||
this.forEach((el, key) => {
|
||||
const callBackExecute = callBack(el);
|
||||
if (callBackExecute) {
|
||||
result.push(key);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
};
|
||||
}
|
||||
};
|
||||
Object();
|
||||
|
|
|
@ -5,4 +5,29 @@ export const NumberExtensions = () => {
|
|||
return Array.from(this.toString()).map((el) => Number(el));
|
||||
};
|
||||
}
|
||||
if (Number().toPx === undefined) {
|
||||
// eslint-disable-next-line no-extend-native
|
||||
Number.prototype.toPx = function () {
|
||||
return String(this) + "px";
|
||||
};
|
||||
}
|
||||
if (Number().unixFromDate === undefined) {
|
||||
// eslint-disable-next-line no-extend-native
|
||||
Number.prototype.unixFromDate = function () {
|
||||
const date = new Date(Number(this) * 1000);
|
||||
return `${date.getUTCFullYear()}.${date.getMonth()}.${date.getDay()} ${date.getHours()}:${date.getMinutes()}`;
|
||||
};
|
||||
}
|
||||
if (Number().isValid === undefined) {
|
||||
// eslint-disable-next-line no-extend-native
|
||||
Number.prototype.isValid = function (str: string) {
|
||||
return !isNaN(Number(str));
|
||||
};
|
||||
}
|
||||
if(Number().randRange === undefined){
|
||||
// eslint-disable-next-line no-extend-native
|
||||
Number.prototype.randRange = function (min,max) {
|
||||
return Math.random() * (max - min) + min;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
/* eslint-disable no-extend-native */
|
||||
export const StringExtensions = () => {
|
||||
if ("".isEmpty === undefined) {
|
||||
// eslint-disable-next-line no-extend-native
|
||||
|
@ -11,4 +12,28 @@ export const StringExtensions = () => {
|
|||
return this.length !== 0;
|
||||
};
|
||||
}
|
||||
if ("".replaceMany === undefined) {
|
||||
String.prototype.replaceMany = function (searchValues: string[], replaceValue: string) {
|
||||
let result = this as string;
|
||||
searchValues.forEach((el) => {
|
||||
result = result.replaceAll(el, replaceValue);
|
||||
});
|
||||
return result;
|
||||
};
|
||||
}
|
||||
if ("".isEqual === undefined) {
|
||||
String.prototype.isEqual = function (str: string) {
|
||||
return this === str;
|
||||
};
|
||||
}
|
||||
if ("".isEqualMany === undefined) {
|
||||
String.prototype.isEqualMany = function (str: string[]) {
|
||||
for (const el of str) {
|
||||
if (el === this) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
@ -105,6 +105,9 @@ export class Asset {
|
|||
centerMassY: string;
|
||||
@IsString()
|
||||
centerMassZ: string;
|
||||
@IsArray()
|
||||
@IsOptional()
|
||||
actions: string[];
|
||||
}
|
||||
|
||||
export class Physics {
|
||||
|
@ -116,13 +119,22 @@ export class Physics {
|
|||
|
||||
export class RobossemblerAssets {
|
||||
@ValidateNested()
|
||||
@Type(() => Asset)
|
||||
@Type(() => Asset, {
|
||||
discriminator: {
|
||||
property: "type",
|
||||
subTypes: [
|
||||
{ value: InstanceRgbCamera, name: InstanceType.RGB_CAMERA },
|
||||
{ value: SceneSimpleObject, name: InstanceType.SCENE_SIMPLE_OBJECT },
|
||||
],
|
||||
},
|
||||
keepDiscriminatorProperty: true,
|
||||
})
|
||||
assets: Asset[];
|
||||
|
||||
@IsArray()
|
||||
@Type(() => Instance, {
|
||||
discriminator: {
|
||||
property: "instanceType",
|
||||
property: "type",
|
||||
subTypes: [
|
||||
{ value: InstanceRgbCamera, name: InstanceType.RGB_CAMERA },
|
||||
{ value: SceneSimpleObject, name: InstanceType.SCENE_SIMPLE_OBJECT },
|
|
@ -21,9 +21,7 @@ import {
|
|||
Quaternion,
|
||||
MeshBasicMaterial,
|
||||
PlaneGeometry,
|
||||
BoxGeometry,
|
||||
BufferGeometry,
|
||||
Line,
|
||||
BoxGeometry
|
||||
} from "three";
|
||||
import { TypedEvent } from "../helper/typed_event";
|
||||
import { Result } from "../helper/result";
|
||||
|
@ -35,13 +33,8 @@ import {
|
|||
} from "../../features/scene_manager/model/scene_assets";
|
||||
import { SceneMode } from "../../features/scene_manager/model/scene_view";
|
||||
import { throttle } from "../helper/throttle";
|
||||
import {
|
||||
InstanceRgbCamera,
|
||||
RobossemblerAssets,
|
||||
SceneSimpleObject,
|
||||
} from "../../features/scene_manager/model/robossembler_assets";
|
||||
import { CoreVector3 } from "../model/core_vector3";
|
||||
|
||||
import { Asset, InstanceRgbCamera, RobossemblerAssets, SceneSimpleObject } from "../model/robossembler_assets";
|
||||
|
||||
export enum UserData {
|
||||
selectedObject = "selected_object",
|
||||
cameraInitialization = "camera_initialization",
|
||||
|
@ -157,6 +150,15 @@ export class CoreThreeRepository extends TypedEvent<BaseSceneItemModel> {
|
|||
}
|
||||
});
|
||||
}
|
||||
loadInstance(asset: Asset, loadCallback?: Function) {
|
||||
this.loader(
|
||||
asset.meshPath,
|
||||
loadCallback ? loadCallback : () => {},
|
||||
asset.name,
|
||||
new Vector3(Number(asset.posX), Number(asset.posY), Number(asset.posZ)),
|
||||
new Quaternion(0, 0, 0, 0)
|
||||
);
|
||||
}
|
||||
|
||||
setTransformMode(mode?: SceneMode) {
|
||||
switch (mode) {
|
||||
|
|
|
@ -4,6 +4,8 @@ import { Result } from "../helper/result";
|
|||
export enum HttpMethod {
|
||||
GET = "GET",
|
||||
POST = "POST",
|
||||
DELETE = "DELETE",
|
||||
PUT = "PUT"
|
||||
}
|
||||
export class HttpError extends Error {
|
||||
status: number;
|
||||
|
|
|
@ -1,14 +1,30 @@
|
|||
import { Socket, io } from "socket.io-client";
|
||||
import { Result } from "../helper/result";
|
||||
import { TypedEvent } from "../helper/typed_event";
|
||||
|
||||
export class SocketRepository {
|
||||
export class SocketRepository extends TypedEvent<any> {
|
||||
serverURL = "ws://localhost:4001";
|
||||
socket: Socket | undefined;
|
||||
async connect() {
|
||||
|
||||
async connect():Promise<Result<boolean, boolean>> {
|
||||
const socket = io(this.serverURL);
|
||||
this.socket = socket;
|
||||
socket.connect();
|
||||
socket.on('realtime', (d) =>{
|
||||
console.log("D")
|
||||
console.log(d)
|
||||
|
||||
this.emit({
|
||||
event:"realtime",
|
||||
payload:d
|
||||
})
|
||||
})
|
||||
if(socket.connected){
|
||||
return Result.ok(true)
|
||||
}
|
||||
return Result.error(false)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const socketRepository = new SocketRepository()
|
|
@ -4,28 +4,23 @@ import {
|
|||
PipelineInstanceScreen,
|
||||
PipelineInstanceScreenPath,
|
||||
} from "../../features/pipeline_instance_main_screen/pipeline_instance_screen";
|
||||
import {
|
||||
SelectProjectScreen,
|
||||
SelectProjectScreenPath,
|
||||
} from "../../features/select_project/presentation/select_project";
|
||||
import {
|
||||
CreatePipelineScreen,
|
||||
CreatePipelineScreenPath,
|
||||
} from "../../features/create_pipeline/presentation/create_pipeline_screen";
|
||||
|
||||
import { CreateProjectScreen, CreateProjectScreenPath } from "../../features/create_project/create_project_screen";
|
||||
import {
|
||||
CreateTriggerScreenPath,
|
||||
TriggerScreen,
|
||||
} from "../../features/create_trigger/presentation/create_trigger_screen";
|
||||
import {
|
||||
CreateProcessScreen,
|
||||
CreateProcessScreenPath,
|
||||
} from "../../features/create_process/presentation/create_process_screen";
|
||||
import {
|
||||
CreateProjectInstancePath,
|
||||
CreateProjectInstanceScreen,
|
||||
} from "../../features/create_project_instance/create_project_instance";
|
||||
|
||||
import { SceneManger, SceneManagerPath } from "../../features/scene_manager/presentation/scene_manager";
|
||||
import {
|
||||
BehaviorTreeBuilderPath,
|
||||
BehaviorTreeBuilderScreen,
|
||||
} from "../../features/behavior_tree_builder/presentation/behavior_tree_builder_screen";
|
||||
import {
|
||||
StickObjectsMarkingScreen,
|
||||
StickObjectsMarkingScreenPath,
|
||||
} from "../../features/_stick_objects_marking/stick_objects_marking_screen";
|
||||
import { DataSetScreen, DatasetsScreenPath } from "../../features/dataset/dataset_screen";
|
||||
import DetailsScreen, { DetailsScreenPath } from "../../features/details/details_screen";
|
||||
import { AssemblesScreen, AssemblesScreenPath } from "../../features/assembles/assembles_screen";
|
||||
import SimulationScreen, { SimulationScreenPath } from "../../features/simulations/simulations_screen";
|
||||
import { EstimateScreen, EstimateScreenPath } from "../../features/estimate/estimate_screen";
|
||||
|
||||
const idURL = ":id";
|
||||
|
||||
|
@ -38,32 +33,43 @@ export const router = createBrowserRouter([
|
|||
path: PipelineInstanceScreenPath + idURL,
|
||||
element: <PipelineInstanceScreen />,
|
||||
},
|
||||
{
|
||||
path: SelectProjectScreenPath,
|
||||
element: <SelectProjectScreen />,
|
||||
},
|
||||
{
|
||||
path: CreatePipelineScreenPath,
|
||||
element: <CreatePipelineScreen />,
|
||||
},
|
||||
|
||||
{
|
||||
path: CreateProjectScreenPath,
|
||||
element: <CreateProjectScreen />,
|
||||
},
|
||||
|
||||
{
|
||||
path: CreateTriggerScreenPath,
|
||||
element: <TriggerScreen />,
|
||||
},
|
||||
{
|
||||
path: CreateProcessScreenPath,
|
||||
element: <CreateProcessScreen />,
|
||||
},
|
||||
{
|
||||
path: CreateProjectInstancePath + idURL,
|
||||
element: <CreateProjectInstanceScreen />,
|
||||
},
|
||||
{
|
||||
path: SceneManagerPath + idURL,
|
||||
path: SceneManagerPath,
|
||||
element: <SceneManger />,
|
||||
},
|
||||
{
|
||||
path: BehaviorTreeBuilderPath,
|
||||
element: <BehaviorTreeBuilderScreen />,
|
||||
},
|
||||
{
|
||||
path: StickObjectsMarkingScreenPath,
|
||||
element: <StickObjectsMarkingScreen />,
|
||||
},
|
||||
{
|
||||
path: DatasetsScreenPath,
|
||||
element: <DataSetScreen />,
|
||||
},
|
||||
{
|
||||
path: DetailsScreenPath,
|
||||
element: <DetailsScreen />,
|
||||
},
|
||||
{
|
||||
path: AssemblesScreenPath,
|
||||
element: <AssemblesScreen />,
|
||||
},
|
||||
{
|
||||
path: SimulationScreenPath,
|
||||
element: <SimulationScreen />,
|
||||
},
|
||||
|
||||
{
|
||||
path: EstimateScreenPath,
|
||||
element: <EstimateScreen />,
|
||||
},
|
||||
]);
|
||||
|
|
33
ui/src/core/ui/button/button.tsx
Normal file
33
ui/src/core/ui/button/button.tsx
Normal file
|
@ -0,0 +1,33 @@
|
|||
import * as React from "react";
|
||||
import { CoreText, CoreTextType } from "../text/text";
|
||||
|
||||
export interface IButtonProps {
|
||||
block?: boolean;
|
||||
filled?: boolean;
|
||||
text?: string;
|
||||
onClick?: any;
|
||||
style?:React.CSSProperties
|
||||
}
|
||||
|
||||
export function CoreButton(props: IButtonProps) {
|
||||
return (
|
||||
<div
|
||||
onClick={() => props.onClick?.call()}
|
||||
style={Object.assign({
|
||||
backgroundColor: props.filled ? "rgba(103, 80, 164, 1)" : "",
|
||||
paddingRight: 20,
|
||||
paddingLeft: 20,
|
||||
paddingTop: 10,
|
||||
paddingBottom: 10,
|
||||
borderRadius: 24,
|
||||
border: props.block ? "1px solid rgba(29, 27, 32, 0.12)" : props.filled ? "" : "1px solid black",
|
||||
},props.style)}
|
||||
>
|
||||
<CoreText
|
||||
text={props.text ?? ""}
|
||||
type={CoreTextType.medium}
|
||||
color={props.block ? "#1D1B20" : props.filled ? "white" : "rgba(103, 80, 164, 1)"}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
155
ui/src/core/ui/form_builder/form_builder.tsx
Normal file
155
ui/src/core/ui/form_builder/form_builder.tsx
Normal file
|
@ -0,0 +1,155 @@
|
|||
import * as React from "react";
|
||||
import { FormViewModel, InputBuilderViewModel, InputType } from "./form_view_model";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { FormBuilderStore } from "./form_builder_store";
|
||||
import { FormBuilderValidationModel } from "../../../features/dataset/dataset_model";
|
||||
import { SelectCore } from "../select/select";
|
||||
import { CoreInput } from "../input/input";
|
||||
import { Icon } from "../icons/icons";
|
||||
import { CoreText, CoreTextType } from "../text/text";
|
||||
|
||||
export interface IFormBuilder {
|
||||
formBuilder: FormBuilderValidationModel;
|
||||
onChange: (change: FormBuilderValidationModel) => void;
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
{store.isError ? (
|
||||
<>Error</>
|
||||
) : (
|
||||
<div>
|
||||
{store.formViewModel?.inputs.map((element) => {
|
||||
if (element.type.isEqual(InputType.ENUM)) {
|
||||
const values = element.values as string[];
|
||||
return (
|
||||
<SelectCore
|
||||
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 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>
|
||||
|
||||
{subArray.map((subSubArrayItem: InputBuilderViewModel, subIndex: number) => {
|
||||
if (subSubArrayItem.type.isEqual(InputType.ENUM)) {
|
||||
return (
|
||||
<>
|
||||
<SelectCore
|
||||
items={subSubArrayItem.values?.map((el) => String(el)) ?? []}
|
||||
value={subSubArrayItem.totalValue ?? subSubArrayItem.defaultValue}
|
||||
onChange={(value) => store.changeTotalSubValue(element.id, subIndex, value)}
|
||||
label={element.name}
|
||||
style={{ margin: 5 }}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
if (subSubArrayItem.type.isEqualMany([InputType.NUMBER, InputType.STRING]))
|
||||
return (
|
||||
<div>
|
||||
<CoreInput
|
||||
style={{ margin: 5 }}
|
||||
onChange={(e) => {
|
||||
store.changeTotalSubValue(element.id, subIndex, e);
|
||||
}}
|
||||
validation={
|
||||
subSubArrayItem.type.isEqual(InputType.NUMBER)
|
||||
? (el) => Number().isValid(el)
|
||||
: undefined
|
||||
}
|
||||
error="только числа"
|
||||
value={subSubArrayItem.totalValue ?? subSubArrayItem.defaultValue}
|
||||
label={subSubArrayItem.name}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
return <>Error</>;
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
})
|
||||
: null}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
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>
|
||||
);
|
||||
|
||||
return <>Error</>;
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
});
|
101
ui/src/core/ui/form_builder/form_builder_store.ts
Normal file
101
ui/src/core/ui/form_builder/form_builder_store.ts
Normal file
|
@ -0,0 +1,101 @@
|
|||
import { makeAutoObservable } from "mobx";
|
||||
import { FormViewModel } from "./form_view_model";
|
||||
import { TypedEvent } from "../../helper/typed_event";
|
||||
import { FormBuilderValidationModel } from "../../../features/dataset/dataset_model";
|
||||
|
||||
export class ChangerForm extends TypedEvent<FormBuilderValidationModel | undefined> {}
|
||||
|
||||
export class FormBuilderStore {
|
||||
isError = false;
|
||||
formViewModel?: FormViewModel;
|
||||
changerForm: ChangerForm;
|
||||
numberValidation: RegExp = new RegExp(/^\s*[+-]?(\d+|\d*\.\d+|\d+\.\d*)([Ee][+-]?\d+)?\s*$/);
|
||||
constructor() {
|
||||
makeAutoObservable(this);
|
||||
this.changerForm = new ChangerForm();
|
||||
}
|
||||
|
||||
changeTotalSubValue(id: string, subIndex: number, value: string) {
|
||||
if (this.formViewModel?.inputs) {
|
||||
this.formViewModel.inputs = this.formViewModel?.inputs.map((el) => {
|
||||
if (!el.id.isEqual(id)) {
|
||||
return el;
|
||||
} else {
|
||||
if (el.totalValue instanceof Array) {
|
||||
el.totalValue = el.totalValue.map((subElement) => {
|
||||
if (subElement instanceof Array) {
|
||||
subElement.map((subSubElement, i) => {
|
||||
if (subIndex !== i) {
|
||||
return subSubElement;
|
||||
}
|
||||
subSubElement.totalValue = value;
|
||||
return subSubElement;
|
||||
});
|
||||
}
|
||||
return subElement;
|
||||
});
|
||||
return el;
|
||||
}
|
||||
return el;
|
||||
}
|
||||
});
|
||||
|
||||
this.changerForm.emit(this.formViewModel?.fromFormBuilderValidationModel());
|
||||
}
|
||||
}
|
||||
|
||||
changeTotalValue(id: string, value: string) {
|
||||
if (this.formViewModel?.inputs)
|
||||
this.formViewModel.inputs = this.formViewModel?.inputs.map((el) => {
|
||||
if (!el.id.isEqual(id)) {
|
||||
return el;
|
||||
}
|
||||
el.totalValue = value;
|
||||
return el;
|
||||
});
|
||||
this.changerForm.emit(this.formViewModel?.fromFormBuilderValidationModel());
|
||||
}
|
||||
|
||||
deleteTotalValueSubItem(id: string, index: number): void {
|
||||
if (this.formViewModel?.inputs)
|
||||
this.formViewModel.inputs = this.formViewModel?.inputs.map((el) => {
|
||||
if (!el.id.isEqual(id)) {
|
||||
return el;
|
||||
} else {
|
||||
if (el.totalValue instanceof Array) {
|
||||
el.totalValue = el.totalValue.filter((_, i) => index !== i);
|
||||
return el;
|
||||
}
|
||||
return el;
|
||||
}
|
||||
});
|
||||
this.changerForm.emit(this.formViewModel?.fromFormBuilderValidationModel());
|
||||
}
|
||||
|
||||
open = (id: string) => {
|
||||
if (this.formViewModel)
|
||||
this.formViewModel.inputs = this.formViewModel?.inputs.map((el) => {
|
||||
if (!el.id.isEqual(id)) {
|
||||
return el;
|
||||
}
|
||||
|
||||
el.isOpen = true;
|
||||
|
||||
if (!(el.totalValue instanceof Array)) {
|
||||
el.totalValue = [];
|
||||
}
|
||||
el.totalValue.push(el.values);
|
||||
return el;
|
||||
});
|
||||
this.changerForm.emit(this.formViewModel?.fromFormBuilderValidationModel());
|
||||
};
|
||||
|
||||
init(context: string, result: string) {
|
||||
FormViewModel.fromString(result, context).fold(
|
||||
(model) => {
|
||||
this.formViewModel = model;
|
||||
},
|
||||
() => (this.isError = true)
|
||||
);
|
||||
}
|
||||
}
|
241
ui/src/core/ui/form_builder/form_view_model.ts
Normal file
241
ui/src/core/ui/form_builder/form_view_model.ts
Normal file
|
@ -0,0 +1,241 @@
|
|||
import { makeAutoObservable, observable } from "mobx";
|
||||
import { Result } from "../../helper/result";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { FormBuilderValidationModel } from "../../../features/dataset/dataset_model";
|
||||
|
||||
export enum InputType {
|
||||
NUMBER = "number",
|
||||
STRING = "string",
|
||||
ARRAY = "Array",
|
||||
ENUM = "Enum",
|
||||
}
|
||||
|
||||
interface IOperation {
|
||||
regExp: RegExp;
|
||||
result: any;
|
||||
}
|
||||
|
||||
export class InputBuilderViewModel {
|
||||
id: string;
|
||||
constructor(
|
||||
public name: string,
|
||||
public type: InputType,
|
||||
public defaultValue: string,
|
||||
public values: string[] | undefined | InputBuilderViewModel[] = undefined,
|
||||
public totalValue: any | undefined = undefined,
|
||||
public isOpen: boolean = false,
|
||||
public subType: string | undefined = undefined,
|
||||
id: string | undefined = undefined
|
||||
) {
|
||||
this.id = id ?? uuidv4();
|
||||
}
|
||||
static fromJSON(json: any) {
|
||||
try {
|
||||
const value = JSON.parse(json);
|
||||
return new InputBuilderViewModel(
|
||||
value.name,
|
||||
value.type,
|
||||
value.defaultValue,
|
||||
value.values,
|
||||
value.totalValue,
|
||||
value.isOpen,
|
||||
value.subType,
|
||||
value.id
|
||||
);
|
||||
} catch (error) {
|
||||
console.log("InputBuilderViewModel.fromJSON(): " + json);
|
||||
throw new Error("InputBuilderViewModel.fromJSON");
|
||||
}
|
||||
}
|
||||
public toJson(): string {
|
||||
try {
|
||||
return JSON.stringify(this);
|
||||
} catch (error) {
|
||||
console.log("InputBuilderViewModel.toJson(): " + this.id);
|
||||
console.log(error);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const tagParse = new RegExp(/\${.*?}/gm);
|
||||
export const bracketParser = new RegExp(/<.*?>/gm);
|
||||
export const enumParser = new RegExp(/ENUM.*=..*?;/gm);
|
||||
export const enumBodyParse = new RegExp(/".*?;/gm);
|
||||
export const enumNameParse = new RegExp(/^(.*?)=/gm);
|
||||
export const typeBodyParse = new RegExp(/{.*?};/gms);
|
||||
export const typeNameParse = new RegExp(/type .*?{/gms);
|
||||
export const typeParse = new RegExp(/type.*?};/gms);
|
||||
|
||||
export class FormViewModel {
|
||||
@observable
|
||||
inputs: InputBuilderViewModel[];
|
||||
|
||||
constructor(inputs: InputBuilderViewModel[], public result: string, public context: string) {
|
||||
this.inputs = inputs;
|
||||
makeAutoObservable(this);
|
||||
}
|
||||
public json() {
|
||||
const result = this.toResult();
|
||||
if (result.isEmpty()) {
|
||||
console.log("result is Empty error");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
return JSON.parse(result.replaceAll("\n", "").replaceAll("\\", "").replaceAll("/", ""));
|
||||
} catch (error) {
|
||||
console.log("ERROR: FormViewModel json() " + result);
|
||||
}
|
||||
}
|
||||
public fromFormBuilderValidationModel() {
|
||||
return new FormBuilderValidationModel(
|
||||
this.context,
|
||||
this.result,
|
||||
this.inputs.map((el) => el.toJson()),
|
||||
this.json() as any
|
||||
);
|
||||
}
|
||||
public toResult(): string {
|
||||
const operations: IOperation[] = [];
|
||||
|
||||
this.inputs.forEach((element) => {
|
||||
let inputResult = element.totalValue ?? element.defaultValue;
|
||||
if (element.type.isEqualMany([InputType.STRING, InputType.ENUM])) {
|
||||
inputResult = `"${String(inputResult)}"`;
|
||||
}
|
||||
if (element.type.isEqual(InputType.NUMBER)) {
|
||||
inputResult = Number(inputResult);
|
||||
}
|
||||
if (element.type.isEqual(InputType.ARRAY)) {
|
||||
if (element.totalValue === undefined) {
|
||||
inputResult = "[]";
|
||||
} else {
|
||||
inputResult = [];
|
||||
if (element.totalValue instanceof Array) {
|
||||
element.totalValue.forEach((el) => {
|
||||
const objectUnion = {};
|
||||
let objectMapperResult = "";
|
||||
if (el instanceof Array)
|
||||
el.forEach((subElement) => {
|
||||
let subResult = subElement.totalValue ?? subElement.defaultValue;
|
||||
console.log(subResult);
|
||||
if (subElement.type.isEqualMany([InputType.STRING, InputType.ENUM])) {
|
||||
subResult = `"${String(subResult)}"`;
|
||||
}
|
||||
if (subElement.type.isEqual(InputType.NUMBER)) {
|
||||
subResult = Number(subResult);
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
objectUnion[subElement.name] = subResult;
|
||||
});
|
||||
if (Object.keys(objectUnion).length !== 0) {
|
||||
if (element.subType) {
|
||||
objectMapperResult = this.getTypeBody(element.subType);
|
||||
if (objectMapperResult !== undefined) {
|
||||
Object.entries(objectUnion).forEach(([key, value]) => {
|
||||
objectMapperResult = objectMapperResult.replace(new RegExp("\\${" + key + ".*?}"), value as any);
|
||||
});
|
||||
}
|
||||
}
|
||||
if (objectMapperResult)
|
||||
inputResult.push(
|
||||
objectMapperResult.replaceAll("\n", "").replaceAll("\\", "").replaceAll("/", "").replaceAll(";", "")
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
if (inputResult instanceof Array) inputResult = JSON.stringify(inputResult.map((el) => JSON.parse(el)));
|
||||
operations.push({ regExp: new RegExp("\\${" + element.name + ".*?}"), result: inputResult });
|
||||
});
|
||||
|
||||
let result = this.result;
|
||||
|
||||
operations.forEach((el) => {
|
||||
result = result.replace(el.regExp, el.result);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
getTypeBody(subType: string): string {
|
||||
let result;
|
||||
this.context.match(typeParse)?.forEach((type) => {
|
||||
const matchTypeName = type.match(typeNameParse)?.at(0)?.split(" ").at(1);
|
||||
if (matchTypeName?.isEqual(subType)) {
|
||||
result = type.match(typeBodyParse)?.at(0);
|
||||
}
|
||||
});
|
||||
return result as unknown as string;
|
||||
}
|
||||
static fromString(result: string, context: string): Result<void, FormViewModel> {
|
||||
try {
|
||||
const enums = new Map<string, string[]>();
|
||||
const types = new Map<string, InputBuilderViewModel[]>();
|
||||
|
||||
context.match(enumParser)?.forEach((el) => {
|
||||
const enumMatch = el.match(enumBodyParse)?.at(0);
|
||||
if (enumMatch !== undefined) {
|
||||
const EnumValue = enumMatch.slice(0, enumMatch.length - 2).split(",");
|
||||
const enumBody = EnumValue.map((el) => el.replaceAll('"', ""));
|
||||
const enumName = el.match(enumNameParse)?.at(0)?.split(" ").at(1);
|
||||
if (enumBody.isNotEmpty() && enumName) enums.set(enumName, enumBody);
|
||||
}
|
||||
});
|
||||
|
||||
context.match(typeParse)?.forEach((type) => {
|
||||
const matchTypeName = type.match(typeNameParse)?.at(0)?.split(" ").at(1);
|
||||
const typeBody = type.match(typeBodyParse)?.at(0);
|
||||
|
||||
if (typeBody && matchTypeName) types.set(matchTypeName, this.typeParse(typeBody, enums));
|
||||
});
|
||||
|
||||
return Result.ok(new FormViewModel(this.typeParse(result, enums, types), result, context));
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return Result.error(undefined);
|
||||
}
|
||||
}
|
||||
static typeParse(
|
||||
result: string,
|
||||
enums: Map<string, string[]>,
|
||||
types: Map<string, InputBuilderViewModel[]> | undefined = undefined
|
||||
) {
|
||||
return result
|
||||
.match(tagParse)
|
||||
?.map((el) => {
|
||||
const inputArray = el.replaceMany(["$", "{", "}"], "").split(":");
|
||||
if (el.includes(InputType.ENUM)) {
|
||||
const enumName = inputArray.at(1)?.replaceMany([InputType.ENUM, "<", ">"], "");
|
||||
|
||||
if (enumName && enums.get(enumName)) {
|
||||
return new InputBuilderViewModel(inputArray[0], InputType.ENUM, inputArray[2], enums.get(enumName));
|
||||
}
|
||||
}
|
||||
if (el.includes(InputType.ARRAY) && types) {
|
||||
const name = inputArray.at(1)?.replaceMany(["Array<", ">"], "");
|
||||
|
||||
if (name) {
|
||||
return new InputBuilderViewModel(
|
||||
inputArray[0],
|
||||
InputType.ARRAY,
|
||||
inputArray[2],
|
||||
types.get(name),
|
||||
undefined,
|
||||
false,
|
||||
name
|
||||
);
|
||||
}
|
||||
}
|
||||
if (el.includes(InputType.NUMBER)) {
|
||||
return new InputBuilderViewModel(inputArray[0], InputType.NUMBER, inputArray[2]);
|
||||
}
|
||||
if (el.includes(InputType.STRING)) {
|
||||
return new InputBuilderViewModel(inputArray[0], InputType.STRING, inputArray[2]);
|
||||
}
|
||||
return el;
|
||||
})
|
||||
.filter((el) => el !== undefined) as InputBuilderViewModel[];
|
||||
}
|
||||
}
|
33
ui/src/core/ui/form_builder/readme.md
Normal file
33
ui/src/core/ui/form_builder/readme.md
Normal file
|
@ -0,0 +1,33 @@
|
|||
# RESULT
|
||||
|
||||
```
|
||||
{
|
||||
"scene":{
|
||||
"objects": \${OBJECTS_SCENE:Array<OBJECTS_SCENE>:[]},
|
||||
},
|
||||
"camera_position":{
|
||||
"center_shell": [\${CENTER_SHELL_1:string:0}, \${COLISION_SHAPE:Enum<L>:"BOX"}]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# CONTEXT
|
||||
|
||||
### хранит в себе type и enum для result
|
||||
|
||||
```
|
||||
ENUM T = "ObjectDetection","PoseEstimation";
|
||||
ENUM L = "POINT","SUN";
|
||||
|
||||
type MODELS = {
|
||||
"id": \${IMAGE_FORMAT:Enum<L>:"jpg"},,
|
||||
"name": \${NAME:string:""},
|
||||
"model": \${MODEL:string:"models/1.fbx"}
|
||||
};
|
||||
|
||||
type OBJECTS_SCENE = {
|
||||
"name": \${NAME:string:""},
|
||||
"collision_shape": \${COLISION_SHAPE:Enum<L>:"BOX"},
|
||||
"loc_xyz": [\${TEST123:string:"test123"}, \${LOC_XYZ_2:number:0}, \${LOC_XYZ_3:number:0}],
|
||||
};
|
||||
```
|
334
ui/src/core/ui/icons/icons.tsx
Normal file
334
ui/src/core/ui/icons/icons.tsx
Normal file
|
@ -0,0 +1,334 @@
|
|||
import * as React from "react";
|
||||
import { Result } from "../../helper/result";
|
||||
|
||||
export interface IIconsProps {
|
||||
type: string;
|
||||
style?: React.CSSProperties;
|
||||
onClick?: Function;
|
||||
}
|
||||
|
||||
export function Icon(props: IIconsProps) {
|
||||
const icon = getIconSvg(props.type);
|
||||
return icon.fold(
|
||||
(node) => {
|
||||
return (
|
||||
<div
|
||||
onClick={() => {
|
||||
if (props.onClick) props.onClick();
|
||||
}}
|
||||
style={props.style}
|
||||
>
|
||||
{node}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
() => (
|
||||
<div
|
||||
onClick={() => {
|
||||
if (props.onClick) props.onClick();
|
||||
}}
|
||||
style={props.style}
|
||||
>
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M6 12C9.31371 12 12 9.31371 12 6C12 2.68629 9.31371 0 6 0C2.68629 0 0 2.68629 0 6C0 9.31371 2.68629 12 6 12Z"
|
||||
fill="#49454F"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
const getIconSvg = (type: string): Result<undefined, React.JSX.Element> => {
|
||||
switch (type) {
|
||||
case "":
|
||||
return Result.ok();
|
||||
case "Setting":
|
||||
return Result.ok(
|
||||
<svg width="13" height="13" viewBox="0 0 13 13" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_68_595)">
|
||||
<path
|
||||
d="M4.85647 2.08936L5.25576 1.05864C5.3231 0.884129 5.44158 0.734023 5.59566 0.627981C5.74976 0.52194 5.93228 0.464908 6.11933 0.464355H6.88076C7.06781 0.464908 7.25034 0.52194 7.40443 0.627981C7.55852 0.734023 7.67699 0.884129 7.74433 1.05864L8.14362 2.08936L9.49929 2.86936L10.595 2.70221C10.7775 2.67745 10.9632 2.70748 11.1286 2.7885C11.2939 2.86951 11.4314 2.99785 11.5236 3.15721L11.895 3.80721C11.9902 3.96911 12.0341 4.15606 12.0208 4.34339C12.0076 4.53072 11.9378 4.70963 11.8207 4.8565L11.1429 5.72007V7.28007L11.8393 8.14364C11.9564 8.29051 12.0261 8.46942 12.0394 8.65675C12.0527 8.84408 12.0088 9.03103 11.9136 9.19293L11.5422 9.84293C11.4499 10.0023 11.3124 10.1306 11.1471 10.2117C10.9818 10.2926 10.796 10.3227 10.6136 10.2979L9.51786 10.1308L8.16219 10.9108L7.7629 11.9415C7.69556 12.116 7.57709 12.2661 7.423 12.3722C7.26891 12.4782 7.08638 12.5352 6.89933 12.5358H6.11933C5.93228 12.5352 5.74976 12.4782 5.59566 12.3722C5.44158 12.2661 5.3231 12.116 5.25576 11.9415L4.85647 10.9108L3.50076 10.1308L2.40504 10.2979C2.2226 10.3227 2.0369 10.2926 1.87156 10.2117C1.70623 10.1306 1.56871 10.0023 1.47647 9.84293L1.10504 9.19293C1.00987 9.03103 0.966019 8.84408 0.979279 8.65675C0.992539 8.46942 1.06229 8.29051 1.17933 8.14364L1.85719 7.28007V5.72007L1.16076 4.8565C1.04372 4.70963 0.973968 4.53072 0.960708 4.34339C0.947448 4.15606 0.991295 3.96911 1.08647 3.80721L1.4579 3.15721C1.55014 2.99785 1.68766 2.86951 1.85299 2.7885C2.01833 2.70748 2.20403 2.67745 2.38647 2.70221L3.48219 2.86936L4.85647 2.08936ZM4.6429 6.50007C4.6429 6.86738 4.75182 7.22644 4.95589 7.53184C5.15996 7.83725 5.45 8.07528 5.78934 8.21585C6.1287 8.3564 6.50211 8.39319 6.86235 8.32153C7.2226 8.24987 7.55352 8.073 7.81325 7.81326C8.07297 7.55354 8.24984 7.22263 8.3215 6.86238C8.39316 6.50213 8.35639 6.12872 8.21582 5.78937C8.07526 5.45002 7.83723 5.15997 7.53182 4.95591C7.22641 4.75185 6.86735 4.64293 6.50004 4.64293C6.0075 4.64293 5.53513 4.83859 5.18685 5.18687C4.83857 5.53515 4.6429 6.00753 4.6429 6.50007Z"
|
||||
stroke="#373737"
|
||||
stroke-width="1.3"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_68_595">
|
||||
<rect width="13" height="13" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
case "Assembly":
|
||||
return Result.ok(
|
||||
<svg width="13" height="13" viewBox="0 0 13 13" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M1.84615 1H4.38462C4.60903 1 4.82425 1.08915 4.98294 1.24783C5.14162 1.40652 5.23077 1.62174 5.23077 1.84615V9.88462C5.23077 10.4456 5.0079 10.9837 4.61119 11.3804C4.21448 11.7771 3.67642 12 3.11538 12C2.83759 12 2.56252 11.9453 2.30586 11.839C2.04921 11.7327 1.81601 11.5768 1.61958 11.3804C1.22287 10.9837 1 10.4456 1 9.88462V1.84615C1 1.62174 1.08915 1.40652 1.24783 1.24783C1.40652 1.08915 1.62174 1 1.84615 1Z"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M5.23079 4.80777L8.19233 1.82931C8.35086 1.67171 8.56532 1.58325 8.78887 1.58325C9.01241 1.58325 9.2269 1.67171 9.38539 1.82931L11.1708 3.62315C11.3284 3.78169 11.4168 3.99615 11.4168 4.21969C11.4168 4.44323 11.3284 4.65769 11.1708 4.81623L4.6131 11.3824"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M8.19231 7.76904H11.1538C11.3782 7.76904 11.5935 7.85819 11.7522 8.01687C11.9108 8.17556 12 8.39078 12 8.6152V11.1537C12 11.3781 11.9108 11.5933 11.7522 11.752C11.5935 11.9106 11.3782 11.9998 11.1538 11.9998H3.11539"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path d="M1 4.38477H5.23077" stroke="#373737" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M1 7.76904H5.23077" stroke="#373737" stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
);
|
||||
case "Datasets":
|
||||
return Result.ok(
|
||||
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.03125 7.5271L3.38477 10" stroke="#373737" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M11.6087 5L10.8444 7.5" stroke="#373737" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M6.75 6.75732L8 8" stroke="#373737" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path
|
||||
d="M13 5H11C10.4477 5 10 4.55228 10 4V2C10 1.44771 10.4477 1 11 1H13C13.5523 1 14 1.44771 14 2V4C14 4.55228 13.5523 5 13 5Z"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M11 11.5H9C8.44772 11.5 8 11.0523 8 10.5V8.5C8 7.94772 8.44772 7.5 9 7.5H11C11.5523 7.5 12 7.94771 12 8.5V10.5C12 11.0523 11.5523 11.5 11 11.5Z"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M5.75 7.5271H3.75C3.19772 7.5271 2.75 7.07938 2.75 6.5271V4.5271C2.75 3.97481 3.19772 3.5271 3.75 3.5271H5.75C6.30228 3.5271 6.75 3.97481 6.75 4.5271V6.5271C6.75 7.07938 6.30228 7.5271 5.75 7.5271Z"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M4 14H2C1.44771 14 1 13.5523 1 13V11C1 10.4477 1.44771 10 2 10H4C4.55228 10 5 10.4477 5 11V13C5 13.5523 4.55228 14 4 14Z"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
case "Layers":
|
||||
return Result.ok(
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M6.6306 6.16821C6.51077 6.22024 6.38154 6.24708 6.25092 6.24708C6.12028 6.24708 5.99105 6.22024 5.87124 6.16821L1.2666 4.03554C1.19183 3.99783 1.12899 3.9401 1.08509 3.86878C1.04119 3.79747 1.01794 3.71537 1.01794 3.63162C1.01794 3.54787 1.04119 3.46577 1.08509 3.39446C1.12899 3.32315 1.19183 3.26542 1.2666 3.22771L5.87124 1.07888C5.99105 1.02685 6.12028 1 6.25092 1C6.38154 1 6.51077 1.02685 6.6306 1.07888L11.2352 3.21155C11.31 3.24927 11.3729 3.30699 11.4167 3.3783C11.4607 3.44962 11.4839 3.53172 11.4839 3.61546C11.4839 3.69921 11.4607 3.78131 11.4167 3.85263C11.3729 3.92394 11.31 3.98167 11.2352 4.01938L6.6306 6.16821Z"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M11.5018 6.53174L6.57403 8.80176C6.46878 8.84974 6.35445 8.87462 6.23878 8.87462C6.1231 8.87462 6.00877 8.84974 5.90353 8.80176L1 6.53174"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M11.5018 9.15723L6.57403 11.4272C6.46878 11.4752 6.35445 11.5001 6.23878 11.5001C6.1231 11.5001 6.00877 11.4752 5.90353 11.4272L1 9.15723"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
case "Rocket":
|
||||
return Result.ok(
|
||||
<svg width="12" height="13" viewBox="0 0 12 13" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M4.93241 4.22177C3.33247 3.11335 1.84086 3.95947 0.590912 5.2625L3.35747 6.95474"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M8.24897 7.58936C9.34058 9.21388 8.50727 10.7284 7.22401 11.9976L5.5574 9.18849"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M3.35748 6.94646L5.56573 9.18868C7.33233 8.1141 9.34058 7.07337 10.2572 6.10879C12.2405 4.09502 11.0905 1.33667 11.0905 1.33667C11.0905 1.33667 8.37396 0.169022 6.3907 2.18279C5.44074 3.11352 4.40744 5.16114 3.35748 6.94646Z"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path d="M4.57413 4.82251L7.65734 7.95316" stroke="#373737" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path
|
||||
d="M8.92393 3.95965C9.154 3.95965 9.34058 3.77023 9.34058 3.53659C9.34058 3.30294 9.154 3.11353 8.92393 3.11353C8.69385 3.11353 8.50728 3.30294 8.50728 3.53659C8.50728 3.77023 8.69385 3.95965 8.92393 3.95965Z"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M3.24081 11.1008C2.7575 11.5746 0.590912 11.9977 0.590912 11.9977C0.590912 11.9977 1.00756 9.79774 1.47421 9.30699C1.58864 9.18236 1.72664 9.08235 1.87996 9.01295C2.03329 8.94358 2.19881 8.90629 2.36664 8.90327C2.53447 8.90027 2.70118 8.93162 2.85683 8.99545C3.01246 9.05925 3.15385 9.15427 3.27254 9.27484C3.39123 9.39533 3.4848 9.53891 3.54767 9.69697C3.61054 9.85494 3.64141 10.0242 3.63845 10.1947C3.63549 10.3651 3.59876 10.5331 3.53043 10.6888C3.46212 10.8445 3.36361 10.9846 3.24081 11.1008Z"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
case "Simulation":
|
||||
return Result.ok(
|
||||
<svg width="13" height="13" viewBox="0 0 13 13" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M3.61364 1H1.87121C1.39005 1 1 1.39606 1 1.88462V3.65385C1 4.1424 1.39005 4.53846 1.87121 4.53846H3.61364C4.09479 4.53846 4.48485 4.1424 4.48485 3.65385V1.88462C4.48485 1.39606 4.09479 1 3.61364 1Z"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M3.61364 8.96143H1.87121C1.39005 8.96143 1 9.35749 1 9.84604V11.6153C1 12.1038 1.39005 12.4999 1.87121 12.4999H3.61364C4.09479 12.4999 4.48485 12.1038 4.48485 11.6153V9.84604C4.48485 9.35749 4.09479 8.96143 3.61364 8.96143Z"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M11.4545 8.96143H9.71212C9.23097 8.96143 8.84091 9.35749 8.84091 9.84604V11.6153C8.84091 12.1038 9.23097 12.4999 9.71212 12.4999H11.4545C11.9357 12.4999 12.3258 12.1038 12.3258 11.6153V9.84604C12.3258 9.35749 11.9357 8.96143 11.4545 8.96143Z"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path d="M2.74243 8.96165V4.53857" stroke="#373737" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path
|
||||
d="M10.5833 8.96159V4.53852C10.5833 4.06929 10.3998 3.61928 10.073 3.28749C9.74618 2.95568 9.30299 2.76929 8.8409 2.76929H6.66287"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M8.4053 1L6.66287 2.76923L8.4053 4.53846"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
case "Grade":
|
||||
return Result.ok(
|
||||
<svg width="13" height="13" viewBox="0 0 13 13" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M10.6786 0.464355H11.6071C11.8534 0.464355 12.0896 0.562187 12.2637 0.736328C12.4378 0.910469 12.5357 1.14665 12.5357 1.39293V2.3215"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M0.464279 2.3215V1.39293C0.464279 1.14665 0.562111 0.910469 0.736251 0.736328C0.910393 0.562187 1.14657 0.464355 1.39285 0.464355H2.32142"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path d="M5.10713 0.464355H7.89285" stroke="#373737" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M12.5357 5.10718V7.89289" stroke="#373737" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M0.464279 5.10718V7.89289" stroke="#373737" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path
|
||||
d="M10.6786 12.5359H11.6071C11.8534 12.5359 12.0896 12.438 12.2637 12.2639C12.4378 12.0898 12.5357 11.8535 12.5357 11.6073V10.6787"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M0.464279 10.6787V11.6073C0.464279 11.8535 0.562111 12.0898 0.736251 12.2639C0.910393 12.438 1.14657 12.5359 1.39285 12.5359H2.32142"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path d="M5.10713 12.5359H7.89285" stroke="#373737" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path
|
||||
d="M8.35714 3.71436H4.64285C4.13002 3.71436 3.71428 4.1301 3.71428 4.64293V8.35721C3.71428 8.87004 4.13002 9.28578 4.64285 9.28578H8.35714C8.86997 9.28578 9.28571 8.87004 9.28571 8.35721V4.64293C9.28571 4.1301 8.86997 3.71436 8.35714 3.71436Z"
|
||||
stroke="#373737"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
case "DeleteCircle":
|
||||
return Result.ok(
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M10 0C4.47 0 0 4.47 0 10C0 15.53 4.47 20 10 20C15.53 20 20 15.53 20 10C20 4.47 15.53 0 10 0ZM10 18C5.59 18 2 14.41 2 10C2 5.59 5.59 2 10 2C14.41 2 18 5.59 18 10C18 14.41 14.41 18 10 18ZM10 8.59L13.59 5L15 6.41L11.41 10L15 13.59L13.59 15L10 11.41L6.41 15L5 13.59L8.59 10L5 6.41L6.41 5L10 8.59Z"
|
||||
fill="#49454F"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
case "Check":
|
||||
return Result.ok(
|
||||
<svg width="12" height="10" viewBox="0 0 12 10" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4 9.4L0 5.4L1.4 4L4 6.6L10.6 0L12 1.4L4 9.4Z" fill="white" />
|
||||
</svg>
|
||||
);
|
||||
case "PlusCircle":
|
||||
return Result.ok(
|
||||
<svg width="33" height="33" viewBox="0 0 33 33" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M16.5003 1.32007C8.09487 1.32007 1.32031 8.09463 1.32031 16.5001C1.32031 24.9055 8.09487 31.6801 16.5003 31.6801C24.9058 31.6801 31.6803 24.9055 31.6803 16.5001C31.6803 8.09463 24.9058 1.32007 16.5003 1.32007ZM16.5003 2.64007C24.1989 2.64007 30.3603 8.80151 30.3603 16.5001C30.3603 24.1986 24.1989 30.3601 16.5003 30.3601C8.80176 30.3601 2.64031 24.1986 2.64031 16.5001C2.64031 8.80151 8.80176 2.64007 16.5003 2.64007ZM15.8403 8.58007V15.8401H8.58031V17.1601H15.8403V24.4201H17.1603V17.1601H24.4203V15.8401H17.1603V8.58007H15.8403Z"
|
||||
fill="black"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
case "Pencil":
|
||||
return Result.ok(
|
||||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M16.06 0.589883L17.41 1.93988C18.2 2.71988 18.2 3.98988 17.41 4.76988L4.18 17.9999H0V13.8199L10.4 3.40988L13.23 0.589883C14.01 -0.190117 15.28 -0.190117 16.06 0.589883ZM2 15.9999L3.41 16.0599L13.23 6.22988L11.82 4.81988L2 14.6399V15.9999Z"
|
||||
fill="#31111D"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
case "MenuFab":
|
||||
return Result.ok(
|
||||
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_54850_114)">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M11 16V14L29 14V16L11 16ZM11 21H29V19H11V21ZM11 26H29V24H11V26Z"
|
||||
fill="#49454F"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_54850_114">
|
||||
<rect width="40" height="40" rx="20" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
case "Settings":
|
||||
return Result.ok(
|
||||
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_54841_7268)">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M27.3102 33.03C27.2102 33.71 26.5902 34.25 25.8502 34.25H22.1502C21.4102 34.25 20.7902 33.71 20.7002 32.98L20.4302 31.09C20.1602 30.95 19.9002 30.8 19.6402 30.63L17.8402 31.35C17.1402 31.61 16.3702 31.32 16.0302 30.7L14.2002 27.53C13.8502 26.87 14.0002 26.09 14.5602 25.65L16.0902 24.46C16.0802 24.31 16.0702 24.16 16.0702 24C16.0702 23.85 16.0802 23.69 16.0902 23.54L14.5702 22.35C13.9802 21.9 13.8302 21.09 14.2002 20.47L16.0502 17.28C16.3902 16.66 17.1602 16.38 17.8402 16.65L19.6502 17.38C19.9102 17.21 20.1702 17.06 20.4302 16.92L20.7002 15.01C20.7902 14.31 21.4102 13.76 22.1402 13.76H25.8402C26.5802 13.76 27.2002 14.3 27.2902 15.03L27.5602 16.92C27.8302 17.06 28.0902 17.21 28.3502 17.38L30.1502 16.66C30.8602 16.4 31.6302 16.69 31.9702 17.31L33.8102 20.49C34.1702 21.15 34.0102 21.93 33.4502 22.37L31.9302 23.56C31.9402 23.71 31.9502 23.86 31.9502 24.02C31.9502 24.18 31.9402 24.33 31.9302 24.48L33.4502 25.67C34.0102 26.12 34.1702 26.9 33.8202 27.53L31.9602 30.75C31.6202 31.37 30.8502 31.65 30.1602 31.38L28.3602 30.66C28.1002 30.83 27.8402 30.98 27.5802 31.12L27.3102 33.03ZM22.6202 32.25H25.3802L25.7502 29.7L26.2802 29.48C26.7202 29.3 27.1602 29.04 27.6202 28.7L28.0702 28.36L30.4502 29.32L31.8302 26.92L29.8002 25.34L29.8702 24.78L29.8733 24.7531C29.9023 24.5027 29.9302 24.2607 29.9302 24C29.9302 23.73 29.9002 23.47 29.8702 23.22L29.8002 22.66L31.8302 21.08L30.4402 18.68L28.0502 19.64L27.6002 19.29C27.1802 18.97 26.7302 18.71 26.2702 18.52L25.7502 18.3L25.3802 15.75H22.6202L22.2502 18.3L21.7202 18.51C21.2802 18.7 20.8402 18.95 20.3802 19.3L19.9302 19.63L17.5502 18.68L16.1602 21.07L18.1902 22.65L18.1202 23.21C18.0902 23.47 18.0602 23.74 18.0602 24C18.0602 24.26 18.0802 24.53 18.1202 24.78L18.1902 25.34L16.1602 26.92L17.5402 29.32L19.9302 28.36L20.3802 28.71C20.8102 29.04 21.2402 29.29 21.7102 29.48L22.2402 29.7L22.6202 32.25ZM27.5002 24C27.5002 25.933 25.9332 27.5 24.0002 27.5C22.0672 27.5 20.5002 25.933 20.5002 24C20.5002 22.067 22.0672 20.5 24.0002 20.5C25.9332 20.5 27.5002 22.067 27.5002 24Z"
|
||||
fill="#49454F"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_54841_7268">
|
||||
<rect x="4" y="4" width="40" height="40" rx="20" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
return Result.error(undefined);
|
||||
};
|
78
ui/src/core/ui/input/input.tsx
Normal file
78
ui/src/core/ui/input/input.tsx
Normal file
|
@ -0,0 +1,78 @@
|
|||
import * as React from "react";
|
||||
import { CoreText, CoreTextType } from "../text/text";
|
||||
|
||||
interface IInputProps {
|
||||
label: string;
|
||||
value?: string;
|
||||
onChange?: (value: string) => void;
|
||||
style?: React.CSSProperties;
|
||||
validation?: (value: string) => boolean;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export const CoreInput = (props: IInputProps) => {
|
||||
const [value, setValue] = React.useState<string>(() => props.value ?? "");
|
||||
const ref = React.useRef<HTMLDivElement>(null);
|
||||
const [isAppendInnerText, setAppendInnerText] = React.useState(true);
|
||||
React.useEffect(() => {
|
||||
if (ref.current && isAppendInnerText) {
|
||||
ref.current.innerText = value;
|
||||
setAppendInnerText(false);
|
||||
}
|
||||
}, [ref, value, isAppendInnerText, setAppendInnerText]);
|
||||
|
||||
|
||||
return (
|
||||
<div
|
||||
style={Object.assign(
|
||||
{
|
||||
backgroundColor: "rgba(230, 224, 233, 1)",
|
||||
height: 58,
|
||||
borderRadius: "4px 4px 0px 0px",
|
||||
borderBottom: "solid 1px black",
|
||||
padding: "10px 10px 10px 10px",
|
||||
},
|
||||
props.style
|
||||
)}
|
||||
>
|
||||
<CoreText type={CoreTextType.small} text={props.label} />
|
||||
|
||||
<input
|
||||
defaultValue={props.value}
|
||||
style={{
|
||||
backgroundColor: "#00008000",
|
||||
border: 1,
|
||||
fontSize: 16,
|
||||
fontFamily: "Roboto",
|
||||
color: "#1D1B20",
|
||||
height: 24,
|
||||
width: "100%",
|
||||
userSelect: 'none',
|
||||
outline:'none'
|
||||
}}
|
||||
onChange={(e) => {
|
||||
const val = e.target.value;
|
||||
setValue(val)
|
||||
if (val) {
|
||||
if (props.validation !== undefined && props.validation(val) && props.onChange) {
|
||||
props.onChange(val);
|
||||
return;
|
||||
}
|
||||
|
||||
if (props.onChange && props.validation === undefined) {
|
||||
props.onChange(val);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{value ? (
|
||||
props.validation ? (
|
||||
props.validation(value) ? null : (
|
||||
<div style={{ color: "#ff1d0c" }}>{props.error ? props.error : "error"}</div>
|
||||
)
|
||||
) : null
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
};
|
127
ui/src/core/ui/pages/main_page.tsx
Normal file
127
ui/src/core/ui/pages/main_page.tsx
Normal file
|
@ -0,0 +1,127 @@
|
|||
import { DatasetsScreenPath } from "../../../features/dataset/dataset_screen";
|
||||
import { Icon } from "../icons/icons";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { Spin } from "antd";
|
||||
import { LoadingOutlined } from "@ant-design/icons";
|
||||
import React from "react";
|
||||
import { SceneManagerPath } from "../../../features/scene_manager/presentation/scene_manager";
|
||||
import { AssemblesScreenPath } from "../../../features/assembles/assembles_screen";
|
||||
import { DetailsScreenPath } from "../../../features/details/details_screen";
|
||||
import { SimulationScreenPath } from "../../../features/simulations/simulations_screen";
|
||||
import { EstimateScreenPath } from "../../../features/estimate/estimate_screen";
|
||||
import { BehaviorTreeBuilderPath } from "../../../features/behavior_tree_builder/presentation/behavior_tree_builder_screen";
|
||||
export interface IBlockProps {
|
||||
name: string;
|
||||
isActive: boolean;
|
||||
path: string;
|
||||
icon?: string;
|
||||
}
|
||||
const Block = (props: IBlockProps) => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<div onClick={() => navigate(props.path)} style={{ height: 56, cursor: "pointer" }}>
|
||||
<div
|
||||
style={
|
||||
props.isActive
|
||||
? {
|
||||
textAlignLast: "center",
|
||||
height: 32,
|
||||
backgroundColor: "rgba(232, 222, 248, 1)",
|
||||
marginLeft: 5,
|
||||
marginRight: 5,
|
||||
alignContent: "center",
|
||||
borderRadius: 12,
|
||||
}
|
||||
: { textAlignLast: "center", alignContent: "center" }
|
||||
}
|
||||
>
|
||||
<Icon type={props.icon ?? ""} />
|
||||
</div>
|
||||
<div style={{ textAlignLast: "center" }}>{props.name}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
export interface IMainPageProps {
|
||||
page: string;
|
||||
bodyChildren?: JSX.Element;
|
||||
isLoading?: boolean;
|
||||
}
|
||||
export const MainPage = (props: IMainPageProps) => {
|
||||
const blocksNames = [
|
||||
{ name: "Детали", path: DetailsScreenPath, icon: "Setting" },
|
||||
{ name: "Сборки", path: AssemblesScreenPath, icon: "Assembly" },
|
||||
{ name: "Датасеты", path: DatasetsScreenPath, icon: "Datasets" },
|
||||
{ name: "Сцена", path: SceneManagerPath, icon: "Layers" },
|
||||
{ name: "Навыки", path: BehaviorTreeBuilderPath, icon: "Rocket" },
|
||||
{ name: "Симуляция", path: SimulationScreenPath, icon: "Simulation" },
|
||||
{ name: "Оценка", path: EstimateScreenPath, icon: "Grade" },
|
||||
];
|
||||
const blocks: IBlockProps[] = blocksNames
|
||||
.map((el) => {
|
||||
return Object.assign({ isActive: false }, el);
|
||||
})
|
||||
.map((el) => {
|
||||
if (el.name.isEqual(props.page)) {
|
||||
el.isActive = true;
|
||||
return el;
|
||||
}
|
||||
return el;
|
||||
});
|
||||
React.useEffect(() => {
|
||||
document.body.style.overflow = "hidden";
|
||||
return () => {
|
||||
document.body.style.overflow = "scroll";
|
||||
};
|
||||
});
|
||||
return (
|
||||
<div style={{ display: "flex" }}>
|
||||
<div
|
||||
style={{
|
||||
width: 90,
|
||||
height: window.innerHeight,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<div style={{ paddingTop: 43 }}>
|
||||
<div style={{ textAlignLast: "center" }}>
|
||||
<Icon type="MenuFab" />
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
textAlignLast: "center",
|
||||
width: 56,
|
||||
height: 56,
|
||||
borderRadius: 12,
|
||||
backgroundColor: "#ffd9e4",
|
||||
alignContent: "center",
|
||||
}}
|
||||
>
|
||||
<Icon style={{ marginTop: 3 }} type="Pencil" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
{blocks.map((el) => (
|
||||
<Block isActive={el.isActive} name={el.name} path={el.path} icon={el.icon} />
|
||||
))}
|
||||
</div>
|
||||
<div style={{ paddingBottom: 10 }}>
|
||||
<Icon type={"Settings"} />
|
||||
</div>
|
||||
</div>
|
||||
{props.isLoading ? (
|
||||
<div style={{ alignContent: "center", width: "100%", textAlign: "center" }}>
|
||||
<Spin indicator={<LoadingOutlined style={{ fontSize: 68, color: "rgba(103, 80, 164, 1)" }} spin />} />
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div style={{ width: 241, height: window.innerHeight, backgroundColor: "#F7F2FA", borderRadius: 16 }}> </div>
|
||||
{props.bodyChildren}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
67
ui/src/core/ui/select/select.tsx
Normal file
67
ui/src/core/ui/select/select.tsx
Normal file
|
@ -0,0 +1,67 @@
|
|||
import React from "react";
|
||||
import { CoreText, CoreTextType } from "../text/text";
|
||||
|
||||
interface ISelectCoreProps {
|
||||
items: string[];
|
||||
value: string;
|
||||
label: string;
|
||||
onChange: (value: string) => void;
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
export const SelectCore = (props: ISelectCoreProps) => {
|
||||
const ref = React.useRef<HTMLDivElement>(null);
|
||||
const [cursorIsCorses, setCursorIsCorses] = React.useState(false);
|
||||
const [value, setValue] = React.useState(props.value);
|
||||
React.useEffect(() => {
|
||||
ref.current?.addEventListener("mousemove", () => {
|
||||
setCursorIsCorses(true);
|
||||
});
|
||||
ref.current?.addEventListener("mouseleave", () => {
|
||||
setCursorIsCorses(false);
|
||||
});
|
||||
}, [ref, setCursorIsCorses]);
|
||||
|
||||
return (
|
||||
<div ref={ref} style={props.style}>
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: "rgba(230, 224, 233, 1)",
|
||||
height: 58,
|
||||
borderRadius: "4px 4px 0px 0px",
|
||||
borderBottom: "solid 1px black",
|
||||
padding: "10px 10px 10px 10px",
|
||||
}}
|
||||
>
|
||||
<CoreText type={CoreTextType.small} text={props.label} />
|
||||
<div style={{ fontSize: 16, fontFamily: "Roboto", color: "#1D1B20", height: 24 }}>{value}</div>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: "rgba(243, 237, 247, 1)",
|
||||
boxShadow: "0px 1px 2px rgba(0, 0, 0, 0.3), 0px 2px 6px 2px rgba(0, 0, 0, 0.15)",
|
||||
borderRadius: 4,
|
||||
}}
|
||||
>
|
||||
{cursorIsCorses
|
||||
? props.items.map((el) => (
|
||||
<div
|
||||
onClick={() => {
|
||||
setValue(el);
|
||||
props.onChange(el);
|
||||
}}
|
||||
style={{
|
||||
height: 48,
|
||||
textAlign: "center",
|
||||
alignContent: "center",
|
||||
cursor: "pointer",
|
||||
borderBottom: "1px solid",
|
||||
}}
|
||||
>
|
||||
{el}
|
||||
</div>
|
||||
))
|
||||
: null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
19
ui/src/core/ui/switch/switch.tsx
Normal file
19
ui/src/core/ui/switch/switch.tsx
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { Icon } from "../icons/icons";
|
||||
|
||||
interface ISwitchProps {
|
||||
isSelected: boolean;
|
||||
id: string;
|
||||
onChange: (status: boolean, id: string) => void;
|
||||
}
|
||||
export const CoreSwitch = (props: ISwitchProps) => {
|
||||
return (
|
||||
<div
|
||||
style={{ height: 40, width: 40, borderRadius: 2, alignContent: "center", cursor: "pointer" }}
|
||||
onClick={() => props.onChange(props.isSelected, props.id)}
|
||||
>
|
||||
<div style={{ backgroundColor: "rgba(104, 80, 164, 1)", width: 20, height: 20, textAlign: "center" }}>
|
||||
{props.isSelected ? <Icon type={"Check"} /> : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -2,14 +2,80 @@ import * as React from "react";
|
|||
|
||||
export enum CoreTextType {
|
||||
header,
|
||||
medium,
|
||||
large,
|
||||
small,
|
||||
}
|
||||
|
||||
export interface ITextProps {
|
||||
text: string;
|
||||
type: CoreTextType;
|
||||
color?: string;
|
||||
}
|
||||
|
||||
export function CoreText(props: ITextProps) {
|
||||
if (props.type === CoreTextType.header) return <div style={{ color: "white", fontSize: "20px" }}>{props.text}</div>;
|
||||
if (props.type === CoreTextType.small) {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
color: props.color ?? "rgba(73, 69, 79, 1)",
|
||||
fontSize: 12,
|
||||
fontFamily: "Roboto",
|
||||
fontWeight: 400,
|
||||
fontSizeAdjust: 14,
|
||||
textOverflow: "ellipsis",
|
||||
}}
|
||||
>
|
||||
{props.text}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (props.type === CoreTextType.large) {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
color: props.color ?? "#1D1B20",
|
||||
fontSize: 16,
|
||||
fontFamily: "Roboto",
|
||||
fontWeight: 400,
|
||||
fontSizeAdjust: 14,
|
||||
textOverflow: "ellipsis",
|
||||
}}
|
||||
>
|
||||
{props.text}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (props.type === CoreTextType.medium)
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
color: props.color ?? "#1D1B20",
|
||||
fontSize: 14,
|
||||
fontFamily: "Roboto",
|
||||
fontWeight: 400,
|
||||
textOverflow: "ellipsis",
|
||||
fontSizeAdjust: 14,
|
||||
}}
|
||||
>
|
||||
{props.text}
|
||||
</div>
|
||||
);
|
||||
if (props.type === CoreTextType.header)
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
color: props.color ?? "#1D1B20",
|
||||
fontSize: 20,
|
||||
fontFamily: "Roboto",
|
||||
fontWeight: 500,
|
||||
textOverflow: "ellipsis",
|
||||
|
||||
fontSizeAdjust: 16,
|
||||
}}
|
||||
>
|
||||
{props.text}
|
||||
</div>
|
||||
);
|
||||
return <div>{props.text}</div>;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
export enum StickObjectsMarkingStoreMode {
|
||||
objectsToWhichItSticks = "objectsToWhichItSticks",
|
||||
objectThatSticks = "objectThatSticks",
|
||||
addPointsObjectsToWhichItSticks = "addPointsObjectsToWhichItSticks",
|
||||
addPointsObjectThatSticks = "addPointsObjectThatSticks",
|
||||
move = "move",
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
import { Vector3 } from "three";
|
||||
|
||||
export class StickyHelper {
|
||||
objectThatSticksName: string;
|
||||
objectThatSticksNamePoints: Vector3[] = [];
|
||||
objectsToWhichItSticksName: string;
|
||||
objectsToWhichItSticksPoints: Vector3[] = [];
|
||||
|
||||
constructor(
|
||||
objectThatSticksName: string,
|
||||
objectThatSticksNamePoints: Vector3[],
|
||||
objectsToWhichItSticksName: string,
|
||||
objectsToWhichItSticksPoints: Vector3[]
|
||||
) {
|
||||
this.objectThatSticksName = objectThatSticksName;
|
||||
this.objectThatSticksNamePoints = objectThatSticksNamePoints;
|
||||
this.objectsToWhichItSticksName = objectsToWhichItSticksName;
|
||||
this.objectsToWhichItSticksPoints = objectsToWhichItSticksPoints;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
export enum StickyLoaderMode {
|
||||
IN = "IN",
|
||||
OUT = "OUT",
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
import * as React from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { Button } from "antd";
|
||||
import { StickObjectsMarkingStore } from "./stick_objects_marking_store";
|
||||
import { Vector3 } from "three";
|
||||
import { StickObjectsMarkingStoreMode } from "./model/stick_objects_marking_store_mode";
|
||||
import { StickyLoaderMode } from "./model/sticky_loader_mode";
|
||||
|
||||
export const StickObjectsMarking = "/stick/objects/marking";
|
||||
interface StickButtonsProps {
|
||||
isVisible: boolean;
|
||||
name: string;
|
||||
groupMode: StickObjectsMarkingStoreMode;
|
||||
storeMode: StickObjectsMarkingStoreMode;
|
||||
storeModePoints: StickObjectsMarkingStoreMode;
|
||||
setMode: Function;
|
||||
setPointMode: Function;
|
||||
points: Vector3[];
|
||||
body: string;
|
||||
}
|
||||
|
||||
export const StickButtons: React.FunctionComponent<StickButtonsProps> = observer((props) => {
|
||||
return (
|
||||
<>
|
||||
{props.isVisible ? (
|
||||
<>
|
||||
{props.name}
|
||||
<Button
|
||||
style={{
|
||||
backgroundColor: props.storeMode === props.storeModePoints ? "#ff7e1e" : "",
|
||||
}}
|
||||
onClick={() => props.setPointMode()}
|
||||
>
|
||||
add points
|
||||
</Button>
|
||||
{props.points.map((el) => {
|
||||
return (
|
||||
<>
|
||||
{el.x} {el.y} {el.z}
|
||||
</>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
) : (
|
||||
<Button
|
||||
style={{
|
||||
backgroundColor: props.groupMode === props.storeMode ? "#ff7e1e" : "",
|
||||
}}
|
||||
onClick={() => props.setMode(props.storeMode)}
|
||||
>
|
||||
{props.body}
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
});
|
||||
export const StickObjectsMarkingScreenPath = "/sticky/objects/mark";
|
||||
export const StickObjectsMarkingScreen = observer(() => {
|
||||
const canvasRef = React.useRef<HTMLCanvasElement>(null);
|
||||
const [store] = React.useState(() => new StickObjectsMarkingStore());
|
||||
const id = useParams().id as string;
|
||||
|
||||
React.useEffect(() => {
|
||||
store.init();
|
||||
store.loadScene(canvasRef.current!);
|
||||
document.body.style.overflow = "hidden";
|
||||
return () => {
|
||||
document.body.style.overflow = "scroll";
|
||||
store.dispose();
|
||||
};
|
||||
}, [id, store]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={canvasRef} style={{ position: "absolute", overflow: "hidden" }} />
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
alignContent: "center",
|
||||
justifyContent: "space-between",
|
||||
position: "absolute",
|
||||
width: "100vw",
|
||||
}}
|
||||
>
|
||||
<div style={{ backgroundColor: "aqua", display: "flex", flexDirection: "column", padding: "5px" }}>
|
||||
{store.stickyObjects?.map((el) => {
|
||||
return (
|
||||
<div>
|
||||
<div style={{ display: "flex" }}>
|
||||
<Button onClick={() => store.loadAsset(el, StickyLoaderMode.IN)}>Sticky in</Button>
|
||||
<Button onClick={() => store.loadAsset(el, StickyLoaderMode.OUT)}>Sticky Out</Button>
|
||||
</div>
|
||||
<div>{el.name}</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{/* <div style={{ backgroundColor: "white", padding: "20px" }}>
|
||||
{Object.keys(store.points).map((el) => {
|
||||
// @ts-expect-error
|
||||
const v = store.points[el];
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
{el as string}:{v}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
})}
|
||||
<div>Marking objects for sticking</div>
|
||||
<div>
|
||||
<StickButtons
|
||||
storeMode={store.mode}
|
||||
setMode={() => store.setMode(StickObjectsMarkingStoreMode.objectThatSticks)}
|
||||
groupMode={StickObjectsMarkingStoreMode.objectThatSticks}
|
||||
name={store.objectThatSticksName}
|
||||
storeModePoints={StickObjectsMarkingStoreMode.addPointsObjectThatSticks}
|
||||
isVisible={store.objectThatSticksName !== undefined}
|
||||
setPointMode={() => store.setMode(StickObjectsMarkingStoreMode.addPointsObjectThatSticks)}
|
||||
points={store.objectThatSticksNamePoints}
|
||||
body="objectThatSticksName"
|
||||
/>
|
||||
<StickButtons
|
||||
body="objectsToWhichItSticksName"
|
||||
points={store.objectsToWhichItSticksPoints}
|
||||
storeMode={store.mode}
|
||||
setMode={() => store.setMode(StickObjectsMarkingStoreMode.objectsToWhichItSticks)}
|
||||
groupMode={StickObjectsMarkingStoreMode.objectsToWhichItSticks}
|
||||
name={store.objectsToWhichItSticksName}
|
||||
storeModePoints={StickObjectsMarkingStoreMode.addPointsObjectsToWhichItSticks}
|
||||
isVisible={store.objectsToWhichItSticksName !== undefined}
|
||||
setPointMode={() => store.setMode(StickObjectsMarkingStoreMode.addPointsObjectsToWhichItSticks)}
|
||||
/>
|
||||
<Button onClick={() => store.onSaveSticky()}>save</Button>
|
||||
<Button
|
||||
onClick={() => store.setMode(StickObjectsMarkingStoreMode.move)}
|
||||
style={{ backgroundColor: store.mode === StickObjectsMarkingStoreMode.move ? "#ff7e1e" : "" }}
|
||||
>
|
||||
run
|
||||
</Button>
|
||||
</div>
|
||||
</div> */}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
|
@ -0,0 +1,120 @@
|
|||
import makeAutoObservable from "mobx-store-inheritance";
|
||||
import { Box3, Vector2, Vector3 } from "three";
|
||||
import { UiErrorState } from "../../core/store/base_store";
|
||||
import { HttpError } from "../../core/repository/http_repository";
|
||||
import { UiBaseError } from "../../core/model/ui_base_error";
|
||||
import { RobossemblerFiles } from "../scene_manager/model/scene_assets";
|
||||
import { StickObjectsMarkingThreeRepository } from "./stick_objects_marking_three_repository";
|
||||
import { UserData } from "../../core/repository/core_three_repository";
|
||||
import { SceneHttpRepository } from "../scene_manager/data/scene_repository";
|
||||
import { Asset, RobossemblerAssets } from "../../core/model/robossembler_assets";
|
||||
import { StickObjectsMarkingStoreMode } from "./model/stick_objects_marking_store_mode";
|
||||
import { StickyHelper } from "./model/sticky_helper";
|
||||
import { StickyLoaderMode } from "./model/sticky_loader_mode";
|
||||
|
||||
export class StickObjectsMarkingStore extends UiErrorState<HttpError> {
|
||||
mode: StickObjectsMarkingStoreMode;
|
||||
stickObjectsMarkingThreeRepository: null | StickObjectsMarkingThreeRepository = null;
|
||||
sceneHttpRepository: SceneHttpRepository;
|
||||
objectThatSticksName: string;
|
||||
objectThatSticksNamePoints: Vector3[] = [];
|
||||
objectsToWhichItSticksName: string;
|
||||
objectsToWhichItSticksPoints: Vector3[] = [];
|
||||
sceneAssets?: RobossemblerAssets;
|
||||
points = {};
|
||||
|
||||
get stickyObjects() {
|
||||
return this.sceneAssets?.assets.filter((el) => el.actions.includes("Sticking"));
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
makeAutoObservable(this);
|
||||
this.points = {};
|
||||
this.mode = StickObjectsMarkingStoreMode.move;
|
||||
this.sceneHttpRepository = new SceneHttpRepository();
|
||||
}
|
||||
updatePoint = (key: string, value: any) => {
|
||||
// @ts-expect-error
|
||||
this.points[key] = value;
|
||||
};
|
||||
onSaveSticky(): void {}
|
||||
setMode(stickObjectsMarkingStoreMode: StickObjectsMarkingStoreMode): void {
|
||||
this.mode = stickObjectsMarkingStoreMode;
|
||||
}
|
||||
|
||||
loaderWatcher() {}
|
||||
|
||||
async init(): Promise<any> {
|
||||
this.mapOk("sceneAssets", this.sceneHttpRepository.getRobossemblerAssets());
|
||||
}
|
||||
|
||||
errorHandingStrategy = (error: HttpError) => {
|
||||
if (error.status === 404) {
|
||||
this.errors.push(new UiBaseError(`${RobossemblerFiles.robossemblerAssets} not found to project`));
|
||||
}
|
||||
};
|
||||
|
||||
async loadScene(canvasRef: HTMLCanvasElement) {
|
||||
this.loadWebGl(canvasRef);
|
||||
}
|
||||
watcherSceneEditorObject() {}
|
||||
loadWebGl(canvasRef: HTMLCanvasElement): void {
|
||||
this.stickObjectsMarkingThreeRepository = new StickObjectsMarkingThreeRepository(
|
||||
canvasRef as HTMLCanvasElement,
|
||||
this.watcherSceneEditorObject,
|
||||
this.updatePoint
|
||||
);
|
||||
this.stickObjectsMarkingThreeRepository.stickyHelperLoader([
|
||||
new StickyHelper(
|
||||
this.objectThatSticksName,
|
||||
this.objectThatSticksNamePoints,
|
||||
this.objectsToWhichItSticksName,
|
||||
this.objectsToWhichItSticksPoints
|
||||
),
|
||||
]);
|
||||
this.stickObjectsMarkingThreeRepository.render();
|
||||
window.addEventListener("click", (event) => this.clickLister(event));
|
||||
}
|
||||
|
||||
clickLister(event: MouseEvent) {
|
||||
const vector = new Vector2();
|
||||
vector.x = (event.clientX / window.innerWidth) * 2 - 1;
|
||||
vector.y = -(event.clientY / window.innerHeight) * 2 + 1;
|
||||
if (this.mode) {
|
||||
if (this.mode === StickObjectsMarkingStoreMode.move) {
|
||||
this.stickObjectsMarkingThreeRepository?.setRayCastAndGetFirstObject(vector).fold(
|
||||
(success) => this.stickObjectsMarkingThreeRepository?.setTransformControlsAttach(success),
|
||||
(_error) => this.stickObjectsMarkingThreeRepository?.disposeTransformControlsMode()
|
||||
);
|
||||
}
|
||||
this.stickObjectsMarkingThreeRepository?.setRayCast(vector).map((touch) => {
|
||||
const objectMagnetism = touch.filter((el) => el.object.userData[UserData.objectForMagnetism] === true);
|
||||
const BoundBoxVector = new Box3().setFromObject(objectMagnetism[0].object).getCenter(new Vector3());
|
||||
const centerRelativeVector = new Vector3().subVectors(BoundBoxVector, objectMagnetism[0].point);
|
||||
if (objectMagnetism.isNotEmpty()) {
|
||||
if (this.mode === StickObjectsMarkingStoreMode.objectsToWhichItSticks) {
|
||||
this.objectsToWhichItSticksName = objectMagnetism[0].object.name;
|
||||
}
|
||||
if (this.mode === StickObjectsMarkingStoreMode.objectThatSticks) {
|
||||
this.objectThatSticksName = objectMagnetism[0].object.name;
|
||||
}
|
||||
if (this.mode === StickObjectsMarkingStoreMode.addPointsObjectThatSticks) {
|
||||
this.objectThatSticksNamePoints.push(centerRelativeVector);
|
||||
}
|
||||
if (this.mode === StickObjectsMarkingStoreMode.addPointsObjectsToWhichItSticks) {
|
||||
this.objectsToWhichItSticksPoints.push(centerRelativeVector);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
loadAsset(asset: Asset, mode: StickyLoaderMode): void {
|
||||
this.stickObjectsMarkingThreeRepository?.loadInstance(asset,() =>{
|
||||
|
||||
});
|
||||
}
|
||||
dispose() {
|
||||
window.removeEventListener("click", this.clickLister);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
import { Box3, BoxGeometry, Mesh, MeshBasicMaterial, Object3D, Vector3 } from "three";
|
||||
import { CoreThreeRepository } from "../../core/repository/core_three_repository";
|
||||
import { CoreVector3 } from "../../core/model/core_vector3";
|
||||
import { StickyHelper } from "./model/sticky_helper";
|
||||
|
||||
export class StickObjectsMarkingThreeRepository extends CoreThreeRepository {
|
||||
stickyObjects: StickyHelper[];
|
||||
drawUiPoint: Function;
|
||||
constructor(htmlCanvasRef: HTMLCanvasElement, watcherSceneEditorObject: Function, updatePoint: Function) {
|
||||
super(htmlCanvasRef, watcherSceneEditorObject);
|
||||
this.drawUiPoint = updatePoint;
|
||||
this.sceneWatcher();
|
||||
}
|
||||
|
||||
getStickyObject(name: string, pointNameHelper: string, index: number) {
|
||||
const objectThatSticksNameMesh = this.scene.getObjectByName(name);
|
||||
const pointName = objectThatSticksNameMesh!.name + ":point:" + index + pointNameHelper;
|
||||
return this.scene.getObjectByName(pointName);
|
||||
}
|
||||
|
||||
mapperStickyObject(point: Vector3, index: number, name: string, pointNameHelper: string) {
|
||||
const objectThatSticksNameMesh = this.scene.getObjectByName(name);
|
||||
const pointName = objectThatSticksNameMesh!.name + ":point:" + index + pointNameHelper;
|
||||
const sceneObject = this.scene.getObjectByName(pointName);
|
||||
let pointMesh: Object3D;
|
||||
if (sceneObject) {
|
||||
pointMesh = sceneObject;
|
||||
} else {
|
||||
pointMesh = new Mesh(new BoxGeometry(1.1, 1.1, 1.1), new MeshBasicMaterial({ color: "#8BC34A" }));
|
||||
}
|
||||
pointMesh.position.copy(new Box3().setFromObject(objectThatSticksNameMesh!).getCenter(new Vector3()).add(point));
|
||||
pointMesh.name = pointName;
|
||||
if (sceneObject === undefined) this.scene.add(pointMesh);
|
||||
}
|
||||
|
||||
stickyHelperLoader(stickyObjects: StickyHelper[]) {
|
||||
this.stickyObjects = stickyObjects;
|
||||
|
||||
stickyObjects.forEach((el) => {
|
||||
el.objectThatSticksNamePoints.forEach((point, index) =>
|
||||
this.mapperStickyObject(point, index, el.objectThatSticksName, "objectThatSticksNamePoints")
|
||||
);
|
||||
el.objectsToWhichItSticksPoints.forEach((point, index) =>
|
||||
this.mapperStickyObject(point, index, el.objectsToWhichItSticksName, "objectsToWhichItSticksPoints")
|
||||
);
|
||||
});
|
||||
}
|
||||
getCenter = (obj: Object3D) => new Box3().setFromObject(obj).getCenter(new Vector3());
|
||||
|
||||
sceneWatcher() {
|
||||
this.transformControls.addEventListener("objectChange", (event) => {
|
||||
//@ts-expect-error
|
||||
const sceneActiveObject = event.target.object as Mesh;
|
||||
|
||||
this.stickyObjects.forEach((stickyHelper) => {
|
||||
if (sceneActiveObject.name === stickyHelper.objectThatSticksName) {
|
||||
const objectsToWhichItSticksPointLocalVector = stickyHelper.objectsToWhichItSticksPoints[0];
|
||||
const globalVectorObjStickyName = this.scene.getObjectByName(stickyHelper.objectThatSticksName);
|
||||
|
||||
const globalVectorObjToWhichSticks = this.scene.getObjectByName(stickyHelper.objectsToWhichItSticksName);
|
||||
|
||||
const objectsToWhichItSticksNamePosition = new CoreVector3(globalVectorObjToWhichSticks!.position).add(
|
||||
objectsToWhichItSticksPointLocalVector
|
||||
).vector;
|
||||
|
||||
this.scene
|
||||
.getObjectByName("cube2:point:0objectThatSticksNamePoints")
|
||||
?.position.copy(objectsToWhichItSticksNamePosition);
|
||||
|
||||
globalVectorObjStickyName?.position.copy(
|
||||
this.scene
|
||||
.getObjectByName("cube2:point:0objectThatSticksNamePoints")!
|
||||
.position.add(objectsToWhichItSticksPointLocalVector)
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,14 +1,16 @@
|
|||
import { ActivePipeline } from "../../../core/model/active_pipeline";
|
||||
import { HttpMethod, HttpRepository } from "../../../core/repository/http_repository";
|
||||
import { IProjectModel } from "../model/project_model";
|
||||
|
||||
export interface UUID {
|
||||
id: string;
|
||||
}
|
||||
export class ProjectRepository extends HttpRepository {
|
||||
async getAllProject() {
|
||||
return this._jsonRequest<IProjectModel[]>(HttpMethod.GET, "/project_instance");
|
||||
return this._jsonRequest<IProjectModel[]>(HttpMethod.GET, "/projects");
|
||||
}
|
||||
|
||||
async getActivePipeline() {
|
||||
return this._jsonRequest<ActivePipeline>(HttpMethod.GET, "/realtime");
|
||||
return this._jsonRequest<UUID>(HttpMethod.GET, "/projects/get/active/project/id");
|
||||
}
|
||||
async setActivePipeline(id: string) {
|
||||
return this._jsonRequest(HttpMethod.POST, `/project_instance/set/active/project?id=${id}`);
|
||||
|
|
|
@ -2,7 +2,7 @@ import { PipelineModel } from "../../create_project/create_project_repository";
|
|||
|
||||
export interface IProjectModel {
|
||||
_id?: string;
|
||||
pipelines: [PipelineModel];
|
||||
rootDir: string;
|
||||
description: string;
|
||||
isActive?: boolean;
|
||||
}
|
||||
|
|
|
@ -3,19 +3,19 @@ import { AllProjectStore } from "./all_projects_store";
|
|||
import { ProjectRepository } from "../data/project_repository";
|
||||
import { LoadPage } from "../../../core/ui/pages/load_page";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { SelectProjectScreenPath } from "../../select_project/presentation/select_project";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { Button } from "antd";
|
||||
import { PipelineInstanceScreenPath } from "../../pipeline_instance_main_screen/pipeline_instance_screen";
|
||||
import { CreateProjectScreenPath } from "../../create_project/create_project_screen";
|
||||
|
||||
export const AllProjectScreenPath = "/";
|
||||
export const AllProjectScreen: React.FunctionComponent = observer(() => {
|
||||
const [allProjectStore] = React.useState(() => new AllProjectStore(new ProjectRepository()));
|
||||
const [store] = React.useState(() => new AllProjectStore(new ProjectRepository()));
|
||||
const navigate = useNavigate();
|
||||
|
||||
React.useEffect(() => {
|
||||
allProjectStore.init();
|
||||
}, [allProjectStore]);
|
||||
store.init();
|
||||
}, [store]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -23,35 +23,23 @@ export const AllProjectScreen: React.FunctionComponent = observer(() => {
|
|||
largeText={"Projects"}
|
||||
needBackButton={false}
|
||||
minText="create project?"
|
||||
path={SelectProjectScreenPath}
|
||||
isError={allProjectStore.isError}
|
||||
isLoading={allProjectStore.isLoading}
|
||||
path={CreateProjectScreenPath}
|
||||
isError={store.isError}
|
||||
isLoading={store.isLoading}
|
||||
children={
|
||||
<div>
|
||||
<h1>Projects</h1>
|
||||
<h5 style={{ backgroundColor: "ButtonShadow" }}>
|
||||
<Button
|
||||
onClick={() => {
|
||||
navigate(PipelineInstanceScreenPath + allProjectStore.activePipeline?.projectId ?? "");
|
||||
}}
|
||||
>
|
||||
Project main panel
|
||||
</Button>
|
||||
{allProjectStore.activePipeline?.projectId ?? "loading"}
|
||||
</h5>
|
||||
{allProjectStore.projectsModels?.map((el) => {
|
||||
{store.projectsModels?.map((el) => {
|
||||
return (
|
||||
<div style={{ margin: "10px", backgroundColor: "Highlight" }}>
|
||||
<Button
|
||||
onClick={() => {
|
||||
allProjectStore.setPipelineActive(el._id ?? "").then(() => {
|
||||
allProjectStore.init();
|
||||
navigate(PipelineInstanceScreenPath + el._id ?? "");
|
||||
});
|
||||
}}
|
||||
>
|
||||
set active project
|
||||
</Button>
|
||||
{el.isActive ? (
|
||||
<Button
|
||||
onClick={() => {
|
||||
navigate(`${PipelineInstanceScreenPath}${el._id}`);
|
||||
}}
|
||||
>
|
||||
instance screen
|
||||
</Button>
|
||||
) : null}
|
||||
<div style={{ margin: "10px", display: "contents" }}> {el.description}</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import makeAutoObservable from "mobx-store-inheritance";
|
||||
import { ProjectRepository } from "../data/project_repository";
|
||||
import { ProjectRepository, UUID } from "../data/project_repository";
|
||||
import { IProjectModel } from "../model/project_model";
|
||||
import { SimpleErrorState } from "../../../core/store/base_store";
|
||||
import { ActivePipeline } from "../../../core/model/active_pipeline";
|
||||
|
@ -21,8 +21,8 @@ export class ProjectView {
|
|||
}
|
||||
export class AllProjectStore extends SimpleErrorState {
|
||||
projectsModels?: IProjectModel[];
|
||||
activeProjectId?: UUID;
|
||||
repository: ProjectRepository;
|
||||
activePipeline?: ActivePipeline;
|
||||
constructor(repository: ProjectRepository) {
|
||||
super();
|
||||
this.repository = repository;
|
||||
|
@ -31,18 +31,14 @@ export class AllProjectStore extends SimpleErrorState {
|
|||
async getProjects(): Promise<void> {
|
||||
await this.mapOk<IProjectModel[]>("projectsModels", this.repository.getAllProject());
|
||||
}
|
||||
|
||||
async getActiveProject(): Promise<void> {
|
||||
await this.mapOk<ActivePipeline>("activePipeline", this.repository.getActivePipeline());
|
||||
async getActiveProjectId(): Promise<void> {
|
||||
await this.mapOk<UUID>("activeProjectId", this.repository.getActivePipeline());
|
||||
}
|
||||
|
||||
async init() {
|
||||
await Promise.all([this.getProjects(), this.getActiveProject()]);
|
||||
await this.projectViewGenerate();
|
||||
}
|
||||
projectViewGenerate() {
|
||||
this.projectsModels = this.projectsModels?.filter((el) => el._id !== this.activePipeline?.projectId);
|
||||
await Promise.all([this.getProjects(), this.getActiveProjectId()]);
|
||||
this.projectsModels?.map((el) => (el._id === this.activeProjectId ? ((el.isActive = true), el) : el));
|
||||
}
|
||||
|
||||
async setPipelineActive(id: string) {
|
||||
await this.httpHelper(this.repository.setActivePipeline(id));
|
||||
}
|
||||
|
|
8
ui/src/features/assembles/assembles_screen.tsx
Normal file
8
ui/src/features/assembles/assembles_screen.tsx
Normal file
|
@ -0,0 +1,8 @@
|
|||
import * as React from "react";
|
||||
import { MainPage } from "../../core/ui/pages/main_page";
|
||||
|
||||
export interface IAssemblesScreenProps {}
|
||||
export const AssemblesScreenPath = "/assembles";
|
||||
export function AssemblesScreen(props: IAssemblesScreenProps) {
|
||||
return <MainPage page={"Сборки"}></MainPage>;
|
||||
}
|
89
ui/src/features/behavior_tree_builder/model/editor_view.ts
Normal file
89
ui/src/features/behavior_tree_builder/model/editor_view.ts
Normal file
|
@ -0,0 +1,89 @@
|
|||
import { TypedEvent } from "../../../core/helper/typed_event";
|
||||
import { NodeEditor } from "rete";
|
||||
import { AreaExtra, Schemes } from "../presentation/ui/editor/editor";
|
||||
import { Result } from "../../../core/helper/result";
|
||||
import { AreaPlugin } from "rete-area-plugin";
|
||||
|
||||
export interface BtDrawDragAndDropView {
|
||||
x: number;
|
||||
y: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export class BtNodeView extends TypedEvent<BtDrawDragAndDropView> {}
|
||||
|
||||
export class ReteObserver extends TypedEvent<any> {}
|
||||
export class BtBuilderModel {
|
||||
public static result = "";
|
||||
static fromReteScene(editor: NodeEditor<Schemes>, area: AreaPlugin<Schemes, AreaExtra>): Result<string, string> {
|
||||
try {
|
||||
this.getFirstSequence(editor).map((sortedSequence) => {
|
||||
const firstNodeId = sortedSequence.getKeyFromValueIsExists(1) as string;
|
||||
this.findSequence(firstNodeId, editor, sortedSequence, 2);
|
||||
this.result += `<${this.getNodeLabelAtId(editor, firstNodeId)} id="${firstNodeId}">`;
|
||||
this.toXML(sortedSequence as Map<string, number>, editor, area, firstNodeId);
|
||||
this.result += `</${this.getNodeLabelAtId(editor, firstNodeId)}>`;
|
||||
});
|
||||
return Result.ok(this.result);
|
||||
} catch (error) {
|
||||
return Result.error("BtBuilderModel fromReteScene error");
|
||||
}
|
||||
}
|
||||
public static getNodeLabelAtId(editor: NodeEditor<Schemes>, id: string) {
|
||||
return editor.getNode(id).label;
|
||||
}
|
||||
|
||||
static toXML(
|
||||
sortedSequence: Map<string, number>,
|
||||
editor: NodeEditor<Schemes>,
|
||||
area: AreaPlugin<Schemes, AreaExtra>,
|
||||
activeElementId = ""
|
||||
) {
|
||||
editor.getConnections().forEach((el) => {
|
||||
if (el.source === activeElementId) {
|
||||
this.result += `<${this.getNodeLabelAtId(editor, el.target)} id="${el.target}">`;
|
||||
this.toXML(sortedSequence, editor, area, el.target);
|
||||
this.result += `</${this.getNodeLabelAtId(editor, el.target)}>`;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static getBtPriorities(ids: string[]) {}
|
||||
public static findSequence(
|
||||
nodeId: string,
|
||||
editor: NodeEditor<Schemes>,
|
||||
result: Map<string, boolean | number>,
|
||||
lvl: number
|
||||
): Map<string, number> | undefined {
|
||||
const connections: string[] = [];
|
||||
|
||||
editor.getConnections().forEach((el) => {
|
||||
if (el.source === nodeId) {
|
||||
connections.push(el.target);
|
||||
}
|
||||
});
|
||||
|
||||
if (connections.isEmpty()) return result as Map<string, number>;
|
||||
let lv = lvl;
|
||||
connections.forEach((el) => {
|
||||
result.set(el, lvl);
|
||||
this.findSequence(el, editor, result, (lv += 1));
|
||||
});
|
||||
}
|
||||
public static getFirstSequence(editor: NodeEditor<Schemes>): Result<undefined, Map<string, boolean | number>> {
|
||||
const node = new Map<string, boolean | number>();
|
||||
editor.getNodes().forEach((el) => {
|
||||
node.set(el.id, 1);
|
||||
});
|
||||
|
||||
editor.getConnections().forEach((el) => {
|
||||
node.set(el.target, false);
|
||||
});
|
||||
|
||||
const key = node.getKeyFromValueIsExists(1);
|
||||
if (key === undefined) {
|
||||
return Result.error(undefined);
|
||||
}
|
||||
return Result.ok(node);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
import { AreaExtra, Schemes } from "../presentation/ui/editor/editor";
|
||||
import { NodeEditor } from "rete";
|
||||
import { AreaPlugin } from "rete-area-plugin";
|
||||
|
||||
export interface Position2D {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
export class NodeBehaviorTree {
|
||||
label: string;
|
||||
id: string;
|
||||
outputs: string[];
|
||||
inputs: string[];
|
||||
connectTo?: string;
|
||||
position: Position2D;
|
||||
|
||||
constructor(label: string, id: string, outputs: string[], inputs: string[], position: Position2D) {
|
||||
this.label = label;
|
||||
this.id = id;
|
||||
this.outputs = outputs;
|
||||
this.inputs = inputs;
|
||||
this.connectTo = undefined;
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
static fromReteScene(editor: NodeEditor<Schemes>, area: AreaPlugin<Schemes, AreaExtra>): NodeBehaviorTree[] {
|
||||
const nodes = new Map<string, NodeBehaviorTree>();
|
||||
editor
|
||||
.getNodes()
|
||||
.forEach((el) =>
|
||||
nodes.set(
|
||||
el.id,
|
||||
new NodeBehaviorTree(
|
||||
el.label,
|
||||
el.id,
|
||||
Object.keys(el.outputs),
|
||||
Object.keys(el.inputs),
|
||||
area.nodeViews.get(el.id)!.position
|
||||
)
|
||||
)
|
||||
);
|
||||
editor.getConnections().forEach((el) => console.log(el));
|
||||
editor.getConnections().forEach((el) => nodes.overrideValue(el.target, { connectTo: el.source }));
|
||||
|
||||
return nodes.toArray();
|
||||
}
|
||||
}
|
|
@ -1,10 +1,12 @@
|
|||
import * as React from "react";
|
||||
import { CoreText, CoreTextType } from "../../../core/ui/text/text";
|
||||
import { Button, Input } from "antd";
|
||||
|
||||
import { useRete } from "rete-react-plugin";
|
||||
import { createEditor } from "./ui/editor/editor";
|
||||
import { SkillTree } from "./ui/skill_tree/skill_tree";
|
||||
import { BehaviorTreeBuilderStore } from "./behavior_tree_builder_store";
|
||||
import { MainPage } from "../../../core/ui/pages/main_page";
|
||||
|
||||
export const behaviorTreeBuilderScreenPath = "behavior/tree/screen/path";
|
||||
|
||||
|
@ -12,27 +14,24 @@ const skills = {
|
|||
name: "",
|
||||
children: [
|
||||
{
|
||||
name: "arm",
|
||||
children: [{ name: "move to", interface: "Vector3", out: "vo" }],
|
||||
name: "Actions",
|
||||
children: [{ name: "MoveToPose", interface: "Vector3", out: "vo" }],
|
||||
},
|
||||
{
|
||||
name: "image",
|
||||
name: "Control",
|
||||
children: [
|
||||
{
|
||||
name: "object detection",
|
||||
interface: "image",
|
||||
out: "BoundBox",
|
||||
},
|
||||
{ name: "people detection", interface: "image", out: "BoundBox" },
|
||||
{ name: "Fallback", interface: "Vector3", out: "vo" },
|
||||
{ name: "Sequence", interface: "Vector3", out: "vo" },
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
export const behaviorTreeBuilderStore = new BehaviorTreeBuilderStore();
|
||||
|
||||
export const BehaviorTreeBuilderPath = "/behavior/tree/";
|
||||
export function BehaviorTreeBuilderScreen() {
|
||||
const [store] = React.useState(behaviorTreeBuilderStore);
|
||||
const store = behaviorTreeBuilderStore;
|
||||
const [ref] = useRete(createEditor);
|
||||
|
||||
React.useEffect(() => {
|
||||
store.init();
|
||||
|
||||
|
@ -42,9 +41,9 @@ export function BehaviorTreeBuilderScreen() {
|
|||
// @ts-expect-error
|
||||
ref.current.offsetLeft,
|
||||
// @ts-expect-error
|
||||
Number(String(ref.current.style.width).replaceAll("px", "")),
|
||||
ref.current.clientWidth,
|
||||
// @ts-expect-error
|
||||
Number(String(ref.current.style.height).replaceAll("px", ""))
|
||||
ref.current.clientHeight
|
||||
);
|
||||
|
||||
return () => {
|
||||
|
@ -53,54 +52,24 @@ export function BehaviorTreeBuilderScreen() {
|
|||
}, [store, ref]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
style={{
|
||||
width: "100vw",
|
||||
height: "86px",
|
||||
background: "#041226",
|
||||
}}
|
||||
>
|
||||
<CoreText text="Robossembler studio" type={CoreTextType.header} />
|
||||
</div>
|
||||
<div style={{ display: "flex" }}>
|
||||
<div
|
||||
style={{
|
||||
width: "30vw",
|
||||
height: "70px",
|
||||
background: "#1B2E42",
|
||||
}}
|
||||
></div>
|
||||
<div
|
||||
style={{
|
||||
width: "1782px",
|
||||
height: "70px",
|
||||
background: "#244366",
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
<div style={{ display: "flex" }}>
|
||||
<div
|
||||
style={{
|
||||
width: "30vw",
|
||||
background: "#1B3041",
|
||||
boxShadow: "inset 0px 10px 4px #16283D",
|
||||
}}
|
||||
>
|
||||
<div style={{ overflow: "auto", height: "100%", width: "100%", padding: "10px" }}>
|
||||
<SkillTree dragEnd={store.dragEnd} skills={skills} />
|
||||
</div>
|
||||
</div>
|
||||
<MainPage
|
||||
page={"Навыки"}
|
||||
bodyChildren={
|
||||
<>
|
||||
<div style={{ display: "flex", width: "100%" }}>
|
||||
{/* <SkillTree dragEnd={store.dragEnd} skills={skills} /> */}
|
||||
|
||||
<div
|
||||
ref={ref}
|
||||
style={{
|
||||
width: "1782px",
|
||||
height: String(window.innerHeight - 86 - 70) + "px",
|
||||
background: "#244366",
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
ref={ref}
|
||||
style={{
|
||||
width: "100%",
|
||||
height: window.innerHeight,
|
||||
background: "white",
|
||||
}}
|
||||
></div>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,24 +1,133 @@
|
|||
import makeAutoObservable from "mobx-store-inheritance";
|
||||
|
||||
import xmlFormat from "xml-formatter";
|
||||
import { HttpError } from "../../../core/repository/http_repository";
|
||||
import { UiErrorState } from "../../../core/store/base_store";
|
||||
import { BtNodeView } from "./ui/editor/editor_view";
|
||||
import { BtBuilderModel, BtNodeView, ReteObserver } from "../model/editor_view";
|
||||
import { NodeEditor } from "rete";
|
||||
import { AreaExtra, Schemes } from "./ui/editor/editor";
|
||||
import { AreaPlugin } from "rete-area-plugin";
|
||||
import { NodeBehaviorTree } from "../model/node_behavior_tree";
|
||||
|
||||
interface I2DArea {
|
||||
x: number;
|
||||
y: number;
|
||||
w: number;
|
||||
h: number;
|
||||
}
|
||||
|
||||
export class BehaviorTreeBuilderStore extends UiErrorState<HttpError> {
|
||||
saveBt(): void {
|
||||
this.reteNode?.emit("200");
|
||||
}
|
||||
validateBt() {}
|
||||
area?: I2DArea;
|
||||
errorHandingStrategy: (error: HttpError) => void;
|
||||
btNodeView: BtNodeView = new BtNodeView();
|
||||
reteNode?: ReteObserver;
|
||||
canRun = true;
|
||||
nodes: NodeBehaviorTree[] = [
|
||||
{
|
||||
label: "Sequence",
|
||||
id: "1",
|
||||
outputs: ["a"],
|
||||
inputs: ["a"],
|
||||
position: { x: -488.1303402823156, y: -227.90861570911895 },
|
||||
},
|
||||
{
|
||||
label: "Sequence",
|
||||
id: "2",
|
||||
outputs: ["a"],
|
||||
inputs: ["a"],
|
||||
connectTo: "1",
|
||||
position: { x: 119.95735514023244, y: -182.24902817216878 },
|
||||
},
|
||||
{
|
||||
label: "Sequence",
|
||||
id: "6",
|
||||
outputs: ["a"],
|
||||
inputs: ["a"],
|
||||
connectTo: "2",
|
||||
position: { x: 402.06111561958505, y: -100.97814086372247 },
|
||||
},
|
||||
{
|
||||
label: "MoveToPose",
|
||||
id: "7",
|
||||
outputs: ["a"],
|
||||
inputs: ["a"],
|
||||
connectTo: "6",
|
||||
position: { x: 932.0688448287292, y: 90.10780461923443 },
|
||||
},
|
||||
{
|
||||
label: "MoveToPose",
|
||||
id: "8",
|
||||
outputs: ["a"],
|
||||
inputs: ["a"],
|
||||
connectTo: "6",
|
||||
position: { x: 898.1684213039546, y: -119.80545806921208 },
|
||||
},
|
||||
{
|
||||
label: "MoveToPose",
|
||||
id: "3",
|
||||
outputs: [],
|
||||
inputs: ["a"],
|
||||
connectTo: "1",
|
||||
position: { x: 208.65749743442188, y: 16.256489050033785 },
|
||||
},
|
||||
{
|
||||
label: "MoveToPose",
|
||||
id: "4",
|
||||
outputs: [],
|
||||
inputs: ["a"],
|
||||
connectTo: "1",
|
||||
position: { x: -50.46426323140673, y: 99.22642447650014 },
|
||||
},
|
||||
{
|
||||
label: "Sequence",
|
||||
id: "5",
|
||||
outputs: ["a"],
|
||||
inputs: ["a"],
|
||||
connectTo: "1",
|
||||
position: { x: -234.54554662642457, y: 245.4012710608967 },
|
||||
},
|
||||
];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
makeAutoObservable(this);
|
||||
}
|
||||
canRun = true;
|
||||
draw() {}
|
||||
|
||||
setBt(value: string): void {
|
||||
JSON.parse(value) as BtNodeView[];
|
||||
}
|
||||
bt = async (editor: NodeEditor<Schemes>, area: AreaPlugin<Schemes, AreaExtra>) => {
|
||||
(await BtBuilderModel.fromReteScene(editor, area)).fold(
|
||||
(s) => {
|
||||
console.log(
|
||||
xmlFormat(`<?xml version="1.0" encoding="UTF-8"?>
|
||||
<root main_tree_to_execute="Main">
|
||||
<BehaviorTree ID="Main">
|
||||
${s}
|
||||
</BehaviorTree>
|
||||
|
||||
<TreeNodesModel>
|
||||
<Action ID="RbsBtAction">
|
||||
<input_port name="do" />
|
||||
<input_port name="command" />
|
||||
<input_port name="server_name" />
|
||||
<input_port name="server_timeout" />
|
||||
</Action>
|
||||
</TreeNodesModel>
|
||||
|
||||
</root>`)
|
||||
);
|
||||
},
|
||||
(_) => _
|
||||
);
|
||||
};
|
||||
copyBt = () => {
|
||||
this.reteNode?.emit("200");
|
||||
};
|
||||
errorHandingStrategy: (error: HttpError) => void;
|
||||
|
||||
dragEnd = (e: EventTarget) => {
|
||||
if (this.canRun) {
|
||||
//@ts-expect-error
|
||||
|
@ -26,7 +135,7 @@ export class BehaviorTreeBuilderStore extends UiErrorState<HttpError> {
|
|||
this.canRun = false;
|
||||
setTimeout(() => {
|
||||
this.canRun = true;
|
||||
}, 1000);
|
||||
}, 100);
|
||||
}
|
||||
};
|
||||
drawSkillCheck(x: number, y: number, name: string) {
|
||||
|
@ -38,16 +147,14 @@ export class BehaviorTreeBuilderStore extends UiErrorState<HttpError> {
|
|||
drawPoint.y < this.area!.y + this.area!.h &&
|
||||
drawPoint.y + drawPoint.h > this.area!.y
|
||||
) {
|
||||
this.drawSkill(x, y - (this.area!.y + this.area!.h / 2), name);
|
||||
this.btNodeView.emit({
|
||||
x: x,
|
||||
y: y - (this.area!.y + this.area!.h / 2),
|
||||
name: name,
|
||||
});
|
||||
}
|
||||
}
|
||||
drawSkill(x: number, y: number, name: string) {
|
||||
this.btNodeView.emit({
|
||||
x: x,
|
||||
y: y,
|
||||
name: name,
|
||||
});
|
||||
}
|
||||
|
||||
async init(): Promise<any> {}
|
||||
dragZoneSetOffset(offsetTop: number, offsetWidth: number, width: number, height: number) {
|
||||
this.area = {
|
||||
|
|
|
@ -14,7 +14,7 @@ const Svg = styled.svg`
|
|||
|
||||
const Path = styled.path<{ styles?: (props: any) => any }>`
|
||||
fill: none;
|
||||
stroke-width: 5px;
|
||||
stroke-width: 3px;
|
||||
stroke: black;
|
||||
pointer-events: auto;
|
||||
${(props) => props.styles && props.styles(props)}
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
import * as React from "react";
|
||||
import { ClassicPreset } from "rete";
|
||||
import styled from "styled-components";
|
||||
import { $socketsize } from "./vars";
|
||||
|
||||
const Styles = styled.div`
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
border: 1px solid grey;
|
||||
width: ${$socketsize}px;
|
||||
height: ${$socketsize * 2}px;
|
||||
border: 1px solid black;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
vertical-align: middle;
|
||||
background: #fff;
|
||||
background: black;
|
||||
z-index: 2;
|
||||
box-sizing: border-box;
|
||||
&:hover {
|
||||
background: #ddd;
|
||||
}
|
||||
border-radius: 50%;
|
||||
`;
|
||||
|
||||
export function CustomSocket<T extends ClassicPreset.Socket>(props: { data: T }) {
|
||||
|
|
|
@ -3,28 +3,39 @@ import { NodeEditor, GetSchemes, ClassicPreset } from "rete";
|
|||
import { AreaPlugin, AreaExtensions } from "rete-area-plugin";
|
||||
import { ConnectionPlugin, Presets as ConnectionPresets } from "rete-connection-plugin";
|
||||
import { ReactPlugin, Presets, ReactArea2D } from "rete-react-plugin";
|
||||
import { CustomNode } from "./custom_node";
|
||||
import { CustomSocket } from "./custom_socket";
|
||||
import { CustomConnection } from "./custom_connection";
|
||||
import { addCustomBackground } from "./custom_background";
|
||||
import { StyledNode } from "./style_node";
|
||||
import { behaviorTreeBuilderStore } from "../../behavior_tree_builder_screen";
|
||||
import { SequenceNode } from "./nodes/controls_node";
|
||||
import { ReteObserver } from "../../../model/editor_view";
|
||||
|
||||
type Schemes = GetSchemes<ClassicPreset.Node, ClassicPreset.Connection<ClassicPreset.Node, ClassicPreset.Node>>;
|
||||
type AreaExtra = ReactArea2D<Schemes>;
|
||||
export type Schemes = GetSchemes<ClassicPreset.Node, ClassicPreset.Connection<ClassicPreset.Node, ClassicPreset.Node>>;
|
||||
export type AreaExtra = ReactArea2D<Schemes>;
|
||||
|
||||
export async function createEditor(container: HTMLElement) {
|
||||
const socket = new ClassicPreset.Socket("socket");
|
||||
const observer = new ReteObserver();
|
||||
|
||||
behaviorTreeBuilderStore.reteNode = observer;
|
||||
|
||||
behaviorTreeBuilderStore.btNodeView.on(async (event) => {
|
||||
setTimeout(async () => {
|
||||
const node = new ClassicPreset.Node(event.name);
|
||||
|
||||
const { x, y } = areaContainer.area.pointer;
|
||||
console.log(x, y);
|
||||
|
||||
node.addOutput("a", new ClassicPreset.Output(socket));
|
||||
node.addInput("a", new ClassicPreset.Input(socket));
|
||||
await editor.addNode(node);
|
||||
await areaContainer.translate(node.id, { x, y });
|
||||
}, 50);
|
||||
}, 100);
|
||||
});
|
||||
|
||||
observer.on(() => {
|
||||
behaviorTreeBuilderStore.bt(editor, areaContainer);
|
||||
});
|
||||
|
||||
const editor = new NodeEditor<Schemes>();
|
||||
const areaContainer = new AreaPlugin<Schemes, AreaExtra>(container);
|
||||
const connection = new ConnectionPlugin<Schemes, AreaExtra>();
|
||||
|
@ -38,13 +49,7 @@ export async function createEditor(container: HTMLElement) {
|
|||
Presets.classic.setup({
|
||||
customize: {
|
||||
node(context) {
|
||||
if (context.payload.label === "Fully customized") {
|
||||
return CustomNode;
|
||||
}
|
||||
if (context.payload.label === "Override styles") {
|
||||
return StyledNode;
|
||||
}
|
||||
return Presets.classic.Node;
|
||||
return SequenceNode;
|
||||
},
|
||||
socket(_context) {
|
||||
return CustomSocket;
|
||||
|
@ -66,18 +71,27 @@ export async function createEditor(container: HTMLElement) {
|
|||
|
||||
AreaExtensions.simpleNodesOrder(areaContainer);
|
||||
|
||||
const a = new ClassicPreset.Node("Override styles");
|
||||
for await (const el of behaviorTreeBuilderStore.nodes) {
|
||||
const node = new ClassicPreset.Node(el.label);
|
||||
node.id = el.id;
|
||||
el.outputs.forEach((outputName) => {
|
||||
node.addOutput(outputName, new ClassicPreset.Output(socket));
|
||||
});
|
||||
el.inputs.forEach((inputName) => {
|
||||
node.addInput(inputName, new ClassicPreset.Input(socket));
|
||||
});
|
||||
|
||||
a.addOutput("a", new ClassicPreset.Output(socket));
|
||||
a.addInput("a", new ClassicPreset.Input(socket));
|
||||
await editor.addNode(a);
|
||||
await editor.addNode(node);
|
||||
await areaContainer.translate(node.id, el.position);
|
||||
}
|
||||
|
||||
const b = new ClassicPreset.Node("Fully customized");
|
||||
b.addOutput("a", new ClassicPreset.Output(socket));
|
||||
b.addInput("a", new ClassicPreset.Input(socket));
|
||||
await editor.addNode(b);
|
||||
|
||||
await editor.addConnection(new ClassicPreset.Connection(a, "a", b, "a"));
|
||||
behaviorTreeBuilderStore.nodes.forEach(async (el) => {
|
||||
if (el.connectTo) {
|
||||
const nodeConnectTo = editor.getNode(el.connectTo!);
|
||||
const nodeConnect = editor.getNode(el.id);
|
||||
await editor.addConnection(new ClassicPreset.Connection(nodeConnectTo, "a", nodeConnect, "a"));
|
||||
}
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
AreaExtensions.zoomAt(areaContainer, editor.getNodes());
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
.fill-area {
|
||||
display: table;
|
||||
z-index: -1;
|
||||
position: absolute;
|
||||
top: -320000px;
|
||||
left: -320000px;
|
||||
width: 640000px;
|
||||
height: 640000px;
|
||||
}
|
||||
|
||||
.background {
|
||||
background-color: #ffffff;
|
||||
opacity: 1;
|
||||
background-image: linear-gradient(#f1f1f1 3.2px, transparent 3.2px),
|
||||
linear-gradient(90deg, #f1f1f1 3.2px, transparent 3.2px), linear-gradient(#f1f1f1 1.6px, transparent 1.6px),
|
||||
linear-gradient(90deg, #f1f1f1 1.6px, #ffffff 1.6px);
|
||||
background-size: 80px 80px, 80px 80px, 16px 16px, 16px 16px;
|
||||
background-position: -3.2px -3.2px, -3.2px -3.2px, -1.6px -1.6px, -1.6px -1.6px;
|
||||
}
|
|
@ -0,0 +1,161 @@
|
|||
import * as React from "react";
|
||||
import { ClassicScheme, RenderEmit, Presets } from "rete-react-plugin";
|
||||
import styled, { css } from "styled-components";
|
||||
import { $nodewidth, $socketmargin, $socketsize } from "./vars";
|
||||
|
||||
const { RefSocket, RefControl } = Presets.classic;
|
||||
|
||||
type NodeExtraData = { width?: number; height?: number };
|
||||
|
||||
export const NodeStyles = styled.div<NodeExtraData & { selected: boolean; styles?: (props: any) => any }>`
|
||||
background: blue;
|
||||
border: 2px solid grey;
|
||||
|
||||
cursor: pointer;
|
||||
box-sizing: border-box;
|
||||
width: ${(props) => (Number.isFinite(props.width) ? `${props.width}px` : `${$nodewidth}px`)};
|
||||
height: ${(props) => (Number.isFinite(props.height) ? `${props.height}px` : "auto")};
|
||||
padding-bottom: 6px;
|
||||
|
||||
user-select: none;
|
||||
&:hover {
|
||||
background: #333;
|
||||
}
|
||||
${(props) =>
|
||||
props.selected &&
|
||||
css`
|
||||
border-color: red;
|
||||
`}
|
||||
.title {
|
||||
color: white;
|
||||
font-family: sans-serif;
|
||||
font-size: 18px;
|
||||
padding: 8px;
|
||||
position: relative;
|
||||
top: 12px;
|
||||
text-align-last: center;
|
||||
}
|
||||
.output {
|
||||
text-align: right;
|
||||
}
|
||||
.input {
|
||||
text-align: left;
|
||||
}
|
||||
.output-socket {
|
||||
text-align: right;
|
||||
margin-right: -1px;
|
||||
display: inline-block;
|
||||
}
|
||||
.input-socket {
|
||||
text-align: left;
|
||||
margin-left: -1px;
|
||||
display: inline-block;
|
||||
}
|
||||
.input-title,
|
||||
.output-title {
|
||||
vertical-align: middle;
|
||||
color: white;
|
||||
display: inline-block;
|
||||
font-family: sans-serif;
|
||||
font-size: 14px;
|
||||
margin: ${$socketmargin}px;
|
||||
line-height: ${$socketsize}px;
|
||||
}
|
||||
.input-control {
|
||||
z-index: 1;
|
||||
width: calc(100% - ${$socketsize + 2 * $socketmargin}px);
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
}
|
||||
.control {
|
||||
display: block;
|
||||
padding: ${$socketmargin}px ${$socketsize / 2 + $socketmargin}px;
|
||||
}
|
||||
${(props) => props.styles && props.styles(props)}
|
||||
`;
|
||||
|
||||
function sortByIndex<T extends [string, undefined | { index?: number }][]>(entries: T) {
|
||||
entries.sort((a, b) => {
|
||||
const ai = a[1]?.index || 0;
|
||||
const bi = b[1]?.index || 0;
|
||||
|
||||
return ai - bi;
|
||||
});
|
||||
}
|
||||
|
||||
type Props<S extends ClassicScheme> = {
|
||||
data: S["Node"] & NodeExtraData;
|
||||
styles?: () => any;
|
||||
emit: RenderEmit<S>;
|
||||
};
|
||||
export type NodeComponent<Scheme extends ClassicScheme> = (props: Props<Scheme>) => JSX.Element;
|
||||
|
||||
export function SequenceNode<Scheme extends ClassicScheme>(props: Props<Scheme>) {
|
||||
const inputs = Object.entries(props.data.inputs);
|
||||
const outputs = Object.entries(props.data.outputs);
|
||||
const controls = Object.entries(props.data.controls);
|
||||
const selected = props.data.selected || false;
|
||||
|
||||
const { id, label, width, height } = props.data;
|
||||
|
||||
sortByIndex(inputs);
|
||||
sortByIndex(outputs);
|
||||
sortByIndex(controls);
|
||||
|
||||
return (
|
||||
<NodeStyles selected={selected} width={width} height={height} styles={props.styles} data-testid="node">
|
||||
<div onPointerDown={(e) => {}} className="title" data-testid="title">
|
||||
{label}
|
||||
<div>{id}</div>
|
||||
</div>
|
||||
|
||||
{outputs.map(
|
||||
([key, output]) =>
|
||||
output && (
|
||||
<div className="output" key={key} data-testid={`output-${key}`}>
|
||||
<div className="output-title" data-testid="output-title">
|
||||
{output?.label}
|
||||
</div>
|
||||
<RefSocket
|
||||
name="output-socket"
|
||||
side="output"
|
||||
emit={props.emit}
|
||||
socketKey={key}
|
||||
nodeId={id}
|
||||
payload={output.socket}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
|
||||
{controls.map(([key, control]) => {
|
||||
return control ? <RefControl key={key} name="control" emit={props.emit} payload={control} /> : null;
|
||||
})}
|
||||
{inputs.map(
|
||||
([key, input]) =>
|
||||
input && (
|
||||
<div className="input" key={key} data-testid={`input-${key}`}>
|
||||
<RefSocket
|
||||
name="input-socket"
|
||||
emit={props.emit}
|
||||
side="input"
|
||||
socketKey={key}
|
||||
nodeId={id}
|
||||
payload={input.socket}
|
||||
/>
|
||||
{input && (!input.control || !input.showControl) && (
|
||||
<div className="input-title" data-testid="input-title">
|
||||
{input?.label}
|
||||
</div>
|
||||
)}
|
||||
{input?.control && input?.showControl && (
|
||||
<span className="input-control">
|
||||
<RefControl key={key} name="input-control" emit={props.emit} payload={input.control} />
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</NodeStyles>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
import * as React from "react";
|
||||
import { ClassicScheme, RenderEmit, Presets } from "rete-react-plugin";
|
||||
import styled, { css } from "styled-components";
|
||||
import { $nodewidth, $socketmargin, $socketsize } from "./vars";
|
||||
|
||||
const { RefSocket, RefControl } = Presets.classic;
|
||||
|
||||
type NodeExtraData = { width?: number; height?: number };
|
||||
|
||||
export const NodeStyles = styled.div<NodeExtraData & { selected: boolean; styles?: (props: any) => any }>`
|
||||
background: black;
|
||||
border: 2px solid grey;
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
box-sizing: border-box;
|
||||
width: ${(props) => (Number.isFinite(props.width) ? `${props.width}px` : `${$nodewidth}px`)};
|
||||
height: ${(props) => (Number.isFinite(props.height) ? `${props.height}px` : "auto")};
|
||||
padding-bottom: 6px;
|
||||
position: relative;
|
||||
user-select: none;
|
||||
&:hover {
|
||||
background: #333;
|
||||
}
|
||||
${(props) =>
|
||||
props.selected &&
|
||||
css`
|
||||
border-color: red;
|
||||
`}
|
||||
.title {
|
||||
color: white;
|
||||
font-family: sans-serif;
|
||||
font-size: 18px;
|
||||
padding: 8px;
|
||||
height: 100%;
|
||||
}
|
||||
.output {
|
||||
text-align: right;
|
||||
}
|
||||
.input {
|
||||
text-align: left;
|
||||
}
|
||||
.output-socket {
|
||||
text-align: right;
|
||||
margin-right: -1px;
|
||||
display: inline-block;
|
||||
}
|
||||
.input-socket {
|
||||
text-align: left;
|
||||
margin-left: -1px;
|
||||
display: inline-block;
|
||||
}
|
||||
.input-title,
|
||||
.output-title {
|
||||
vertical-align: middle;
|
||||
color: white;
|
||||
display: inline-block;
|
||||
font-family: sans-serif;
|
||||
font-size: 14px;
|
||||
margin: ${$socketmargin}px;
|
||||
line-height: ${$socketsize}px;
|
||||
}
|
||||
.input-control {
|
||||
z-index: 1;
|
||||
width: calc(100% - ${$socketsize + 2 * $socketmargin}px);
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
}
|
||||
.control {
|
||||
display: block;
|
||||
padding: ${$socketmargin}px ${$socketsize / 2 + $socketmargin}px;
|
||||
}
|
||||
${(props) => props.styles && props.styles(props)}
|
||||
`;
|
||||
|
||||
function sortByIndex<T extends [string, undefined | { index?: number }][]>(entries: T) {
|
||||
entries.sort((a, b) => {
|
||||
const ai = a[1]?.index || 0;
|
||||
const bi = b[1]?.index || 0;
|
||||
|
||||
return ai - bi;
|
||||
});
|
||||
}
|
||||
|
||||
type Props<S extends ClassicScheme> = {
|
||||
data: S["Node"] & NodeExtraData;
|
||||
styles?: () => any;
|
||||
emit: RenderEmit<S>;
|
||||
};
|
||||
export type NodeComponent<Scheme extends ClassicScheme> = (props: Props<Scheme>) => JSX.Element;
|
||||
|
||||
export function CustomNode<Scheme extends ClassicScheme>(props: Props<Scheme>) {
|
||||
const inputs = Object.entries(props.data.inputs);
|
||||
const outputs = Object.entries(props.data.outputs);
|
||||
const controls = Object.entries(props.data.controls);
|
||||
const selected = props.data.selected || false;
|
||||
const { id, label, width, height } = props.data;
|
||||
|
||||
sortByIndex(inputs);
|
||||
sortByIndex(outputs);
|
||||
sortByIndex(controls);
|
||||
|
||||
return (
|
||||
<NodeStyles selected={selected} width={width} height={height} styles={props.styles} data-testid="node">
|
||||
<div
|
||||
onPointerDown={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
className="title"
|
||||
data-testid="title"
|
||||
>
|
||||
{label}
|
||||
</div>
|
||||
|
||||
{outputs.map(
|
||||
([key, output]) =>
|
||||
output && (
|
||||
<div className="output" key={key} data-testid={`output-${key}`}>
|
||||
<div style={{ color: "white" }}>BODY</div>
|
||||
<div className="output-title" data-testid="output-title">
|
||||
{output?.label}
|
||||
</div>
|
||||
<RefSocket
|
||||
name="output-socket"
|
||||
side="output"
|
||||
emit={props.emit}
|
||||
socketKey={key}
|
||||
nodeId={id}
|
||||
payload={output.socket}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
|
||||
{controls.map(([key, control]) => {
|
||||
return control ? <RefControl key={key} name="control" emit={props.emit} payload={control} /> : null;
|
||||
})}
|
||||
{inputs.map(
|
||||
([key, input]) =>
|
||||
input && (
|
||||
<div className="input" key={key} data-testid={`input-${key}`}>
|
||||
<RefSocket
|
||||
name="input-socket"
|
||||
emit={props.emit}
|
||||
side="input"
|
||||
socketKey={key}
|
||||
nodeId={id}
|
||||
payload={input.socket}
|
||||
/>
|
||||
{input && (!input.control || !input.showControl) && (
|
||||
<div className="input-title" data-testid="input-title">
|
||||
{input?.label}
|
||||
</div>
|
||||
)}
|
||||
{input?.control && input?.showControl && (
|
||||
<span className="input-control">
|
||||
<RefControl key={key} name="input-control" emit={props.emit} payload={input.control} />
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</NodeStyles>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
import { Presets } from "rete-react-plugin";
|
||||
import { css } from "styled-components";
|
||||
import "./background.css";
|
||||
|
||||
const styles = css<{ selected?: boolean }>`
|
||||
background: #ebebeb;
|
||||
border-color: #646464;
|
||||
.title {
|
||||
color: #646464;
|
||||
}
|
||||
&:hover {
|
||||
background: #f2f2f2;
|
||||
}
|
||||
.output-socket {
|
||||
margin-right: -1px;
|
||||
}
|
||||
.input-socket {
|
||||
margin-left: -1px;
|
||||
}
|
||||
${(props) =>
|
||||
props.selected &&
|
||||
css`
|
||||
border-color: red;
|
||||
`}
|
||||
`;
|
||||
|
||||
export function StyledNode(props: any) {
|
||||
// eslint-disable-next-line react/jsx-pascal-case
|
||||
return <Presets.classic.Node styles={() => styles} {...props} />;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
export const $nodewidth = 200;
|
||||
export const $socketmargin = 6;
|
||||
export const $socketsize = 16;
|
|
@ -16,10 +16,10 @@ interface IRefListerProps {
|
|||
}
|
||||
|
||||
export const RefListener = (props: IRefListerProps) => {
|
||||
const canvasRef = React.useRef<HTMLDataElement>(null);
|
||||
const ref = React.useRef<HTMLDataElement>(null);
|
||||
React.useEffect(() => {
|
||||
if (canvasRef.current) {
|
||||
canvasRef.current.addEventListener("dragend", (e) => {
|
||||
if (ref.current) {
|
||||
ref.current.addEventListener("dragend", (e) => {
|
||||
// @ts-expect-error
|
||||
if (e.target.innerHTML) {
|
||||
// @ts-expect-error
|
||||
|
@ -27,7 +27,7 @@ export const RefListener = (props: IRefListerProps) => {
|
|||
}
|
||||
});
|
||||
}
|
||||
}, [canvasRef, props]);
|
||||
}, [ref, props]);
|
||||
|
||||
return (
|
||||
<div {...props.getNodeProps({ onClick: props.handleExpand })}>
|
||||
|
@ -36,7 +36,7 @@ export const RefListener = (props: IRefListerProps) => {
|
|||
props.handleSelect(e);
|
||||
}}
|
||||
/>
|
||||
<span ref={canvasRef} style={{ color: "white" }} draggable="true">
|
||||
<span ref={ref} style={{ color: "white" }} draggable="true">
|
||||
{props.element.name}
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
import { HttpMethod, HttpRepository } from "../../../core/repository/http_repository";
|
||||
import { ITriggerModel } from "../../../core/model/trigger_model";
|
||||
import { Result } from "../../../core/helper/result";
|
||||
import { IProcess } from "../../create_process/model/process_model";
|
||||
import { PipelineModelDataBase } from "../model/pipeline_model";
|
||||
|
||||
export class CreatePipelineRepository extends HttpRepository {
|
||||
async savePipeline(model: PipelineModelDataBase): Promise<Result<Error, any>> {
|
||||
return await this._jsonRequest(HttpMethod.POST, `/pipeline`, model);
|
||||
}
|
||||
async getTriggers(page = 1): Promise<Result<Error, ITriggerModel[]>> {
|
||||
return await this._jsonRequest(HttpMethod.GET, `/trigger?${page}`);
|
||||
}
|
||||
|
||||
async getProcessed(page = 1): Promise<Result<Error, IProcess[]>> {
|
||||
return await this._jsonRequest<IProcess[]>(HttpMethod.GET, `/process?${page}`);
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
export interface IColor {
|
||||
color: string;
|
||||
}
|
||||
|
||||
export interface PipelineModelDataBase {
|
||||
process: string;
|
||||
trigger: string;
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
import * as React from "react";
|
||||
import { Row, Button } from "antd";
|
||||
import { LoadPage } from "../../../core/ui/pages/load_page";
|
||||
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Icon, List } from "../../../core/ui/list/list";
|
||||
import { CreateTriggerScreenPath } from "../../create_trigger/presentation/create_trigger_screen";
|
||||
import { CreateProcessScreenPath } from "../../create_process/presentation/create_process_screen";
|
||||
import { CreatePipelineStore } from "./create_pipeline_store";
|
||||
import { CreatePipelineRepository } from "../data/create_pipeline_repository";
|
||||
|
||||
export const CreatePipelineScreenPath = "/create_pipeline";
|
||||
|
||||
export const CreatePipelineScreen: React.FunctionComponent = observer(() => {
|
||||
const [createPipelineStore] = React.useState(() => new CreatePipelineStore(new CreatePipelineRepository()));
|
||||
|
||||
React.useEffect(() => {}, [createPipelineStore]);
|
||||
return (
|
||||
<>
|
||||
<LoadPage
|
||||
largeText={"Create pipeline"}
|
||||
isError={createPipelineStore.isError}
|
||||
isLoading={createPipelineStore.isLoading}
|
||||
children={
|
||||
<>
|
||||
<Row>
|
||||
<List
|
||||
headers={"process"}
|
||||
link={{ path: CreateProcessScreenPath, text: "create process" }}
|
||||
values={createPipelineStore.processModels.map((el) => {
|
||||
return { text: el.description, id: el._id };
|
||||
})}
|
||||
onClick={(e) => createPipelineStore.addProcess(e.text, e.id!)}
|
||||
icon={Icon.add}
|
||||
/>
|
||||
<div style={{ flexGrow: "1" }}>
|
||||
<Button onClick={() => createPipelineStore.createPipeline()}>Save result</Button>
|
||||
<List
|
||||
headers="new pipeline"
|
||||
values={createPipelineStore.pipelineViewModels}
|
||||
icon={Icon.delete}
|
||||
onClick={(_e, index) => {
|
||||
createPipelineStore.filterPipelineViewModel(index);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<List
|
||||
headers="triggers"
|
||||
link={{ path: CreateTriggerScreenPath, text: "create trigger" }}
|
||||
values={createPipelineStore.triggersModels.map((el) => {
|
||||
return { text: el.description, id: el._id };
|
||||
})}
|
||||
onClick={(e) => createPipelineStore.addTrigger(e.text, e.id!)}
|
||||
icon={Icon.add}
|
||||
/>
|
||||
</Row>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
});
|
|
@ -1,99 +0,0 @@
|
|||
import makeAutoObservable from "mobx-store-inheritance";
|
||||
import { CreatePipelineRepository } from "../data/create_pipeline_repository";
|
||||
import { ITriggerModel } from "../../../core/model/trigger_model";
|
||||
import { IProcess } from "../../create_process/model/process_model";
|
||||
import { message } from "antd";
|
||||
import { SimpleErrorState } from "../../../core/store/base_store";
|
||||
|
||||
enum Type {
|
||||
PROCESS,
|
||||
TRIGGER,
|
||||
}
|
||||
export interface UnionView {
|
||||
text: string;
|
||||
color: string;
|
||||
type: Type;
|
||||
uuid?: string;
|
||||
}
|
||||
|
||||
export class CreatePipelineStore extends SimpleErrorState {
|
||||
repository: CreatePipelineRepository;
|
||||
triggersModels: ITriggerModel[] = [];
|
||||
processModels: IProcess[] = [];
|
||||
pipelineViewModels: UnionView[] = [];
|
||||
|
||||
constructor(repository: CreatePipelineRepository) {
|
||||
super();
|
||||
this.repository = repository;
|
||||
makeAutoObservable(this);
|
||||
this.init();
|
||||
}
|
||||
private init() {
|
||||
this.loadTriggers();
|
||||
this.loadProcess();
|
||||
}
|
||||
|
||||
filterPipelineViewModel(index: number): void {
|
||||
this.pipelineViewModels = this.pipelineViewModels.filter((_el, i) => i !== index);
|
||||
}
|
||||
addTrigger(e: string, id: string): void {
|
||||
const lastElement = this.pipelineViewModels.lastElement();
|
||||
if (this.pipelineViewModels.length === 2) {
|
||||
return;
|
||||
}
|
||||
if (lastElement !== undefined) {
|
||||
if (lastElement.type !== Type.PROCESS) {
|
||||
message.error("Need process");
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.pipelineViewModels.push({
|
||||
uuid: id,
|
||||
text: e,
|
||||
color: "blanchedalmond",
|
||||
type: Type.TRIGGER,
|
||||
});
|
||||
}
|
||||
addProcess(e: string, id: string): void {
|
||||
const lastElement = this.pipelineViewModels.lastElement();
|
||||
if (this.pipelineViewModels.length === 2) {
|
||||
return;
|
||||
}
|
||||
if (lastElement !== undefined) {
|
||||
if (lastElement.type !== Type.TRIGGER) {
|
||||
message.error("Need trigger");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.pipelineViewModels.push({
|
||||
uuid: id,
|
||||
text: e,
|
||||
color: "activeborder",
|
||||
type: Type.PROCESS,
|
||||
});
|
||||
}
|
||||
|
||||
async createPipeline(): Promise<void> {
|
||||
if (this.pipelineViewModels.isEmpty()) {
|
||||
message.error("not found pipelines process");
|
||||
return;
|
||||
}
|
||||
const triggerId = this.pipelineViewModels.find((el) => el.type === Type.TRIGGER)!.uuid as string;
|
||||
const processId = this.pipelineViewModels.find((el) => el.type === Type.PROCESS)!.uuid as string;
|
||||
|
||||
this.repository.savePipeline({
|
||||
process: processId,
|
||||
trigger: triggerId,
|
||||
});
|
||||
}
|
||||
|
||||
async loadProcess() {
|
||||
this.mapOk("processModels", this.repository.getProcessed());
|
||||
}
|
||||
|
||||
async loadTriggers() {
|
||||
this.mapOk("triggersModels", this.repository.getTriggers());
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
import { HttpMethod, HttpRepository } from "../../../core/repository/http_repository";
|
||||
import { IProcess } from "../model/process_model";
|
||||
|
||||
export class ProcessRepository extends HttpRepository {
|
||||
async save(model: IProcess): Promise<void> {
|
||||
await this._jsonRequest(HttpMethod.POST, "/process", model);
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
import { DatabaseModel } from "../../../core/model/database_model";
|
||||
|
||||
export interface IProcess extends DatabaseModel {
|
||||
description: string;
|
||||
type: EXEC_TYPE | string;
|
||||
command: string;
|
||||
isGenerating: boolean;
|
||||
isLocaleCode: boolean;
|
||||
issueType: IssueType | string;
|
||||
timeout?: number;
|
||||
commit?: string | undefined;
|
||||
}
|
||||
|
||||
export enum EXEC_TYPE {
|
||||
SPAWN = "SPAWN",
|
||||
EXEC = "EXEC",
|
||||
}
|
||||
export enum IssueType {
|
||||
WARNING = "WARNING",
|
||||
ERROR = "ERROR",
|
||||
}
|
||||
export const processModelMock: IProcess = {
|
||||
description: "",
|
||||
type: EXEC_TYPE.SPAWN,
|
||||
command: "",
|
||||
isGenerating: true,
|
||||
isLocaleCode: true,
|
||||
issueType: IssueType.WARNING,
|
||||
};
|
|
@ -1,65 +0,0 @@
|
|||
import * as React from "react";
|
||||
import { processStore } from "./logic/process_store";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { SubmitButton, Input, ResetButton, Form, Radio, Switch } from "formik-antd";
|
||||
import { Formik } from "formik";
|
||||
import { Row, Col } from "antd";
|
||||
import { EXEC_TYPE, IssueType, processModelMock } from "../model/process_model";
|
||||
export const CreateProcessScreenPath = "/create/process";
|
||||
export const CreateProcessScreen = observer(() => {
|
||||
return (
|
||||
<div>
|
||||
<div style={{ marginTop: 80 }}>
|
||||
<Formik
|
||||
initialValues={processModelMock}
|
||||
onSubmit={async (values, actions) => {
|
||||
await processStore.saveResult(values);
|
||||
actions.setSubmitting(false);
|
||||
actions.resetForm();
|
||||
}}
|
||||
validate={(values) => {
|
||||
if (!values.command) {
|
||||
return { command: "required" };
|
||||
}
|
||||
if (!values.description) {
|
||||
return { description: "required" };
|
||||
}
|
||||
return {};
|
||||
}}
|
||||
render={() => (
|
||||
<Form>
|
||||
<div
|
||||
style={{
|
||||
background: "white",
|
||||
flex: 1,
|
||||
padding: 40,
|
||||
width: "400px",
|
||||
}}
|
||||
>
|
||||
<Input name="description" placeholder="Description" />
|
||||
|
||||
<Input name="command" placeholder="Command process" />
|
||||
|
||||
<Radio.Group name="type" options={Object.values(EXEC_TYPE)} />
|
||||
|
||||
<Radio.Group name="issueType" options={Object.values(IssueType)} />
|
||||
|
||||
<Col style={{ marginTop: 20, justifyContent: "center" }}>
|
||||
<Switch name="isGenerating" checkedChildren="is generating" unCheckedChildren="is generating" />
|
||||
<Switch name="isLocalCode" checkedChildren="is local code" unCheckedChildren="is local code" />
|
||||
</Col>
|
||||
|
||||
<Row style={{ marginTop: 20, justifyContent: "center" }}>
|
||||
<Col>
|
||||
<ResetButton>Reset</ResetButton>
|
||||
<SubmitButton>Submit</SubmitButton>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
</Form>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
|
@ -1,16 +0,0 @@
|
|||
import { makeAutoObservable } from "mobx";
|
||||
import { ProcessRepository } from "../../data/process_repostiory";
|
||||
import { IProcess } from "../../model/process_model";
|
||||
|
||||
class ProcessStore {
|
||||
repository: ProcessRepository;
|
||||
constructor(repository: ProcessRepository) {
|
||||
this.repository = repository;
|
||||
makeAutoObservable(this);
|
||||
}
|
||||
async saveResult(model: IProcess) {
|
||||
await this.repository.save(model);
|
||||
}
|
||||
}
|
||||
|
||||
export const processStore = new ProcessStore(new ProcessRepository());
|
|
@ -2,19 +2,19 @@ import { Result } from "../../core/helper/result";
|
|||
import { DatabaseModel } from "../../core/model/database_model";
|
||||
import { ITriggerModel } from "../../core/model/trigger_model";
|
||||
import { HttpMethod, HttpRepository } from "../../core/repository/http_repository";
|
||||
import { IProcess } from "../create_process/model/process_model";
|
||||
import { UUID } from "../all_projects/data/project_repository";
|
||||
import { ICreateProjectViewModel } from "./project_model";
|
||||
|
||||
export interface PipelineModel extends DatabaseModel {
|
||||
process: IProcess;
|
||||
process: any;
|
||||
trigger: ITriggerModel;
|
||||
}
|
||||
|
||||
export class CreateProjectRepository extends HttpRepository {
|
||||
async getAllPipelines(page = 1): Promise<Result<Error, PipelineModel[]>> {
|
||||
return await this._jsonRequest<PipelineModel[]>(HttpMethod.GET, "/pipeline");
|
||||
async saveProject(model: ICreateProjectViewModel): Promise<Result<Error, UUID>> {
|
||||
return await this._jsonRequest<UUID>(HttpMethod.POST, "/projects", model);
|
||||
}
|
||||
async saveProject(model: ICreateProjectViewModel): Promise<Result<Error, void>> {
|
||||
return await this._jsonRequest<void>(HttpMethod.POST, "/project", model);
|
||||
async setProjectRootFile(file: File, projectId: string) {
|
||||
return await this._formDataRequest(HttpMethod.POST, `/projects/upload?id=${projectId}`, file);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,85 +1,46 @@
|
|||
import * as React from "react";
|
||||
import { LoadPage as MainPage } from "../../core/ui/pages/load_page";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Col, Row, Input, Button } from "antd";
|
||||
import { ReactComponent as AddIcon } from "../../core/assets/icons/add.svg";
|
||||
import { CreatePipelineScreenPath } from "../create_pipeline/presentation/create_pipeline_screen";
|
||||
import { Col, Row, Input, Button, Upload } from "antd";
|
||||
import type { UploadProps } from "antd";
|
||||
import { CreateProjectStore } from "./create_project_store";
|
||||
import { CreateProjectRepository } from "./create_project_repository";
|
||||
|
||||
export const CreateProjectScreenPath = "/create_project";
|
||||
|
||||
export const CreateProjectScreen: React.FunctionComponent = observer(() => {
|
||||
const [createProjectStore] = React.useState(() => new CreateProjectStore(new CreateProjectRepository()));
|
||||
const [store] = React.useState(() => new CreateProjectStore(new CreateProjectRepository()));
|
||||
|
||||
React.useEffect(() => {
|
||||
createProjectStore.init();
|
||||
}, [createProjectStore]);
|
||||
store.init();
|
||||
}, [store]);
|
||||
return (
|
||||
<>
|
||||
<MainPage
|
||||
path={CreatePipelineScreenPath}
|
||||
path={""}
|
||||
largeText={"Create project"}
|
||||
minText={"add new pipelines?"}
|
||||
isLoading={createProjectStore.isLoading}
|
||||
isError={createProjectStore.isError}
|
||||
isLoading={store.isLoading}
|
||||
isError={store.isError}
|
||||
children={
|
||||
<Row>
|
||||
<Col>
|
||||
<>Pipelines</>
|
||||
{createProjectStore.pipelineModels?.map((el) => {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: "ActiveBorder",
|
||||
margin: "10px",
|
||||
width: "400px",
|
||||
<Row>
|
||||
<Col>
|
||||
<Row>
|
||||
<Input
|
||||
style={{ width: "250px" }}
|
||||
onChange={(e) => store.setDescriptionToNewProject(e.target.value)}
|
||||
placeholder="project description"
|
||||
/>
|
||||
<Upload
|
||||
onChange={(e) => {
|
||||
store.file = e.file.originFileObj;
|
||||
}}
|
||||
>
|
||||
<div>{el.process.description}</div>
|
||||
<div>{el.trigger.description}</div>
|
||||
<AddIcon
|
||||
style={{
|
||||
width: "50px",
|
||||
cursor: "pointer",
|
||||
height: "50px",
|
||||
marginRight: "40px",
|
||||
}}
|
||||
onClick={() => {
|
||||
createProjectStore.addPipeline(el);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</Col>
|
||||
<Col>
|
||||
<Row>
|
||||
<Input
|
||||
style={{ width: "250px" }}
|
||||
onChange={(e) => createProjectStore.setDescriptionToNewProject(e.target.value)}
|
||||
placeholder="project description"
|
||||
/>
|
||||
|
||||
<Button onClick={() => createProjectStore.saveProject()}>save</Button>
|
||||
</Row>
|
||||
|
||||
{createProjectStore.newProjectViews.map((el, index) => {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: "ActiveBorder",
|
||||
margin: "10px",
|
||||
width: "400px",
|
||||
}}
|
||||
>
|
||||
<div>{el.process.description}</div>
|
||||
<div>{el.trigger.description}</div>
|
||||
<div>{index + 1}</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</Col>
|
||||
<Button>Click to Upload</Button>
|
||||
</Upload>
|
||||
<Button onClick={() => store.saveProject()}>save</Button>
|
||||
</Row>
|
||||
</Col>
|
||||
</Row>
|
||||
</Row>
|
||||
}
|
||||
/>
|
||||
|
|
|
@ -1,30 +1,20 @@
|
|||
import makeAutoObservable from "mobx-store-inheritance";
|
||||
import { CreateProjectRepository, PipelineModel } from "./create_project_repository";
|
||||
import { CreateProjectRepository } from "./create_project_repository";
|
||||
import { message } from "antd";
|
||||
import { SimpleErrorState } from "../../core/store/base_store";
|
||||
|
||||
export class CreateProjectStore extends SimpleErrorState {
|
||||
repository: CreateProjectRepository;
|
||||
|
||||
pipelineModels?: PipelineModel[];
|
||||
newProjectDescription: string = "";
|
||||
newProjectViews: PipelineModel[] = [];
|
||||
file?: File;
|
||||
|
||||
constructor(repository: CreateProjectRepository) {
|
||||
super();
|
||||
this.repository = repository;
|
||||
makeAutoObservable(this);
|
||||
}
|
||||
async init() {
|
||||
await this.loadPipelines();
|
||||
}
|
||||
async addPipeline(model: PipelineModel) {
|
||||
this.newProjectViews.push(model);
|
||||
}
|
||||
|
||||
async loadPipelines() {
|
||||
this.mapOk("pipelineModels", this.repository.getAllPipelines());
|
||||
}
|
||||
async init() {}
|
||||
|
||||
setDescriptionToNewProject(value: string): void {
|
||||
this.newProjectDescription = value;
|
||||
|
@ -35,27 +25,27 @@ export class CreateProjectStore extends SimpleErrorState {
|
|||
message.error("project description is not empty");
|
||||
return;
|
||||
}
|
||||
if (this.newProjectViews.isEmpty()) {
|
||||
message.error("project view is not empty");
|
||||
if (this.file === undefined) {
|
||||
message.error("upload file");
|
||||
return;
|
||||
}
|
||||
|
||||
this.isLoading = true;
|
||||
const result = await this.repository.saveProject({
|
||||
description: this.newProjectDescription,
|
||||
pipelines: this.newProjectViews.map((el) => el._id ?? ""),
|
||||
});
|
||||
|
||||
this.newProjectDescription = "";
|
||||
this.newProjectViews = [];
|
||||
this.isLoading = false;
|
||||
|
||||
result.fold(
|
||||
(_s) => {
|
||||
message.success("save");
|
||||
(
|
||||
await this.repository.saveProject({
|
||||
description: this.newProjectDescription,
|
||||
})
|
||||
).fold(
|
||||
(uuid) => {
|
||||
this.newProjectDescription = "";
|
||||
this.isLoading = false;
|
||||
this.repository.setProjectRootFile(this.file as File, uuid.id);
|
||||
},
|
||||
(_e) => {
|
||||
(_) => {
|
||||
this.isError = true;
|
||||
}
|
||||
);
|
||||
|
||||
//
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
export interface ICreateProjectViewModel {
|
||||
pipelines: string[];
|
||||
|
||||
description: string;
|
||||
}
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
import * as React from "react";
|
||||
import { CreateProjectInstanceStore } from "./create_project_instance_store";
|
||||
import { CreateProjectInstanceRepository } from "./create_project_instance_repository";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { Upload, Button } from "antd";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import { Input } from "antd";
|
||||
|
||||
export const CreateProjectInstancePath = "/create/project/instance/";
|
||||
|
||||
export const CreateProjectInstanceScreen = observer(() => {
|
||||
const [createProjectInstanceStore] = React.useState(
|
||||
() => new CreateProjectInstanceStore(new CreateProjectInstanceRepository())
|
||||
);
|
||||
const id = useParams().id;
|
||||
const navigate = useNavigate();
|
||||
|
||||
React.useEffect(() => {
|
||||
createProjectInstanceStore.init(navigate, id as string);
|
||||
}, [id, createProjectInstanceStore, navigate]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1>project description</h1>
|
||||
<Input onChange={(e) => createProjectInstanceStore.setProjectDescription(e.target.value)}></Input>
|
||||
<h1>root entity</h1>
|
||||
<Upload
|
||||
onChange={(e) => {
|
||||
createProjectInstanceStore.file = e.file.originFileObj;
|
||||
}}
|
||||
>
|
||||
<Button>Upload root entity</Button>
|
||||
</Upload>
|
||||
<Button onClick={() => createProjectInstanceStore.saveInstance()}>Save</Button>
|
||||
</>
|
||||
);
|
||||
});
|
|
@ -1,16 +0,0 @@
|
|||
import { HttpMethod, HttpRepository } from "../../core/repository/http_repository";
|
||||
import { NewProjectModel } from "./new_project_model";
|
||||
|
||||
export class CreateProjectInstanceRepository extends HttpRepository {
|
||||
async setProjectRootFile(file: File) {
|
||||
return await this._formDataRequest(HttpMethod.POST, "/project_instance/upload", file);
|
||||
}
|
||||
|
||||
async createNewProject(project: NewProjectModel) {
|
||||
return await this._jsonRequest(HttpMethod.POST, "/project_instance", project);
|
||||
}
|
||||
|
||||
async setActiveProject(id: string) {
|
||||
return await this._jsonRequest(HttpMethod.POST, `/project_instance/set/active/project?id=${id}`);
|
||||
}
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
import makeAutoObservable from "mobx-store-inheritance";
|
||||
import { SimpleErrorState } from "../../core/store/base_store";
|
||||
import { CreateProjectInstanceRepository } from "./create_project_instance_repository";
|
||||
import { message } from "antd";
|
||||
import { HttpMethod } from "../../core/repository/http_repository";
|
||||
import { NavigateFunction } from "react-router-dom";
|
||||
import { NewProjectModel } from "./new_project_model";
|
||||
|
||||
export class CreateProjectInstanceStore extends SimpleErrorState {
|
||||
newProjectModel: NewProjectModel;
|
||||
repository: CreateProjectInstanceRepository;
|
||||
file?: File;
|
||||
navigate?: NavigateFunction;
|
||||
constructor(repository: CreateProjectInstanceRepository) {
|
||||
super();
|
||||
this.repository = repository;
|
||||
makeAutoObservable(this);
|
||||
this.newProjectModel = NewProjectModel.empty();
|
||||
}
|
||||
|
||||
setProjectDescription(value: string): void {
|
||||
this.newProjectModel.description = value;
|
||||
}
|
||||
init(navigate: NavigateFunction, projectId: string) {
|
||||
this.navigate = navigate;
|
||||
this.newProjectModel.project = projectId;
|
||||
}
|
||||
async saveInstance(): Promise<void> {
|
||||
if (this.file === undefined) {
|
||||
message.error("Need upload file");
|
||||
} else {
|
||||
if (this.newProjectModel.isValid()) {
|
||||
await this.repository.createNewProject(this.newProjectModel);
|
||||
await this.repository.setProjectRootFile(this.file);
|
||||
if (this.navigate !== undefined) this.navigate("/");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
export class NewProjectModel {
|
||||
project: string;
|
||||
description: string;
|
||||
constructor(project: string, description: string) {
|
||||
this.project = project;
|
||||
this.description = description;
|
||||
}
|
||||
static empty() {
|
||||
return new NewProjectModel("", "");
|
||||
}
|
||||
isValid(): boolean {
|
||||
return this.project.isNotEmpty() && this.description.isNotEmpty();
|
||||
}
|
||||
messages() {
|
||||
return "";
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
import { HttpMethod, HttpRepository } from "../../../core/repository/http_repository";
|
||||
import { ITriggerModel } from "../../../core/model/trigger_model";
|
||||
|
||||
export class TriggerRepository extends HttpRepository {
|
||||
public async save(model: ITriggerModel) {
|
||||
return await this._jsonRequest(HttpMethod.POST, "/trigger", model);
|
||||
}
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
import { TriggerType } from "../../../core/model/trigger_model";
|
||||
|
||||
export interface ITriggerViewValueModel {
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface TriggerViewModel extends ITriggerViewValueModel {
|
||||
id: string;
|
||||
type: TriggerType;
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
import * as React from "react";
|
||||
import Editor from "@monaco-editor/react";
|
||||
import { Button } from "antd";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { CallBackStringVoidFunction } from "../../../../core/extensions/extensions";
|
||||
|
||||
interface ICodeTriggerFormProps {
|
||||
codeTriggerValue: string;
|
||||
clearTriggerCode: VoidFunction;
|
||||
saveCode: VoidFunction;
|
||||
writeNewTrigger: CallBackStringVoidFunction;
|
||||
}
|
||||
|
||||
export const CodeTriggerForm: React.FunctionComponent<ICodeTriggerFormProps> = observer(
|
||||
(props: ICodeTriggerFormProps) => {
|
||||
return (
|
||||
<>
|
||||
<div style={{ width: "100%", backgroundColor: "black", height: "1px" }} />
|
||||
|
||||
<Editor
|
||||
height="40vh"
|
||||
defaultLanguage="javascript"
|
||||
value={props.codeTriggerValue}
|
||||
onChange={(v) => {
|
||||
props.writeNewTrigger(v ?? "");
|
||||
}}
|
||||
onValidate={(_m) => {}}
|
||||
/>
|
||||
|
||||
<div style={{ width: "100%", backgroundColor: "black", height: "1px" }} />
|
||||
<div style={{ height: "10px" }} />
|
||||
|
||||
<Button onClick={() => props.saveCode()} style={{ marginLeft: "10px", marginRight: "10px" }}>
|
||||
Save code
|
||||
</Button>
|
||||
|
||||
<Button onClick={() => props.clearTriggerCode()}>Reset code</Button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
);
|
|
@ -1,51 +0,0 @@
|
|||
import * as React from "react";
|
||||
import { Formik } from "formik";
|
||||
import { SubmitButton, Input, ResetButton, Form, FormItem } from "formik-antd";
|
||||
import { Row, Col } from "antd";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { TriggerType } from "../../../../core/model/trigger_model";
|
||||
import { validateRequired } from "../../../../core/helper/validate";
|
||||
export interface IFileTriggerFormProps {
|
||||
pushTrigger: (value: string, type: TriggerType) => void;
|
||||
}
|
||||
export const FileTriggerForm: React.FunctionComponent<IFileTriggerFormProps> = observer(
|
||||
(props: IFileTriggerFormProps) => {
|
||||
return (
|
||||
<>
|
||||
<div style={{ marginTop: 80 }}>
|
||||
<Formik
|
||||
initialValues={{
|
||||
value: "",
|
||||
}}
|
||||
onSubmit={(values, actions) => {
|
||||
props.pushTrigger(values.value, TriggerType.FILE);
|
||||
actions.setSubmitting(false);
|
||||
actions.resetForm();
|
||||
}}
|
||||
validate={(values) => {
|
||||
if (values.value.length === 0) {
|
||||
return false;
|
||||
}
|
||||
return {};
|
||||
}}
|
||||
render={() => (
|
||||
<Form>
|
||||
<div style={{ background: "white", flex: 1, padding: 40 }}>
|
||||
<FormItem name="value" required={true} validate={validateRequired}>
|
||||
<Input name="value" placeholder="regExp file" />
|
||||
</FormItem>
|
||||
<Row style={{ marginTop: 60 }}>
|
||||
<Col offset={8}>
|
||||
<ResetButton>Reset</ResetButton>
|
||||
<SubmitButton>Submit</SubmitButton>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
</Form>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
);
|
|
@ -1,75 +0,0 @@
|
|||
import * as React from "react";
|
||||
import { Button, Col, Row, Switch, Typography, Input } from "antd";
|
||||
import { CodeTriggerForm } from "./components/code_trigger_form";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { FileTriggerForm } from "./components/file_trigger_form";
|
||||
import { ReactComponent as DeleteIcon } from "../../../core/assets/icons/delete.svg";
|
||||
import { Loader } from "../../../core/ui/loader/loader";
|
||||
import { TriggerRepository } from "../data/trigger_repository";
|
||||
import { TriggerStore } from "./trigger_store";
|
||||
import { TriggerViewModel } from "../model/trigger_form_view_model";
|
||||
import { CallBackStringVoidFunction } from "../../../core/extensions/extensions";
|
||||
|
||||
const { Title } = Typography;
|
||||
|
||||
const Bottom = observer((props: { triggers: TriggerViewModel[]; callBack: CallBackStringVoidFunction }) => {
|
||||
return (
|
||||
<Col>
|
||||
{props.triggers.map((el) => {
|
||||
return (
|
||||
<Row
|
||||
style={{
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
{el.value}
|
||||
<DeleteIcon onClick={() => props.callBack(el.id)} />
|
||||
</Row>
|
||||
);
|
||||
})}
|
||||
</Col>
|
||||
);
|
||||
});
|
||||
export const CreateTriggerScreenPath = "/create/trigger";
|
||||
|
||||
export const TriggerScreen: React.FunctionComponent = observer(() => {
|
||||
const [triggerStore] = React.useState(() => new TriggerStore(new TriggerRepository()));
|
||||
return (
|
||||
<>
|
||||
<main>
|
||||
{!triggerStore.isLoading ? (
|
||||
<>
|
||||
<Row style={{ justifyItems: "center", alignItems: "center" }}>
|
||||
<div style={{ height: "37px" }}>
|
||||
<Switch checked={triggerStore.getTriggerType()} onChange={() => triggerStore.setTriggerType()} />
|
||||
</div>
|
||||
<Title level={2}>Trigger editor: {triggerStore.getTriggerDescription()}</Title>
|
||||
<div style={{ width: "10px" }}></div>
|
||||
<Button onClick={() => triggerStore.saveResult()}>Save result</Button>
|
||||
</Row>
|
||||
<Input placeholder="trigger description" onChange={() => triggerStore.changeTriggerDescription} />
|
||||
{triggerStore.getTriggerType() ? (
|
||||
<>
|
||||
<FileTriggerForm pushTrigger={triggerStore.pushTrigger} />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<CodeTriggerForm
|
||||
codeTriggerValue={triggerStore.codeTriggerValue}
|
||||
clearTriggerCode={triggerStore.clearTriggerCode}
|
||||
saveCode={triggerStore.saveCode}
|
||||
writeNewTrigger={triggerStore.writeNewTrigger}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<Bottom triggers={triggerStore.triggers} callBack={triggerStore.deleteItem} />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Loader />
|
||||
</>
|
||||
)}
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
});
|
|
@ -1,84 +0,0 @@
|
|||
import makeAutoObservable from "mobx-store-inheritance";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { TriggerType } from "../../../core/model/trigger_model";
|
||||
import { TriggerRepository } from "../data/trigger_repository";
|
||||
import { TriggerViewModel } from "../model/trigger_form_view_model";
|
||||
import { SimpleErrorState } from "../../../core/store/base_store";
|
||||
|
||||
export class TriggerStore extends SimpleErrorState {
|
||||
constructor(repository: TriggerRepository) {
|
||||
super();
|
||||
this.triggerType = TriggerType.FILE;
|
||||
this.repository = repository;
|
||||
makeAutoObservable(this);
|
||||
}
|
||||
|
||||
triggerDescription: string = "";
|
||||
triggerType: TriggerType;
|
||||
codeTriggerValue = "";
|
||||
triggers: TriggerViewModel[] = [];
|
||||
repository: TriggerRepository;
|
||||
|
||||
changeTriggerDescription(value: string): void {
|
||||
this.triggerDescription = value;
|
||||
}
|
||||
|
||||
deleteItem(id: string): void {
|
||||
this.triggers = this.triggers.filter((el) => el.id !== id);
|
||||
}
|
||||
|
||||
getTriggerType = (): boolean => {
|
||||
return this.triggerType === TriggerType.FILE;
|
||||
};
|
||||
setTriggerType = (): void => {
|
||||
this.triggers = [];
|
||||
if (this.triggerType === TriggerType.FILE) {
|
||||
this.triggerType = TriggerType.PROCESS;
|
||||
return;
|
||||
}
|
||||
this.triggerType = TriggerType.FILE;
|
||||
};
|
||||
getTriggerDescription = (): string => {
|
||||
return this.triggerType === TriggerType.FILE ? TriggerType.FILE : TriggerType.PROCESS;
|
||||
};
|
||||
pushTrigger = (value: string, type: TriggerType): void => {
|
||||
this.triggers.push({
|
||||
value: value,
|
||||
id: uuidv4(),
|
||||
type: type,
|
||||
});
|
||||
};
|
||||
|
||||
writeNewTrigger(v: string | undefined): void {
|
||||
if (v === undefined) {
|
||||
throw new Error("Editor Value is undefined");
|
||||
}
|
||||
this.codeTriggerValue = v;
|
||||
}
|
||||
clearTriggerCode(): void {
|
||||
this.codeTriggerValue = "";
|
||||
}
|
||||
saveCode(): void {
|
||||
if (this.codeTriggerValue !== "") {
|
||||
this.triggers.push({
|
||||
id: uuidv4(),
|
||||
value: this.codeTriggerValue,
|
||||
type: TriggerType.PROCESS,
|
||||
});
|
||||
|
||||
this.codeTriggerValue = "";
|
||||
}
|
||||
}
|
||||
async saveResult(): Promise<void> {
|
||||
await this.httpHelper(
|
||||
this.repository.save({
|
||||
type: this.getTriggerDescription(),
|
||||
description: this.triggerDescription,
|
||||
value: this.triggers.map((el) => {
|
||||
return el.value;
|
||||
}),
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
export const triggerStore = new TriggerStore(new TriggerRepository());
|
166
ui/src/features/dataset/card_dataset.tsx
Normal file
166
ui/src/features/dataset/card_dataset.tsx
Normal file
|
@ -0,0 +1,166 @@
|
|||
import { Spin } from "antd";
|
||||
import { LoadingOutlined } from "@ant-design/icons";
|
||||
import poseIMG from "../../core/assets/images/pose_estemation.jpg";
|
||||
import { Icon } from "../../core/ui/icons/icons";
|
||||
import { CoreText, CoreTextType } from "../../core/ui/text/text";
|
||||
import { CoreButton } from "../../core/ui/button/button";
|
||||
import type { MenuProps } from "antd";
|
||||
import { Dropdown } from "antd";
|
||||
import { ProcessStatus } from "./dataset_model";
|
||||
|
||||
interface IMenuItem {
|
||||
onClick: Function;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export const enum CardDataSetType {
|
||||
EMPTY = "EMPTY",
|
||||
COMPLETED = "COMPLETED",
|
||||
}
|
||||
|
||||
interface ICardDataSetProps {
|
||||
type: CardDataSetType;
|
||||
neuralNetworkAction?: string;
|
||||
neuralNetworkName?: string;
|
||||
objects?: string[];
|
||||
unixDate?: number;
|
||||
processStatus?: string;
|
||||
onClickButton?: (id: string) => void;
|
||||
onClickEmptyCard?: Function;
|
||||
onEdit?: Function;
|
||||
onDelete?: Function;
|
||||
id?: string;
|
||||
}
|
||||
|
||||
export const CardDataSet = (props: ICardDataSetProps) => {
|
||||
const menu: IMenuItem[] = [
|
||||
{
|
||||
onClick: () => {
|
||||
if (props.onEdit) props.onEdit(props.id);
|
||||
},
|
||||
name: "Редактировать",
|
||||
},
|
||||
{
|
||||
onClick: () => {
|
||||
if (props.onDelete) props.onDelete(props.id);
|
||||
},
|
||||
name: "Удалить",
|
||||
},
|
||||
];
|
||||
const items: MenuProps["items"] = menu.map((el, index) => {
|
||||
return {
|
||||
key: String(index),
|
||||
label: <CoreText text={el.name} type={CoreTextType.medium} />,
|
||||
onClick: () => el.onClick(props.id),
|
||||
};
|
||||
});
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
width: 272,
|
||||
height: 372,
|
||||
borderRadius: 12,
|
||||
border: "1px solid #CAC4D0",
|
||||
backgroundColor: "rgba(254, 247, 255, 1)",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
onClick={() => {
|
||||
if (props.type.isEqual(CardDataSetType.EMPTY) && props.onClickEmptyCard) {
|
||||
props.onClickEmptyCard();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div style={{ height: 80 }}>
|
||||
{props.type.isEqual(CardDataSetType.EMPTY) ? null : (
|
||||
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
|
||||
<div style={{ width: 70, marginTop: 11 }}></div>
|
||||
<div style={{ height: 80, alignContent: "center", marginRight: 40 }}>
|
||||
<CoreText text={props.neuralNetworkName ?? ""} type={CoreTextType.header} />
|
||||
<CoreText text={props.neuralNetworkAction ?? ""} type={CoreTextType.medium} />
|
||||
</div>
|
||||
<div>
|
||||
<Dropdown overlayStyle={{ backgroundColor: "rgba(243, 237, 247, 1)" }} menu={{ items }}>
|
||||
<div>
|
||||
<Icon type="Settings" />
|
||||
</div>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<img alt="pose" style={{ width: "100%" }} src={poseIMG}></img>
|
||||
<div
|
||||
style={{
|
||||
textAlignLast: props.type.isEqual(CardDataSetType.EMPTY) ? "center" : "auto",
|
||||
marginTop: props.type.isEqual(CardDataSetType.EMPTY) ? 80 : 10,
|
||||
}}
|
||||
>
|
||||
{props.type === CardDataSetType.EMPTY ? (
|
||||
<Icon type="PlusCircle" />
|
||||
) : (
|
||||
<div style={{ margin: 10 }}>
|
||||
<CoreText text={`Объектов: ${props.objects?.length ?? 0}`} type={CoreTextType.large} />
|
||||
<CoreText text={Number(props.unixDate).unixFromDate()} type={CoreTextType.medium} color="#49454F" />
|
||||
<div style={{ height: 40 }} />
|
||||
<div style={{ width: 240, overflow: "hidden", whiteSpace: "nowrap", height: 40 }}>
|
||||
<CoreText text={props.objects?.join(", ") ?? ""} type={CoreTextType.medium} color="#49454F" />
|
||||
</div>
|
||||
<div style={{ display: "flex", flexDirection: "row", justifyContent: "flex-end", alignItems: "center" }}>
|
||||
{props.processStatus === ProcessStatus.RUN ? (
|
||||
<Spin indicator={<LoadingOutlined style={{ fontSize: 34, color: "rgba(103, 80, 164, 1)" }} spin />} />
|
||||
) : null}
|
||||
<div style={{ width: 20 }} />
|
||||
{props.processStatus === ProcessStatus.NEW ? (
|
||||
<CoreButton
|
||||
onClick={() => {
|
||||
if (props.type.isEqual(CardDataSetType.COMPLETED) && props.onClickButton && props.id) {
|
||||
props.onClickButton(props.id);
|
||||
}
|
||||
}}
|
||||
filled={true}
|
||||
text="Старт"
|
||||
/>
|
||||
) : null}
|
||||
{props.processStatus === ProcessStatus.END ? ( <CoreButton
|
||||
|
||||
onClick={() => {
|
||||
if (props.type.isEqual(CardDataSetType.COMPLETED) && props.onClickButton && props.id) {
|
||||
props.onClickButton(props.id);
|
||||
}
|
||||
}}
|
||||
|
||||
text="Завершен"
|
||||
/>):null}
|
||||
{props.processStatus === ProcessStatus.RUN ? (<>
|
||||
<CoreButton
|
||||
|
||||
onClick={() => {
|
||||
if (props.type.isEqual(CardDataSetType.COMPLETED) && props.onClickButton && props.id) {
|
||||
props.onClickButton(props.id);
|
||||
}
|
||||
}}
|
||||
block={true}
|
||||
text="Стоп"
|
||||
/>
|
||||
</>):null}
|
||||
{props.processStatus === ProcessStatus.ERROR ? (
|
||||
<CoreButton
|
||||
style={{
|
||||
backgroundColor: "red",
|
||||
}}
|
||||
onClick={() => {
|
||||
if (props.type.isEqual(CardDataSetType.COMPLETED) && props.onClickButton && props.id) {
|
||||
props.onClickButton(props.id);
|
||||
}
|
||||
}}
|
||||
filled={true}
|
||||
text="Ошибка"
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
181
ui/src/features/dataset/dataset_model.ts
Normal file
181
ui/src/features/dataset/dataset_model.ts
Normal file
|
@ -0,0 +1,181 @@
|
|||
import { Result } from "../../core/helper/result";
|
||||
import makeAutoObservable from "mobx-store-inheritance";
|
||||
|
||||
export enum ProcessStatus {
|
||||
END = "END",
|
||||
ERROR = "ERROR",
|
||||
NEW = "NEW",
|
||||
RUN = "RUN",
|
||||
}
|
||||
|
||||
export interface IDatasetModel {
|
||||
_id: string;
|
||||
dataSetObjects: string[];
|
||||
processStatus: ProcessStatus;
|
||||
projectId: string;
|
||||
name: string;
|
||||
formBuilder: FormBuilderValidationModel;
|
||||
unixTime: number;
|
||||
datasetType: string;
|
||||
local_path: string;
|
||||
__v: number;
|
||||
processLogs: string;
|
||||
}
|
||||
|
||||
export interface Dataset {
|
||||
name: string;
|
||||
local_path: string;
|
||||
dataSetObjects: string[];
|
||||
unixDate: number;
|
||||
formBuilder: FormBuilderValidationModel;
|
||||
}
|
||||
|
||||
export interface Assets {
|
||||
assets: Asset[];
|
||||
}
|
||||
|
||||
export interface Asset {
|
||||
name: string;
|
||||
mesh: string;
|
||||
image: string;
|
||||
}
|
||||
export class FormBuilderValidationModel {
|
||||
public result: string;
|
||||
public context: string;
|
||||
public form: string[];
|
||||
public output: any;
|
||||
constructor(context: string, result: string, form: string[], output: string) {
|
||||
this.context = context;
|
||||
this.result = result;
|
||||
this.form = form;
|
||||
this.output = output;
|
||||
}
|
||||
static empty() {
|
||||
return new FormBuilderValidationModel(datasetFormMockContext, datasetFormMockResult, [], defaultFormValue);
|
||||
}
|
||||
}
|
||||
|
||||
export class DataSetModel {
|
||||
dataSetObjects: string[];
|
||||
datasetType: string;
|
||||
name: string;
|
||||
formBuilder: FormBuilderValidationModel = FormBuilderValidationModel.empty();
|
||||
project?: string;
|
||||
processStatus?: string;
|
||||
isNew: boolean;
|
||||
_id?: string;
|
||||
constructor(
|
||||
dataSetObjects: string[],
|
||||
datasetType = datasetTypes[0],
|
||||
datasetName: string,
|
||||
isNew = true,
|
||||
id: string | undefined = undefined
|
||||
) {
|
||||
this.dataSetObjects = dataSetObjects;
|
||||
this.datasetType = datasetType;
|
||||
this.name = datasetName;
|
||||
this.isNew = isNew;
|
||||
this._id = id;
|
||||
makeAutoObservable(this);
|
||||
}
|
||||
|
||||
static empty() {
|
||||
return new DataSetModel([], "", "", true);
|
||||
}
|
||||
|
||||
isValid(): Result<string, void> {
|
||||
if (this.project === undefined) {
|
||||
return Result.error("project is unknow");
|
||||
}
|
||||
if (this.dataSetObjects.isEmpty()) {
|
||||
return Result.error("Не выделены детали");
|
||||
}
|
||||
if (this.name.isEmpty()) {
|
||||
return Result.error("ВВедите имя датасета");
|
||||
}
|
||||
return Result.ok();
|
||||
}
|
||||
|
||||
static fromIDatasetModel(model: IDatasetModel) {
|
||||
const dataSetModel = new DataSetModel(model.dataSetObjects, model.datasetType, model.name, false, model._id);
|
||||
dataSetModel.formBuilder = model.formBuilder;
|
||||
return dataSetModel;
|
||||
}
|
||||
}
|
||||
|
||||
export const datasetTypes = ["Object Detection - YOLOv8", "Pose Estimation - DOPE"];
|
||||
|
||||
export const datasetFormMockResult = `{
|
||||
"typedataset": \${typedataset:Enum<T>:ObjectDetection},
|
||||
"models_randomization":{
|
||||
"loc_range_low": [\${LOC_RANGE_LOW_1:number:-1}, \${LOC_RANGE_LOW_2:number:-1},/\${LOC_RANGE_LOW_3:number:0}],
|
||||
"loc_range_high": [\${LOC_RANGE_HIGH_1:number:1}, \${LOC_RANGE_HIGH_2:number:1},/\${LOC_RANGE_HIGH_3:number:2}]
|
||||
},
|
||||
"scene":{
|
||||
"objects": \${OBJECTS_SCENE:Array<OBJECTS_SCENE>:[]},
|
||||
"lights": \${LIGHTS:Array<LIGHTS>:[]}
|
||||
},
|
||||
"camera_position":{
|
||||
"center_shell": [\${CENTER_SHELL_1:number:0}, \${CENTER_SHELL_2:number:0}, \${CENTER_SHELL_3:number:0}],
|
||||
"radius_range": [\${RADIUS_RANGE_1:number:1}, \${RADIUS_RANGE_2:number:1.4}],
|
||||
"elevation_range": [\${ELEVATION_RANGE_1:number:10}, \${ELEVATION_RANGE_2:number:90}]
|
||||
},
|
||||
"generation":{
|
||||
"n_cam_pose": \${N_CAM_POSE:number:5},
|
||||
"n_sample_on_pose": \${N_SAMPLE_ON_POSE:number:3},
|
||||
"n_series": \${N_SERIES:number:100},
|
||||
"image_format": \${image_format:Enum<F>:JPEG},
|
||||
"image_size_wh": [\${IMAGE_SIZE_WH_1:number:640}, \${IMAGE_SIZE_WH_2:number:480}]
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const datasetFormMockContext = `
|
||||
ENUM T = "ObjectDetection","PoseEstimation";
|
||||
ENUM L = "POINT","SUN";
|
||||
ENUM F = "JPEG","PNG";
|
||||
ENUM COLLISION_SHAPE = "SHAPE","COLLISION";
|
||||
|
||||
type OBJECTS_SCENE = {
|
||||
"name": \${NAME:string:default},
|
||||
"collision_shape": \${collision_shape:Enum<COLLISION_SHAPE>:BOX},
|
||||
"loc_xyz": [\${LOC_XYZ_1:number:0}, \${LOC_XYZ_2:number:0}, \${LOC_XYZ_3:number:0}],
|
||||
"rot_euler": [\${ROT_EULER_1:number:0},\${ROT_EULER_2:number:0}, \${ROT_EULER_3:number:0}],
|
||||
"material_randomization": {
|
||||
"specular": [\${SPECULAR_1:number:0}, \${SPECULAR_2:number:1}],
|
||||
"roughness": [\${ROUGHNESS_1:number:0}, \${ROUGHNESS_2:number:1}],
|
||||
"metallic": [\${METALLIC_1:number:0}, \${METALLIC_2:number:1}],
|
||||
"base_color": [
|
||||
[
|
||||
\${BASE_COLOR_1:number:0},
|
||||
\${BASE_COLOR_2:number:0},
|
||||
\${BASE_COLOR_3:number:0},
|
||||
\${BASE_COLOR_4:number:1}
|
||||
],
|
||||
[
|
||||
\${BASE_COLOR_5:number:1},
|
||||
\${BASE_COLOR_6:number:1},
|
||||
\${BASE_COLOR_7:number:1},
|
||||
\${BASE_COLOR_8:number:1}
|
||||
]
|
||||
]
|
||||
}
|
||||
};
|
||||
type LIGHTS = {
|
||||
"id": \${ID:number:1},
|
||||
"type": \${type:Enum<L>:POINT},
|
||||
"loc_xyz": [\${LOC_XYZ_1:number:5}, \${LOC_XYZ_2:number:5}, \${LOC_XYZ_3:number:5}],
|
||||
"rot_euler": [\${ROT_EULER_1:number:-0.06}, \${ROT_EULER_2:number:0.61}, \${ROT_EULER_3:number:-0.19}],
|
||||
"color_range_low": [\${COLOR_RANGE_LOW_1:number:0.5}, \${COLOR_RANGE_LOW_2:number:0.5}, \${COLOR_RANGE_LOW_3:number:0.5}],
|
||||
"color_range_high":[\${COLOR_RANGE_HIGH_1:number:1}, \${COLOR_RANGE_HIGH_2:number:1}, $\{COLOR_RANGE_HIGH_3:number:1}],
|
||||
"energy_range":[\${ENERGY_RANGE_1:number:400},\${ENERGY_RANGE_2:number:900}]
|
||||
};
|
||||
`;
|
||||
|
||||
export const defaultFormValue: any = {
|
||||
typedataset: "PoseEstimation",
|
||||
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] },
|
||||
};
|
35
ui/src/features/dataset/dataset_repository.ts
Normal file
35
ui/src/features/dataset/dataset_repository.ts
Normal file
|
@ -0,0 +1,35 @@
|
|||
import { Result } from "../../core/helper/result";
|
||||
import { HttpError, HttpMethod, HttpRepository } from "../../core/repository/http_repository";
|
||||
import { UUID } from "../all_projects/data/project_repository";
|
||||
import { Assets, DataSetModel, Dataset, IDatasetModel, ProcessStatus } from "./dataset_model";
|
||||
|
||||
export class DataSetRepository extends HttpRepository {
|
||||
editDataset(dataSetModel: DataSetModel) {
|
||||
dataSetModel.processStatus = ProcessStatus.NEW;
|
||||
return this._jsonRequest<void>(HttpMethod.PUT, `/datasets`, dataSetModel);
|
||||
}
|
||||
deleteDataset(id: string) {
|
||||
return this._jsonRequest<void>(HttpMethod.DELETE, `/datasets/dataset?id=${id}`);
|
||||
}
|
||||
getActiveProjectId(): Promise<Result<HttpError, UUID>> {
|
||||
return this._jsonRequest<UUID>(HttpMethod.GET, "/projects/get/active/project/id");
|
||||
}
|
||||
getAssetsActiveProject = async (): Promise<Result<HttpError, Assets>> => {
|
||||
return this._jsonRequest<Assets>(HttpMethod.GET, "/projects/assets");
|
||||
};
|
||||
saveDataSet = async (model: DataSetModel) => {
|
||||
return this._jsonRequest<Dataset>(HttpMethod.POST, "/datasets", model);
|
||||
};
|
||||
getDatasetsActiveProject = async (): Promise<Result<HttpError, IDatasetModel[]>> => {
|
||||
return this._jsonRequest(HttpMethod.GET, "/datasets");
|
||||
};
|
||||
execDatasetProcess = async (id: string) => {
|
||||
return this._jsonRequest(HttpMethod.POST, `/datasets/exec?id=${id}`);
|
||||
};
|
||||
isRunningProcess = async () => {
|
||||
return this._jsonRequest(HttpMethod.GET, "/datasets/is/running");
|
||||
};
|
||||
deleteProcess = async () => {
|
||||
return this._jsonRequest(HttpMethod.GET, "/datasets/delete/process");
|
||||
};
|
||||
}
|
130
ui/src/features/dataset/dataset_screen.tsx
Normal file
130
ui/src/features/dataset/dataset_screen.tsx
Normal file
|
@ -0,0 +1,130 @@
|
|||
import * as React from "react";
|
||||
import { Drawer } from "antd";
|
||||
import { FormBuilder } from "../../core/ui/form_builder/form_builder";
|
||||
import { DataSetStore, DrawersDataset } from "./dataset_store";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { MainPage } from "../../core/ui/pages/main_page";
|
||||
import { CardDataSet, CardDataSetType } from "./card_dataset";
|
||||
import { CoreInput } from "../../core/ui/input/input";
|
||||
import { CoreButton } from "../../core/ui/button/button";
|
||||
import { ListItem } from "./list_item";
|
||||
|
||||
export const DatasetsScreenPath = "/dataset";
|
||||
|
||||
export const DataSetScreen: React.FunctionComponent = observer(() => {
|
||||
const [store] = React.useState(() => new DataSetStore());
|
||||
|
||||
React.useEffect(() => {
|
||||
store.init();
|
||||
}, [store]);
|
||||
return (
|
||||
<>
|
||||
<MainPage
|
||||
isLoading={false}
|
||||
page="Датасеты"
|
||||
bodyChildren={
|
||||
<div
|
||||
style={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: "repeat(auto-fill, minmax(272px, 1fr))",
|
||||
gridGap: 10,
|
||||
width: "100%",
|
||||
margin: 12,
|
||||
overflow: "auto",
|
||||
height: window.innerHeight,
|
||||
}}
|
||||
>
|
||||
{store.datasets?.map((el) => {
|
||||
return (
|
||||
<CardDataSet
|
||||
type={CardDataSetType.COMPLETED}
|
||||
objects={el.dataSetObjects}
|
||||
unixDate={el.unixTime}
|
||||
processStatus={el.processStatus}
|
||||
neuralNetworkAction={el.datasetType}
|
||||
neuralNetworkName={el.name}
|
||||
id={el._id}
|
||||
onClickButton={(id: string) => store.runProcess(id)}
|
||||
onDelete={(id: string) => store.deleteDataset(id)}
|
||||
onEdit={(id: string) => store.editDataset(id)}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
<CardDataSet
|
||||
type={CardDataSetType.EMPTY}
|
||||
onClickEmptyCard={() => {
|
||||
store.openEmptyCard();
|
||||
}}
|
||||
/>
|
||||
<Drawer
|
||||
title={DrawersDataset.FormBuilderDrawer}
|
||||
destroyOnClose={true}
|
||||
onClose={() => store.editDrawer(DrawersDataset.FormBuilderDrawer, false)}
|
||||
open={store.drawers.find((el) => el.name === DrawersDataset.FormBuilderDrawer)?.status ?? false}
|
||||
>
|
||||
<FormBuilder
|
||||
formBuilder={store.dataSetModel.formBuilder}
|
||||
onChange={(el) => {
|
||||
store.dataSetModel.formBuilder = el;
|
||||
}}
|
||||
/>
|
||||
<div style={{ display: "flex" }}>
|
||||
<CoreButton
|
||||
text="Сохранить"
|
||||
filled={true}
|
||||
onClick={() => store.editDrawer(DrawersDataset.FormBuilderDrawer, false)}
|
||||
/>
|
||||
<div style={{ width: 10 }} />
|
||||
<CoreButton text="Отмена" onClick={() => store.editDrawer(DrawersDataset.FormBuilderDrawer, false)} />
|
||||
</div>
|
||||
</Drawer>
|
||||
<Drawer
|
||||
title={store.titleDrawer}
|
||||
destroyOnClose={true}
|
||||
onClose={() => store.editDrawer(DrawersDataset.NewDataset, false)}
|
||||
open={store.drawers.find((el) => el.name === DrawersDataset.NewDataset)?.status}
|
||||
>
|
||||
<div
|
||||
style={{ display: "flex", flexDirection: "column", justifyContent: "space-between", height: "100%" }}
|
||||
>
|
||||
<div>
|
||||
<CoreInput
|
||||
value={store.dataSetModel.name}
|
||||
label={"Имя датасета"}
|
||||
onChange={(e) => store.setNewDatasetName(e)}
|
||||
/>
|
||||
|
||||
<div style={{ width: 180, marginTop: 10, marginBottom: 30 }}>
|
||||
<CoreButton
|
||||
onClick={() => store.editDrawer(DrawersDataset.FormBuilderDrawer, true)}
|
||||
text="Настройки датасета"
|
||||
filled={true}
|
||||
/>
|
||||
</div>
|
||||
{store.assets?.assets?.map((el) => {
|
||||
return (
|
||||
<ListItem
|
||||
status={store.assetStatus(el.name)}
|
||||
name={el.name}
|
||||
imgURL={el.image}
|
||||
onChange={() => {
|
||||
store.datasetCheckBox(el);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div style={{ display: "flex" }}>
|
||||
<CoreButton text="Сохранить" filled={true} onClick={() => store.saveDataset()} />
|
||||
<div style={{ width: 10 }} />
|
||||
<CoreButton text="Отмена" onClick={() => store.editDrawer(DrawersDataset.NewDataset, false)} />
|
||||
</div>
|
||||
</div>
|
||||
</Drawer>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
});
|
167
ui/src/features/dataset/dataset_store.ts
Normal file
167
ui/src/features/dataset/dataset_store.ts
Normal file
|
@ -0,0 +1,167 @@
|
|||
import makeAutoObservable from "mobx-store-inheritance";
|
||||
import { DataSetRepository } from "./dataset_repository";
|
||||
import { UiErrorState } from "../../core/store/base_store";
|
||||
import { HttpError } from "../../core/repository/http_repository";
|
||||
import { Asset, Assets, DataSetModel, IDatasetModel, ProcessStatus } from "./dataset_model";
|
||||
import { message } from "antd";
|
||||
import { UUID } from "../all_projects/data/project_repository";
|
||||
import { SocketRepository, socketRepository } from "../../core/repository/socket_repository";
|
||||
|
||||
export enum DrawersDataset {
|
||||
NewDataset = "Новый датасет",
|
||||
FormBuilderDrawer = "Редактировать датасет",
|
||||
}
|
||||
export interface Drawer {
|
||||
name: string;
|
||||
status: boolean;
|
||||
}
|
||||
export class DataSetStore extends UiErrorState<HttpError> {
|
||||
dataSetRepository: DataSetRepository;
|
||||
assets?: Assets;
|
||||
datasets?: IDatasetModel[];
|
||||
activeProject: UUID;
|
||||
dataSetModel = DataSetModel.empty();
|
||||
drawers: Drawer[];
|
||||
socketRepository: SocketRepository;
|
||||
titleDrawer: string = DrawersDataset.NewDataset;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.socketRepository = socketRepository;
|
||||
this.dataSetRepository = new DataSetRepository();
|
||||
this.drawers = Object.entries(DrawersDataset).map((k, v) => {
|
||||
return {
|
||||
name: k.at(1) ?? "",
|
||||
status: false,
|
||||
};
|
||||
});
|
||||
makeAutoObservable(this);
|
||||
socketRepository.on((e) => {
|
||||
if (e.event === "realtime") {
|
||||
if (e.payload !== undefined && e.payload.value !== undefined && e.payload.value.id !== undefined) {
|
||||
this.updateDatasetStatus(String(e.payload.value.id), ProcessStatus.END);
|
||||
}
|
||||
if (e.payload !== undefined && e.payload.error !== undefined && e.payload.error.id !== undefined) {
|
||||
this.updateDatasetStatus(String(e.payload.error.id), ProcessStatus.ERROR);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
updateDatasetStatus(id: string, status: ProcessStatus) {
|
||||
this.datasets = this.datasets?.map((el) => {
|
||||
if (el._id.isEqual(id)) {
|
||||
el.processStatus = status;
|
||||
}
|
||||
return el;
|
||||
});
|
||||
}
|
||||
openEmptyCard() {
|
||||
this.dataSetModel = DataSetModel.empty();
|
||||
this.titleDrawer = DrawersDataset.NewDataset;
|
||||
this.editDrawer(DrawersDataset.NewDataset, true);
|
||||
}
|
||||
|
||||
setNewDatasetName(e: string): void {
|
||||
this.dataSetModel.name = e;
|
||||
}
|
||||
|
||||
setSkillSelected(el: string): void {
|
||||
this.dataSetModel.datasetType = el;
|
||||
}
|
||||
|
||||
skillIsSelected(el: string): boolean {
|
||||
return el === this.dataSetModel.datasetType;
|
||||
}
|
||||
|
||||
assetStatus(name: string): boolean {
|
||||
return this.dataSetModel.dataSetObjects.includes(name);
|
||||
}
|
||||
|
||||
datasetCheckBox(asset: Asset): void {
|
||||
if (this.assetStatus(asset.name)) {
|
||||
this.dataSetModel.dataSetObjects = this.dataSetModel.dataSetObjects.filter((el) => !el.isEqual(asset.name));
|
||||
} else {
|
||||
this.dataSetModel.dataSetObjects.push(asset.name);
|
||||
}
|
||||
}
|
||||
|
||||
editDrawer(drawerName: DrawersDataset, status: boolean): void {
|
||||
this.drawers = this.drawers.map((el) => {
|
||||
if (el.name === drawerName) {
|
||||
el.status = status;
|
||||
}
|
||||
return el;
|
||||
});
|
||||
}
|
||||
|
||||
editDataset(id: string) {
|
||||
this.titleDrawer = DrawersDataset.FormBuilderDrawer;
|
||||
this.dataSetModel = DataSetModel.fromIDatasetModel(this.datasets?.find((el) => el._id === id) as IDatasetModel);
|
||||
this.editDrawer(DrawersDataset.NewDataset, true);
|
||||
}
|
||||
|
||||
deleteDataset = async (id: string) => {
|
||||
(await this.dataSetRepository.deleteDataset(id)).fold(
|
||||
async () => {
|
||||
message.success("датасет удален");
|
||||
await this.getDatasets();
|
||||
},
|
||||
async (e) => message.error(e.message)
|
||||
);
|
||||
};
|
||||
|
||||
runProcess = async (id: string): Promise<void> => {
|
||||
(await this.dataSetRepository.isRunningProcess()).fold(
|
||||
async (s) => {
|
||||
(await this.dataSetRepository.execDatasetProcess(id)).fold(
|
||||
() => {
|
||||
this.updateDatasetStatus(id, ProcessStatus.RUN);
|
||||
message.success("Процесс запущен");
|
||||
},
|
||||
(e) => message.error(e.message)
|
||||
);
|
||||
},
|
||||
async (e) => message.error(e.message)
|
||||
);
|
||||
};
|
||||
errorHandingStrategy = (error: HttpError) => {
|
||||
message.error(error.message);
|
||||
};
|
||||
|
||||
saveDataset(): void {
|
||||
this.dataSetModel.project = this.activeProject.id;
|
||||
this.dataSetModel.isValid().fold(
|
||||
async () => {
|
||||
if (this.dataSetModel.isNew) {
|
||||
(await this.dataSetRepository.saveDataSet(this.dataSetModel)).fold(
|
||||
async () => {
|
||||
message.success("Датасет сохранен");
|
||||
await this.getDatasets();
|
||||
this.editDrawer(DrawersDataset.NewDataset, false);
|
||||
},
|
||||
async (error) => message.error(error.message)
|
||||
);
|
||||
} else {
|
||||
(await this.dataSetRepository.editDataset(this.dataSetModel)).fold(
|
||||
async () => {
|
||||
message.success("Настройки датасета измнены");
|
||||
await this.getDatasets();
|
||||
this.editDrawer(DrawersDataset.NewDataset, false);
|
||||
},
|
||||
async (error) => message.error(error.message)
|
||||
);
|
||||
}
|
||||
},
|
||||
async (error) => message.error(error)
|
||||
);
|
||||
}
|
||||
|
||||
init = async () => {
|
||||
await this.mapOk("assets", this.dataSetRepository.getAssetsActiveProject());
|
||||
await this.getDatasets();
|
||||
await this.mapOk("activeProject", this.dataSetRepository.getActiveProjectId());
|
||||
};
|
||||
getDatasets = async () => {
|
||||
await this.mapOk("datasets", this.dataSetRepository.getDatasetsActiveProject());
|
||||
};
|
||||
}
|
31
ui/src/features/dataset/list_item.tsx
Normal file
31
ui/src/features/dataset/list_item.tsx
Normal file
|
@ -0,0 +1,31 @@
|
|||
import { CoreSwitch } from "../../core/ui/switch/switch";
|
||||
import { CoreText, CoreTextType } from "../../core/ui/text/text";
|
||||
|
||||
export interface IListItemProps {
|
||||
name: string;
|
||||
imgURL: string;
|
||||
status: boolean;
|
||||
onChange: (status: boolean, id: string) => void;
|
||||
}
|
||||
export const ListItem = (props: IListItemProps) => {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: "rgba(254, 247, 255, 1)",
|
||||
border: "1px #6750a4 solid",
|
||||
width: "100%",
|
||||
height: 110,
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
borderRadius: 12,
|
||||
marginTop: 10,
|
||||
marginBottom: 10,
|
||||
}}
|
||||
>
|
||||
<img style={{ height: 50, margin: 10 }} src={props.imgURL} alt="" />
|
||||
<CoreText text={props.name} type={CoreTextType.large} />
|
||||
<CoreSwitch isSelected={props.status} id={props.name} onChange={props.onChange} />
|
||||
</div>
|
||||
);
|
||||
};
|
1
ui/src/features/dataset/p.json
Normal file
1
ui/src/features/dataset/p.json
Normal file
|
@ -0,0 +1 @@
|
|||
|
8
ui/src/features/details/details_screen.tsx
Normal file
8
ui/src/features/details/details_screen.tsx
Normal file
|
@ -0,0 +1,8 @@
|
|||
import * as React from "react";
|
||||
import { MainPage } from "../../core/ui/pages/main_page";
|
||||
|
||||
export interface IDetailsScreenProps {}
|
||||
export const DetailsScreenPath = "/detail";
|
||||
export default function DetailsScreen(props: IDetailsScreenProps) {
|
||||
return <MainPage page={"Детали"}></MainPage>;
|
||||
}
|
8
ui/src/features/estimate/estimate_screen.tsx
Normal file
8
ui/src/features/estimate/estimate_screen.tsx
Normal file
|
@ -0,0 +1,8 @@
|
|||
import * as React from "react";
|
||||
import { MainPage } from "../../core/ui/pages/main_page";
|
||||
|
||||
export interface IEstimateScreenProps {}
|
||||
export const EstimateScreenPath = "/estimate";
|
||||
export function EstimateScreen(props: IEstimateScreenProps) {
|
||||
return <MainPage page={"Оценка"}></MainPage>;
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
export {};
|
||||
// import React from "react";
|
||||
// import { CoreError, UiErrorState } from "../core/store/base_store";
|
||||
// import { SelectProjectStore } from "./select_project/presentation/select_project_store";
|
||||
|
||||
// export declare type ClassConstructor<T> = {
|
||||
// new (...args: any[]): T;
|
||||
// };
|
||||
// interface MobxReactComponentProps<T extends UiErrorState<CoreError>, ClassConstructor> {
|
||||
// store: ClassConstructor;
|
||||
// children: (element: T) => React.ReactElement;
|
||||
// }
|
||||
|
||||
// class UiStateErrorComponent<T extends UiErrorState<CoreError>, K> extends React.Component<
|
||||
// MobxReactComponentProps<T, K>,
|
||||
// { store: T | undefined }
|
||||
// > {
|
||||
// async componentDidMount(): Promise<void> {
|
||||
// const store = this.props.store as ClassConstructor<T>;
|
||||
// console.log(store);
|
||||
// const s = new store();
|
||||
// this.setState({ store: s });
|
||||
// if (this.state !== null) {
|
||||
// await this.state.store?.init();
|
||||
// }
|
||||
// }
|
||||
// componentWillUnmount(): void {
|
||||
// if (this.state.store !== undefined) {
|
||||
// this.state.store.dispose();
|
||||
// }
|
||||
// }
|
||||
|
||||
// render() {
|
||||
// if (this.state !== null) {
|
||||
// if (this.state.store?.isLoading) {
|
||||
// return <>Loading</>;
|
||||
// }
|
||||
// if (this.state.store !== undefined) {
|
||||
// return this.props.children(this.state.store);
|
||||
// }
|
||||
// }
|
||||
|
||||
// return (
|
||||
// <div>
|
||||
// <>{this.props.children}</>
|
||||
// </div>
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
// export const ExampleScreen: React.FC = () => {
|
||||
// return (
|
||||
// <div>
|
||||
// <UiStateErrorComponent<SelectProjectStore, {}> store={SelectProjectStore}>
|
||||
// {(store) => {
|
||||
// console.log(store);
|
||||
// return (
|
||||
// <div>
|
||||
// {store.projects.map((el) => {
|
||||
// return <>{el}</>;
|
||||
// })}
|
||||
// </div>
|
||||
// );
|
||||
// }}
|
||||
// </UiStateErrorComponent>
|
||||
// </div>
|
||||
// );
|
||||
// };
|
|
@ -1,18 +1,27 @@
|
|||
import * as React from "react";
|
||||
import { LoadPage } from "../../core/ui/pages/load_page";
|
||||
import { PipelineInstanceStore } from "./pipeline_instance_store";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { Icon } from "../../core/ui/icons/icons";
|
||||
import { CardDataSet, CardDataSetType } from "../dataset/card_dataset";
|
||||
import { MainPage } from "../../core/ui/pages/main_page";
|
||||
|
||||
export const PipelineInstanceScreenPath = "/pipeline_instance/";
|
||||
|
||||
export const PipelineInstanceScreen: React.FunctionComponent = () => {
|
||||
const [pipelineInstanceStore] = React.useState(() => new PipelineInstanceStore());
|
||||
React.useEffect(() => {}, [pipelineInstanceStore]);
|
||||
const [store] = React.useState(() => {
|
||||
new PipelineInstanceStore();
|
||||
});
|
||||
React.useEffect(() => {
|
||||
document.body.style.overflow = "hidden";
|
||||
return () => {
|
||||
document.body.style.overflow = "scroll";
|
||||
};
|
||||
}, [store]);
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<LoadPage
|
||||
needBackButton={true}
|
||||
largeText={"Project instance active"}
|
||||
isError={pipelineInstanceStore.isError}
|
||||
isLoading={pipelineInstanceStore.isLoading}
|
||||
children={<div></div>}
|
||||
/>
|
||||
<>
|
||||
<MainPage page="Датасеты" />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Result } from "../../../core/helper/result";
|
||||
import { HttpMethod, HttpRepository } from "../../../core/repository/http_repository";
|
||||
import { CoreError } from "../../../core/store/base_store";
|
||||
import { RobossemblerAssets } from "../model/robossembler_assets";
|
||||
import { RobossemblerAssets } from "../../../core/model/robossembler_assets";
|
||||
|
||||
export class SceneHttpRepository extends HttpRepository {
|
||||
async getRobossemblerAssets() {
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
import { CameraHelper, Object3D, PerspectiveCamera, Quaternion, Scene, Vector3 } from "three";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { UserData } from "../../../core/repository/core_three_repository";
|
||||
import { Asset, Instance, InstanceRgbCamera, InstanceType, SceneSimpleObject } from "./robossembler_assets";
|
||||
import {
|
||||
Asset,
|
||||
Instance,
|
||||
InstanceRgbCamera,
|
||||
InstanceType,
|
||||
SceneSimpleObject,
|
||||
} from "../../../core/model/robossembler_assets";
|
||||
|
||||
export enum RobossemblerFiles {
|
||||
robossemblerAssets = "robossembler_assets.json",
|
||||
|
|
|
@ -57,7 +57,6 @@ export const SceneWidget = () => {
|
|||
<h1
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
console.log(201);
|
||||
}}
|
||||
>
|
||||
HYO
|
||||
|
|
|
@ -1,13 +1,9 @@
|
|||
import * as React from "react";
|
||||
import { SceneMangerStore } from "./scene_manager_store";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { StaticAssetModelView } from "./components/static_asset_item_view";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { SceneManagerView, SceneMode } from "../model/scene_view";
|
||||
import { Button } from "antd";
|
||||
import { Form, Input, ResetButton, SubmitButton } from "formik-antd";
|
||||
import { Formik } from "formik";
|
||||
import { CameraViewModel } from "../model/scene_assets";
|
||||
import { MainPage } from "../../../core/ui/pages/main_page";
|
||||
|
||||
export const SceneManagerPath = "/scene/manager/";
|
||||
|
||||
|
@ -18,7 +14,7 @@ export const SceneManger = observer(() => {
|
|||
|
||||
React.useEffect(() => {
|
||||
store.init();
|
||||
store.loadScene(id, canvasRef.current!);
|
||||
store.loadScene(canvasRef.current!);
|
||||
document.body.style.overflow = "hidden";
|
||||
return () => {
|
||||
document.body.style.overflow = "scroll";
|
||||
|
@ -33,141 +29,150 @@ export const SceneManger = observer(() => {
|
|||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<canvas ref={canvasRef} style={{ position: "absolute", overflow: "hidden" }} />
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
alignContent: "center",
|
||||
justifyContent: "space-between",
|
||||
position: "absolute",
|
||||
width: "100vw",
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
{sceneIcons.map((el) => {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: store.sceneMode === el.name ? "aqua" : "ActiveBorder",
|
||||
}}
|
||||
onClick={() => {
|
||||
el.clickHandel();
|
||||
}}
|
||||
>
|
||||
{el.name}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
<MainPage
|
||||
page={"Сцена"}
|
||||
bodyChildren={
|
||||
<>
|
||||
<canvas ref={canvasRef} style={{ overflow: "hidden" }} />
|
||||
<div
|
||||
style={{
|
||||
marginTop: "10px",
|
||||
backgroundColor: "GrayText",
|
||||
border: "solid",
|
||||
borderRadius: "10px",
|
||||
padding: "8px",
|
||||
borderColor: "white",
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
alignContent: "center",
|
||||
justifyContent: "space-between",
|
||||
position: "absolute",
|
||||
width: "100vw",
|
||||
}}
|
||||
>
|
||||
<div style={{ color: "white" }}>Scene manager</div>
|
||||
{store.isVisibleSaveButton ? (
|
||||
<>
|
||||
<Button onClick={() => store.onTapSave()}>Save</Button>
|
||||
</>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{store.isLoading ? <>Loading...</> : <></>}
|
||||
{store.sceneMode === SceneMode.ADD_CAMERA ? (
|
||||
<div>
|
||||
<Formik
|
||||
initialValues={CameraViewModel.empty()}
|
||||
onSubmit={async (model, actions) => {
|
||||
store.addNewCamera(model);
|
||||
actions.setSubmitting(false);
|
||||
actions.resetForm();
|
||||
}}
|
||||
validate={(model) => {
|
||||
return model.validate(store.getCameraLinkNames());
|
||||
}}
|
||||
render={() => (
|
||||
<Form>
|
||||
<div
|
||||
style={{
|
||||
background: "white",
|
||||
flex: 1,
|
||||
padding: 40,
|
||||
width: "400px",
|
||||
}}
|
||||
>
|
||||
<Input name="cameraLink" placeholder="Camera link" />
|
||||
<Input name="topicImage" placeholder="Topic Image" />
|
||||
<Input name="topicCameraInfo" placeholder="Topic Camera Info" />
|
||||
<Input name="topicDepth" placeholder="Topic Depth" />
|
||||
|
||||
<ResetButton>Reset</ResetButton>
|
||||
<SubmitButton>Submit</SubmitButton>
|
||||
</div>
|
||||
</Form>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{store.sceneMode === SceneMode.MOVING || SceneMode.ROTATE ? (
|
||||
<>
|
||||
{store.robossemblerAssets?.assets.map((el) => {
|
||||
return (
|
||||
<div>
|
||||
<div style={{ color: "white", marginLeft: "10px", marginRight: "10px", display: "contents" }}>
|
||||
{el.name}
|
||||
{store.isRenderedAsset(el.name) ? (
|
||||
<></>
|
||||
) : (
|
||||
<Button
|
||||
onClick={() => {
|
||||
store.loadSceneRobossemblerAsset(el.name);
|
||||
/>
|
||||
{/* <div>
|
||||
{sceneIcons.map((el) => {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: store.sceneMode === el.name ? "aqua" : "ActiveBorder",
|
||||
}}
|
||||
onClick={() => {
|
||||
el.clickHandel();
|
||||
}}
|
||||
>
|
||||
{el.name}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
<div
|
||||
style={{
|
||||
marginTop: "10px",
|
||||
backgroundColor: "GrayText",
|
||||
border: "solid",
|
||||
borderRadius: "10px",
|
||||
padding: "8px",
|
||||
borderColor: "white",
|
||||
}}
|
||||
>
|
||||
{/* <div style={{ color: "white" }}>Scene manager</div>
|
||||
{store.isVisibleSaveButton ? (
|
||||
<>
|
||||
<Button onClick={() => store.onTapSave()}>Save</Button>
|
||||
</>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{store.isLoading ? <>Loading...</> : <></>}
|
||||
{store.sceneMode === SceneMode.ADD_CAMERA ? (
|
||||
<div>
|
||||
<Formik
|
||||
initialValues={CameraViewModel.empty()}
|
||||
onSubmit={async (model, actions) => {
|
||||
store.addNewCamera(model);
|
||||
actions.setSubmitting(false);
|
||||
actions.resetForm();
|
||||
}}
|
||||
validate={(model) => {
|
||||
return model.validate(store.getCameraLinkNames());
|
||||
}}
|
||||
render={() => (
|
||||
<Form>
|
||||
<div
|
||||
style={{
|
||||
background: "white",
|
||||
flex: 1,
|
||||
padding: 40,
|
||||
width: "400px",
|
||||
}}
|
||||
>
|
||||
add scene
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
<Input name="cameraLink" placeholder="Camera link" />
|
||||
<Input name="topicImage" placeholder="Topic Image" />
|
||||
<Input name="topicCameraInfo" placeholder="Topic Camera Info" />
|
||||
<Input name="topicDepth" placeholder="Topic Depth" />
|
||||
|
||||
<ResetButton>Reset</ResetButton>
|
||||
<SubmitButton>Submit</SubmitButton>
|
||||
</div>
|
||||
</Form>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{store.sceneMode === SceneMode.MOVING || SceneMode.ROTATE ? (
|
||||
<>
|
||||
{store.robossemblerAssets?.assets.map((el) => {
|
||||
return (
|
||||
<div>
|
||||
<div style={{ color: "white", marginLeft: "10px", marginRight: "10px", display: "contents" }}>
|
||||
{el.name}
|
||||
{store.isRenderedAsset(el.name) ? (
|
||||
<></>
|
||||
) : (
|
||||
<Button
|
||||
onClick={() => {
|
||||
store.loadSceneRobossemblerAsset(el.name);
|
||||
}}
|
||||
>
|
||||
add scene
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
{store.sceneModels.map((el) => {
|
||||
return <StaticAssetModelView onTap={() => store.deleteSceneItem(el)} model={el} />;
|
||||
})}
|
||||
</div>
|
||||
|
||||
{store.sceneMode === SceneMode.MAGNETISM_MARKING ? (
|
||||
<>
|
||||
<div style={{ backgroundColor: "white" }}>
|
||||
<div>completion of objects</div>
|
||||
<div>
|
||||
{store.objectMagnetism ? (
|
||||
<>{store.objectMagnetism}</>
|
||||
) : (
|
||||
<Button>Selects an object for magnetism</Button>
|
||||
)}
|
||||
{store.objectForMagnetism ? (
|
||||
<>{store.objectForMagnetism}</>
|
||||
) : (
|
||||
<Button>Selects an object magnet</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
{store.sceneModels.map((el) => {
|
||||
return <StaticAssetModelView onTap={() => store.deleteSceneItem(el)} model={el} />;
|
||||
})}
|
||||
</div>
|
||||
|
||||
{store.sceneMode === SceneMode.MAGNETISM_MARKING ? (
|
||||
<>
|
||||
<div style={{ backgroundColor: "white" }}>
|
||||
<div>completion of objects</div>
|
||||
<div>
|
||||
{store.objectMagnetism ? (
|
||||
<>{store.objectMagnetism}</>
|
||||
) : (
|
||||
<Button>Selects an object for magnetism</Button>
|
||||
)}
|
||||
{store.objectForMagnetism ? <>{store.objectForMagnetism}</> : <Button>Selects an object magnet</Button>}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)} */}
|
||||
{/* </div> */}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
|
|
@ -8,7 +8,7 @@ import { SceneMenu, SceneMode } from "../model/scene_view";
|
|||
import { BaseSceneItemModel, CameraViewModel, RobossemblerFiles, StaticAssetItemModel } from "../model/scene_assets";
|
||||
import { SceneHttpRepository } from "../data/scene_repository";
|
||||
import { message } from "antd";
|
||||
import { RobossemblerAssets } from "../model/robossembler_assets";
|
||||
import { RobossemblerAssets } from "../../../core/model/robossembler_assets";
|
||||
|
||||
export class SceneMangerStore extends UiErrorState<HttpError> {
|
||||
sceneMode: SceneMode;
|
||||
|
@ -112,7 +112,7 @@ export class SceneMangerStore extends UiErrorState<HttpError> {
|
|||
}
|
||||
};
|
||||
|
||||
async loadScene(sceneId: string, canvasRef: HTMLCanvasElement) {
|
||||
async loadScene(canvasRef: HTMLCanvasElement) {
|
||||
this.loadWebGl(canvasRef);
|
||||
await this.mapOk<RobossemblerAssets>("robossemblerAssets", this.sceneHttpRepository.getRobossemblerAssets());
|
||||
if (this.robossemblerAssets) {
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
import { Result } from "../../../core/helper/result";
|
||||
import { HttpMethod, HttpRepository } from "../../../core/repository/http_repository";
|
||||
import { IProjectModel } from "../model/project_model";
|
||||
|
||||
export class SelectProjectRepository extends HttpRepository {
|
||||
async setActiveProject(id: string) {
|
||||
return await this._jsonRequest(HttpMethod.POST, `/project?${id}`);
|
||||
}
|
||||
async getAllProjects(page = 1): Promise<Result<Error, IProjectModel[]>> {
|
||||
return await this._jsonRequest(HttpMethod.GET, `/project?${page}`);
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
import { DatabaseModel } from "../../../core/model/database_model";
|
||||
import { IProcess } from "../../create_process/model/process_model";
|
||||
|
||||
export interface IProjectModel extends DatabaseModel {
|
||||
pipelines: [IProcess];
|
||||
rootDir: string;
|
||||
description: string;
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
import * as React from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { LoadPage } from "../../../core/ui/pages/load_page";
|
||||
import { CreateProjectScreenPath } from "../../create_project/create_project_screen";
|
||||
import { SelectProjectStore } from "./select_project_store";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { CreateProjectInstancePath } from "../../create_project_instance/create_project_instance";
|
||||
import { Button } from "antd";
|
||||
export const SelectProjectScreenPath = "/select_project";
|
||||
|
||||
export const SelectProjectScreen: React.FunctionComponent = observer(() => {
|
||||
const [selectProjectStore] = React.useState(() => new SelectProjectStore());
|
||||
const navigate = useNavigate();
|
||||
|
||||
React.useEffect(() => {
|
||||
selectProjectStore.init();
|
||||
}, [selectProjectStore, navigate]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<LoadPage
|
||||
path={CreateProjectScreenPath}
|
||||
largeText={"Select project"}
|
||||
minText={"add new project?"}
|
||||
isLoading={selectProjectStore.isLoading}
|
||||
isError={selectProjectStore.errors.isNotEmpty()}
|
||||
children={selectProjectStore.projects.map((el) => {
|
||||
return (
|
||||
<>
|
||||
<div>{el.description}</div>
|
||||
<div>
|
||||
<Button onClick={() => navigate(CreateProjectInstancePath + el._id)}>create instance</Button>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
})}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
});
|
|
@ -1,28 +0,0 @@
|
|||
import makeAutoObservable from "mobx-store-inheritance";
|
||||
import { SelectProjectRepository } from "../data/select_project_repository";
|
||||
import { IProjectModel } from "../model/project_model";
|
||||
import { CoreError, UiErrorState } from "../../../core/store/base_store";
|
||||
|
||||
export class SelectProjectStore extends UiErrorState<CoreError> {
|
||||
errorHandingStrategy = (error: CoreError) => {};
|
||||
repository: SelectProjectRepository;
|
||||
errors = [];
|
||||
page = 1;
|
||||
projects: IProjectModel[] = [];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.repository = new SelectProjectRepository();
|
||||
makeAutoObservable(this);
|
||||
}
|
||||
async setActiveProject(id: string): Promise<void> {
|
||||
this.httpHelper(this.repository.setActiveProject(id));
|
||||
}
|
||||
async getPipelines(): Promise<void> {
|
||||
await this.mapOk("projects", this.repository.getAllProjects(this.page));
|
||||
}
|
||||
async init() {
|
||||
await this.getPipelines();
|
||||
}
|
||||
}
|
11
ui/src/features/simulations/simulations_screen.tsx
Normal file
11
ui/src/features/simulations/simulations_screen.tsx
Normal file
|
@ -0,0 +1,11 @@
|
|||
import * as React from "react";
|
||||
import { MainPage } from "../../core/ui/pages/main_page";
|
||||
|
||||
export interface ISimulationScreenProps {}
|
||||
export const SimulationScreenPath = "/simulation";
|
||||
|
||||
export default class SimulationScreen extends React.Component<ISimulationScreenProps> {
|
||||
public render() {
|
||||
return <MainPage page={"Симуляция"}></MainPage>;
|
||||
}
|
||||
}
|
|
@ -9,7 +9,7 @@ export interface ISocketListerProps {
|
|||
|
||||
export const SocketLister = observer((props: ISocketListerProps) => {
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
{socketListerStore.socketHasDisconnect ? (
|
||||
<ReloadIcon
|
||||
onClick={() => {
|
||||
|
@ -28,6 +28,6 @@ export const SocketLister = observer((props: ISocketListerProps) => {
|
|||
)}
|
||||
|
||||
{props.children}
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
|
|
@ -1,20 +1,30 @@
|
|||
import { makeAutoObservable } from "mobx";
|
||||
import { SocketRepository } from "../../core/repository/socket_repository";
|
||||
import { SocketRepository, socketRepository } from "../../core/repository/socket_repository";
|
||||
import { setTimeout } from "timers/promises";
|
||||
|
||||
class SocketListerStore {
|
||||
repository: SocketRepository;
|
||||
socketHasDisconnect = false;
|
||||
socketHasDisconnect = true;
|
||||
|
||||
constructor(repository: SocketRepository) {
|
||||
this.repository = repository;
|
||||
makeAutoObservable(this);
|
||||
repository.connect()
|
||||
this.init();
|
||||
}
|
||||
async init() {
|
||||
(await this.repository.connect()).fold(
|
||||
() => {
|
||||
this.socketHasDisconnect = false
|
||||
},
|
||||
() => {
|
||||
this.socketHasDisconnect = true
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async reconnect() {
|
||||
await this.repository.connect()
|
||||
this.socketHasDisconnect = false
|
||||
await this.repository.connect();
|
||||
this.socketHasDisconnect = false;
|
||||
}
|
||||
}
|
||||
|
||||
export const socketListerStore = new SocketListerStore(new SocketRepository());
|
||||
export const socketListerStore = new SocketListerStore(socketRepository);
|
||||
|
|
|
@ -1,31 +1,19 @@
|
|||
import React from "react";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import "reflect-metadata";
|
||||
|
||||
import "antd/dist/antd.min.css";
|
||||
|
||||
import { extensions } from "./core/extensions/extensions";
|
||||
import { SocketLister } from "./features/socket_lister/socket_lister";
|
||||
import { RouterProvider } from "react-router-dom";
|
||||
import { router } from "./core/routers/routers";
|
||||
import { SceneManger } from "./features/scene_manager/presentation/scene_manager";
|
||||
import { BehaviorTreeBuilderScreen } from "./features/behavior_tree_builder/presentation/behavior_tree_builder_screen";
|
||||
import { StickObjectsMarkingScreen } from "./features/stick_objects_marking/stick_objects_marking_screen";
|
||||
|
||||
extensions();
|
||||
const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement);
|
||||
|
||||
root.render(
|
||||
<>
|
||||
{/* <SocketLister> */}
|
||||
{/* <RouterProvider router={router} /> */}
|
||||
|
||||
{/* </SocketLister> */}
|
||||
<>
|
||||
{/* <SceneManger></SceneManger> */}
|
||||
{/* <StickObjectsMarkingScreen /> */}
|
||||
|
||||
<BehaviorTreeBuilderScreen />
|
||||
</>
|
||||
<SocketLister>
|
||||
<RouterProvider router={router} />
|
||||
</SocketLister>
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"target": "es6",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue