Skip to content

Commit

Permalink
further work on adding support for component building
Browse files Browse the repository at this point in the history
  • Loading branch information
Krande committed Oct 3, 2024
1 parent 2d8e50b commit a6da96e
Show file tree
Hide file tree
Showing 7 changed files with 135 additions and 55 deletions.
23 changes: 17 additions & 6 deletions examples/procedure_example/components/create_floor.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,27 @@

app = typer.Typer()

@procedure_decorator(app, export_file_type=FileTypeDC.IFC, export_file_var="output_file")
def main(
name: str = 'floor1',
origin: tuple[float, float, float] = (0, 0, 0),
width: float = 40,
length: float = 20,
thickness: float = 0.01,
output_file: pathlib.Path = None
) -> pathlib.Path:

@procedure_decorator(app, export_file_type=FileTypeDC.IFC)
def main(name: str = 'floor1', p0: tuple[float, float, float] = (0, 0, 0), width: float = 40, length: float = 20) -> pathlib.Path:
temp_dir = Config().websockets_server_temp_dir
comp_dir = temp_dir / "components"
p = ada.Part(name) / ada.Plate("pl1", [(0, 0), (width, 0), (width, length), (0, length)], 0.01, origin=p0)
this_file_name = pathlib.Path(__file__).stem
output_dir = temp_dir / "components" / this_file_name

p = ada.Part(name) / ada.Plate("pl1", [(0, 0), (width, 0), (width, length), (0, length)], thickness, origin=origin)
# The assembly level is to be discarded. Only the part is relevant for merging into another IFC file
a = ada.Assembly("TempAssembly") / p
comp_dir.mkdir(parents=True, exist_ok=True)
new_file = comp_dir / f"{name}.ifc"
output_dir.mkdir(parents=True, exist_ok=True)
new_file = output_dir / f"{this_file_name}.ifc"
if new_file.exists():
new_file.unlink()
a.to_ifc(new_file)

return new_file
Expand Down
2 changes: 1 addition & 1 deletion examples/procedure_example/procedures/add_stiffeners.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def add_stiffeners(pl: ada.Plate) -> list[ada.Beam]:


@procedure_decorator(app, input_file_var="ifc_file", input_file_type=FileTypeDC.IFC, export_file_type=FileTypeDC.IFC)
def main(ifc_file: pathlib.Path = None) -> pathlib.Path:
def main(ifc_file: pathlib.Path = None, output_file: pathlib.Path = None) -> pathlib.Path:
"""A procedure to add stiffeners to all plates in the IFC file"""

a = ada.from_ifc(ifc_file)
Expand Down
54 changes: 25 additions & 29 deletions src/ada/comms/msg_handling/run_procedure.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import pathlib
from typing import TYPE_CHECKING

