Drive the Lynx6Arm service from another service (in C#)

This might look like a ripoff from this topic but I don’t think it is.

I’m trying to drive de lynx6arm service (provided by lynxmotion) from another service. What I’ve done jet:

Made a new service with dssnewservice.exe.

Added the namespace to my sevice:

using lynx6arm = CoroWare.Robotics.Services.Lynx6Arm.Proxy;

Created in my service a partner (the lynx6arm service):

//Partners [Partner("Lynx6Arm", Contract = lynx6arm.Contract.Identifier, CreationPolicy = PartnerCreationPolicy.UseExistingOrCreate, Optional = false)] private lynx6arm.Lynx6ArmOperations _lynx6armPort = new lynx6arm.Lynx6ArmOperations();
When I start my service the lynx6arm and the ssc32 services are both started as well because the arm gets initialised and goes into the startposition.

In the topic about driving the ssc32 service from another service there is a public class which is used to send commands.
Unfortunatly i couldn’t find a public class in the lynx6arm service.
How can I call GetEndEffectorPose and SetEndEffectorPose from my own service?

This service is slightly different that controlling the SSC-32. The major difference is this line from Lynx6Arm.cs:

[AlternateContract(armproxy.Contract.Identifier)]

This specifies the arm not only supports it’s own contract, it also supports the generic arm contract to control it. Where this gets confusing is in the file Lynx6ArmTypes.cs where you see

public class Lynx6ArmOperations : PortSet< DsspDefaultLookup, DsspDefaultDrop, Get>
This makes it looks like the arm can only be queried for information (Get) without sending it any new commands. Not very helpful.
However, while the _mainPort named /Lynx6Arm only supports these operations there is a second port named /arm which supports a whole new set of operations.
I find the simplest way to deetermine what you can do with the arm is to right-click on the type for the /arm port (ArticulatedArmOperations) and select “Go To Definition”.
This will move to a metadata file and show you a whole group of operations:

[code] public static implicit operator Port(ArticulatedArmOperations portSet);
public static implicit operator Port(ArticulatedArmOperations portSet);
public static implicit operator Port(ArticulatedArmOperations portSet);
public static implicit operator Port(ArticulatedArmOperations portSet);
public static implicit operator Port(ArticulatedArmOperations portSet);
public static implicit operator Port(ArticulatedArmOperations portSet);
public static implicit operator Port(ArticulatedArmOperations portSet);
public static implicit operator Port(ArticulatedArmOperations portSet);
public static implicit operator Port(ArticulatedArmOperations portSet);
public static implicit operator Port(ArticulatedArmOperations portSet);

    public virtual PortSet<DefaultDropResponseType, Fault> DsspDefaultDrop();
    public virtual PortSet<DefaultDropResponseType, Fault> DsspDefaultDrop(DropRequestType body);
    public virtual PortSet<LookupResponse, Fault> DsspDefaultLookup();
    public virtual PortSet<LookupResponse, Fault> DsspDefaultLookup(LookupRequestType body);
    public virtual PortSet<ArticulatedArmState, Fault> Get();
    public virtual PortSet<ArticulatedArmState, Fault> Get(GetRequestType body);
    public virtual PortSet<GetEndEffectorPoseResponse, Fault> GetEndEffectorPose();
    public virtual PortSet<GetEndEffectorPoseResponse, Fault> GetEndEffectorPose(GetEndEffectorPoseRequest body);
    public void Post(DsspDefaultDrop item);
    public void Post(DsspDefaultLookup item);
    public void Post(Get item);
    public void Post(GetEndEffectorPose item);
    public void Post(ReliableSubscribe item);
    public void Post(Replace item);
    public void Post(SetEndEffectorPose item);
    public void Post(SetJointTargetPose item);
    public void Post(SetJointTargetVelocity item);
    public void Post(Subscribe item);
    public virtual PortSet<SubscribeResponseType, Fault> ReliableSubscribe(IPort notificationPort);
    public virtual PortSet<SubscribeResponseType, Fault> ReliableSubscribe(ReliableSubscribeRequestType body, IPort notificationPort);
    public virtual PortSet<DefaultReplaceResponseType, Fault> Replace();
    public virtual PortSet<DefaultReplaceResponseType, Fault> Replace(ArticulatedArmState body);
    public virtual PortSet<DefaultUpdateResponseType, Fault> SetEndEffectorPose();
    public virtual PortSet<DefaultUpdateResponseType, Fault> SetEndEffectorPose(SetEndEffectorPoseRequest body);
    public virtual PortSet<DefaultUpdateResponseType, Fault> SetJointTargetPose(SetJointTargetPoseRequest body);
    public virtual PortSet<DefaultUpdateResponseType, Fault> SetJointTargetPose(string jointName, Vector3 targetPosition, AxisAngle targetOrientation);
    public virtual PortSet<DefaultUpdateResponseType, Fault> SetJointTargetVelocity(SetJointTargetVelocityRequest body);
    public virtual PortSet<DefaultUpdateResponseType, Fault> SetJointTargetVelocity(string jointName, Vector3 targetVelocity);
    public virtual PortSet<SubscribeResponseType, Fault> Subscribe(IPort notificationPort);
    public virtual PortSet<SubscribeResponseType, Fault> Subscribe(SubscribeRequestType body, IPort notificationPort);

[/code]
Most of these aren’t helpful, but you are interested in a couple of them: SetEndEffectorPose and GetEndEffectorPose. These will allow you to control the position of the gripper using inverse kinematics. If you wish to control the joints more directly you can use SetJointTargetPose which allows you to specify absolute joint positions.
As a test you can bring up the simple dashboard that comes with MSRS and open the arm to control it directly.

The “Go To Definition”-tip is very usefull! Thanks for that.

To post a request to the lynx6arm service I have to use the “/arm”-port. But how can I get a reference to that port in my service? Now I use this but I don’t think thats right:

[code][AlternateServicePort("/arm", AlternateContract = armproxy.Contract.Identifier)]

private armproxy.ArticulatedArmOperations _armPort = new armproxy.ArticulatedArmOperations();[/code]
To post a request I use this code:

[code]armproxy.SetEndEffectorPoseRequest request = new armproxy.SetEndEffectorPoseRequest();

physicalmodel.Pose temp_pose = new physicalmodel.Pose();
temp_pose.Position = new physicalmodel.Vector3(-1, 2, 3);
temp_pose.Orientation = new physicalmodel.Quaternion(0, 0, 0, 1);
request.EndEffectorPose = temp_pose;

this._armPort.Post(new armproxy.SetEndEffectorPose(request, null));[/code]
Unfortunately that doesn’t work. Am I missing something?

Now is the time to look at this post which covers how to partner with a service.

Don’t use the alternate contract construct, as that is how you define a port you will be receiving operations on, rather than a port you’ll be sending commands to.

Make the Lynx6Arm service a partner, then define another port based on the generic arm contract. Then you can drive the arm through that port.