Skip to content

Commit 7d2e613

Browse files
authored
fix: graph refactor and render node update (#31)
* fix: allow always rendering with camera node. * fix: graph refactor, object_spec [not yet working] * fix: test actuator without outputs and synchronization * fix: update eagerx version
1 parent fbd576e commit 7d2e613

File tree

9 files changed

+95
-195
lines changed

9 files changed

+95
-195
lines changed

eagerx_reality/engine.py

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import eagerx
66
import eagerx.core.register as register
77
from eagerx.core.entities import Engine
8-
from eagerx.core.specs import EngineSpec, ObjectSpec
8+
from eagerx.core.specs import EngineSpec
99

1010

1111
class RealEngine(Engine):
@@ -34,20 +34,14 @@ def make(
3434
return spec
3535

3636
def initialize(self, spec: EngineSpec):
37-
self.simulator = dict()
38-
39-
def add_object(self, spec: ObjectSpec):
40-
object_name = spec.config.name
41-
entity_id = spec.config.entity_id
37+
pass
4238

39+
def add_object(self, name: str):
4340
# add object to simulator (we have a ref to the simulator with self.simulator)
44-
self.backend.loginfo(f'Adding object "{object_name}" of type "{entity_id}" to the simulator.')
45-
46-
# Extract relevant agnostic_params
47-
obj_name = object_name
41+
self.backend.loginfo(f'Adding object "{name}" to the simulator.')
4842

49-
# Create new env, and add to simulator
50-
self.simulator[obj_name] = dict(state=None, input=None)
43+
# Nothing to be added to the simulator
44+
self.simulator[name].update()
5145

5246
def pre_reset(self):
5347
pass

eagerx_reality/enginenodes.py

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import eagerx.core.register as register
1010
from eagerx.utils.utils import Msg
1111
from eagerx.core.entities import EngineNode
12-
from eagerx.core.specs import NodeSpec, ObjectSpec
12+
from eagerx.core.specs import NodeSpec
1313

1414

1515
class CameraRender(EngineNode):
@@ -22,6 +22,7 @@ def make(
2222
color: str = "cyan",
2323
shape: Optional[List[int]] = None,
2424
camera_idx: int = 0,
25+
always_render: bool = False,
2526
) -> NodeSpec:
2627
"""CameraRender spec"""
2728
spec = cls.get_specification()
@@ -33,18 +34,24 @@ def make(
3334
# Modify custom node params
3435
spec.config.shape = shape if isinstance(shape, list) else [480, 480]
3536
spec.config.camera_idx = camera_idx
37+
spec.config.always_render = always_render
3638

3739
# Set image space
3840
spec.outputs.image.space.update(low=0, high=255, shape=[spec.config.shape[0], spec.config.shape[1], 3])
3941
return spec
4042

41-
def initialize(self, spec: NodeSpec, object_spec: ObjectSpec, simulator: Any):
43+
def initialize(self, spec: NodeSpec, simulator: Any):
44+
self.always_render = spec.config.always_render
4245
self.cam = None
4346
self.height, self.width = spec.config.shape
4447
self.camera_idx = spec.config.camera_idx
4548
self.render_toggle = False
4649
self.sub_toggle = self.backend.Subscriber("%s/env/render/toggle" % self.ns, "bool", self._set_render_toggle)
4750

51+
# initialize camera if always rendering
52+
if self.always_render:
53+
self._init_cam()
54+
4855
@register.states()
4956
def reset(self):
5057
# This sensor is stateless (in contrast to e.g. a Kalman filter).
@@ -53,7 +60,7 @@ def reset(self):
5360
@register.inputs(tick=Space(shape=(), dtype="int64")) # Dummy, because a node must have at least one input.
5461
@register.outputs(image=Space(dtype="uint8"))
5562
def callback(self, t_n: float, tick: Optional[Msg] = None):
56-
if self.render_toggle:
63+
if self.render_toggle or self.always_render:
5764
self.cam: cv2.VideoCapture
5865
ret, cv_img = self.cam.read()
5966
if ret:
@@ -69,17 +76,22 @@ def callback(self, t_n: float, tick: Optional[Msg] = None):
6976
img = np.zeros((self.height, self.width, 3), dtype="uint8")
7077
return dict(image=img)
7178

72-
def _set_render_toggle(self, msg):
73-
if msg:
74-
self.backend.logdebug("[%s] START RENDERING!" % self.name)
79+
def _init_cam(self):
80+
if self.cam is None:
7581
self.cam = cv2.VideoCapture(self.camera_idx)
7682
self.cam.set(cv2.CAP_PROP_FRAME_WIDTH, self.width)
7783
self.cam.set(cv2.CAP_PROP_FRAME_HEIGHT, self.height)
78-
else:
79-
self.backend.logdebug("[%s] STOPPED RENDERING!" % self.name)
80-
if self.cam is not None:
81-
self.cam.release()
82-
self.cam = None
84+
85+
def _set_render_toggle(self, msg):
86+
if not self.always_render:
87+
if msg:
88+
self.backend.logdebug("[%s] START RENDERING!" % self.name)
89+
self._init_cam()
90+
else:
91+
self.backend.logdebug("[%s] STOPPED RENDERING!" % self.name)
92+
if self.cam is not None:
93+
self.cam.release()
94+
self.cam = None
8395
self.render_toggle = msg
8496

8597
def shutdown(self):

poetry.lock

Lines changed: 35 additions & 151 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ documentation = "https://eagerx.readthedocs.io/en/master/"
1010

1111
[tool.poetry.dependencies]
1212
python = "^3.6.2"
13-
eagerx = "^0.1.26"
13+
eagerx = "^0.1.32"
1414

1515
[tool.poetry.dev-dependencies]
1616
black = "^22.3.0"

scripts/run_tests.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
#!/bin/bash
2-
poetry run pytest --cov-config .coveragerc --rootdir=${PACKAGE_NAME} --cov-report html --cov-report xml --cov-report term --cov=. -v --color=yes
2+
poetry run pytest -s --cov-config .coveragerc --rootdir=${PACKAGE_NAME} --cov-report html --cov-report xml --cov-report term --cov=. -v --color=yes

tests/dummy/engine_nodes.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
# IMPORT EAGERX
55
import eagerx
6-
from eagerx.core.specs import NodeSpec, ObjectSpec
6+
from eagerx.core.specs import NodeSpec
77
import eagerx.core.register as register
88
from eagerx.utils.utils import Msg
99

@@ -21,7 +21,7 @@ def make(cls, name: str, rate: float, process: int = eagerx.NEW_PROCESS, color:
2121
spec.config.update(name=name, rate=rate, process=process, color=color, inputs=["tick"], outputs=["dummy_output"])
2222
return spec
2323

24-
def initialize(self, spec: NodeSpec, object_spec: ObjectSpec, simulator: Any):
24+
def initialize(self, spec: NodeSpec, simulator: Any):
2525
pass
2626

2727
@register.states()
@@ -42,19 +42,18 @@ def make(cls, name: str, rate: float, process: int = eagerx.NEW_PROCESS, color:
4242
spec = cls.get_specification()
4343

4444
# Modify default node params
45-
spec.config.update(name=name, rate=rate, process=process, color=color, inputs=['tick', 'dummy_input'], outputs=[])
45+
spec.config.update(name=name, rate=rate, process=process, color=color, inputs=['dummy_input'], outputs=[])
4646
return spec
4747

48-
def initialize(self, spec: NodeSpec, object_spec: ObjectSpec, simulator: Any):
48+
def initialize(self, spec: NodeSpec, simulator: Any):
4949
pass
5050

5151
@register.states()
5252
def reset(self):
5353
pass
5454

55-
@register.inputs(tick=eagerx.Space(shape=(), dtype="int64"),
56-
dummy_input=eagerx.Space(low=np.array([-1], dtype="float32"), high=np.array([1], dtype="float32")))
57-
@register.outputs(dummy_output=eagerx.Space(low=np.array([-1], dtype="float32"), high=np.array([1], dtype="float32")))
58-
def callback(self, t_n: float, tick: Msg, dummy_input: Msg):
55+
@register.inputs(dummy_input=eagerx.Space(low=np.array([-1], dtype="float32"), high=np.array([1], dtype="float32")))
56+
@register.outputs()
57+
def callback(self, t_n: float, dummy_input: Msg):
5958
data = np.array([random()], dtype="float32")
60-
return dict(dummy_output=data)
59+
return dict()

tests/dummy/engine_states.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ def make(cls, sleep_time: float = 1., repeat: int = 1) -> EngineStateSpec:
1212
spec.config.repeat = repeat
1313
return spec
1414

15-
def initialize(self, spec: EngineStateSpec, object_spec: ObjectSpec, simulator: Any):
15+
def initialize(self, spec: EngineStateSpec, simulator: Any):
1616
self.sleep_time = spec.config.sleep_time
1717
self.repeat = spec.config.repeat
1818

tests/dummy/objects.py

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
class Dummy(Object):
1111
@classmethod
1212
@register.sensors(dummy_output=Space(low=np.array([-1], dtype="float32"), high=np.array([1], dtype="float32")))
13-
@register.actuators(dummy_input=Space(low=np.array([-1], dtype="float32"), high=np.array([1], dtype="float32")))
13+
@register.actuators(dummyy_input=Space(low=np.array([-1], dtype="float32"), high=np.array([1], dtype="float32")))
1414
@register.engine_states(dummy_state=Space(low=np.array([-1], dtype="float32"), high=np.array([1], dtype="float32")))
1515
def make(cls, name: str, sensors=None, states=None, rate=30) -> ObjectSpec:
1616
"""Object spec of dummy object"""
@@ -19,33 +19,44 @@ def make(cls, name: str, sensors=None, states=None, rate=30) -> ObjectSpec:
1919
# Set parameters
2020
spec.config.update(name=name, rate=rate)
2121
spec.config.sensors = sensors if sensors else ["dummy_output"]
22-
spec.config.actuators = ["dummy_input"]
22+
spec.config.actuators = ["dummyy_input"]
2323
spec.config.states = states if states else ["dummy_state"]
2424

2525
# Set rates
2626
spec.sensors.dummy_output.rate = rate
27-
spec.actuators.dummy_input.rate = rate
27+
spec.actuators.dummyy_input.rate = rate
2828
return spec
2929

3030
@staticmethod
3131
@register.engine(RealEngine) # This decorator pre-initializes engine implementation with default object_params
3232
def real_engine(spec: ObjectSpec, graph: EngineGraph):
3333
"""Engine-specific implementation (RealEngine) of the object."""
34+
try:
35+
from tests.dummy.engine_states import DummyReset
36+
from tests.dummy.engine_nodes import DummyOutput
37+
from tests.dummy.engine_nodes import DummyInput
38+
except ImportError:
39+
try:
40+
from .dummy.engine_states import DummyReset
41+
from .dummy.engine_nodes import DummyOutput
42+
from .dummy.engine_nodes import DummyInput
43+
except ImportError:
44+
from dummy.engine_states import DummyReset
45+
from dummy.engine_nodes import DummyOutput
46+
from dummy.engine_nodes import DummyInput
47+
3448
# Couple engine states
35-
from tests.dummy.engine_states import DummyReset
3649
spec.engine.states.dummy_state = DummyReset.make(sleep_time=1.0, repeat=1)
3750

3851
# Create sensor engine nodes
3952
# Rate=None, because we will connect them to sensors (thus uses the rate set in the agnostic specification)
40-
from tests.dummy.engine_nodes import DummyOutput
4153
obs = DummyOutput.make("dummy_output", rate=spec.sensors.dummy_output.rate, process=0)
4254

4355
# Create actuator engine nodes
4456
# Rate=None, because we will connect it to an actuator (thus uses the rate set in the agnostic specification)
45-
from tests.dummy.engine_nodes import DummyInput
46-
action = DummyInput.make("dummy_input", rate=spec.actuators.dummy_input.rate, process=0)
57+
action = DummyInput.make("dummyy_input", rate=spec.actuators.dummyy_input.rate, process=0)
4758

4859
# Connect all engine nodes
4960
graph.add([obs, action])
5061
graph.connect(source=obs.outputs.dummy_output, sensor="dummy_output")
51-
graph.connect(actuator="dummy_input", target=action.inputs.dummy_input)
62+
graph.connect(actuator="dummyy_input", target=action.inputs.dummy_input)

tests/test_eagerx_reality.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def test_real_engine(eps, steps, sync, p):
3434
graph.add(dummy)
3535

3636
# Connect the nodes
37-
graph.connect(action="action", target=dummy.actuators.dummy_input)
37+
graph.connect(action="action", target=dummy.actuators.dummyy_input)
3838
graph.connect(source=dummy.sensors.dummy_output, observation="observation", window=1)
3939

4040
# Define engines

0 commit comments

Comments
 (0)