diff --git a/server/src/core/controllers/http_controller.ts b/server/src/core/controllers/http_controller.ts index 736e8e0..1ee352b 100644 --- a/server/src/core/controllers/http_controller.ts +++ b/server/src/core/controllers/http_controller.ts @@ -29,6 +29,10 @@ export abstract class CallbackStrategyWithFileUpload { abstract idValidationExpression: CoreValidation; abstract call(file: File, id: string): ResponseBase; } +export abstract class CallbackStrategyWithFilesUploads { + abstract chuckingFileExpressions: RegExp[]; + abstract call(files: File[]): ResponseBase; +} interface ISubSetFeatureRouter { method: HttpMethodType; @@ -38,7 +42,8 @@ interface ISubSetFeatureRouter { | CallbackStrategyWithEmpty | CallbackStrategyWithIdQuery | CallBackStrategyWithQueryPage - | CallbackStrategyWithFileUpload; + | CallbackStrategyWithFileUpload + | CallbackStrategyWithFilesUploads; } abstract class ICoreHttpController { @@ -105,6 +110,7 @@ export class CoreHttpController implements ICoreHttpController { await this.responseHelper(res, el.fn.call(req["files"]["file"])); } } + if (el.fn instanceof CallBackStrategyWithQueryPage) { throw Error("needs to be implimed"); } @@ -112,7 +118,7 @@ export class CoreHttpController implements ICoreHttpController { await this.responseHelper(res, el.fn.call()); return; } - + if (el.fn instanceof CallbackStrategyWithFileUpload) { if (req["files"] === undefined) { res.status(400).json("need files to form-data request"); @@ -143,6 +149,7 @@ export class CoreHttpController implements ICoreHttpController { return; } } + await this.responseHelper(res, el.fn.call(req["files"]["file"], req.query.id)); } }); diff --git a/server/src/features/projects/domain/upload_new_env_scenario.ts b/server/src/features/projects/domain/upload_new_env_scenario.ts new file mode 100644 index 0000000..1eac9d9 --- /dev/null +++ b/server/src/features/projects/domain/upload_new_env_scenario.ts @@ -0,0 +1,61 @@ +import { CallbackStrategyWithFilesUploads, ResponseBase } from "../../../core/controllers/http_controller"; +import { Result } from "../../../core/helpers/result"; +import { CreateFileUseCase } from "../../../core/usecases/create_file_usecase"; +import { ReadFileAndParseJsonUseCase } from "../../../core/usecases/read_file_and_parse_json"; +export interface Parts { + name: string; + part_path: string; + material_path: string; + mass: number; + inertia: { + ixx: number; + ixy: number; + ixz: number; + iyy: number; + iyz: number; + izz: number; + }; + visual: string; + collision: string; + type: string; +} +export class UploadNewEnvScenario { + call = async ( + files: { name: string; file?: Buffer }[], + name: string, + inertia: number, + mass: number, + projectPath: string + ): ResponseBase => { + await files.map( + async (el) => await new CreateFileUseCase().call(projectPath + `/assets/libs/objects/${name}.${el.name}`, el.file) + ); + // "part_path": "parts/objects/body_down.stl", + await new CreateFileUseCase().call( + projectPath + `/parts/objects/${name}.${files.find((el) => el.name.isEqual("stl")).name}`, + files.find((el) => el.name.isEqual("stl")).file + ); + return (await new ReadFileAndParseJsonUseCase().call(projectPath + "/assets/parts.json")).map( + async (el) => { + el.push({ + name, + inertia: { + ixx: 0.1, + ixy: 0, + ixz: 0, + iyy: 0.1, + iyz: 0, + izz: 0.1, + }, + mass, + visual: `/assets/libs/objects/${name}.dae`, + collision: `/assets/libs/objects/${name}.stl`, + type: "env", + material_path: "", + part_path: `/libs/objects/${name}.stl`, + }); + await new CreateFileUseCase().call(projectPath + "/assets/parts.json", Buffer.from(JSON.stringify(el), "utf8")); + } + ); + }; +} diff --git a/server/src/features/projects/projects_presentation.ts b/server/src/features/projects/projects_presentation.ts index a49e2d6..88e12cd 100644 --- a/server/src/features/projects/projects_presentation.ts +++ b/server/src/features/projects/projects_presentation.ts @@ -1,11 +1,13 @@ import { CrudController } from "../../core/controllers/crud_controller"; - import { CreateNewProjectInstanceScenario, ProjectValidationModel } from "./domain/create_new_project_scenario"; +import { SearchManyDataBaseModelUseCase } from "../../core/usecases/search_many_database_model_usecase"; +import { CreateNewProjectInstanceScenario, ProjectValidationModel } from "./domain/create_new_project_scenario"; import { GetActiveProjectIdScenario } from "./domain/get_active_project_id_scenario"; import { RobossemblerAssetsNetworkMapperScenario } from "./domain/robossembler_assets_network_mapper_scenario"; import { SetActiveProjectScenario } from "./domain/set_active_project_use_scenario"; import { UploadCadFileToProjectScenario } from "./domain/upload_file_to_to_project_scenario"; -import { ProjectDBModel } from "./models/project_model_database_model"; - +import { UploadNewEnvScenario } from "./domain/upload_new_env_scenario"; +import { IProjectModel, ProjectDBModel } from "./models/project_model_database_model"; + export class ProjectsPresentation extends CrudController { constructor() { super({ @@ -37,6 +39,42 @@ export class ProjectsPresentation extends CrudController { + try { + } catch (error) {} + ( + await new SearchManyDataBaseModelUseCase(ProjectDBModel).call( + { isActive: true }, + "is dont active projects" + ) + ).map(async (projectModel) => { + const { rootDir } = projectModel[0]; + const files: { name: string; file?: Buffer }[] = [ + { name: "blend" }, + { name: "fbx" }, + { name: "ply" }, + { name: "glb" }, + { name: "dae" }, + { name: "fbx" }, + { name: "png" }, + { name: "stl" }, + ]; + // @ts-ignore + const reqFiles = req.files; + + files.forEach((el) => { + reqFiles[el.name].data; + el.file = reqFiles[el.name].data; + }); + + await new UploadNewEnvScenario().call(files, req.body.name, req.body.inertia, req.body.mass, rootDir); + return res.status(200).json(""); + }); + }); } } diff --git a/ui/src/core/repository/http_repository.ts b/ui/src/core/repository/http_repository.ts index afbee9c..84ddcca 100644 --- a/ui/src/core/repository/http_repository.ts +++ b/ui/src/core/repository/http_repository.ts @@ -7,7 +7,7 @@ export enum HttpMethod { GET = "GET", POST = "POST", DELETE = "DELETE", - PUT = "PUT" + PUT = "PUT", } export class HttpError extends Error { status: number; @@ -20,10 +20,7 @@ export class HttpError extends Error { } export class HttpRepository { private server = "http://localhost:4001"; - public async _formDataRequest(method: HttpMethod, url: string, data?: any): Promise> { - let formData = new FormData(); - formData.append("file", data); - + public async _formDataRequest(method: HttpMethod, url: string, formData: FormData): Promise> { const reqInit = { body: formData, method: method, diff --git a/ui/src/core/ui/pages/main_page.tsx b/ui/src/core/ui/pages/main_page.tsx index 203e54d..e0ab603 100644 --- a/ui/src/core/ui/pages/main_page.tsx +++ b/ui/src/core/ui/pages/main_page.tsx @@ -56,6 +56,8 @@ export interface IMainPageProps { maskLoader?: boolean; error?: UiBaseError[]; } + + export const MainPage = (props: IMainPageProps) => { const blocksNames = [ { name: "Детали", path: DetailsScreenPath, icon: "Setting" }, diff --git a/ui/src/core/ui/text/text.tsx b/ui/src/core/ui/text/text.tsx index b5e4a80..6559377 100644 --- a/ui/src/core/ui/text/text.tsx +++ b/ui/src/core/ui/text/text.tsx @@ -83,7 +83,5 @@ export function CoreText(props: ITextProps) { {props.text} ); - - - } +// CREATE COMPONENT , HTML WRITE , JSX->JS TSX -> TS, PROPS , LIFE CYCLE , REACT HOOK, STATE MANAGMENT -> PUBSUB -> Конечный автомат -> ROUTER diff --git a/ui/src/features/all_projects/data/project_http_repository.ts b/ui/src/features/all_projects/data/project_http_repository.ts index f3350f3..cacff5b 100644 --- a/ui/src/features/all_projects/data/project_http_repository.ts +++ b/ui/src/features/all_projects/data/project_http_repository.ts @@ -15,7 +15,9 @@ export class ProjectHttpRepository extends CoreHttpRepository { return await this._jsonRequest(HttpMethod.POST, "/projects", model); } async setProjectRootFile(file: File, projectId: string) { - return await this._formDataRequest(HttpMethod.POST, `/projects/upload?id=${projectId}`, file); + const formData = new FormData(); + formData.append("file", file); + return await this._formDataRequest(HttpMethod.POST, `/projects/upload?id=${projectId}`, formData); } async setActivePipeline(id: string) { diff --git a/ui/src/features/details/details_http_repository.ts b/ui/src/features/details/details_http_repository.ts index 68e6813..dd8d4b2 100644 --- a/ui/src/features/details/details_http_repository.ts +++ b/ui/src/features/details/details_http_repository.ts @@ -1,12 +1,19 @@ import { HttpMethod, CoreHttpRepository } from "../../core/repository/http_repository"; +import { EnvelopmentViewModel } from "./envelopment_view_model"; export interface Parts { - name: string; - part_path: string; - material_path: string; - stlUrl: string; - image: string; + name: string; + part_path: string; + material_path: string; + stlUrl: string; + image: string; } - export class DetailsHttpRepository extends CoreHttpRepository { - } \ No newline at end of file + uploadNewEnv = (model: EnvelopmentViewModel) => { + const formData = new FormData(); + Object.entries(model).forEach(([k, v]) => { + formData.append(k, v); + }); + this._formDataRequest(HttpMethod.POST, "/projects/upload/env", formData); + }; +} diff --git a/ui/src/features/details/details_screen.tsx b/ui/src/features/details/details_screen.tsx index 7fa6ff9..552cbc5 100644 --- a/ui/src/features/details/details_screen.tsx +++ b/ui/src/features/details/details_screen.tsx @@ -1,8 +1,11 @@ import { observer } from "mobx-react-lite"; -import { DetailsStore } from "./details_store"; +import { DetailsStore, DrawersDetailsStore } from "./details_store"; import { MainPage } from "../../core/ui/pages/main_page"; -import React, { useEffect } from "react"; +import React from "react"; import { CoreText, CoreTextType } from "../../core/ui/text/text"; +import { CoreButton } from "../../core/ui/button/button"; +import { Drawer, Upload } from "antd"; +import { CoreInput } from "../../core/ui/input/input"; export const DetailsScreenPath = "/details"; @@ -20,6 +23,12 @@ export const DetailsScreen = observer(() => { bodyChildren={} panelChildren={ <> + store.editDrawer(DrawersDetailsStore.NewEnvelopment, true)} + text="Добавить окружение" + style={{ margin: 10 }} + /> {store.detailsViewModel.map((el, i) => (
store.selectedDetail(el.label)} style={{ margin: 5, cursor: "pointer" }}>
@@ -31,6 +40,37 @@ export const DetailsScreen = observer(() => {
))} + store.editDrawer(DrawersDetailsStore.NewEnvelopment, false)} + open={store.drawers.find((el) => el.name === DrawersDetailsStore.NewEnvelopment)?.status} + > + store.updateForm({ name: text })} /> + store.updateForm({ blend: file.file.originFileObj })}> + + + store.updateForm({ ply: file.file.originFileObj })}> + + + store.updateForm({ stl: file.file.originFileObj })}> + + + store.updateForm({ glb: file.file.originFileObj })}> + + + store.updateForm({ dae: file.file.originFileObj })}> + + + store.updateForm({ fbx: file.file.originFileObj })}> + + + store.updateForm({ png: file.file.originFileObj })}> + + +
+ store.uploadEnv()} /> + } /> diff --git a/ui/src/features/details/details_store.ts b/ui/src/features/details/details_store.ts index c5ed561..dc9d476 100644 --- a/ui/src/features/details/details_store.ts +++ b/ui/src/features/details/details_store.ts @@ -1,27 +1,32 @@ import makeAutoObservable from "mobx-store-inheritance"; -import { CoreError, UiErrorState } from "../../core/store/base_store"; +import { CoreError, UiDrawerFormState } from "../../core/store/base_store"; import { NavigateFunction } from "react-router-dom"; import { DetailsHttpRepository, Parts } from "./details_http_repository"; import { DetailsThreeRepository } from "./details_three_repository"; +import { EnvelopmentViewModel } from "./envelopment_view_model"; +import { message } from "antd"; interface IDetailViewModel { label: string; selected: boolean; httpUrl: string; } +export enum DrawersDetailsStore { + NewEnvelopment = "Новое окружение", +} - -export class DetailsStore extends UiErrorState { +export class DetailsStore extends UiDrawerFormState { + viewModel: EnvelopmentViewModel = EnvelopmentViewModel.empty(); detailsViewModel: IDetailViewModel[] = []; parts: Parts[] = []; detailsHttpRepository: DetailsHttpRepository = new DetailsHttpRepository(); detailsThreeRepository?: DetailsThreeRepository; constructor() { - super(); + super(DrawersDetailsStore); makeAutoObservable(this); this.init(); } - errorHandingStrategy = (error: CoreError) => { }; + errorHandingStrategy = (error: CoreError) => {}; init = async (navigate?: NavigateFunction | undefined): Promise => { await this.mapOk("parts", this.detailsHttpRepository.getAssetsActiveProject()); this.detailsViewModel = this.parts.map((el) => { @@ -41,16 +46,24 @@ export class DetailsStore extends UiErrorState { this.detailsViewModel.map((el) => { if (el.label.match(label)) { el.selected = true; - this.detailsThreeRepository?.deleteAllObjectsScene() - this.detailsThreeRepository?.loadHttpAndPreview(el.httpUrl, el.label, () => { }) + this.detailsThreeRepository?.deleteAllObjectsScene(); + this.detailsThreeRepository?.loadHttpAndPreview(el.httpUrl, el.label, () => {}); return el; } - return el + return el; }); }; loadScene = async (ref: HTMLCanvasElement) => { - this.detailsThreeRepository = new DetailsThreeRepository(ref, () => { }); + this.detailsThreeRepository = new DetailsThreeRepository(ref, () => {}); this.detailsThreeRepository.render(); }; - + uploadEnv = () => + this.viewModel.isValid().fold( + async (model) => { + await this.detailsHttpRepository.uploadNewEnv(model); + await this.init(); + this.editDrawer(DrawersDetailsStore.NewEnvelopment, false); + }, + async (e) => message.error(e) + ); } diff --git a/ui/src/features/details/envelopment_view_model.ts b/ui/src/features/details/envelopment_view_model.ts new file mode 100644 index 0000000..55ddfdd --- /dev/null +++ b/ui/src/features/details/envelopment_view_model.ts @@ -0,0 +1,32 @@ +import { Result } from "../../core/helper/result"; + +export class EnvelopmentViewModel { + public name: string; + public blend?: File; + public ply?: File; + public glb?: File; + public dae?: File; + public stl?: File; + public fbx?: File; + public png?: File; + public mass: number = 0; + public inertia = { + ixx: 0.1, + ixy: 0, + ixz: 0, + iyy: 0.1, + iyz: 0, + izz: 0.1, + }; + + constructor(name: string) { + this.name = name; + } + static empty = () => new EnvelopmentViewModel(""); + isValid = (): Result => { + if (this.name.isEmpty()) { + return Result.error("name is empty"); + } + return Result.ok(this); + }; +} diff --git a/web_p/blender/parts.json b/web_p/blender/parts.json index 3069bbb..a16b460 100644 --- a/web_p/blender/parts.json +++ b/web_p/blender/parts.json @@ -2,31 +2,95 @@ { "name": "body_down", "part_path": "parts/objects/body_down.stl", - "material_path": "" + "material_path": "", + "mass": 100, + "inertia": { + "ixx": 0.1, + "ixy": 0, + "ixz": 0, + "iyy": 0.1, + "iyz": 0, + "izz": 0.1 + }, + "visual": ".dae", + "collision": ".stl" }, { "name": "sol_gear", "part_path": "parts/objects/sol_gear.stl", - "material_path": "" + "material_path": "", + "mass": 100, + "inertia": { + "ixx": 0.1, + "ixy": 0, + "ixz": 0, + "iyy": 0.1, + "iyz": 0, + "izz": 0.1 + }, + "visual": ".dae", + "collision": ".stl" }, { "name": "output_shaft", "part_path": "parts/objects/output_shaft.stl", - "material_path": "" + "material_path": "", + "mass": 100, + "inertia": { + "ixx": 0.1, + "ixy": 0, + "ixz": 0, + "iyy": 0.1, + "iyz": 0, + "izz": 0.1 + }, + "visual": ".dae", + "collision": ".stl" }, { "name": "planet_gear", "part_path": "parts/objects/planet_gear.stl", - "material_path": "" + "material_path": "", + "mass": 100, + "inertia": { + "ixx": 0.1, + "ixy": 0, + "ixz": 0, + "iyy": 0.1, + "iyz": 0, + "izz": 0.1 + }, + "visual": ".dae", + "collision": ".stl" }, { "name": "body_up", "part_path": "parts/objects/body_up.stl", - "material_path": "" + "material_path": "", + "inertia": { + "ixx": 0.1, + "ixy": 0, + "ixz": 0, + "iyy": 0.1, + "iyz": 0, + "izz": 0.1 + }, + "visual": ".dae", + "collision": ".stl" }, { "name": "bolt", "part_path": "parts/objects/bolt.stl", - "material_path": "" + "material_path": "", + "inertia": { + "ixx": 0.1, + "ixy": 0, + "ixz": 0, + "iyy": 0.1, + "iyz": 0, + "izz": 0.1 + }, + "visual": ".dae", + "collision": ".stl" } ] \ No newline at end of file