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 c开发者_运维技巧lass 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
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.
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.
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.
精彩评论