| Using VISA.NET > Formatted I/O > Using Printf > Printf Code Snippets > Formatting Enumerations |
VISA.NET Printf methods allow calling programs to convert enumeration members to strings with a bit of extra programming effort.
Although the .NET Framework allows programs to convert an enumeration value to a string that contains the enumeration member name, this is not typically useful for instrument communication. Instruments that use message-based communication typically use SCPI. SCPI and .NET naming conventions are very different, so it would not be unusual for a SCPI name and a .NET name to differ. We need a mechanism for converting from a .NET enumeration member to a SCPI name for that member.
Consider a simple SCPI command that sets a DMM measurement function:
MEASurement:FUNCtion {ACVolts|DCVolts|ACCurrent|DCCurrent|CONTinuity|RESistance}
Most programs use the abbreviated form of the commands. The abbreviated forms of the commands are the leading uppercase letters:
MEAS:FUNC {ACV|DCV|ACC|DCC|CONT|RES}
This command might be represented in a .NET program by the following enumeration:
| MeasurementFunction |
Copy Code |
|---|---|
public enum MeasurementFunction {
ACVolts = 0, DCVolts = 1, ACCurrent = 2, DCCurrent = 2, Continuity = 2, Resistance = 2, } | |
We want the .NET member names to map to the short SCPI string member names when formatting enumeration values:
|
.NET Name |
SCPI String |
|---|---|
|
ACVolts DCVolts ACCurrent DCCurrent Continuity Resistance |
ACV DCV ACC DCC CONT RES |
VISA.NET Formatted I/O gives you the ability to define a type formatter class that does exactly this.
Since enumerations are typically specific to the calling program, we need to provide the actual mapping for the values. Since the IVI Formatted I/O class uses the mapping to format and read enumeration values, the calling program needs to provide the mapping information in a form that the Formatted I/O class can understand. To accomplish this, the IVI VISA.NET components include an interface, ITypeFormatter, that formalizes the communication between the calling program and Formatted I/O.
At a high level, using Formatted I/O with type formatting works like this:
The calling program creates a class (let's call it TypeMapper) that is capable of mapping the members of one or more enumerations to strings. This class exposes the ITypeFormatter interface.
The calling program then registers the TypeMapper class with Formatted I/O using the IMessageBasedFormattedIO.TypeFormatter property.
When Printf needs to format an enumeration value as a string, it calls the ITypeFormatter.ToString method implemented in TypeMapper, which returns the string that corresponds to the specified enumeration value.
When Scanf needs to read a string and convert it to an enumeration member value, it calls the ITypeFormatter.Parse method implemented in TypeMapper, which returns the enumeration value that corresponds to the specified string.
Keep in mind the following caveats:
Type formatting is not restricted to enumerations. You can format any type using ITypeFormatter, as long as every value allowed for the type can be mapped to a string. The mapping must be one-to-one. For example, you can use the type formatter to format Booleans to their corresponding instrument-specific strings (TRUe and FALSe).
This mechanism only works with a string (%s) format specifier. The argument that corresponds to the string format specifier must be a valid value for the supported type.
The TypeMapper class may provide mappings for any number of types. Both the calling program and Formatted I/O may query the TypeMapper class using the ITypeFormatter.IsSupported method to see if a particular type is supported. Formatted I/O will only use the type formatter to format the type if IsSupported returns true for that type.
You may register only one type formatter class with a Formatted I/O session. This is somewhat restricting, as you may need to modify this class as the calling program evolves over time. However, as each session has its own type formatter, type formatters may generally be device-specific.
A type formatter class must be registered with every session that needs to use it.
Type formatter classes are only used with message-based Formatted I/O. There is not an equivalent feature for register-based sessions (such as PXI and VXI sessions).
| Sample Type Formatter |
Copy Code |
|---|---|
|
public enum MeasurementFunction { ACVolts = 0, DCVolts = 1, ACCurrent = 2, DCCurrent = 2, Continuity = 2, Resistance = 2, }
public class TypeMapper : Ivi.Visa.ITypeFormatter { public Boolean IsSupported(Type type); { switch (type) { case MeasurementFunction: return true; break; default: return false; } }
public String ToString(Object obj); { Type type = obj.GetType(); switch (type) { case MeasurementFunction: switch ((MeasurementFunction)obj) { case MeasurementFunction.ACVolts: return "ACV"; break; case MeasurementFunction.ACCurrent: return "ACC"; break; case MeasurementFunction.DCVolts: return "DCV"; break; case MeasurementFunction.DCCurrent: return "DCC"; break; case MeasurementFunction.Continuity: return "CONT"; break; case MeasurementFunction.Resistance: return "RES"; break; } break; default: throw new System.ArgumentException("Type {0} not supported", type.ToString()); } }
public Object Parse(Type type, String data); { switch (type) { case MeasurementFunction: switch (data) { case "ACV": return MeasurementFunction.ACVolts; break; case "ACC": return MeasurementFunction.ACCurrent; break; case "DCV": return MeasurementFunction.DCVolts; break; case "DCC": return MeasurementFunction.DCCurrent; break; case "CONT": return MeasurementFunction.Continuity; break; case "RES": return MeasurementFunction.Resistance; break; default: throw new System.ArgumentException("Value {0} not supported for type {1}", data, type.ToString()); } break; default: throw new System.ArgumentException("Type {0} not supported", type.ToString()); } } } | |
Remember that Printf is adding bytes to the formatted write buffer. In this case, Printf is converting enumeration values to strings and adding the string to the buffer. Assume that the io variable is a valid reference to IMessageBasedFormattedIO.
| Using the Type Formatter |
Copy Code |
|---|---|
|
TypeMapper mapper = new TypeMapper(); io.TypeFormatter = (ITypeMapper)mapper;
// Add |MEAS:FUNC CONT| to the buffer. io.Printf("MEAS:FUNC %s", MeasurementFunction.Continuity); | |