tags:

views:

55

answers:

3

I've been struggling to get my mind around classes. Even after reading several websites and books, I still don't feel like I really 'get it'.

The application that I'm working on has a sub that will control a few different pieces of test equipment over a USB to GPIB interface.

So, I have:

Laptop
   USB - GPIB Interface
        Power Supply (Device Address 5)
        HP34970 Datalogger (Device Address 6)

In my sub I would like to Open the GPIB device, send a few SCPI commands to turn on the power supply, send a few more to the HP34970 to switch relays or measure voltage.

Seems simple enough and I can easily make everything work in the same module. However, I think I it would be much better to have a separate class for the GPIB Interface, Power Supply, and HP34970. If this was the case, I could very easily reuse the code in other projects.

Here is where I just can't get a mental model- If I create an instance of the GPIB class and have a method to 'open' a channel to the GPIB bus, how can I allow methods in other classes (like power supply) to use the open channel created in the GPIB class? For example, a method in the power supply class to set voltage.

If someone anyone could take a few minutes to post pseudo code and a little explaination to illustrate how I could / should organize this, I would greatly appreciate the help!

Thanks

+1  A: 

how can I allow methods in other classes (like power supply) to use the open channel created in the GPIB class?

One way is to create a composite class that holds instances of your Power Supply class and your GPIB class (sorry about the C#):

public class PowerSupplyGPIBController
{
    // Members
    GPIB gpib;
    PowerSupply powerSupply;

    // Constructor
    public PowerSupplyGPIBController(GPIB myGPIB, PowerSupply myPowerSupply)
    {
        gpib = myGPIB;
        powerSupply = myPowerSupply;
    }

    // Method
    public void SendPowerSupplyVoltage()
    {
        GPIB.Send(myPowerSupply.Voltage);
    }
}

Basically you need to hold GPIB and PowerSupply objects somewhere, and pass data/messages between them.

Robert Harvey
That seems overly complicated. If any particular power supply is only going to be useful in conjunction with one particular GPIB object (the GPIB device to which the particular supply is connected) it would seem more natural to have the power supply object itself hold a reference to the GPIB controller.
supercat
@supercat: I agree, it is a lot of ceremony. *If a GPIB is the only way the Power Supply class is going to communicate,* then having the Power Supply class hold a reference to a GPIB does seem the logical way to do it.
Robert Harvey
@Robert Harvey: If one wants to be extensible to means of communication other than GPIB, wouldn't the way to do that be to still have a GpibPowerSupply class which holds a reference to the GPIB controller and communicates with it, but have it implement an iPowerSupply interface? I'm not clear what your wrapper class adds to the equation.
supercat
@supercat: It's a way to provide some decoupling. Using a wrapper class means that neither the GPIB nor the Power Supply class have to know anything about each other.
Robert Harvey
@supercat: Also, it favors composition over inheritance.
Robert Harvey
@Robert Harvey: Are you considering interfaces to be inheritance? Some class somewhere is going to have to know that one sets the voltage of a GPIB power supply by using some particular command. If type "PowerSupply" were called "PowerSupplyAddress", and your set voltage function were more like "void setVoltage(double voltage) {GPIB.Send(myPowerSupplyAddress, GPIB_VOLTAGE_COMMAND, (int)(voltage * VOLTAGE_SCALE));" that would seem much clearer.
supercat
@Robert Harvey: If the GpibPowerSupply class implemented iPowerSupply (which containsd a setVoltage command) one could substitute any other type of power supply by replacing the code that creates a GpibPowerSupply with code to create a UsbPowerSupply. If one wants to use the same PowerSupply type, one could have the interface code subscribe to a VoltageChanged event (in which case the code would look more like yours) but that would seem more complicated.
supercat
+1  A: 

I would suggest that the constructors for PowerSupply, Datalogger, etc. each accept an instance of a GPIB class which actually does the communication. Then when someone calls MyPowersupply.SetVoltage, the PowerSupply.SetVoltage routine can use that GPIB object to send the appropriate request. If you'll be having multiple threads, you should probably use locks to ensure that the communications don't get munged. I don't really see much benefit to having a composite class. Just give have each PowerSupply, Datalogger, etc. hold an instance of a GPIB class that can be used to communicate with the physical device.

supercat
+1  A: 

Think of classes as devices themselves. The main benefit here would be to be able to reuse code written once - for example, all of your devices have addresses, they can connect, test connection, disconnect - why not have that in one abstract "Device" class, and make all devices inherit from it?

This could be one of the usages - written in C# (sorry if I misunderstood the actual usage :) here I'm assuming powersupply and dataLogger are connected to the interface, and interface to the laptop).

public class DeviceTesting
{
   private string powerSupplyAddress = "DA5";
   private string dataLoggerAddress = "DA6";

   public static void main()
   {

      //bring interface into life
      DeviceInterface interface = GPIBInterface(); //example of inheritance - GPIB could be just one of the many device interfaces

      //the constructor in PowerSupply takes on the responsibility of initializing itself
      PowerSupply ps = new PowerSupply(powerSupplyAddress, interface); //plugged in with interface - has a reference to it
      //you could have multiple types of data loggers - all could inherit from DataLogger, and here so does HP34970
      //This means you can reuse the common code for data loggers, and write only the specifics for each specific device of that kind
      DataLogger myLogger = new HP34970(dataLoggerAddress, interface);


      //now, do whatever you do with the real devices
      ps.SetVoltage(220); //send voltage command directly
      interface.SendLogMessage("Interface is operational");
      interface.ExecuteTest("test1"); //send voltage command through interface
      //etc... you get the idea...
   }
}

Now, since the interface has the knowledge of the devices it's interfacing with, it has to have them in it's constructor (in object oriented design, also known as Dependency injection, more specifically here it's Constructor Injection):

public GPIBInterface : DeviceInterface //here we are reusing code that's same for all interfaces
{
    //whoever attaches to this interface is responsible for registering itself with it
    public PowerSupply Power{get;set;}
    public DataLogger Logger{get;set;}
    //notice here that it can work with any class that inherits from DataLogger
    public GPIBInterface()
    {

    }

    private override void TestInitialization() //as an example, we write our own testing by overriding some method in base class
    {
        base.TestInitialization(); //make sure that the base class checks it's ok - e.g. if it's own display is working or similar...

        if (Power.TestConnection() == false)
            throw new DeviceNotWorkingException(ps);
        if (Logger.TestConnection() == false)
            throw new DeviceNotWorkingException(_logger);
    }

    public void SendLogMessage(string message)
    {
        Logger.SendMessage(message);
    }

    public void ExecuteTest(string testName)
    {
        switch(testName)
        {
            case "test1":
                Power.SetVoltage(280);
                Logger.SendMessage("Voltage increased to 280V");
                break;
        }
    }
}

Basically, if your devices interact with each other in 'real life', than it means they should have a reference to each other device they are connected to. For example, if you plugin PowerSupply directly to logger, than the PowerSupply class should have a reference to logger. But, if it's connected to interface, and then interface to logger, the PowerSupply must have only reference to interface and not the logger (since it's not connected to it).

I hope you get the idea - it's not called 'object oriented' for nothing - think of classes as real objects - if an object can do thing1, than you should have method 'thing1()' in you class; if the object has connection to object441, than the class should have a reference to that class. Just follow this principle and see where it takes you...

When you master that, your program would really benefit from some Inversion of Control (IoC) pattern - that way you just define at the beginning what is a logger, what is powerSupply, who is performing as a display, and whenever someone needs some specific device an IoC container would provide it. This way you don't have to rewrite code every time there's a new device in town - just register the new type with IoC container and everything just works.

veljkoz