Click or drag to resize

Understanding Nimbus Code Weaving

Nimbus-based IVI.NET drivers utilize a powerful technology referred to as code weaving. In Nimbus, code weaving is characterized by the following.

  • Attributes defined in the Nimbus Class Library are applied to method, property and event declarations. These attributes express one or more desired behaviors that the driver developer would like the Nimbus code weaver to supply automatically. Examples of code weaver supplied behaviors include, but are not limited to, simulation (SimulationAttribute), caching (CacheAttribute), and range checking (RangeCheckMinMaxAttribute, RangeCheckDiscreteAttribute).

  • As you use the Nimbus driver designer to enable, disable and configure various options (simulation, range checking, etc), the designer adds, removes and updates code weaver attributes in the source code automatically.

  • When the driver project is built, the compiler produces a driver assembly (for example, Acme.Acme4321.Fx20.dll). The desired behaviors expressed as attributes in the source code are encoded by the compiler as-is in the resulting binary, and are available for use by the Nimbus code weaver.

  • After the compiler builds the driver assembly, the Nimbus code weaver runs, analyzing all of the attributes applied to driver methods, properties and events. Based on the desired behaviors expressed by attributes, the code weaver rewrites the implementations of driver methods, properties and events so that each is augmented with the additional behaviors expressed by the attributes that were applied.

    Note Note

    Nimbus code weaving does not use reflection to provide the requested behaviors at runtime. Instead, the compiler-produced implementation of each driver method, property and event is rewritten at build time so that it includes the original developer implementation as well as the code weaver-supplied behaviors. This means that driver methods, properties and events have the identical performance characteristics as methods, properties and events that are not processed by the code weaver and instead perform the same operations (simulation, range checking, etc) manually using developer-supplied code.

  • Code weaver attributes can only be applied to instance (not static) properties, methods and events defined on the following:

    • Classes that derive from the DriverNode class, or that implement the IDriverNode interface.

    • Structures that implement the IDriverNode interface.

    The Nimbus driver designer handles this automatically for any code it adds to your driver.

  • Nimbus code weaving is enabled by default for the driver project, and any method, property or event created by the designer. However, code weaving may be disabled for the entire driver project, or disabled selectively for individual methods, properties or events.

Consider the simple driver property shown below.

C#
[DriverProperty]
public double Frequency
{
    get
    {
        this.IO.FormattedIO.PrintfAndFlush("FREQ:RANG\n");
        double value;
        this.IO.FormattedIO.Scanf("%g", out value);

        return value;
    }
    set
    {
        this.IO.FormattedIO.PrintfAndFlush("FREQ:RANG %0.15g\n", value);
    }
}

If code weaving is disabled for the driver, then viewing the disassembled code in the driver assembly using a tool such as .NET Reflector would look as shown below in Figure 1.

Figure 1: A Driver Property with Code Weaving Disabled

Note that the disassembled view of the property's binary implementation looks nearly identical to the original source code. However, if code weaving is enabled (which is the default), then the disassembled code as viewed using .NET Reflector would look as shown below in Figure 2.

Figure 2: A Driver Property with Code Weaving Enabled

Note that in addition to the recognizable developer-supplied code, the Nimbus code weaver has injected a call to an AcquireLock method, which is a Nimbus Class Library method that provides standard IVI.NET-defined thread synchronization. The lock acquired by that method is automatically released at the conclusion of the using construct shown in Figure 2, where its implementation of IDisposable.Dispose is called.

Note that in addition to providing IVI.NET-standard thread synchronization, the code weaver has also injected a call to CheckDisposed, which implements the .NET standard idiom of checking to see if the driver object is being used after its own IDisposable.Dispose method has been called.

If the Nimbus driver designer is now used to enable simulation for this property (specifying a default value in simulation mode equal to 50), the source code would automatically be updated by the designer to look like this:

C#
[DriverProperty]
[Simulation(SimulationMode.LastValue, Default = "5.000000E+001")]
public double Frequency
{
    get
    {
        this.IO.FormattedIO.PrintfAndFlush("FREQ:RANG\n");
        double value;
        this.IO.FormattedIO.Scanf("%g", out value);

        return value;
    }
    set
    {
        this.IO.FormattedIO.PrintfAndFlush("FREQ:RANG %0.15g\n", value);
    }
}

When the driver is rebuilt, .NET Reflector would reveal the implementation of the property to be similar to what's shown in Figure 3.

Figure 3: A Driver Property with Simulation Enabled

Note that in addition to the standard driver property preamble, the code weaver injected a call to the this.Session.InitOptions.SimulationEnabled property (to see if simulation has been enabled via the driver's setup option) into both the property getter and setter. If simulation is enabled, and because the simulation style requested via the attribute is SimulationMode.LastValue, the code weaver modified the getter to return the previously cached value (if any) or the requested default value of 50. Likewise, the property setter was modified to cache any new property values passed to the setter.

Finally, if the Nimbus driver designer is now used to enable range checking of values being passed to the setter to enforce that values must be between 0 (exclusive) and 300 (inclusive), the source code would automatically be updated by the designer to look like this:

C#
[DriverProperty]
[Simulation(SimulationMode.LastValue, Default = "5.000000E+001")]
[RangeCheckMinMax(0, 300, MaxInclusive = true)]
public double Frequency
{
    get
    {
        this.IO.FormattedIO.PrintfAndFlush("FREQ:RANG\n");
        double value;
        this.IO.FormattedIO.Scanf("%g", out value);

        return value;
    }
    set
    {
        this.IO.FormattedIO.PrintfAndFlush("FREQ:RANG %0.15g\n", value);
    }
}

When the driver is rebuilt, .NET Reflector would reveal the implementation of the property to be similar to what's shown in Figure 4.

Figure 4: A Driver Property with Simulation and Range Checking Enabled

With this modification in place, the Nimbus code weaver has now also modified the property setter to check whether range checking has been enabled (this.Session.InitOptions.RangeCheckingEnabled) and, if so, use the ErrorService class to throw an IVI.NET standard exception if the value passed into the setter was out of range.

Note Note

In this case, note that the Nimbus code weaver used the minimum and maximum values indicated by the RangeCheckMinMaxAttribute attribute to directly inject the requested range checking behavior without the need for additional method calls.

The topics listed below provide additional information on working with the Nimbus code weaver.

In This Section
See Also

Download a complete CHM version of this documentation here.