次の方法で共有


Controlling Phidgets using WCF Services

After the initial development of Mopbot using phidgets I was having lot of reliability issues. Specially got PhidgetException about “Phidget not physically attached" or sometimes the motors got stuck because the program could not reset them. I also saw huge difference between running in debug mode in Visual Studio vs running the application itself. After some investigation I found out that one of the main causes of these failures were because of the single threaded apartment state of the application. I was using a windows forms application to control the bot. So I started down the multi threading part which soon was getting complicated. I have finally replaced everything with WCF services, one service each for Motor controller, Input Output Controller, and LCD Controller.

By definition web services/WCF services are about stateless calls, in this case I have to maintain state of the controller throughout the client connection. WCF services allows you to create session based services by setting SessionMode property to Required in the ServiceContract attribute of the Interface. I also marked operations as OneWay because the main program does not need if the controller succeeded in setting the right state or not. You also need to set the InstanceContextMode to PerSession in ServiceBehavior attribute of the implementation class which maintains the service instance for the entire duration of the session. In order to host the service you need to use a binding which supports sessions. NetTcpBinding is a great one that supports sessions and it also support reliable sessions which can be used in future to create reliable session for controllers. Here is the interface code from Motor controller service.

 [ServiceContract(SessionMode=SessionMode.Required)]
public interface IMotorService
{
    [OperationContract(IsOneWay=true)]
    void SetVelocity(int motor, int velocity);
    [OperationContract(IsOneWay = true)]
    void SetAcceleration(int motor, int acceleration);
    [OperationContract()]
    int GetMotors();
}

Here is implementation class code.

 [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
    public class MotorService: IMotorService
    {
        MotorControl motorControl;
        public MotorService() 
        {
            motorControl = new MotorControl();
            motorControl.open(41746);
            motorControl.waitForAttachment(2000);
        }
        public void SetVelocity(int motor, int velocity)
        {
            if (motor < motorControl.motors.Count)
            {
                motorControl.motors[motor].Velocity = velocity;
            }
        }
        public void SetAcceleration(int motor, int acceleration)
        {
            if (motor < motorControl.motors.Count)
            {
                motorControl.motors[motor].Acceleration = acceleration;
            }
        }
        public int GetMotors() 
        {
            return motorControl.motors.Count;
        }
        ~MotorService()
        {
            if (motorControl != null)
            {
                try
                {
                    for (int i = 0; i < motorControl.motors.Count; i++)
                    {
                        motorControl.motors[i].Velocity = 0;
                    }
                
                    motorControl.close();
                    motorControl = null;
                }
                catch { }
            }
        }

    }

As you see in the implementation the constructor creates a new instance of Phidgets MotorControl class which controls the physical motors. The destructor of the class resets the velocity just in case if the client does not reset it and releases the MotorControl object. In order to test the WCF service, I quickly hosted the service in a console application which I will eventually move to a service. Here is the code for the console application.

 static void Main(string[] args)
        {
            ServiceHost serviceHost = new ServiceHost(typeof(MotorService),
                    new Uri[] { new Uri("net.tcp://localhost:8000/MotorService") });
            serviceHost.AddServiceEndpoint(typeof(IMotorService), new 
                    NetTcpBinding(SecurityMode.None), new Uri("net.tcp://localhost:8000/MotorService/service"));
            ServiceMetadataBehavior smb = serviceHost.Description.Behaviors.Find<ServiceMetadataBehavior>();
            if (smb == null)
                smb = new ServiceMetadataBehavior();
            smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
            serviceHost.Description.Behaviors.Add(smb);
            serviceHost.AddServiceEndpoint(
              ServiceMetadataBehavior.MexContractName,
              MetadataExchangeBindings.CreateMexTcpBinding(),
              "mex"
            );

            serviceHost.Open();
            Console.WriteLine("Motor controller service started...press any key to shutdown");
            Console.ReadKey();
            serviceHost.Close();
        }

As I mentioned earlier I am using NetTcpBinding which supports sessions. After creating all the WCF services, the controller is working without any issues. WCF Services also makes it easier for creating windows services which can finally host all the controllers as well as the main program.

image

I will be posting the entire codebase after some more tweaking and tinkering, meanwhile keep those phidgets do your stuff.

Thanks

RV