Pointer to Device


andrewjfox

Recommended Posts

Is it possible to create a pointer to a device?

I have 4 ethernet devices, each using 2 ports (1515 & 1516) for different commands (don't even get me started on how frustrating this is)

Lets call these ports Control and Data, so I effectively have 8 devices,

Device.RdrCtrl01

Device.RdrData01

.

.

Device.RdrCtrl04

Device.RdrData04

To read & write I currently do;

Device.RdrCtrl01.Write

or

Device.RdrData01.Read

but, is it possible to create a pointer to the device like

global pDevice = Device.RdrCtrl01

or

global pDevice = evaluate("Device.RdrCtrl" + Format("%02d",Num))

then use that in scripts instead ie

pDevice.Write & pDevice.Read

Regards

Andrew

Link to comment
Share on other sites

Yes and no. First, in normal DAQFactory there are no real pointers. You can kind of simulate them using a string and evaluate() or execute(), so:

global string pDevice = "Device.RdrCtrl01"

then, for example, to write:

execute(pDevice + ".Write('test')")

That all said, you can dynamically create devices as objects and then they are pointers (because objects are always references). This is a more involved and only for those that like scripting. You won't have access to these devices except in their object form and not through the Device Configuration. There are three objects:

CCommEthernet

CCommSerial

CCommDevice

The first two correspond to the desired transport layer, Ethernet (TCP) or serial port. The last takes a reference to one of those ports and a protocol name, and then is simply a reference to a device and takes commands just like a normal device would. So for example:

global CommPort = new(CCommEthernet)

CommPort.Address = "10.0.0.10"

CommPort.Port = 502

CommPort.InitConnection()

global CommDevice = new(CCommDevice)

CommDevice.PortObject = CommPort

CommDevice.ProtocolName = "ModbusTCP"

You can determine valid protocol names by simply creating a device normally through Device Configuration, then doing:

? device.myDevice.protocolName

I have never tried it, but I'm guessing you could create a device using Device Configuration and then using the PortObject member variable, assign a dynamically created port:

device.myDevice.PortObject = CommPort

But I don't really see an advantage to this as once you do this, the Device Configuration won't work for that device since the dynamically created port won't appear in the list.

Link to comment
Share on other sites

I've been trying out the dynamic objects and have a query about the behaviour of the following code.

global CommPort[2]

CommPort[0] = new(CCommEthernet)

CommPort[0].Address = "192.168.1.203"
CommPort[0].Port = 1516

global CommDevice = new(CCommDevice)

CommDevice.PortObject = CommPort[0]
CommDevice.ProtocolName = "NULL Protocol"
CommDevice.InitComm()

The above code fails at last line, InitComm(), which I am assuming is because the PortObject is not set correctly using the indexed CommPort object, however the following code works ok

global CommPort = new(CCommEthernet)

CommPort.Address = "192.168.1.203"
CommPort.Port = 1516

private pPort[2]

pPort[0] = CommPort

global CommDevice = new(CCommDevice)

CommDevice.PortObject = pPort[0]
CommDevice.ProtocolName = "NULL Protocol"
CommDevice.InitComm()

Am I missing something? To me they're doing the same thing.

Regards

Link to comment
Share on other sites

This is kind of why for a long time OOP in DAQFactory remained undocumented. There are a few little quirks in it that can throw people if you don't know about it. However, since OOP is so useful for the more experienced user, and the quirks are easy to get around, I keep bringing it up in the forum. The two main quirks are:

1) when you have a member function and there is an error, the line number is from the beginning of the member function, not the beginning of the sequence. This means you have to manually count line numbers.

2) the system often does not like having objects in arrays. You can store them in arrays, but often when referencing it, you need to copy to a private variable and reference it that way. This usually only occurs when you have something after the subsetting (like Commport[0].address). So, for your code, this also works:

global CommPort[2]

CommPort[0] = new(CCommEthernet)

private z = commport[0]
z.Address = "192.168.1.203"
z.Port = 1516

global CommDevice = new(CCommDevice)

CommDevice.PortObject = commPort[0]
CommDevice.ProtocolName = "NULL Protocol"
CommDevice.InitComm()

See how I substituted z for commport[0] whenever I needed to reference a member variable (or function) of the arrayed element. When I simply needed to copy the reference (CommDevice.portObject = ) it was no issue.

Link to comment
Share on other sites

  • 2 years later...

I thinking somehow the following code should know which object to operate on when calling any of the functions in IOClass. For example when calling io.PointAdd(1, "Test") because it is the io object calling the function within the IOClass shouldn't the function know to use the point array of the io object without it having to be specified? A work around is the PointNew() function where the obj is passed in as a parameter, but isn't there a way to make the code aware it should be using the io object that called it?

 

 

class PointClass
   local string name = ""
   local addr = 0
   
   function OnCreate()
      ? "Point created"
   endfunction
   
endclass

class IOClass
   local state = 0
   local port = 1000
   local string protocol = "MyProtocol"
   local point // IO points array
   
   function PointAdd(addr, string name)
      private n = NumRows(io.point)
      private x
      
      io.point[n] = new(PointClass)
      x = io.point[n]
      x.addr = addr
      x.name = name
   endfunction
   
   function PointName(offset, string name)
      private x = io.point[offset]
      x.name = name
   endfunction
   
   function PointNew(obj, addr, string name)
      private n = NumRows(obj.point)
      private x
      
      obj.point[n] = new(PointClass)
      x = obj.point[n]
      x.addr = addr
      x.name = name
   endfunction
    
endclass


global io = new(IOClass)

Link to comment
Share on other sites

You are correct.  All those references to "io" or even "obj" in PointNew are unnecessary.  The problem is that "point" is reserved word in the global scope, thus the reason you get the error "Function Without Parenthesis".  That's the give away, that something is a function that you didn't put () after.  Point() is in 4.12.12 of the user's guide.  A good way to check if something is a reserved word is to simply go to the command / alert window and type:

 

? foo

 

If you get "Channel or function not found" then it doesn't exist in the global scope.  If you get anything else, like "function without parenthesis", or "'Value' found in expression" then you know its a reserved word.  This isn't perfect.  There are things in the global scope that you can end up overriding.  For example, if you did:

 

global channel = 5

 

you will basically override the meaning of "channel" and you will no longer have access to the internal channel object for things like channel.listall() and the like.  Likewise, you can do:

 

global if  = 5

 

and assign a value to the variable named "if".  This will actually work fine, but will make your code really confusing!  It won't effect the if() statement.

 

I should also add that you can write these functions a lot simpler by using append():

 

function pointNew(addr, string name)

   private p = new(PointClass)

   p.addr = addr

   p.name = name

   pointArray.append(p)

endfunction

 

Note also how I initialized the new pointclass object BEFORE adding it to the array.  This is the proper way to do it in a multithreaded environment, that way the point isn't in the array where it might be processed by another thread until after it already has its proper parameters.

 

Finally, you don't need this either:

 

x = obj.point[n]
x.addr = addr
x.name = name

 

you can just do:

 

obj.point[n].addr = addr

obj.point[n].name = name

 

The only time you need to assign it to a private variable is if you are going to create a situation where you have multiple [] or () in a single symbol:

 

obj.point[n].foo()

or:

obj.point[n].addr[2]

 

Those won't work, and you'd have to store a reference to obj.point[n] in a private, then use that reference instead.

Link to comment
Share on other sites

Archived

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