Skip to content

Commit

Permalink
make file node objects viewable
Browse files Browse the repository at this point in the history
  • Loading branch information
Krande committed Oct 1, 2024
1 parent 1952566 commit 5ec1ebf
Show file tree
Hide file tree
Showing 36 changed files with 682 additions and 196 deletions.
1 change: 1 addition & 0 deletions examples/procedure_example/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
server_temp/
6 changes: 5 additions & 1 deletion examples/procedure_example/ada_config.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
[procedures]
script_dir = "./procedures"
script_dir = "./procedures"

[websockets]
server_temp_dir = "./server_temp"
auto_load_files = true
6 changes: 1 addition & 5 deletions examples/procedure_example/list_server_file_objects.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import asyncio
import pathlib

from ada.comms.fb_model_gen import (
FileObjectDC,
FilePurposeDC,
FileTypeDC,
)
from ada.comms.fb_model_gen import FileObjectDC, FilePurposeDC, FileTypeDC
from ada.comms.wsock_client_async import WebSocketClientAsync

THIS_DIR = pathlib.Path(__file__).parent
Expand Down
13 changes: 12 additions & 1 deletion src/ada/comms/fb_deserializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ def deserialize_fileobject(fb_obj) -> FileObjectDC | None:
purpose=FilePurposeDC(fb_obj.Purpose()),
filepath=fb_obj.Filepath().decode("utf-8") if fb_obj.Filepath() is not None else None,
filedata=bytes(fb_obj.FiledataAsNumpy()) if fb_obj.FiledataLength() > 0 else None,
glb_file=deserialize_fileobject(fb_obj.GlbFile()),
ifcsqlite_file=deserialize_fileobject(fb_obj.IfcsqliteFile()),
is_procedure_output=fb_obj.IsProcedureOutput(),
procedure_parent=deserialize_procedurestart(fb_obj.ProcedureParent()),
)


Expand Down Expand Up @@ -89,12 +93,18 @@ def deserialize_server(fb_obj) -> ServerDC | None:
return None

return ServerDC(
add_file_object=deserialize_fileobject(fb_obj.AddFileObject()),
new_file_object=deserialize_fileobject(fb_obj.NewFileObject()),
all_file_objects=(
[deserialize_fileobject(fb_obj.AllFileObjects(i)) for i in range(fb_obj.AllFileObjectsLength())]
if fb_obj.AllFileObjectsLength() > 0
else None
),
get_file_object_by_name=(
fb_obj.GetFileObjectByName().decode("utf-8") if fb_obj.GetFileObjectByName() is not None else None
),
get_file_object_by_path=(
fb_obj.GetFileObjectByPath().decode("utf-8") if fb_obj.GetFileObjectByPath() is not None else None
),
)


Expand Down Expand Up @@ -174,6 +184,7 @@ def deserialize_serverreply(fb_obj) -> ServerReplyDC | None:

return ServerReplyDC(
message=fb_obj.Message().decode("utf-8") if fb_obj.Message() is not None else None,
file_object=deserialize_fileobject(fb_obj.FileObject()),
reply_to=CommandTypeDC(fb_obj.ReplyTo()),
error=deserialize_error(fb_obj.Error()),
)
Expand Down
10 changes: 9 additions & 1 deletion src/ada/comms/fb_model_gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class CommandTypeDC(Enum):
RUN_PROCEDURE = 9
ERROR = 10
SERVER_REPLY = 11
VIEW_FILE_OBJECT = 12


class TargetTypeDC(Enum):
Expand Down Expand Up @@ -67,6 +68,10 @@ class FileObjectDC:
purpose: Optional[FilePurposeDC] = None
filepath: pathlib.Path | str = ""
filedata: bytes = None
glb_file: Optional[FileObjectDC] = None
ifcsqlite_file: Optional[FileObjectDC] = None
is_procedure_output: bool = None
procedure_parent: Optional[ProcedureStartDC] = None


@dataclass
Expand Down Expand Up @@ -96,8 +101,10 @@ class SceneDC:

@dataclass
class ServerDC:
add_file_object: Optional[FileObjectDC] = None
new_file_object: Optional[FileObjectDC] = None
all_file_objects: Optional[List[FileObjectDC]] = None
get_file_object_by_name: str = ""
get_file_object_by_path: pathlib.Path | str = ""


