Home AMX User Forum NetLinx Studio

Buffer SEND_COMMAND/STRING between virtuals?

It's common practice, if not a necessity, to buffer any string from an external serial or IP device. I was just working on a module when I found myself absent-mindedly setting up buffering for strings coming in from a virtual device on another master when I thought, "That's silly, there's NEVER a need to buffer these strings." Of course those hyperboles always sends a little red flag up in my mind. Never is never never.

Though I've never experienced an issue sending commands or strings between virtual devices, multi-master or otherwise, I'm curious if anyone has ever found it beneficial. Have you ever had a virtual to virtual command or string arrive in 2 parts? If so, when and why?

Thanks!

Comments

  • ericmedleyericmedley Posts: 4,177
    I've found that as long as its AMX to AMX it's usually not a problem. (that's assuming the string size is relatively small...)

    For everything else, I make a buffer and queue so I can tweak the timing and performance.
  • AuserAuser Posts: 506
    Have you ever had a virtual to virtual command or string arrive in 2 parts? If so, when and why?

    The simple answer is no, only one data event is generated for send_command and send_string between the program and virtual devices.

    The more complete answer is that the text parameter for data events has a fixed size and if you try sending more information in one send_string/send_command than Data.Text can make available to the data event, the information you receive will be truncated. I couldn't find documented reference to this size (I'm sure its in the NetLinx programmers guide or similar) but it is likely to be 2048 characters, as implied by NetLinx.axi below:
    STRUCTURE TDATA
    {
      DEV      DEVICE;
      LONG     NUMBER;
      CHAR     TEXT[2048];
      CHAR     SOURCEIP[32];
      INTEGER  SOURCEPORT;
      DEV      SOURCEDEV;
    }
    

    If you need to send bulk data using send_command/string to a virtual device you'll need to chop it up and send it in chunks.

    On the odd occasions when I need to do this I use an ASCII STX character to mark the start of the bulk data and an ETX character to denote the end of the data so that whatever is using the information knows the transmission has completed and processing can begin.
  • I've figured as much.

    As far as the max is concerned, you make a good point about the 2048 limit. If you look at device status:
    >Device Status 33001:1
    Device Status
    -------------
    Device 33001 AMX LLC,Virtual,v3.60.453 contains 1 Ports.
     Port     1 - Channels:255 Levels:8
                  MaxStringLen=2000 Types=8 bit  MaxCommandLen=2000 Types=8 bit
                  The following input channels are on:None
                  The following output channels are on:1,91
                  The following feedback channels are on:1,91
                  Level 1=0 Supported data types=UByte,SByte,UInt,SInt,ULong,SLong,Float,Double
                  Level 2=0 Supported data types=UByte,SByte,UInt,SInt,ULong,SLong,Float,Double
                  Level 3=0 Supported data types=UByte,SByte,UInt,SInt,ULong,SLong,Float,Double
                  Level 4=0 Supported data types=UByte,SByte,UInt,SInt,ULong,SLong,Float,Double
                  Level 5=0 Supported data types=UByte,SByte,UInt,SInt,ULong,SLong,Float,Double
                  Level 6=0 Supported data types=UByte,SByte,UInt,SInt,ULong,SLong,Float,Double
                  Level 7=0 Supported data types=UByte,SByte,UInt,SInt,ULong,SLong,Float,Double
                  Level 8=0 Supported data types=UByte,SByte,UInt,SInt,ULong,SLong,Float,Double
    
    >Device Status 5001:1
    Device Status
    -------------
    Device 5001 AMX LLC,NI-3100,v1.30.8 contains 17 Ports.
     Port     1 - Channels:255 Levels:8
                  MaxStringLen=64 Types=8 bit  MaxCommandLen=64 Types=8 bit
                  The following input channels are on:None
                  The following output channels are on:None
                  The following feedback channels are on:None
                  Level 1=0 Supported data types=UByte,UInt
                  Level 2=0 Supported data types=UByte,UInt
                  Level 3=0 Supported data types=UByte,UInt
                  Level 4=0 Supported data types=UByte,UInt
                  Level 5=0 Supported data types=UByte,UInt
                  Level 6=0 Supported data types=UByte,UInt
                  Level 7=0 Supported data types=UByte,UInt
                  Level 8=0 Supported data types=UByte,UInt
    
    it lists MaxStringLen and MaxCommandLen for the virutal (33001:1) at 2000 but the serial port (5001:1) at only 64. Grant it the 2000 is 48 bytes shorter than Netlinx's purported 2048, but why be greedy...
  • viningvining Posts: 4,368
    I have a note that says the max string from/to a virtual device is 31212. I assume I got that from an AMXer here on the forum but that's obvious in a created buffer otherwise I think the max size of a string expression is the 15999 (16K) limit for a var. This $hit still confuses me!

    Of course then there's the data.text limit of 2048 that Auser just mentioned and if the string is longer it get's truncated unless you handle breaking up the string in code and sending it in chunks in which case you'll have multiple data.text's before you receive the complete string.

    Possibly with M2M you might get a 2 part data.text of a string under the data.text limit if the string is over 1500 bytes since the M2M communicate via ethernet and the masters should handle the ethernet packets and transmit no more thant the max MTU (ethernet = 1500) at a time but it will send the complete string. The truncating occurs on the receive side due the data.text limit.
  • Everybody is right! :)

    Check out technote 886:
    "The maximum length of a string expression [in] NetLinx is 15999 bytes"
    ...
    "The default length of a string expression passed into a subroutine is 2048 bytes"
    ...
    "The maximum size of a SEND_STRING to a virtual device on the same master is 31212 bytes when the CHAR array sent is not enclosed in double-quotes."
    ...
    [and more]
  • AuserAuser Posts: 506
    Everybody wins! Kinda...
  • jjamesjjames Posts: 2,908
    Auser wrote: »
    If you need to send bulk data using send_command/string to a virtual device you'll need to chop it up and send it in chunks.

    Something I've been doing lately if I need to share large chunks of data between modules is to use a shared variable. For example, for my weather module, I do a string_to_variable, and then pulse a channel to tell the responding module that it's ready to be converted to a structure.

    Why, or, when is this helpful? You can't pass structures to modules, but you can pass variables - and they're all passed by reference. So, within my main code I would have a large char var (ex. volatile char s_enc[65535]) and pass that to both a comm and to the ui modules. The comm module populates a stack_var of the structure, then encodes it to the string, pulses a channel at which point the UI module takes that same s_enc variable that was pass in as reference, and uses string_to_variable() to populate a global structure. I see no reason why if you're transferring large forms of data, that you couldn't use the same transfer mechanism.
  • viningvining Posts: 4,368
    I do something similar but pass the structure via send_command/string. Providing it will be under the 31212 limit and I use a buffer not data.text on the recieve side.
    DEFINE_FUNCTION SLONG fnSitrepQueMSG(CHAR iTask[],CHAR iFrom[],CHAR iURL[],CHAR iUser[],CHAR iPass[],CHAR iSubject[],CHAR iBody[],CHAR iFile[])
    
         {
         STACK_VAR _sSitrepMsg sSitrepMsg ;
         STACK_VAR CHAR cReturn[8192] ;
         
         sSitrepMsg.cTask   	= iTask ;
         sSitrepMsg.cFrom   	= iFrom ;
         sSitrepMsg.cURL      	= iURL ;
         sSitrepMsg.cUser		= iUser ;
         sSitrepMsg.cPass     	= iPass ;
         sSitrepMsg.cSubj    	= iSubject ;
         sSitrepMsg.cBody     	= iBody ;
         sSitrepMsg.cFile		= iFile ;
         sSitrepMsg.cTime     	= '' ; //TIME ; not used, why bother//set on the other side
         sSitrepMsg.cLDate    	= '' ; //LDATE ; 
    
         cReturn = fnSitrepMod_VarToStr(sSitrepMsg) ;
         if(length_string(cReturn))
    	  {
    	  SEND_STRING vSitrep_Client,"1,cReturn" ;
    	  }
         }
    
    on the recceiving end (main)
     STRING: 
    	  {
    	  if(cSitRep_vBuff[1] == 1)
    	       {
    	       STACK_VAR _sSitrepMsg sSitrepModMsg ;
    	       STACK_VAR SINTEGER nResult ;
    	       STACK_VAR INTEGER nLenBuf ;
    	       STACK_VAR LONG nPos ;
    	       
    	       GET_BUFFER_CHAR(cSitRep_vBuff) ;
    	       nLenBuf = length_string(cSitRep_vBuff) ;
    	     
    	       nPos = 1 ;
    	       nResult = STRING_TO_VARIABLE(sSitrepModMsg,cSitRep_vBuff,nPos) ;
    	       if(nResult == 0)
    		    {
    

    in this snippet I'm send my structure back to the main code hence the "send_string". I use "1" as my first byte so in the main code I can take that byte and determine if the remaing string needs to be decoded (string to var).

    If I have a large attachement to pass back I just write that to file and pass the file name back and if the .cFile has a value the main will know there's an attachment to retrieve. The file name is generic to the module so module instance 1 would be SITREP_Mod_1.txt.
  • jjamesjjames Posts: 2,908
    vining wrote: »
    I do something similar but pass the structure via send_command/string. Providing it will be under the 31212 limit and I use a buffer not data.text on the recieve side.
    Good one - didn't think of that. To be honest, I was unaware of the 31,212 limit.
Sign In or Register to comment.