from ada.comms.fb_model_gen import FileObjectDC, MessageDC
from ada.comms.fb_model_gen import FileObjectDC, MessageDC, ParameterDC, ParameterTypeDC, ProcedureStartDC
from ada.comms.msg_handling.update_server import update_server
from ada.comms.procedures import Procedure
from ada.config import logger
Expand All @@ -17,47 +17,43 @@ def run_procedure(server: WebSocketAsyncServer, client: ConnectedClient, message
start_procedure = message.procedure_store.start_procedure

procedure: Procedure = server.procedure_store.get(start_procedure.procedure_name)
params = procedure.params
for param in start_procedure.parameters:
if param.type == "string":
params[param.name].value = param.value.string_value
elif param.type == "float":
params[param.name].value = param.value.float_value
elif param.type == "integer":
params[param.name].value = param.value.integer_value
elif param.type == "boolean":
params[param.name].value = param.value.boolean_value
elif param.type == "array":
params[param.name].value = param.value.array_value
else:
if param.value.string_value:
params[param.name].value = param.value.string_value
else:
raise ValueError(f"Unknown parameter type {param.type}")

params = {p.name: p for p in start_procedure.parameters}
procedure(**params)

procedure(**procedure.params)
logger.info(f"Procedure {procedure.name} ran successfully")
update_server_on_successful_procedure_run(server, procedure, client, message)

update_server_on_successful_procedure_run(server, procedure, client, message, start_procedure)


def update_server_on_successful_procedure_run(
server: WebSocketAsyncServer, procedure: Procedure, client: ConnectedClient, message: MessageDC
server: WebSocketAsyncServer, procedure: Procedure, client: ConnectedClient, message: MessageDC, start_procedure: ProcedureStartDC
) -> None:
param = procedure.params.get(procedure.input_file_var)
if isinstance(param.value, str):
input_file_path = pathlib.Path(param.value)
params = [p for p in start_procedure.parameters if p.name == procedure.input_file_var]
if len(params) == 0:
# it's a component procedure
input_file_path = None
output_dir = procedure.get_component_output_dir()
if procedure.export_file_type
output_file = output_dir /
else:
input_file_path = pathlib.Path(param.value.string_value)
# it's a modification procedure on an existing file
param = params[0]
if param.type == ParameterTypeDC.STRING:
input_file_path = pathlib.Path(param.value.string_value)
elif param.type == ParameterTypeDC.UNKNOWN and param.value.string_value:
input_file_path = pathlib.Path(param.value.string_value)
else:
raise NotImplementedError("Only string input file paths are supported for now")

server_file_object = server.scene.get_file_object(input_file_path.stem)
output_file = procedure.get_procedure_output(input_file_path.stem)
server_file_object = server.scene.get_file_object(input_file_path.stem)
output_file = procedure.get_procedure_output(input_file_path.stem)
purpose = server_file_object.purpose

new_file_object = FileObjectDC(
name=output_file.name,
filepath=output_file,
file_type=procedure.export_file_type,
purpose=server_file_object.purpose,
purpose=purpose,
is_procedure_output=True,
procedure_parent=message.procedure_store.start_procedure,
)
Expand Down
53 changes: 49 additions & 4 deletions src/ada/comms/procedures.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,16 @@ def _call_script_subprocess(self, *args, **kwargs):
for arg in args:
call_args.append(str(arg))
# Build the command-line arguments
for arg_name, value in kwargs.items():
for arg_name, param_dc in kwargs.items():
param_dc: ParameterDC
value = make_param_value(param_dc)

# Convert underscores to hyphens
if isinstance(value, ParameterDC):
value = value.value
arg_name_cli = arg_name.replace("_", "-")
if isinstance(value, bool):
if param_dc.type == ParameterTypeDC.ARRAY:
call_args.append(f"--{arg_name_cli}")
call_args.extend(value)
elif isinstance(value, bool):
if value:
call_args.append(f"--{arg_name_cli}")
else:
Expand Down Expand Up @@ -79,6 +83,13 @@ def to_procedure_dc(self) -> ProcedureDC:
export_file_type=self.export_file_type,
)

def get_component_output_dir(self):
from ada.comms.scene_model import Scene
temp_dir = Scene.get_temp_dir()
component_dir = temp_dir / "components"

return component_dir / self.name

def get_procedure_output(self, input_file_name: str):
from ada.comms.scene_model import Scene

Expand All @@ -104,6 +115,38 @@ def get_procedure_output(self, input_file_name: str):
else:
raise NotImplementedError(f"Export file type {self.export_file_type} not implemented")

def make_param_value(param: ParameterDC) -> str | list[str]:
if param.type == ParameterTypeDC.STRING:
return param.value.string_value
elif param.type == ParameterTypeDC.INTEGER:
return str(param.value.integer_value)
elif param.type == ParameterTypeDC.FLOAT:
return str(param.value.float_value)
elif param.type == ParameterTypeDC.BOOLEAN:
return str(param.value.boolean_value)
elif param.type == ParameterTypeDC.ARRAY:
if param.value.array_value is None:
raise ValueError("Array value is None")
values = []

for val in param.value.array_value:
if param.value.array_value_type == ParameterTypeDC.STRING:
values.append(val.string_value)
elif param.value.array_value_type == ParameterTypeDC.INTEGER:
values.append(str(val.integer_value))
elif param.value.array_value_type == ParameterTypeDC.FLOAT:
values.append(str(val.float_value))
elif param.value.array_value_type == ParameterTypeDC.BOOLEAN:
values.append(str(val.boolean_value))
else:
raise NotImplementedError(f"Parameter type {param.value.array_value_type} not implemented")

return values
else:
if param.type == ParameterTypeDC.UNKNOWN and param.value.string_value is not None:
return param.value.string_value
raise NotImplementedError(f"Parameter type {param.type} not implemented")


@dataclass
class ProcedureStore:
Expand Down Expand Up @@ -149,11 +192,13 @@ def procedure_decorator(
input_file_var: str | None = None,
input_file_type: FileTypeDC | None = None,
export_file_type: FileTypeDC | None = None,
export_file_var: str | None = None,
) -> Callable:
def wrapper(func: Callable) -> Callable:
func.input_file_var = input_file_var
func.input_file_type = input_file_type
func.export_file_type = export_file_type
func.export_file_var = export_file_var
if Typer is not None:
app.command()(func) # Apply the app.command decorator
return func
Expand Down
2 changes: 1 addition & 1 deletion src/frontend/src/components/node_editor/DynamicHandle.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// DynamicHandle.tsx
import React from 'react';
import {Connection, Handle, HandleType, Position} from '@xyflow/react';
import {FileObject} from "../../flatbuffers/wsock/file-object";