@dataclass
Expand Down Expand Up @@ -140,6 +147,7 @@ class ErrorDC:
@dataclass
class ServerReplyDC:
message: str = ""
file_object: Optional[FileObjectDC] = None
reply_to: Optional[CommandTypeDC] = None
error: Optional[ErrorDC] = None

Expand Down
42 changes: 37 additions & 5 deletions src/ada/comms/fb_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,15 @@ def serialize_fileobject(builder: flatbuffers.Builder, obj: Optional[FileObjectD
filedata_vector = None
if obj.filedata is not None:
filedata_vector = builder.CreateByteVector(obj.filedata)
glb_file_obj = None
if obj.glb_file is not None:
glb_file_obj = serialize_fileobject(builder, obj.glb_file)
ifcsqlite_file_obj = None
if obj.ifcsqlite_file is not None:
ifcsqlite_file_obj = serialize_fileobject(builder, obj.ifcsqlite_file)
procedure_parent_obj = None
if obj.procedure_parent is not None:
procedure_parent_obj = serialize_procedurestart(builder, obj.procedure_parent)

FileObject.Start(builder)
if name_str is not None:
Expand All @@ -79,6 +88,14 @@ def serialize_fileobject(builder: flatbuffers.Builder, obj: Optional[FileObjectD
FileObject.AddFilepath(builder, filepath_str)
if filedata_vector is not None:
FileObject.AddFiledata(builder, filedata_vector)
if obj.glb_file is not None:
FileObject.AddGlbFile(builder, glb_file_obj)
if obj.ifcsqlite_file is not None:
FileObject.AddIfcsqliteFile(builder, ifcsqlite_file_obj)
if obj.is_procedure_output is not None:
FileObject.AddIsProcedureOutput(builder, obj.is_procedure_output)
if obj.procedure_parent is not None:
FileObject.AddProcedureParent(builder, procedure_parent_obj)
return FileObject.End(builder)


Expand Down Expand Up @@ -147,22 +164,32 @@ def serialize_scene(builder: flatbuffers.Builder, obj: Optional[SceneDC]) -> Opt
def serialize_server(builder: flatbuffers.Builder, obj: Optional[ServerDC]) -> Optional[int]:
if obj is None:
return None
add_file_object_obj = None
if obj.add_file_object is not None:
add_file_object_obj = serialize_fileobject(builder, obj.add_file_object)
new_file_object_obj = None
if obj.new_file_object is not None:
new_file_object_obj = serialize_fileobject(builder, obj.new_file_object)
all_file_objects_vector = None
if obj.all_file_objects is not None and len(obj.all_file_objects) > 0:
all_file_objects_list = [serialize_fileobject(builder, item) for item in obj.all_file_objects]
Server.StartAllFileObjectsVector(builder, len(all_file_objects_list))
for item in reversed(all_file_objects_list):
builder.PrependUOffsetTRelative(item)
all_file_objects_vector = builder.EndVector(len(all_file_objects_list))
get_file_object_by_name_str = None
if obj.get_file_object_by_name is not None:
get_file_object_by_name_str = builder.CreateString(str(obj.get_file_object_by_name))
get_file_object_by_path_str = None
if obj.get_file_object_by_path is not None:
get_file_object_by_path_str = builder.CreateString(str(obj.get_file_object_by_path))

Server.Start(builder)
if obj.add_file_object is not None:
Server.AddAddFileObject(builder, add_file_object_obj)
if obj.new_file_object is not None:
Server.AddNewFileObject(builder, new_file_object_obj)
if obj.all_file_objects is not None and len(obj.all_file_objects) > 0:
Server.AddAllFileObjects(builder, all_file_objects_vector)
if get_file_object_by_name_str is not None:
Server.AddGetFileObjectByName(builder, get_file_object_by_name_str)
if get_file_object_by_path_str is not None:
Server.AddGetFileObjectByPath(builder, get_file_object_by_path_str)
return Server.End(builder)


Expand Down Expand Up @@ -297,13 +324,18 @@ def serialize_serverreply(builder: flatbuffers.Builder, obj: Optional[ServerRepl
message_str = None
if obj.message is not None:
message_str = builder.CreateString(str(obj.message))
file_object_obj = None
if obj.file_object is not None:
file_object_obj = serialize_fileobject(builder, obj.file_object)
error_obj = None
if obj.error is not None:
error_obj = serialize_error(builder, obj.error)

ServerReply.Start(builder)
if message_str is not None:
ServerReply.AddMessage(builder, message_str)
if obj.file_object is not None:
ServerReply.AddFileObject(builder, file_object_obj)
if obj.reply_to is not None:
ServerReply.AddReplyTo(builder, obj.reply_to.value)
if obj.error is not None:
Expand Down
5 changes: 4 additions & 1 deletion src/ada/comms/msg_handling/default_on_message.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from ada.comms.msg_handling.run_procedure import run_procedure
from ada.comms.msg_handling.update_scene import update_scene
from ada.comms.msg_handling.update_server import update_server
from ada.comms.msg_handling.view_file_object import view_file_object
from ada.config import logger

if TYPE_CHECKING:
Expand All @@ -23,7 +24,7 @@ def default_on_message(server: WebSocketAsyncServer, client: ConnectedClient, me
if message.command_type == CommandTypeDC.UPDATE_SCENE:
update_scene(server, client, message)
elif message.command_type == CommandTypeDC.UPDATE_SERVER:
update_server(server, client, message)
update_server(server, client, message.server.new_file_object)
elif message.command_type == CommandTypeDC.MESH_INFO_CALLBACK:
mesh_info_callback(server, client, message)
elif message.command_type == CommandTypeDC.LIST_PROCEDURES:
Expand All @@ -32,6 +33,8 @@ def default_on_message(server: WebSocketAsyncServer, client: ConnectedClient, me
run_procedure(server, client, message)
elif message.command_type == CommandTypeDC.LIST_FILE_OBJECTS:
list_file_objects(server, client, message)
elif message.command_type == CommandTypeDC.VIEW_FILE_OBJECT:
view_file_object(server, client, message)
else:
logger.error(f"Unknown command type: {message.command_type}")
on_error_reply(server, client, error_message=f"Unknown command type: {message.command_type}")
Expand Down
23 changes: 22 additions & 1 deletion src/ada/comms/msg_handling/run_procedure.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from __future__ import annotations

import pathlib
from typing import TYPE_CHECKING

from ada.comms.fb_model_gen import MessageDC
from ada.comms.fb_model_gen import FileObjectDC, MessageDC
from ada.comms.msg_handling.update_server import update_server
from ada.comms.procedures import Procedure
from ada.config import logger

Expand All @@ -21,3 +23,22 @@ def run_procedure(server: WebSocketAsyncServer, client: ConnectedClient, message

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


def update_server_on_successful_procedure_run(
server: WebSocketAsyncServer, procedure: Procedure, client: ConnectedClient, message: MessageDC
) -> None:
input_file_path = pathlib.Path(procedure.params.get(procedure.input_file_var))
server_file_object = server.scene.get_file_object(input_file_path.stem)
output_file = procedure.get_procedure_output(input_file_path.stem)
new_file_object = FileObjectDC(
name=output_file.name,
filepath=output_file,
file_type=procedure.export_file_type,
purpose=server_file_object.purpose,
is_procedure_output=True,
procedure_parent=message.procedure_store.start_procedure,
)
update_server(server, client, new_file_object)
logger.info(f"Added {procedure.name} to the server")
3 changes: 2 additions & 1 deletion src/ada/comms/msg_handling/update_scene.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,5 @@ def update_scene(server: WebSocketAsyncServer, client: ConnectedClient, message:
file_type=glb_file_data.file_type,
purpose=glb_file_data.purpose,
)
server.scene.file_objects.append(file_object)

server.scene.add_file_object(file_object)
47 changes: 31 additions & 16 deletions src/ada/comms/msg_handling/update_server.py
Original file line number Diff line number Diff line change
@@ -1,35 +1,50 @@
from __future__ import annotations

import pathlib
import platform
import shutil
import subprocess
from typing import TYPE_CHECKING

import ada
from ada.cadit.ifc.ifc2sql import Ifc2SqlPatcher
from ada.cadit.ifc.sql_model import IfcSqlModel
from ada.comms.fb_model_gen import FileTypeDC, MessageDC
from ada.comms.fb_model_gen import FileObjectDC, FileTypeDC
from ada.config import logger

if TYPE_CHECKING:
from ada.comms.wsock_server import ConnectedClient, WebSocketAsyncServer


def update_server(server: WebSocketAsyncServer, client: ConnectedClient, message: MessageDC) -> None:
def update_server(server: WebSocketAsyncServer, client: ConnectedClient, add_file: FileObjectDC) -> None:
logger.info(f"Received message from {client} to update server")
logger.info(f"Message: {message}")
add_file = message.server.add_file_object
if add_file.file_type == FileTypeDC.IFC and add_file.filepath:
tmp_ifc_fp = pathlib.Path(add_file.filepath)
if not tmp_ifc_fp.exists():
raise FileNotFoundError(f"File not found: {tmp_ifc_fp}")
tmp_sql_fp = tmp_ifc_fp.with_suffix(".sqlite")

Ifc2SqlPatcher(tmp_ifc_fp, logger, dest_sql_file=tmp_sql_fp).patch()

server.scene.ifc_sql_store = IfcSqlModel(tmp_sql_fp)
remove_existing_idx = None
for i, fo in enumerate(server.scene.file_objects):
if fo.name == add_file.name:
remove_existing_idx = i
if remove_existing_idx is not None:
server.scene.file_objects.pop(remove_existing_idx)

server.scene.file_objects.append(add_file)
if add_file.ifcsqlite_file is None:
tmp_sql_fp = tmp_ifc_fp.with_suffix(".sqlite")
Ifc2SqlPatcher(tmp_ifc_fp, logger, dest_sql_file=tmp_sql_fp).patch()
add_file.ifcsqlite_file = FileObjectDC(
name=add_file.name, filepath=tmp_sql_fp, file_type=FileTypeDC.SQLITE, purpose=add_file.purpose
)

if add_file.glb_file is None:
tmp_glb_fp = tmp_ifc_fp.with_suffix(".glb")
ifc_convert_exe = shutil.which("ifcconvert")
if platform.platform().startswith("Windows"):
ifc_convert_exe = shutil.which("ifcconvert.exe")

if ifc_convert_exe:
subprocess.run([ifc_convert_exe, tmp_ifc_fp.as_posix(), tmp_glb_fp.as_posix()])
else:
a = ada.from_ifc(add_file.filepath)
a.to_gltf(tmp_glb_fp)

add_file.glb_file = FileObjectDC(
name=add_file.name, filepath=tmp_glb_fp, file_type=FileTypeDC.GLB, purpose=add_file.purpose
)

server.scene.ifc_sql_store = IfcSqlModel(add_file.ifcsqlite_file.filepath)
server.scene.add_file_object(add_file)
53 changes: 53 additions & 0 deletions src/ada/comms/msg_handling/view_file_object.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
from __future__ import annotations

import asyncio
import io
from typing import TYPE_CHECKING

import trimesh

from ada.comms.exceptions import ServerError
from ada.comms.fb_model_gen import (
CommandTypeDC,
FileObjectDC,
FileTypeDC,
MessageDC,
SceneDC,
ServerReplyDC,
)
from ada.comms.fb_serializer import serialize_message
from ada.config import logger

if TYPE_CHECKING:
from ada.comms.wsock_server import ConnectedClient, WebSocketAsyncServer


def view_file_object(server: WebSocketAsyncServer, client: ConnectedClient, message: MessageDC) -> None:
logger.info(f"Received message from {client} to get file object")
file_object_name = message.server.get_file_object_by_name
result = server.scene.get_file_object(file_object_name)
if result is None:
raise ServerError(f"File object {file_object_name} not found")

glb_file_obj = result.glb_file
scene = trimesh.load(glb_file_obj.filepath)
with io.BytesIO() as data:
scene.export(
file_obj=data,
file_type="glb",
)
file_object = FileObjectDC(
name=glb_file_obj.name, file_type=FileTypeDC.GLB, purpose=glb_file_obj.purpose, filedata=data.getvalue()
)

msg = MessageDC(
instance_id=server.instance_id,
command_type=CommandTypeDC.SERVER_REPLY,
server_reply=ServerReplyDC(reply_to=message.command_type, file_object=file_object),
scene=SceneDC(current_file=file_object),
target_id=client.instance_id,
target_group=client.group_type,
)

fb_message = serialize_message(msg)
asyncio.run(client.websocket.send(fb_message))
Loading

0 comments on commit 5ec1ebf

Please sign in to comment.