Добавлены модули создания проектов сборки и подготовки датасетов

This commit is contained in:
IDONTSUDO 2024-04-23 10:32:43 +00:00 committed by Igor Brylyov
parent 40b9b116c1
commit f57438b404
173 changed files with 6750 additions and 1857 deletions

315
ui/package-lock.json generated
View file

@ -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",

View file

@ -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",

View file

@ -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>

View file

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View file

@ -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]);
};
}
};

View file

@ -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();
};

View file

@ -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();

View file

@ -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;
}
}
};

View file

@ -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;
};
}
};

View file

@ -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 },

View file

@ -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) {

View file

@ -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;

View file

@ -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()

View file

@ -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 />,
},
]);

View 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>
);
}

View 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>
);
});

View 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)
);
}
}

View 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[];
}
}

View 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}],
};
```

View 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);
};

View 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>
);
};

View 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>
);
};

View 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>
);
};

View 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>
);
};

View file

@ -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>;
}

View file

@ -0,0 +1,7 @@
export enum StickObjectsMarkingStoreMode {
objectsToWhichItSticks = "objectsToWhichItSticks",
objectThatSticks = "objectThatSticks",
addPointsObjectsToWhichItSticks = "addPointsObjectsToWhichItSticks",
addPointsObjectThatSticks = "addPointsObjectThatSticks",
move = "move",
}

View file

@ -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;
}
}

View file

@ -0,0 +1,4 @@
export enum StickyLoaderMode {
IN = "IN",
OUT = "OUT",
}

View file

@ -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>
);
});

View file

@ -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);
}
}

View file

@ -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)
);
}
});
});
}
}

View file

@ -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}`);

View file

@ -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;
}

View file

@ -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>
);

View file

@ -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));
}

View 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>;
}

View 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);
}
}

View file

@ -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();
}
}

View file

@ -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>
</>
}
/>
);
}

View file

@ -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 = {

View file

@ -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)}

View file

@ -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 }) {

View file

@ -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());

View file

@ -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;
}

View file

@ -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>
);
}

View file

@ -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>
);
}

View file

@ -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} />;
}

View file

@ -0,0 +1,3 @@
export const $nodewidth = 200;
export const $socketmargin = 6;
export const $socketsize = 16;

View file

@ -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>

View file

@ -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}`);
}
}

View file

@ -1,8 +0,0 @@
export interface IColor {
color: string;
}
export interface PipelineModelDataBase {
process: string;
trigger: string;
}

View file

@ -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>
</>
}
/>
</>
);
});

View file

@ -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());
}
}

View file

@ -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);
}
}

View file

@ -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,
};

View file

@ -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>
);
});

View file

@ -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());

View file

@ -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);
}
}

View 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>
}
/>

View file

@ -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;
}
);
//
}
}

View file

@ -1,5 +1,3 @@
export interface ICreateProjectViewModel {
pipelines: string[];
description: string;
}

View file

@ -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>
</>
);
});

View file

@ -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}`);
}
}

View file

@ -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("/");
}
}
}
}

View file

@ -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 "";
}
}

View file

@ -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);
}
}

View file

@ -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;
}

View file

@ -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>
</>
);
}
);

View file

@ -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>
</>
);
}
);

View file

@ -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>
</>
);
});

View file

@ -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());

View 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>
);
};

View 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] },
};

View 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");
};
}

View 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>
}
/>
</>
);
});

View 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());
};
}

View 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>
);
};

View file

@ -0,0 +1 @@

View 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>;
}

View 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>;
}

View file

@ -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>
// );
// };

View file

@ -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="Датасеты" />
</>
);
};

View file

@ -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() {

View file

@ -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",

View file

@ -57,7 +57,6 @@ export const SceneWidget = () => {
<h1
onClick={(event) => {
event.stopPropagation();
console.log(201);
}}
>
HYO

View file

@ -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> */}
</>
}
/>
);
});

View file

@ -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) {

View file

@ -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}`);
}
}

View file

@ -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;
}

View file

@ -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>
</>
);
})}
/>
</>
);
});

View file

@ -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();
}
}

View 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>;
}
}

View file

@ -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>
);
});

View file

@ -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);

View file

@ -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>
</>
);

View file

@ -1,6 +1,6 @@
{
"compilerOptions": {
"target": "es5",
"target": "es6",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,