Skip to content

Commit 6593f34

Browse files
authored
[RPyC] Add new exposed methods to SOFA server (#570)
* Add a way to init the root remotly * Add method to change working dir on remote
1 parent bdbaece commit 6593f34

File tree

2 files changed

+121
-2
lines changed

2 files changed

+121
-2
lines changed
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import multiprocessing
2+
import threading
3+
import time
4+
import rpyc
5+
from SOFAService import SOFAService, SOFAClient
6+
import Sofa
7+
8+
if __name__ == "__main__":
9+
10+
SC = SOFAClient()
11+
SC.start_server(port=18818)
12+
SC.connect_client(port=18818)
13+
14+
root = SC.sofa_root
15+
16+
# Need to use getValue and setValue instead of the syntax `root.dt = 0.02` or `root.dt.value` to get the actual value
17+
root.gravity.setValue([0, -9.81, 0])
18+
root.dt.setValue(0.02)
19+
20+
# RPyC deal in a strange way with list of strings, which leads to error if you use the syntax where you pass a list of plugin name to required plugin. You need to add one required plugin per plugin for it to work, such as done in xml.
21+
root.addObject("RequiredPlugin", pluginName='Sofa.Component.Collision.Detection.Algorithm')
22+
root.addObject("RequiredPlugin", pluginName='Sofa.Component.Collision.Detection.Intersection')
23+
root.addObject("RequiredPlugin", pluginName='Sofa.Component.Collision.Geometry')
24+
root.addObject("RequiredPlugin", pluginName='Sofa.Component.Collision.Response.Contact')
25+
root.addObject("RequiredPlugin", pluginName='Sofa.Component.Constraint.Projective')
26+
root.addObject("RequiredPlugin", pluginName='Sofa.Component.IO.Mesh')
27+
root.addObject("RequiredPlugin", pluginName='Sofa.Component.LinearSolver.Iterative')
28+
root.addObject("RequiredPlugin", pluginName='Sofa.Component.Mapping.Linear')
29+
root.addObject("RequiredPlugin", pluginName='Sofa.Component.Mass')
30+
root.addObject("RequiredPlugin", pluginName='Sofa.Component.ODESolver.Backward')
31+
root.addObject("RequiredPlugin", pluginName='Sofa.Component.SolidMechanics.FEM.Elastic')
32+
root.addObject("RequiredPlugin", pluginName='Sofa.Component.StateContainer')
33+
root.addObject("RequiredPlugin", pluginName='Sofa.Component.Topology.Container.Dynamic')
34+
root.addObject("RequiredPlugin", pluginName='Sofa.Component.Visual')
35+
root.addObject("RequiredPlugin", pluginName='Sofa.GL.Component.Rendering3D')
36+
37+
root.addObject('DefaultAnimationLoop')
38+
39+
root.addObject('VisualStyle', displayFlags="showCollisionModels")
40+
root.addObject('CollisionPipeline', name="CollisionPipeline")
41+
root.addObject('BruteForceBroadPhase', name="BroadPhase")
42+
root.addObject('BVHNarrowPhase', name="NarrowPhase")
43+
root.addObject('CollisionResponse', name="CollisionResponse", response="PenalityContactForceField")
44+
root.addObject('DiscreteIntersection')
45+
46+
# Don't forget that this will be launched in the remote, all files should be on its disk or else, use lambda function to capture data and access theme in this function
47+
root.addObject('MeshOBJLoader', name="LiverSurface", filename="mesh/liver-smooth.obj")
48+
49+
liver = root.addChild('Liver')
50+
liver.addObject('EulerImplicitSolver', name="cg_odesolver", rayleighStiffness="0.1", rayleighMass="0.1")
51+
liver.addObject('CGLinearSolver', name="linear_solver", iterations="25", tolerance="1e-09", threshold="1e-09")
52+
liver.addObject('MeshGmshLoader', name="meshLoader", filename="mesh/liver.msh")
53+
liver.addObject('TetrahedronSetTopologyContainer', name="topo", src="@meshLoader")
54+
liver.addObject('MechanicalObject', name="dofs", src="@meshLoader")
55+
liver.addObject('TetrahedronSetGeometryAlgorithms', template="Vec3d", name="GeomAlgo")
56+
liver.addObject('DiagonalMass', name="Mass", massDensity="1.0")
57+
liver.addObject('TetrahedralCorotationalFEMForceField', template="Vec3d", name="FEM", method="large", poissonRatio="0.3", youngModulus="3000", computeGlobalMatrix="0")
58+
liver.addObject('FixedProjectiveConstraint', name="FixedConstraint", indices="3 39 64")
59+
60+
visu = liver.addChild('Visu')
61+
visu.addObject('OglModel', name="VisualModel", src="@../../LiverSurface")
62+
visu.addObject('BarycentricMapping', name="VisualMapping", input="@../dofs", output="@VisualModel")
63+
64+
surf = liver.addChild('Surf')
65+
surf.addObject('SphereLoader', name="sphereLoader", filename="mesh/liver.sph")
66+
surf.addObject('MechanicalObject', name="spheres", position="@sphereLoader.position")
67+
surf.addObject('SphereCollisionModel', name="CollisionModel", listRadius="@sphereLoader.listRadius")
68+
surf.addObject('BarycentricMapping', name="CollisionMapping", input="@../dofs", output="@spheres")
69+
70+
SC.init_root()
71+
72+
# This works only for server and client on the same machine. This tell the server that the passed data path should be copied in shared memory when accessed through the client instead of RPyC.
73+
SC.setup_shared_memory_for_data(["Liver/dofs.position","Liver/Surf/spheres.position"])
74+
75+
asynch_step = None
76+
currentTime = 0.0
77+
while currentTime<0.2:
78+
if(asynch_step is None or asynch_step.ready):
79+
# Time to get data from object
80+
currentTime = SC.sofa_root.getTime()
81+
print(currentTime)
82+
83+
print(f"This comes with the socket : {SC.sofa_root.Liver.cg_odesolver.name.value}")
84+
print(f"This comes with shared memory : {SC.sofa_root.Liver.Surf.spheres.position.value}")
85+
SC.sofa_root.Liver.Surf.spheres.position.setValue([[0,0,0]])
86+
SC.sofa_root.Liver.cg_odesolver.printLog.setValue(True)
87+
print(f"This getValue comes with the socket : {SC.sofa_root.Liver.cg_odesolver.name.getValue()}")
88+
print(f"This getValue comes with shared memory : {SC.sofa_root.Liver.Surf.spheres.position.getValue()}")
89+
90+
# Launch next step
91+
asynch_step = SC.asynch_step()
92+
else:
93+
print("waiting 0.1 sec")
94+
time.sleep(0.1)
95+
96+
SC.stop_server()
97+

examples/RPYC/SOFAService.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import os
2+
13
import rpyc
24
import time
35
import threading
@@ -279,7 +281,7 @@ def exposed_build_scene_graph_from_method(self, createScene):
279281
"""
280282
self.exposed_sofa_root = Sofa.Core.Node("root")
281283
createScene(self.exposed_sofa_root)
282-
Sofa.Simulation.initRoot(self.exposed_sofa_root)
284+
self.exposed_init_root()
283285

284286
def exposed_build_scene_graph_from_file(self, filename:str):
285287
"""
@@ -294,7 +296,7 @@ def exposed_build_scene_graph_from_file(self, filename:str):
294296

295297
self.exposed_sofa_root = Sofa.Core.Node("root")
296298
foo.createScene(self.exposed_sofa_root)
297-
Sofa.Simulation.initRoot(self.exposed_sofa_root)
299+
self.exposed_init_root()
298300

299301

300302
def exposed_setup_shared_memory_for_data(self, dataPaths:list[str], delayed=False):
@@ -354,17 +356,36 @@ def getSharedMemoryNames(self):
354356
"""Return list of all data paths currently shared via shared memory."""
355357
return self.sharedPaths
356358

359+
def exposed_init_root(self):
360+
"""Initialize the root node."""
361+
if(not self.exposed_sofa_root.isInitialized()):
362+
Sofa.Simulation.initRoot(self.exposed_sofa_root)
357363

358364
def exposed_step_simulation(self):
359365
"""
360366
Run one step of the simulation.
361367
If shared memory hasn’t been set up yet, attempt setup now.
362368
"""
369+
363370
Sofa.Simulation.animate(self.exposed_sofa_root, self.exposed_sofa_root.dt.value)
371+
364372
if(not self.sharedMemoryIsSet):
365373
self.__internal_setup_shared_memory()
366374

375+
def exposed_get_cwd(self):
376+
"""
377+
Returns current working dir
378+
"""
379+
return os.getcwd()
367380

381+
def exposed_set_cwd(self, dir):
382+
"""
383+
Change the current working dir of the server
384+
Returns old dir,current working dir
385+
"""
386+
oldDir = os.getcwd()
387+
os.chdir(dir)
388+
return oldDir, os.getcwd()
368389

369390
#################################################
370391
### Multithreaded automatic execution methods
@@ -382,6 +403,7 @@ def __wait_for_the_animation_to_stop(self):
382403

383404
def __simulation_loop(self):
384405
"""Continuous simulation loop run by background thread."""
406+
385407
while self.animate:
386408
Sofa.Simulation.animate(self.exposed_sofa_root, self.exposed_sofa_root.dt.value)
387409

0 commit comments

Comments
 (0)