tags:

views:

522

answers:

6

My application uses measurement instruments that are connected to the PC. I want to make it possible to use similar instruments from different vendors.

So I defined an interface:

interface IMeasurementInterface
    {
        void Initialize();
        void Close();
    }

So far so good. Before a measurement I need to setup the instrument and this means for different instruments very different parameters. So I want to define a method that takes parameters that can have different structures:

interface IMeasurementInterface
{
    void Initialize();
    void Close();
    void Setup(object Parameters);
}

I will then cast the object to whatever I need. Is this the way to go?

A: 

That would probably work. Another option is to pass the parameters in a dictionary.

Glomek
+7  A: 

You might be better off coming up with an abstract "Parameters" class that is extended by each different instruments parameters... e.g. and then using Generics to ensure that the correct parameters are passed to the correct classes...

public interface IMeasurement<PARAMTYPE> where PARAMTYPE : Parameters
{
    void Init();
    void Close();
    void Setup(PARAMTYPE p);
}

public abstract class Parameters
{

}

And then for each specific Device,

public class DeviceOne : IMeasurement<ParametersForDeviceOne>
{
    public void Init() { }
    public void Close() { }
    public void Setup(ParametersForDeviceOne p) { }
}

public class ParametersForDeviceOne : Parameters
{

}
Eoin Campbell
Parameters ought to be an interface, no?I prefer the description 'Configuration' as opposed to setup.
Egwor
Thank you for your answer, however this brings up another question. In my model I have a private field named: MeasurementDevice which should be of type IMeasurement<Parameters>If I want to initiate DeviceOne I do: MeasurementDevice = new DeviceOne() This does not work (cannot convert DeviceOne.
Enrico
When you create a device you will need to know the kind of device it is to hold a reference to it via the interface so you lose the power of the abstraction a little bit
Wolfbyte
+3  A: 

To me it sound like the Factory pattern might be usefull, especially if your are going to unit test your app.

Kasper
+1  A: 

If you are going to deal with even more than one device type, then controller + device interface seperation, which communicates using Name vlaue pairs would be a good solution

DECOUPLING

Using name value pairs allows you to seperate your code into a device + controller + application code structure

Sample Code

class DeviceInterface
    {
    void Initialize(IController & Controller);
    void Close();
    bool ChangeParameter(const string & Name, const string & Value); 
    bool GetParam(string & Name, string &Value );
    }

Each device implementation, when created should be created with the identification of the controller that can accept its commands and translate them into the actual device commands

interface IController
   {
   Initialize(DeviceSpecific & Params);
   Close();
   bool ChangeParameter(string & Name, string & Value);
   bool ChangeParams(string & Name[], string &Value []);
   }

Your user code would look something like this

IController     objController = new MeasurementDevice(MeasureParram);

DeviceInterface MeasureDevice = new DeviceInterface(objController);

string Value;

MeasureDevice.GetParam("Temperature", Value);

if (ConvertStringToInt(Value) > 80)
     {
     MeasureDevice.ChangeParameter("Shutdown", "True");
     RaiseAlert();
     }

All that the DeviceInterface class should do is take care of passing the commands to the controller. The controller should take care of the device communication.

Advantages of the interface seperation

Protect againt changes

This sort of decoupling will allow you to isolate your app code from the controller. Changes in the device does not affect your user code

Maintainability of Appliction Code

Addtionally the user code is always clean and you need bother only with the application logic. But had you defined multiple interfaces / created templates or generics with multiple types of parameter structs specific to controller, your code would have lots of device dependent junk in it which might hurt readability and create maintenance issues whenever your device / its parameters changes.

Implementation ease

You can also hive off different controller implementations into its own projects. Plus your application can also configure commands and responses in a more dynamic naure using XML files etc that can ship along with the controller classes such that your entire application becomes more dynamic in nature.

Real Life

One of the latest production controller projects from the leader in that domain works in the same manner. But they use LON for the device communication.

LON ?

LON protocol used in controllers (think air-conditioner / boiler / fans etc) networks use this concept to talk to various devices

So all that you would need to have is a single interface that can talk to your device and then sends the name value pair to it using LON. he use of a standard protocol will also allow you to talk to other devices besides your measurement instrument. There are open source implementations of LON available if your device uses LON.

If your device does not support LON then you might have to design something where the user code still works on name value pairs and an opposite interface translates your name value pairs into an equivalet corresponding cotroller struct+ and communicates to the individua device in the way the device understands .

Hope this comes useful.

+1  A: 

It depends on how you are going to get the parameters in the first place. If they are stored off in a database table or a config file somewhere and it's just values that need to be set then passing in a dictionary will probably do it (although you do lose type safety). If your setup processes are going to be a little more complicated then I'd consider abstracting away the setup process a little further and performing a double dispatch (pushing the cast operation into a new setup class). Like this

public interface IMeasurementInterface
{
  void Initialize();
  void Close();
  void Setup( IConfigurer config );
}

public interface IConfigurer
{
  void ApplyTo( object obj );
}

public abstract ConfigurerBase<T> : IConfigurer where T : IMeasurementInterface
{
  protected abstract void ApplyTo( T item );

  void IConfigurator.ApplyTo(object obj )
  {
    var item = obj as T;
    if( item == null )
      throw new InvalidOperationException("Configurer can't be applied to this type");
    ApplyTo(item);
  }
}

This way you aren't messing up your Measurement class hierarchy (or providing no implementation and assuming that all implementations will do what you want anyway). It also means that you can test your Setup code by passing in a fake (or Mocked) Measurement device.

If the setup process needs to manipulate private or protected data then you can make the concrete implementation of the IConfigurer reside inside its corresponding Measurement class.

Wolfbyte
+1  A: 

I have to this for my software as I need to support many different types of motion controllers for metal cutting machines.

Your interface has the basics you need. The thing you need to remember is that you don't need to pass in a list of parameters. You pointed out each type of device could have a very different type of setup.

The way I do it is as follows

interface IMeasurementInterface
{
    void Initialize();
    void Close();
    void Setup();
    void Read (FileReader as <whatever read file object you are using>)
    void Store (FileReader as <whatever read file object you are using>)
    string Name();
}

Setup calls a dialog box created in the assembly of the IMeasurementDevice. The dialog is NOT Visible outside of the assembly.

Now I know some object oriented or MVC purist may object to this. However I feel the concept of hiding the internals of a specific Measurement class outweighs strict adherence to the MVC architecture.

My general philosophy is that trival dialog are implemented in the same assembly provided that it is private to the assembly and called by an object implementing on the standard interfaces that I setup. Again the reason for this is I find hiding the internals to be more valuable than trying to implement all the dialogs in the top level assembly.

By specifying a Read Method and a Store method you eliminate the need to expose the internal setup parameters for saving. All you need is to pass whatever type of file storage object you are using to save your setup parameters.

Finally like another poster stated you need to setup a Factory Class in your assembly containing all your measurement devices. During your setup you need to instantiate this class and retrieve the list of supported measurement devices.

The way I do it is my factory class retrieves a list of motion controllers. This list is part of a master class where all the setup classes are stored. When I read my setup files I get the controllers that are actually being used. I retrieve those classes out of the list and place them in another list that is actually used during the cutting process.

The reason I do it this way is that when the user is setting up motion controllers he need to be able to pick from a list of ALL the available controls in order to tell the software which one he has. I find it more responsive to keep a list of available controllers around.

RS Conley
This could work, however I need to pass some configuration parameters from the UI, without opening a dialog. If I only want to change the number of points to measure I want to do that from the main UI and not from a setup dialog. Therefore I wanted the Setup to accept parameters
Enrico
In our software we have Shop Standards which are accessible from anywhere in the software. I recommend putting that parameter (and others like it) in your version of the Shop Standards.
RS Conley
Which parameter goes where I use the rule of 1) If it pertains to ALL devices of the same class. Put in the Global Shop Standards. 2) If it is specific to the device hide it behind the device interface.In other case you don't need to pass extra parameters through setup.
RS Conley
However you really need to pass specific parameters then I recommend adding a CustomSetup procedure to the interface. It would be defined asSub CustomSetup(Parameter_Name as String, Value as String).
RS Conley