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.