-
Notifications
You must be signed in to change notification settings - Fork 6
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
Initial OpenRPC docs - with state subscription #5
base: dev
Are you sure you want to change the base?
Changes from all commits
ff1900c
ce9d4ba
a86eef4
ad3d830
b4a87c1
d9ccf0a
72b1414
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
# OpenRPC Document | ||
|
||
The API is documented here using the OpenRPC 1.2.4 standard. Note that there are later published versions of the standard, but the available tooling is implemented for the v1.2.4 spec. | ||
|
||
The full api is defined in openrpc.json, however, this means reading and updating a single file would become unwieldy and merge conflicts would arise. To mitigate this, [open-rpc-compiler](https://www.npmjs.com/package/open-rpc-compiler?activeTab=readme) is used to break each method and schema into its own file organized in this directory's subdirectories, named for the elements they comprise in the openrpc.json doc. | ||
|
||
|
||
## Install | ||
``` | ||
npm install open-rpc-compiler --no-save | ||
``` | ||
|
||
To compile the elements into the openrpc.json file run the following command from this directory. | ||
``` | ||
open-rpc-compile > openrpc.json | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
{ | ||
"type": "object", | ||
"properties": { | ||
"jointIndex": { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps redundant if we're doing an array of these? I can't see a lot of uses for reading back just one joint at a time. |
||
"type": "integer" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure what the spec says about this but should we define ints etc with exact widths, eg a 32-bit integer? It's probably not too much of an issue to just define integer as 64-bit signed in all cases, but maybe we'd want to pack from/unpack to specific types on the motion controller side (ie C, C++ or Rust)? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. JSON schema does not allow for this. We need to map keys to memory space on the controller anyway, so we probably can specify the type for C / Rust in the same lookup table we use for memory mapping? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
}, | ||
"position": { | ||
"type": "integer" | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"type": "array", | ||
"items": { | ||
"$ref": "#/components/schemas/jointPosition" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like being able to reference stuff in like this, seems useful |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
{ | ||
"type": "object", | ||
"properties": { | ||
"robotOnline": { | ||
"type": "boolean" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems like it's going to be an enum, since we could have a lot of potential states - eg There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we can define an enum There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have to be careful about this. Most operators manuals for industrial robots have a state chapter (including error states) that are dozens of pages long. This could be daunting to write out on an enum There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the robotOnline was literally just put in as an example value. The point of this was just to establish a format that people like you can use to develop the details There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point on the enum getting pretty big - I can see us maybe having a hierarchy of states, eg the main state is error, the substate is error-hardware-overcurrent and there's also an associated error message. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On second thoughts maybe the state is just presented as a string (for showing to the user) and anything that needs to make decisions checks state flags which the controller toggles based on the state, eg |
||
}, | ||
"jointPositions": { | ||
"$ref": "#/components/schemas/jointPositions" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd keep this separate from the controller state - the complete controller state does include joint positions, but it also includes a lot of other values and I think we need to be able to pick and choose. Also it just seems cleaner to keep things separate. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the getState method provides the ability to include or exclude keys. You can also call the getPosition method to get just that. The reason that all state items are also included in a state object is so that there is a place to list all of the state parameters and it is a mechanism for transmitting any changes to state. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is a way to generalize the API. So we dont' necessarily have to create methods for every parameter that gets added to state. If you add a new key to state, it can automatically be subscribed to. |
||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"version": "0.1.0", | ||
"title": "Motion Controller API" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
{ | ||
"name": "getActiveSubscriptions", | ||
"description": "Get a list of state keys for which the client is currently subscribed to value changes", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Very useful |
||
"params": [], | ||
"result": { | ||
"name": "activeStateKeySubscriptions", | ||
"description": "the state keys to which the client is currently subsribed", | ||
"schema": { | ||
"type": "array", | ||
"items": { | ||
"type": "string" | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
{ | ||
"name": "getState", | ||
"description": "Get the current state and optionally subscribe to changes", | ||
"params": [ | ||
{ | ||
"name": "include", | ||
"description": "state keys that should be included in the response. If undefined, all keys except any specifially excluded are included.", | ||
"required": false, | ||
"schema": { | ||
"type": "array", | ||
"items": { | ||
"type": "string" | ||
} | ||
} | ||
}, | ||
{ | ||
"name": "exclude", | ||
"description": "state keys that should not be included in the response.", | ||
"required": false, | ||
"schema": { | ||
"type": "array", | ||
"items": { | ||
"type": "string" | ||
} | ||
} | ||
}, | ||
{ | ||
"name": "subscribeToChanges", | ||
"required": false, | ||
"description": "Subscribe to changes in the keys included in the request", | ||
"schema": { | ||
"type": "boolean" | ||
} | ||
} | ||
], | ||
"result": { | ||
"name": "state", | ||
"description": "the current state. For subscription updates, only keys for changed values will be included.", | ||
"schema": { | ||
"$ref": "#/components/schemas/state" | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
{ | ||
"name": "getPosition", | ||
"description": "Get current joint positions", | ||
"params": [], | ||
"result": { | ||
"name": "jointPositions", | ||
"description": "updated joint positions", | ||
"schema": { | ||
"type": "array", | ||
"items": { | ||
"$ref": "#/components/schemas/jointPosition" | ||
} | ||
} | ||
}, | ||
"examples": [ | ||
{ | ||
"name": "getPosition", | ||
"params": [], | ||
"result": { | ||
"name": "jointPositions", | ||
"value": [ | ||
{ | ||
"jointIndex": 1, | ||
"position": 393 | ||
}, | ||
{ | ||
"jointIndex": 5, | ||
"position": 1829 | ||
} | ||
] | ||
} | ||
} | ||
] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
{ | ||
"name": "jogJoint", | ||
"description": "Jog an joint with a specified velocity", | ||
"params": [ | ||
{ | ||
"name": "jointIndex", | ||
"description": "The index of the joint to jog", | ||
"schema": { | ||
"type": "integer" | ||
} | ||
}, | ||
{ | ||
"name": "direction", | ||
"description": "Direction to jog the joint.", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Presumably you could just specify a negative velocity rather than having this be separate? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes. That makes sense There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You have to be careful with the velocity field. There are other factors that contribute to the final velocity of the jog. Most of these factors are determined in the safety circuit. For example if a specific safety switch is open (like a person gate) than the max jog speed of the robot is radically lowered and would be outside of whatever value you sent to the API. In that respect. I believe the velocity value should instead be a percentage. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess that would depend on what you expected to happen if something changed the maximum permissible speed while the robot was moving, for example if the control setpoint is 20% and robot is moving at 20mm/s then the safety system changes the limit from 100mm/s to 50mm/s, would you expect the robot to drop back to 10mm/s or remain constant at 20mm/s. We'd probably also want a separate absolute interface for use with external control systems (ROS etc) - perhaps that would be enabled in addition to the jog interface if the client has an extra permission enabled, since it shouldn't be accessible from the UI as it'd be operational in run mode instead of teach/manual mode. |
||
"schema": { | ||
"type": "integer" | ||
} | ||
}, | ||
{ | ||
"name": "speed", | ||
"description": "The speed at which to jog.", | ||
"schema": { | ||
"type": "integer" | ||
} | ||
} | ||
], | ||
"result": { | ||
"name": "noResult", | ||
"schema": { | ||
"type": "null" | ||
} | ||
}, | ||
"examples": [ | ||
{ | ||
"name": "jogJoint", | ||
"params": [ | ||
{ | ||
"name": "jointIndex", | ||
"value": 3 | ||
}, | ||
{ | ||
"name": "direction", | ||
"value": 1 | ||
}, | ||
{ | ||
"name": "speed", | ||
"value": 88 | ||
} | ||
], | ||
"result": { | ||
"name": "noResult", | ||
"value": null | ||
} | ||
} | ||
] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
{ | ||
"name": "unsubscribeState", | ||
"description": "Unsubscribe from changes to state", | ||
"params": [ | ||
{ | ||
"name": "include", | ||
"description": "state keys that should be unsubscribed from. If undefined, all keys except any specifially excluded are included.", | ||
"required": false, | ||
"schema": { | ||
"type": "array", | ||
"items": { | ||
"type": "string" | ||
} | ||
} | ||
}, | ||
{ | ||
"name": "exclude", | ||
"description": "state keys that should not be unsubscribed from", | ||
"required": false, | ||
"schema": { | ||
"type": "array", | ||
"items": { | ||
"type": "string" | ||
} | ||
} | ||
} | ||
], | ||
"result": { | ||
"name": "noResult", | ||
"schema": { | ||
"type": "null" | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Somewhere we need to include unit information. Presumably this is in μm for linear joints, but not sure what we'd be doing angular joints in? I'd maybe lean towards making it a double and using radians
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think defining the units in the name of the data type would be a good idea. For example jointPositionRadians
I didn't know what the unit was and none of this was meant to be finalized data types, just simple examples.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Usually if you are trying to look at raw motor positions. (which is really just for troubleshooting and calibrating) You would just get the step count from origin. Knowing the current radians of a motor that may fully rotate 20 to 30 times in the joints full range of motion is not as useful as you might think
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can see us needing both values - the raw positions seems useful for calibration/troubleshooting as you mention, the joint position in radians would be the easiest way to display a 3D representation of the robot though I think.