Skip to content

Commit 8f0b06a

Browse files
committed
FIX: convert pickle to JSON serialization
1 parent fdd3b3f commit 8f0b06a

File tree

4 files changed

+178
-18
lines changed

4 files changed

+178
-18
lines changed

src/gh/components/DF_export_results/code.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ def RunScript(self, i_dump: bool, i_export_dir: str, i_results):
5252
if i_dump is None or i_export_dir is None or i_results is None:
5353
return None
5454

55-
i_results.dump_pickle(i_export_dir)
55+
if i_dump:
56+
i_results.dump_serialization(i_export_dir)
5657

5758
return None

src/gh/components/DF_import_results/code.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,6 @@ def RunScript(self, i_import_path: str):
1010
if i_import_path is None:
1111
return None
1212

13-
o_results = DFVizResults.load_pickle(i_import_path)
13+
o_results = DFVizResults.load_serialization(i_import_path)
1414

1515
return o_results

src/gh/diffCheck/diffCheck/df_error_estimation.py

Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from datetime import datetime
99
import os
1010

11-
import pickle
11+
import json
1212

1313
import numpy as np
1414

@@ -21,6 +21,13 @@
2121
from diffCheck.df_geometries import DFAssembly
2222

2323

24+
class NumpyEncoder(json.JSONEncoder):
25+
""" Special json encoder for numpy ndarray types. """
26+
def default(self, obj):
27+
if isinstance(obj, np.ndarray):
28+
return obj.tolist()
29+
return super().default(obj)
30+
2431
class DFInvalidData(Enum):
2532
"""
2633
Enum to define the type of invalid data for joint or assembly analysis
@@ -36,6 +43,8 @@ class DFVizResults:
3643
"""
3744
This class compiles the resluts of the error estimation into one object
3845
"""
46+
__serial_file_extenion: str = ".diffCheck"
47+
3948
def __init__(self, assembly):
4049

4150
self.assembly: DFAssembly = assembly
@@ -52,28 +61,46 @@ def __init__(self, assembly):
5261
self.distances_sd_deviation = []
5362
self.distances = []
5463

55-
self.__serial_file_extenion: str = ".diffCheck"
56-
5764
def __repr__(self):
5865
return f"DFVizResults of({self.assembly})"
5966

6067
def __getstate__(self):
6168
state = self.__dict__.copy()
69+
if "assembly" in state and state["assembly"] is not None:
70+
state["assembly"] = self.assembly.__getstate__()
6271
if "source" in state and state["source"] is not None:
6372
state["source"] = [df_cvt_bindings.cvt_dfcloud_2_dict(pcd) for pcd in state["source"]]
6473
if "target" in state and state["target"] is not None:
6574
state["target"] = [mesh.ToJSON(SerializationOptions()) for mesh in state["target"]]
75+
if "sanity_check" in state and state["sanity_check"] is not None:
76+
state["sanity_check"] = [s.value if isinstance(s, DFInvalidData) else s for s in self.sanity_check]
6677
return state
6778

6879
def __setstate__(self, state: typing.Dict):
80+
if "assembly" in state and state["assembly"] is not None:
81+
assembly = DFAssembly.__new__(DFAssembly)
82+
assembly.__setstate__(state["assembly"])
83+
state["assembly"] = assembly
6984
if "source" in state and state["source"] is not None:
70-
self.source = [df_cvt_bindings.cvt_dict_2_dfcloud(state["source"][i]) for i in range(len(state["source"]))]
85+
source = []
86+
for pcd_dict in state["source"]:
87+
pcd = diffcheck_bindings.dfb_geometry.DFPointCloud()
88+
pcd = df_cvt_bindings.cvt_dict_2_dfcloud(pcd_dict)
89+
source.append(pcd)
90+
state["source"] = source
7191
if "target" in state and state["target"] is not None:
72-
self.target = [rg.Mesh.FromJSON(state["target"][i]) for i in range(len(state["target"]))]
92+
target = []
93+
for mesh_json in state["target"]:
94+
mesh = rg.Mesh()
95+
mesh = mesh.FromJSON(mesh_json)
96+
target.append(mesh)
97+
state["target"] = target
98+
if "sanity_check" in state and state["sanity_check"] is not None:
99+
state["sanity_check"] = [DFInvalidData(s) for s in state["sanity_check"]]
73100
self.__dict__.update(state)
74101

75-
def dump_pickle(self, dir: str) -> None:
76-
""" Dump the results into a pickle file for serialization """
102+
def dump_serialization(self, dir: str) -> str:
103+
""" Dump the results into a JSON file for serialization """
77104
if not os.path.exists(os.path.dirname(dir)):
78105
try:
79106
os.makedirs(os.path.dirname(dir))
@@ -85,21 +112,25 @@ def dump_pickle(self, dir: str) -> None:
85112
serial_file_path = os.path.join(dir, f"{assembly_name}_{timestamp}{self.__serial_file_extenion}")
86113

87114
try:
88-
with open(serial_file_path, "wb") as f:
89-
pickle.dump(self, f)
115+
with open(serial_file_path, "w") as f:
116+
json.dump(self.__getstate__(), f, cls=NumpyEncoder)
90117
except Exception as e:
91118
raise e
92119

120+
return serial_file_path
121+
93122
@staticmethod
94-
def load_pickle(file_path: str):
95-
""" Load the results from a pickle file """
123+
def load_serialization(file_path: str) -> 'DFVizResults':
124+
""" Load the results from a JSON file """
96125
if not os.path.exists(file_path):
97126
raise FileNotFoundError(f"File {file_path} not found")
98-
if not file_path.endswith(".diffCheck"):
127+
if not file_path.endswith(DFVizResults.__serial_file_extenion):
99128
raise ValueError(f"File {file_path} is not a valid diffCheck file")
100129
try:
101-
with open(file_path, "rb") as f:
102-
obj = pickle.load(f)
130+
with open(file_path, "r") as f:
131+
state = json.load(f)
132+
obj = DFVizResults.__new__(DFVizResults)
133+
obj.__setstate__(state)
103134
except Exception as e:
104135
raise e
105136
return obj

src/gh/diffCheck/diffCheck/df_geometries.py

Lines changed: 130 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ def __post_init__(self):
3636

3737
self.__uuid = uuid.uuid4().int
3838

39+
def __getstate__(self):
40+
return self.__dict__
41+
42+
def __setstate__(self, state: typing.Dict):
43+
self.__dict__.update(state)
44+
3945
def __repr__(self):
4046
return f"Vertex: X={self.x}, Y={self.y}, Z={self.z}"
4147

@@ -94,13 +100,29 @@ def __post_init__(self):
94100

95101
def __getstate__(self):
96102
state = self.__dict__.copy()
103+
if "all_loops" in state and state["all_loops"] is not None:
104+
state["all_loops"] = [[vertex.__getstate__() for vertex in loop] for loop in state["all_loops"]]
105+
# note: rg.BrepFaces cannot be serialized, so we need to convert it to a Surface >> JSON >> brep >> brepface (and vice versa)
97106
if "_rh_brepface" in state and state["_rh_brepface"] is not None:
98-
state["_rh_brepface"] = self._rh_brepface.ToJSON(SerializationOptions())
107+
state["_rh_brepface"] = self.to_brep_face().DuplicateFace(True).ToJSON(SerializationOptions())
108+
return state
99109

100110
def __setstate__(self, state: typing.Dict):
111+
if "all_loops" in state and state["all_loops"] is not None:
112+
all_loops = []
113+
for loop_state in state["all_loops"]:
114+
loop = [DFVertex.__new__(DFVertex) for _ in loop_state]
115+
for vertex, vertex_state in zip(loop, loop_state):
116+
vertex.__setstate__(vertex_state)
117+
all_loops.append(loop)
118+
state["all_loops"] = all_loops
119+
# note: rg.BrepFaces cannot be serialized, so we need to convert it to a Surface >> JSON >> brep >> brepface (and vice versa)
101120
if "_rh_brepface" in state and state["_rh_brepface"] is not None:
102-
state["_rh_brepface"] = rg.BrepFace.FromJSON(state["_rh_brepface"])
121+
state["_rh_brepface"] = rg.Surface.FromJSON(state["_rh_brepface"]).Faces[0]
103122
self.__dict__.update(state)
123+
if self._rh_brepface is not None:
124+
self.from_brep_face(self._rh_brepface, self.joint_id)
125+
104126

105127
def __repr__(self):
106128
return f"Face id: {(self.id)}, IsJoint: {self.is_joint} Loops: {len(self.all_loops)}"
@@ -235,6 +257,22 @@ def __post_init__(self):
235257
# this is an automatic identifier
236258
self.__uuid = uuid.uuid4().int
237259

260+
def __getstate__(self):
261+
state = self.__dict__.copy()
262+
if "faces" in state and state["faces"] is not None:
263+
state["faces"] = [face.__getstate__() for face in self.faces]
264+
return state
265+
266+
def __setstate__(self, state: typing.Dict):
267+
if "faces" in state and state["faces"] is not None:
268+
faces = []
269+
for face_state in state["faces"]:
270+
face = DFFace.__new__(DFFace)
271+
face.__setstate__(face_state)
272+
faces.append(face)
273+
state["faces"] = faces
274+
self.__dict__.update(state)
275+
238276
def __repr__(self):
239277
return f"Joint id: {self.id}, Faces: {len(self.faces)}"
240278

@@ -305,11 +343,56 @@ def __post_init__(self):
305343

306344
def __getstate__(self):
307345
state = self.__dict__.copy()
346+
if "faces" in state and state["faces"] is not None:
347+
state["faces"] = [face.__getstate__() for face in self.faces]
348+
if "_joint_faces" in state and state["_joint_faces"] is not None:
349+
state["_joint_faces"] = [face.__getstate__() for face in state["_joint_faces"]]
350+
if "_side_faces" in state and state["_side_faces"] is not None:
351+
state["_side_faces"] = [face.__getstate__() for face in state["_side_faces"]]
352+
if "_vertices" in state and state["_vertices"] is not None:
353+
state["_vertices"] = [vertex.__getstate__() for vertex in state["_vertices"]]
354+
if "_joints" in state and state["_joints"] is not None:
355+
state["_joints"] = [joint.__getstate__() for joint in state["_joints"]]
308356
if "_center" in state and state["_center"] is not None:
309357
state["_center"] = self._center.ToJSON(SerializationOptions())
310358
return state
311359

312360
def __setstate__(self, state: typing.Dict):
361+
if "faces" in state and state["faces"] is not None:
362+
faces = []
363+
for face_state in state["faces"]:
364+
face = DFFace.__new__(DFFace)
365+
face.__setstate__(face_state)
366+
faces.append(face)
367+
state["faces"] = faces
368+
if "_joint_faces" in state and state["_joint_faces"] is not None:
369+
joint_faces = []
370+
for face_state in state["_joint_faces"]:
371+
face = DFFace.__new__(DFFace)
372+
face.__setstate__(face_state)
373+
joint_faces.append(face)
374+
state["_joint_faces"] = joint_faces
375+
if "_side_faces" in state and state["_side_faces"] is not None:
376+
side_faces = []
377+
for face_state in state["_side_faces"]:
378+
face = DFFace.__new__(DFFace)
379+
face.__setstate__(face_state)
380+
side_faces.append(face)
381+
state["_side_faces"] = side_faces
382+
if "_vertices" in state and state["_vertices"] is not None:
383+
vertices = []
384+
for vertex_state in state["_vertices"]:
385+
vertex = DFVertex.__new__(DFVertex)
386+
vertex.__setstate__(vertex_state)
387+
vertices.append(vertex)
388+
state["_vertices"] = vertices
389+
if "_joints" in state and state["_joints"] is not None:
390+
joints = []
391+
for joint_state in state["_joints"]:
392+
joint = DFJoint.__new__(DFJoint)
393+
joint.__setstate__(joint_state)
394+
joints.append(joint)
395+
state["_joints"] = joints
313396
if "_center" in state and state["_center"] is not None:
314397
state["_center"] = rg.Point3d.FromJSON(state["_center"])
315398
self.__dict__.update(state)
@@ -445,13 +528,58 @@ def __post_init__(self):
445528

446529
def __getstate__(self):
447530
state = self.__dict__.copy()
531+
if "beams" in state and state["beams"] is not None:
532+
state["beams"] = [beam.__getstate__() for beam in self.beams]
448533
if "_mass_center" in state and state["_mass_center"] is not None:
449534
state["_mass_center"] = self._mass_center.ToJSON(SerializationOptions())
535+
if "_all_jointfaces" in state and state["_all_jointfaces"] is not None:
536+
state["_all_jointfaces"] = [face.__getstate__() for face in state["_all_jointfaces"]]
537+
if "_all_sidefaces" in state and state["_all_sidefaces"] is not None:
538+
state["_all_sidefaces"] = [face.__getstate__() for face in state["_all_sidefaces"]]
539+
if "_all_vertices" in state and state["_all_vertices"] is not None:
540+
state["_all_vertices"] = [vertex.__getstate__() for vertex in state["_all_vertices"]]
541+
if "_all_joints" in state and state["_all_joints"] is not None:
542+
state["_all_joints"] = [joint.__getstate__() for joint in state["_all_joints"]]
450543
return state
451544

452545
def __setstate__(self, state: typing.Dict):
546+
if "beams" in state and state["beams"] is not None:
547+
beams = []
548+
for beam_state in state["beams"]:
549+
beam = DFBeam.__new__(DFBeam)
550+
beam.__setstate__(beam_state)
551+
beams.append(beam)
552+
state["beams"] = beams
453553
if "_mass_center" in state and state["_mass_center"] is not None:
454554
state["_mass_center"] = rg.Point3d.FromJSON(state["_mass_center"])
555+
if "_all_jointfaces" in state and state["_all_jointfaces"] is not None:
556+
joint_faces = []
557+
for face_state in state["_all_jointfaces"]:
558+
face = DFFace.__new__(DFFace)
559+
face.__setstate__(face_state)
560+
joint_faces.append(face)
561+
state["_all_jointfaces"] = joint_faces
562+
if "_all_sidefaces" in state and state["_all_sidefaces"] is not None:
563+
side_faces = []
564+
for face_state in state["_all_sidefaces"]:
565+
face = DFFace.__new__(DFFace)
566+
face.__setstate__(face_state)
567+
side_faces.append(face)
568+
state["_all_sidefaces"] = side_faces
569+
if "_all_vertices" in state and state["_all_vertices"] is not None:
570+
vertices = []
571+
for vertex_state in state["_all_vertices"]:
572+
vertex = DFVertex.__new__(DFVertex)
573+
vertex.__setstate__(vertex_state)
574+
vertices.append(vertex)
575+
state["_all_vertices"] = vertices
576+
if "_all_joints" in state and state["_all_joints"] is not None:
577+
joints = []
578+
for joint_state in state["_all_joints"]:
579+
joint = DFJoint.__new__(DFJoint)
580+
joint.__setstate__(joint_state)
581+
joints.append(joint)
582+
state["_all_joints"] = joints
455583
self.__dict__.update(state)
456584

457585
def __repr__(self):

0 commit comments

Comments
 (0)