interface DynamicHandleProps {
id: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ function ProcedureNode(props: { id: string, data: Record<string, string | Proced
if (!props.data.paramids) {
props.data.paramids = [];
}
if (index == 0) {
props.data.paramids = [];
}
(props.data.paramids as string[]).push(paramId);

if (paramName === input_file_var) {
Expand Down Expand Up @@ -92,7 +95,6 @@ function ProcedureNode(props: { id: string, data: Record<string, string | Proced
</div>
);
} else if (param.type() === ParameterType.ARRAY) {
let default_value = null;
let values = [];
if (paramDefaultValue) {
let array_value_type = paramDefaultValue.arrayValueType()
Expand Down Expand Up @@ -151,4 +153,4 @@ function ProcedureNode(props: { id: string, data: Record<string, string | Proced
);
}

export default memo(ProcedureNode);
export default memo(ProcedureNode);
50 changes: 38 additions & 12 deletions src/frontend/src/utils/node_editor/run_procedure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import * as flatbuffers from "flatbuffers"; // Import the node editor Zustand st
import {webSocketHandler} from "../websocket_connector";
import {Builder} from "flatbuffers";

function extract_input_params(builder: Builder, params: string[], procedure: Procedure) {
function extract_input_params(builder: Builder, params: string[], procedure: Procedure) {
// the param strings are div keys in the form of 'param-<procedure_name>-<index>'
// we need to first get the parent div, then get the input value from the input element

Expand Down Expand Up @@ -67,29 +67,54 @@ function extract_input_params(builder: Builder, params: string[], procedure: Pro
Parameter.startParameter(builder);
Parameter.addName(builder, param_name_str);
Parameter.addValue(builder, input_value_buf);
Parameter.addType(builder, param_type);
let input_param = Parameter.endParameter(builder);
input_params.push(input_param)
continue
} else if (param_input.length > 1) {
let tuple_values = []
let tuple_values = [];
let array_value_type = parameter.defaultValue?.arrayValueType;

for (let input of param_input) {
let input_value = input.value
let input_value = input.value;

// Start building a Value based on the arrayValueType (STRING, FLOAT, etc.)
Value.startValue(builder);
if (parameter.value?.arrayValueType === ParameterType.STRING) {
let input_value_str = builder.createString(input_value)
Value.addStringValue(builder, input_value_str);

if (array_value_type === ParameterType.STRING) {
let input_value_str = builder.createString(input_value);
Value.addStringValue(builder, input_value_str);
} else if (array_value_type === ParameterType.FLOAT) {
Value.addFloatValue(builder, parseFloat(input_value));
} else if (array_value_type === ParameterType.INTEGER) {
Value.addIntegerValue(builder, parseInt(input_value));
} else if (array_value_type === ParameterType.BOOLEAN) {
Value.addBooleanValue(builder, input_value === "true");
}

// End building Value and add to the tuple_values array
let input_value_buf = Value.endValue(builder);
tuple_values.push(input_value_buf)
tuple_values.push(input_value_buf);
}

let tuple_values_vector = Value.createArrayValueVector(builder, tuple_values);
// Create a vector for tuple_values
Value.startValue(builder);
Value.addArrayValue(builder, tuple_values_vector);
if (array_value_type){
Value.addArrayValueType(builder, array_value_type);
}
let array_value = Value.endValue(builder);

// Start building the Parameter with the array of values
Parameter.startParameter(builder);
Parameter.addName(builder, param_name_str);
Parameter.addValue(builder, tuple_values_vector);
Parameter.addValue(builder, array_value);
Parameter.addType(builder, ParameterType.ARRAY); // Indicate that this is an array type parameter

let input_param = Parameter.endParameter(builder);
input_params.push(input_param)
continue
input_params.push(input_param);
continue;
}

// if param_div is an input element, get the value directly
Expand Down Expand Up @@ -149,8 +174,9 @@ export function run_procedure(props: { id: string, data: Record<string, string |
parameters_list.push(input_file)
}
let input_params = extract_input_params(builder, props.data.paramids as string[], procedure);
parameters_list.push(...input_params)

if (input_params)
parameters_list.push(...input_params)

let parameters = ProcedureStart.createParametersVector(builder, parameters_list);

ProcedureStart.startProcedureStart(builder);
Expand Down

0 comments on commit a6da96e

Please sign in to comment.