Thursday, May 26, 2011

Packaging an external library as a module

As I've been working with programming modules for Ignition, I've needed to use certain libraries along with my projects.

One way to do this: simply include the library's jars in your .modl file, and include them in your module.xml. This will make the library accessible for your module's classes' classloader.

What if the library is very large, though? This makes development take longer, since every module load requires reloading the entire library.

The solution I came up with was to package the library in its own module.

Steps
1) Grab the library's .jar files, and throw them in a directory.
2) Create a module.xml, like what I have below.
3) Package these in a .zip file, and change the extention to .modl

My library's module.xml:
<?xml version="1.0" encoding="UTF-8"?>
<modules>
 <module>
  <id>ApacheCXF</id>
  <name>Apache CXF</name>
  <description>The full Apache CXF 2.4.0 library and supporting libraries.</description>
  <version>2.4.0.1</version>
  <requiredignitionversion>7.2.5.76</requiredignitionversion>
  <requiredframeworkversion>2</requiredframeworkversion>
  <freemodule>true</freemodule>
  
  <!-- All the Apache CXF and CXF support libraries -->
  
  <jar scope="DG">antlr-2.7.7.jar</jar>
  <jar scope="DG">aopalliance-1.0.jar</jar>
  <jar scope="DG">asm-3.3.jar</jar>
                ...
 </module>
</modules>

Then, in your main module's module.xml, include this module as a dependency.

My main module.xml:
<?xml version="1.0" encoding="UTF-8"?>
<modules>
 <module>
  <id>myawesomemodule</id>
  <name>@NAME@</name>
  <description>@DESCRIPTION@</description>
  <version>@VERSION@</version>
  <requiredignitionversion>7.2.5.76</requiredignitionversion>
  <requiredframeworkversion>2</requiredframeworkversion>
  <freemodule>false</freemodule>

  <depends scope="G">ApacheCXF</depends>
  <depends scope="D">ApacheCXF</depends>

  ...

 </module>
</modules>

Note: A module can't have two dependencies. If your module already depends on fpmi, you'll need to move that dependency to the library module, and have your module just depend on the library module. So: "Your module" ->(depends)-> "Library module" ->(depends)->"fpmi"

Note 2: Your module will still load, even if it's missing dependencies. In your module's start up, make sure to check for the library's presence, and fault your module if it's not there.

Ignition Version: 7.2 (7.2.5)

Thursday, May 19, 2011

SQLTags DataType casting

One interesting feature when programming with SQLTags is that you can store just about any type of object you want within a tag.  Want a tag that stores a List?  Go for it!  Want to store a 3d model of your car?  Why not.

But hold on, maestro.  When Ignition gets a tag's value, it expects it in a certain form, so you actually can't just hand it your photo of Jennifer Aniston.

The Tag interface defines a few basic methods.  The ones we care about are:

public TagValue getValue()
public DataType getDataType()

TagValue (and its simple implementation BasicTagValue) contains this:

public Object getValue()

This method deceptively doesn't really want you to store an Object.  It's looking for that object to be a certain data type.  That data type is defined back up in your Tag object, not in your TagValue object.

In order to make sure your object conforms, a convenience method exists.

DataType.getTypeForClass(object.getClass());

If this method returns null, you know your object doesn't conform to any known data type.

If it does conform, why not create a new tag with that type?

BasicTagValue tagValue = BasicTagValue.fromObject(object);
BasicTag tag = new BasicTag(tagValue, DataType.getTypeForClass(object));

Note: make sure you use the correct DataType for sqltags:

com.inductiveautomation.ignition.common.sqltags.model.types.DataType

But wait! I actually *do* want to store a dancing baby in a tag, you say? Well, all you need to do is subclass BasicTagValue. Of course, you won't be able to hand Ignition's SQLTags browser the groovin picture, but you'll know it's there.

public class AnimationTagValue extends BasicTagValue {
 /** For serialization ** /
 @Deprecated
 public AnimationTagValue() {super();}

 public AnimationTagValue(AnimatedGif animation) {
  super(animation);
 }

 @Override
 public Object getValue() {
  AnimatedGif animation = (AnimatedGif) object;
  return animation.getNumberOfFrames();
 }

 public AnimatedGif getAnimation() {
  return (AnimatedGif) object;
 }
}

And while I suppose it would be possible to write a component for Vision that would display this data, it's really a bad idea. You don't want pictures of cats flying across your network and clogging up the tubes.  At least, not unless you've got mice in there.

Until next time.


Ignition Version: 7.2 (7.2.5)