DAQFactory 5.85 New Features


AzeoTech

Recommended Posts

DAQFactory 5.85 includes several new and useful features. In this topic we'll talk about them in some detail, including how to use them:

Network Encryption: because of export regulations and because DAQFactory networking was assumed to be used behind firewalls only (for outside firewall communication, we recommend DAQConnect.com with DAQFactory), DAQFactory networking was not encrypted. However, it as the Internet has grown more prevalent in the last 10 years, hackers are finding their way into SCADA systems through business networks and other creative ways and so DAQFactory now encrypts the traffic between instances of DAQFactory. Because of U.S. export regulations, the level of encryption is determined by your location, and more specifically your Windows installation. Use of encryption is automatic, but here are a few good points:

1) if you are using Express, Starter, Lite, or Base and no Runtimes, there is no networking, so skip to the next feature
2) if you are using Standard, Pro or Developer, or using a Runtime created with any version, but not using the DAQFactory to DAQFactory networking, you should simply disable networking by going to File - Document Settings and check the box that says "Disable Broadcasting". All new documents will have this disabled by default.
3) if you are using DAQFactory networking, you should make sure and provide a password for both the slim and full streams. The password is used as part of the encryption key. If you don't provide a password, then your data is encrypted using the same key as everyone else who leaves the password blank, largely defeating the purpose of the encryption.
4) DAQFactory supports remote reboot / shutdown capabilities triggered through DAQFactory networking. If you aren't using it, disable it by going to File - Document Settings. All new documents will have this disabled by default.

On to the more fun stuff:

Channel.read() and channel.ReadGroup(): there are lots of times where you want to be able to read a bunch of I/O points from script instead of using the Timing parameters of channels. To do this, you either needed to call the read() function for each channel you wanted, which was slow and didn't allow for query optimization that protocols like Modbus can do to combine multiple requests into a single query, or you had to use raw device.mydevice.xxx() functions and then deal with the result, usually stuffing the data into channels using addValue().

With Channel.Read(), you can trigger the read of a list of channels (in an array of strings) the same way read() and normal channel Timing do. This allows for optimization and automatically puts the data into the channels, applies conversions, etc. For example, if you wanted to read ChanA and ChanB, the command would be:
 

Channel.Read({"ChanA", "ChanB"})

Of course the array of channels could be a variable.

Channel.ReadGroup() offers the same benefits, but which channels get read is determined by the Group assigned to the channel. So:
 

Channel.ReadGroup("myGroup")

would read all the channels associated with "myGroup". Channel groups are one of the parameters of channels and the last column in the channel table. This method is often more convenient because you can change what channels get read from the channel table and not have to delve into your code.

Whichever method you use, make sure that you don't specify and output channels, as "reading" an output channel will typically set the output to 0.

Push() and Pop(): these are member functions of variables. For example:
 

myVar.push(3)

will "push" 3 into the [0] position of the variable, moving anything already there back. This is largely the same as addValue().
 

myVar.pop()

will return the value in the [0] position (or empty if nothing is there) and then shift everything so [0] is removed.

An example of how this might be used is for page navigation. Instead of statically defining all your page moves with:
 

page.strCurrentPage = "xxx"

create a global string variable to hold a page stack:
 

global string pageStack

then whenever you are drilling down in pages (i.e. going from an overview page to detail, to more detail), do something like this:
 

pageStack.push(page.strCurrentPage)
page.strCurrentPage = "drillDown"

Then, in your drill down page, to move back up to a more general page you can do:
 

page.strCurrentPage = pageStack.pop()

This is especially useful if drill down pages are accessible from more than one overview page.

system.Minimize() / Maximize(): these functions allow you, from script, to minimize or maximize the DAQFactory window. They are pretty straightforward...

JSON support: For those taking advantage of the OOP features of DAQFactory, or if you are looking to be able to store structured data and persist that data to disk, DAQFactory now supports JSONification (if you want to call it that) of data structures into strings. So, you can take an object (and any member variables containing objects or arrays of objects, etc) and convert it into a string representation that can be written to disk using the standard File. functions. You can then load that string and recreate the object hierarchy dynamically. In addition to persisting to disk, this makes for an easy way to duplicate objects. I think this is best demonstrated with an example:
 

clearGlobals()

class a
   local y = 4
   local string z = "def"
   local transient w
endclass

class b
   local string m = 67
   local n
   local string transient s = "abc"

   function OnCreate()
      n[0] = new(a)
      n[1] = new(a)
      n[1].y = 6
      n[1].z = "ghi"
   endfunction
endclass

global o = new(B)

private string json = o.toJson()
? json

global oo = fromJson(json)

Here we create two class types, "a" and "b". "a" simply contains 3 member variables. One is marked "transient" which means it is not processed by the JSON stringifying routine. Transient variables would be ones that are used internally in the object and don't need to be saved, or included when an object is duplicated. "b" also just contains 3 variables, but the member variable "n" is initialized to contain an array of two instances of "a".

We then instantiate "b" and store it in a global variable. Then we use the toJson() function, common to all DAQFactory objects, to convert b into a json string representation. Since its just a string, we can store it in a variable, print (?) it to the Command / Alert window, or write it to disk.

Next, we call the global fromJson() function, passing that string, to instantiate a new "b" object. This new object will contain the exact same data as the first one, except for any transient variables. This includes the array of objects contained in the "n" member variable. This demonstrates simple object duplication. For object persistance, all we'd need to do is write our private "json" variable to disk, and then on startup, load it from disk and call the fromJson() function with it.

There is also a fromJson() member function of all objects. This basically works the same as the global versions, but the top level object is specified by you and not the json string. So:
 

global oo = fromJson(json)

and
 

global oo = new(B)
oo.fromJson(json)

are essentially the same. The reason for using the second form would be if you wanted to copy data from one object type to another that has the same member variables but perhaps different functionality.

Link to comment
Share on other sites

  • 2 years later...

(I'm using 5.87) 

Is "local transient string" broken? I can't seem to get it to work at all; on the other hand, "local transient" works fine.

 

For example, append either of these two lines to the "Json support" example above:

?o.s  //produces "C1000 Channel or function not found", expected "abc"
?oo.s //produces "C1000 Channel or function not found", expected "abc"

Thanks

Matt

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.