Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GH components #21

Draft
wants to merge 13 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,6 @@ ENV/

# autogenerated sphinx
docs/reference/generated/

# ghcomponents
src/compas_rrc/ghpython/components/ghuser/*.ghuser
1 change: 1 addition & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Unreleased
**Added**

* Added `to_configuration` and `to_configuration_primitive` to `compas_rrc.ExternalAxes` and `compas_rrc.RobotJoints`
* Added Grasshopper components for RRC

**Changed**

Expand Down
5 changes: 4 additions & 1 deletion src/compas_rrc/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,10 @@
StopWatch
)

__all_plugins__ = ['compas_rrc.__install']
__all_plugins__ = [
'compas_rrc.rhino.install',
'compas_rrc.ghpython.install'
]
__all__ = [
'__url__',
'__version__',
Expand Down
16 changes: 16 additions & 0 deletions src/compas_rrc/ghpython/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from __future__ import absolute_import


def create_id(component, name):
return '{}_{}'.format(name, component.InstanceGuid)


def coerce_frame(plane):
import Rhino
from compas.geometry import Frame
if isinstance(plane, Rhino.Geometry.Plane):
return Frame(plane.Origin, plane.XAxis, plane.YAxis)
elif isinstance(plane, Frame):
return plane
else:
return Frame(*plane)
81 changes: 81 additions & 0 deletions src/compas_rrc/ghpython/components/CompasRrc_ExecuteBatch/code.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
"""
Execute a batch of instructions.
"""
from ghpythonlib.component import add_error
from ghpythonlib.component import add_warning
from ghpythonlib.componentbase import executingcomponent as component
from scriptcontext import sticky as st

import compas_rrc as rrc
from compas_rrc.ghpython import create_id


class CompasRrcExecuteBatch(component):
TIMEOUT = 60

def __init__(self):
super(CompasRrcExecuteBatch, self).__init__()
self.wait_for_completion = True

def RunScript(self, rrc_client, instructions, run):
if not rrc_client:
add_warning('rrc_client is not assigned. The component did not run.')
self.Message = ''
return False, None

if len(instructions) == 0:
self.Message = 'No instructions'
return False, None

run_key = create_id(self, 'run_status')
res_key = create_id(self, 'results')

# TODO: Add checks for rrc_client.ros.is_connected before attempting to execute
if run:
futures = []
for instruction in instructions[:-1]:
future = rrc_client.send(instruction)
if instruction.feedback_level > rrc.FeedbackLevel.NONE:
futures.append(future)

last_instruction = instructions[-1]

if not self.wait_for_completion:
future = rrc_client.send(last_instruction)
if last_instruction.feedback_level > rrc.FeedbackLevel.NONE:
futures.append(future)

# In the non-wait mode, the results are futures
results = futures
else:
feedback = rrc_client.send_and_wait(last_instruction, timeout=self.TIMEOUT)

# Since we wait for the last instruction, all previous ones
# must have completed as well, so we can collect their futures
results = [future.result(timeout=self.TIMEOUT) for future in futures]
results.append(feedback)

self.Message = 'Executed {} actions'.format(len(instructions))
st[run_key] = True
st[res_key] = results

done = st.get(run_key)
results = st.get(res_key)
return done, results

def OnTextMenuClick(self, _sender, _args):
try:
self.wait_for_completion = not self.wait_for_completion
self.ExpireSolution(True)
except Exception as ex:
add_error(str(ex))

def AppendAdditionalMenuItems(self, items):
component.AppendAdditionalMenuItems(self, items)

try:
image = None
item = items.Items.Add('Wait for completion', image, self.OnTextMenuClick)
item.Checked = self.wait_for_completion
except Exception as ex:
add_error(str(ex))
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"name": "Execute Batch",
"nickname": "Execute",
"category": "COMPAS RRC",
"subcategory": "Execute",
"description": "Execute a batch of instructions.",
"exposure": 2,

"ghpython": {
"isAdvancedMode": true,
"iconDisplay": 2,
"inputParameters": [
{
"name": "rrc_client",
"description": "The RRC client."
},
{
"name": "instructions",
"description": "List or tree of instructions to be executed as a batch",
"scriptParamAccess": "list"
},
{
"name": "run",
"description": "If True, executes the batch of instruction on the robot.",
"typeHintID": "bool"
}
],
"outputParameters": [
{
"name": "done",
"description": "Confirmation that the instructions have been executed on the robot."
},
{
"name": "results",
"description": "Feedback received from the batch of instructions, or future result objects if the option to wait for completion is deactivated."
}
]
}
}
11 changes: 11 additions & 0 deletions src/compas_rrc/ghpython/components/CompasRrc_GetFrame/code.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"""
Request the current frame of the robot.
"""
from ghpythonlib.componentbase import executingcomponent as component

import compas_rrc as rrc


class CompasRrcGetFrame(component):
def RunScript(self):
return rrc.GetFrame()
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@

{
"name": "Get Frame",
"nickname": "Get Frame",
"category": "COMPAS RRC",
"subcategory": "Utility instructions",
"description": "Request the current frame of the robot.",
"exposure": 2,

"ghpython": {
"isAdvancedMode": true,
"iconDisplay": 2,
"inputParameters": [],
"outputParameters": [
{
"name": "ins",
"description": "Instruction instance."
beverlylytle marked this conversation as resolved.
Show resolved Hide resolved
}
]
}
}
51 changes: 51 additions & 0 deletions src/compas_rrc/ghpython/components/CompasRrc_MoveToFrame/code.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""
Move robot to the specified frame in cartesian space.
"""
from ghpythonlib.component import add_error
from ghpythonlib.component import add_warning
from ghpythonlib.componentbase import executingcomponent as component

import compas_rrc as rrc
from compas_rrc.ghpython import coerce_frame


class CompasRrcMoveToFrame(component):
def __init__(self):
super(CompasRrcMoveToFrame, self).__init__()
self.motion_type = rrc.Motion.JOINT

def RunScript(self, frame, speed, zone, feedback):
if not frame:
add_warning('Frame is not defined. The component did not run.')
return None

if not speed or speed < 0.01:
add_warning('Speed must be greater or equal to 0.01 mm/s.')
return None

if not zone:
zone = rrc.Zone.FINE

frame = coerce_frame(frame)
feedback = feedback or rrc.FeedbackLevel.NONE
instruction = rrc.MoveToFrame(frame, speed, zone, motion_type=self.motion_type, feedback_level=feedback)
self.Message = '{} Motion'.format('Joint' if self.motion_type == rrc.Motion.JOINT else 'Linear')

return instruction

def OnTextMenuClick(self, _sender, _args):
try:
self.motion_type = rrc.Motion.JOINT if self.motion_type == rrc.Motion.LINEAR else rrc.Motion.LINEAR
self.ExpireSolution(True)
except Exception as ex:
add_error(str(ex))

def AppendAdditionalMenuItems(self, items):
component.AppendAdditionalMenuItems(self, items)

try:
image = None
item = items.Items.Add('Move in joint space', image, self.OnTextMenuClick)
item.Checked = self.motion_type == rrc.Motion.JOINT
except Exception as ex:
add_error(str(ex))
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@

{
"name": "Move To Frame",
"nickname": "To Frame",
"category": "COMPAS RRC",
"subcategory": "Motion instructions",
"description": "Move robot to the specified frame in cartesian space.",
"exposure": 2,

"ghpython": {
"isAdvancedMode": true,
"iconDisplay": 2,
"inputParameters": [
{
"name": "frame",
"description": "Target frame."
},
{
"name": "speed",
"description": "Integer specifying TCP translational speed in mm/s.",
"typeHintID": "int"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not float?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not float... ABB RAPID is an inflexible friend sometimes... :P

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but

if not speed or speed < 0.01:
            add_warning('Speed must be greater or equal to 0.01 mm/s.')

},
{
"name": "zone",
"description": "Zone data, either an integer or Zone.FINE. Defaults to Zone.FINE.",
"typeHintID": "int"
},
{
"name": "feedback",
"description": "Feedback level. Defaults to DONE. Use together with Zone.FINE to ensure the motion planner has executed the instruction fully.",
"typeHintID": "int"
}
],
"outputParameters": [
{
"name": "ins",
"description": "Instruction instance."
}
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
"""
Move robot to the specified robtarget in cartesian space.
"""
from ghpythonlib.component import add_error
from ghpythonlib.component import add_warning
from ghpythonlib.componentbase import executingcomponent as component

import compas_rrc as rrc
from compas_rrc.ghpython import coerce_frame


class CompasRrcMoveToRobtarget(component):
def __init__(self):
super(CompasRrcMoveToRobtarget, self).__init__()
self.motion_type = rrc.Motion.JOINT

def RunScript(self, frame, external_axes, speed, zone, feedback):
if not frame:
add_warning('Frame is not defined. The component did not run.')
return None

if not external_axes:
add_warning('External axes is not defined. The component did not run.')
return None

if not speed or speed < 0.01:
add_warning('Speed must be greater or equal to 0.01 mm/s.')
return None

if not zone:
zone = rrc.Zone.FINE

frame = coerce_frame(frame)
feedback = feedback or rrc.FeedbackLevel.NONE
instruction = rrc.MoveToRobtarget(frame, external_axes, speed, zone, motion_type=self.motion_type, feedback_level=feedback)
self.Message = '{} Motion'.format('Joint' if self.motion_type == rrc.Motion.JOINT else 'Linear')

return instruction

def OnTextMenuClick(self, _sender, _args):
try:
self.motion_type = rrc.Motion.JOINT if self.motion_type == rrc.Motion.LINEAR else rrc.Motion.LINEAR
self.ExpireSolution(True)
except Exception as ex:
add_error(str(ex))

def AppendAdditionalMenuItems(self, items):
component.AppendAdditionalMenuItems(self, items)

try:
image = None
item = items.Items.Add('Move in joint space', image, self.OnTextMenuClick)
item.Checked = self.motion_type == rrc.Motion.JOINT
except Exception as ex:
add_error(str(ex))
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@

{
"name": "Move To Robtarget",
"nickname": "To Robtarget",
"category": "COMPAS RRC",
"subcategory": "Motion instructions",
"description": "Move robot to the specified robtarget in cartesian space.",
"exposure": 2,

"ghpython": {
"isAdvancedMode": true,
"iconDisplay": 2,
"inputParameters": [
{
"name": "frame",
"description": "Target frame."
},
{
"name": "external_axes",
"description": "External axes.",
"scriptParamAccess": "list"
},
{
"name": "speed",
"description": "Integer specifying TCP translational speed in mm/s.",
"typeHintID": "int"
},
{
"name": "zone",
"description": "Zone data, either an integer or Zone.FINE. Defaults to Zone.FINE.",
"typeHintID": "int"
},
{
"name": "feedback",
"description": "Feedback level. Defaults to DONE. Use together with Zone.FINE to ensure the motion planner has executed the instruction fully.",
"typeHintID": "int"
}
],
"outputParameters": [
{
"name": "ins",
"description": "Instruction instance."
}
]
}
}
Loading