Home AMX User Forum AMXForums Archive Threads Tips and Tricks

Queuing Methods

I saw the queuing method posted in a recent technote and I was not happy to see all of the string manipulation. I think that string operations are some of the most processor intensive commands, so I decided to test the performance of the provided queue versus the two queues I have written. Here are the results:
Line      1 (10:58:20.046)::  *********************************************************
Line      2 (10:58:20.062)::  * TEST 1 REPORT: Load String Queue
Line      3 (10:58:20.062)::  * Most recent 5 runs:
Line      4 (10:58:20.062)::  * 1: 4542ms
Line      5 (10:58:20.062)::  * 2: 4562ms
Line      6 (10:58:20.062)::  * 3: 5252ms
Line      7 (10:58:20.062)::  * 4: 4544ms
Line      8 (10:58:20.062)::  * 5: 4542ms
Line      9 (10:58:20.062)::  *----------------------------------------------------------
Line     10 (10:58:20.062)::  * Average run time: 4687ms - over 5 tests
Line     11 (10:58:20.062)::  *********************************************************
Line     12 (10:58:20.062)::  *********************************************************
Line     13 (10:58:20.062)::  * TEST 2 REPORT: Load Structure Queue, no tracking
Line     14 (10:58:20.062)::  * Most recent 5 runs:
Line     15 (10:58:20.062)::  * 1: 199ms
Line     16 (10:58:20.062)::  * 2: 229ms
Line     17 (10:58:20.062)::  * 3: 227ms
Line     18 (10:58:20.062)::  * 4: 198ms
Line     19 (10:58:20.062)::  * 5: 196ms
Line     20 (10:58:20.062)::  *----------------------------------------------------------
Line     21 (10:58:20.062)::  * Average run time: 209ms - over 5 tests
Line     22 (10:58:20.078)::  *********************************************************
Line     23 (10:58:20.078)::  *********************************************************
Line     24 (10:58:20.078)::  * TEST 3 REPORT: Load Structure Queue, last command tracking
Line     25 (10:58:20.078)::  * Most recent 5 runs:
Line     26 (10:58:20.078)::  * 1: 206ms
Line     27 (10:58:20.078)::  * 2: 241ms
Line     28 (10:58:20.078)::  * 3: 238ms
Line     29 (10:58:20.078)::  * 4: 207ms
Line     30 (10:58:20.078)::  * 5: 204ms
Line     31 (10:58:20.078)::  *----------------------------------------------------------
Line     32 (10:58:20.078)::  * Average run time: 218ms - over 5 tests
Line     33 (10:58:20.078)::  *********************************************************
Line     34 (10:58:20.078)::  *********************************************************
Line     35 (10:58:20.078)::  * TEST 4 REPORT: Send String Queue
Line     36 (10:58:20.078)::  * Most recent 5 runs:
Line     37 (10:58:20.078)::  * 1: 3232ms
Line     38 (10:58:20.078)::  * 2: 3299ms
Line     39 (10:58:20.078)::  * 3: 3687ms
Line     40 (10:58:20.093)::  * 4: 3234ms
Line     41 (10:58:20.093)::  * 5: 3232ms
Line     42 (10:58:20.093)::  *----------------------------------------------------------
Line     43 (10:58:20.093)::  * Average run time: 3336ms - over 5 tests
Line     44 (10:58:20.093)::  *********************************************************
Line     45 (10:58:20.093)::  *********************************************************
Line     46 (10:58:20.093)::  * TEST 5 REPORT: Send Structure Queue, no tracking
Line     47 (10:58:20.093)::  * Most recent 5 runs:
Line     48 (10:58:20.093)::  * 1: 257ms
Line     49 (10:58:20.093)::  * 2: 304ms
Line     50 (10:58:20.093)::  * 3: 269ms
Line     51 (10:58:20.093)::  * 4: 256ms
Line     52 (10:58:20.093)::  * 5: 257ms
Line     53 (10:58:20.093)::  *----------------------------------------------------------
Line     54 (10:58:20.093)::  * Average run time: 268ms - over 5 tests
Line     55 (10:58:20.093)::  *********************************************************
Line     56 (10:58:20.093)::  *********************************************************
Line     57 (10:58:20.093)::  * TEST 6 REPORT: Send Structure Queue, last command tracking
Line     58 (10:58:20.109)::  * Most recent 5 runs:
Line     59 (10:58:20.109)::  * 1: 233ms
Line     60 (10:58:20.109)::  * 2: 253ms
Line     61 (10:58:20.109)::  * 3: 232ms
Line     62 (10:58:20.109)::  * 4: 233ms
Line     63 (10:58:20.109)::  * 5: 234ms
Line     64 (10:58:20.109)::  *----------------------------------------------------------
Line     65 (10:58:20.109)::  * Average run time: 236ms - over 5 tests
Line     66 (10:58:20.109)::  *********************************************************

The two queues I use are storing the data in a structure (or it could be an array depending on needs) and using two pointers one that indicates insertion point and one the indicates current command to send. You can see that using pointers is WAY faster than string concatenation.

I also found one other problem with the queue provided by AMX. They have a limit of 50000 chars for queuing, but netlinx concatenation falls apart at 16000 chars. If you are using 200 character commands (think to a touch panel more than to a 232 device), then you will hit the problem area at around 79 commands being queued. Be aware of this limitation when using the queue they provide.

Jeff

Comments

  • viningvining Posts: 4,368
    But if you're using a structure or array for your queue how much overhead is there to shift the contents of the structure or array up one index position every time you send the contents of index postion 1. Assuming you using a FIFO type of queue after you send index position 1 you now need to move positon 2 to 1, 3 to 2, 4 to 3 and so on. It may be quicker using your method to load the queue but probably slower to process the queue. What if you did a test and fired out the contents of the queue as fast a possible, from a string queue and from a content shifting array queue and see what happens. Not that any one is ever likely to send out queued contents as fast as possible, usually timing is a primary reason for queing in the first place.

    Plus loading the queue is usually event related, push a button or a string_event and you load a command into queue and possibly a follow up query. So how fast does queue loading really need to be?
  • PhreaKPhreaK Posts: 966
    vining wrote: »
    But if you're using a structure or array for your queue how much overhead is there to shift the contents of the structure or array up one index position every time you send the contents of index postion 1. Assuming you using a FIFO type of queue after you send index position 1 you now need to move positon 2 to 1, 3 to 2, 4 to 3 and so on. It may be quicker using your method to load the queue but probably slower to process the queue.

    The easiest way to get around that is what I believe Spire_Jeff was getting at. Rather than continuously shifting the queue upwards so that it is always firing out the command thats sitting in index 1 on the array use two pointers. One which tracking the next available position to fill and one which point to the next position to fire out. That way you can create something along the lines of:
    STRUCTURE _sDeviceQueue
    {
        CHAR cCommand[50][32]
        INTEGER nOut
        INTEGER nIn
    }
    

    Then use a timeline to fire out the commands at an interval the device likes. When a command is fired out increment the out pointer and if it is the same as the in pointer set them both back to 1.
  • viningvining Posts: 4,368
    PhreaK wrote:
    The easiest way to get around that is what I believe Spire_Jeff was getting at. Rather than continuously shifting the queue upwards so that it is always firing out the command thats sitting in index 1 on the array use two pointers.
    Yeah, after awhile I started thinking that's what he might be doing. It's a fair amount of work though keeping track of two pointers to determine where the "next" queued location is for sending and the next array location is for adding to queue. It gives me a headache just thinking about it, it's like a revolving door appraoch for a queue but I guess once you've done it once it's a simple cut & paste to repeat.
  • Spire_JeffSpire_Jeff Posts: 1,917
    Here is the insertion code for the queue. It's really not that difficult. This code will overwrite the oldest queued message if the queue fills up, but it's easy to convert to throw an error if the queue fills up.
    			IF(!(nCURRENT_COMMAND == 1 + ( nCURRENT_QUEUE % AP_QUEUE_SIZE)))
    			{
    				nCURRENT_QUEUE = 1 + ( nCURRENT_QUEUE % AP_QUEUE_SIZE)  //Increase queue insertion point to next place unless we have reached max queue size 
    				uAP_CMD[nCURRENT_QUEUE].CMD = sCMND
    				uAP_CMD[nCURRENT_QUEUE].dvDEV = dvOUT
    			}  
    			ELSE
    			{
    				nCURRENT_COMMAND = 1 + ( nCURRENT_COMMAND % AP_QUEUE_SIZE) //Drop oldest command
    				nCURRENT_QUEUE = 1 + ( nCURRENT_QUEUE % AP_QUEUE_SIZE)  //Increase queue insertion point to next place unless we have reached max queue size 
    				uAP_CMD[nCURRENT_QUEUE].CMD = sCMND
    				uAP_CMD[nCURRENT_QUEUE].dvDEV = dvOUT
    			}
    

    Jeff

    P.S.
    As I look at this code, I am thinking that reversing the if and else code and dropping the ! could make the code more efficient :( gotta change it now.
  • viningvining Posts: 4,368
    Spire_Jeff wrote:
    Here is the insertion code for the queue. It's really not that difficult. This code will overwrite the oldest queued message if the queue fills up, but it's easy to convert to throw an error if the queue fills up.
    Yeah, that's not bad at all. I think throwing an error might be better than over writing the oldest since queues are generally sized larger than ever should be necassary so if it's backing up there's probably something wrong.

    I guess "find_string" is a time consuming process when you think about it and remove string possibly even longer.

    Not sure if I'm ready to convert but it is an interested concept for queueing which I hadn't considered. Changing the pointer for song lists yes but for a send_command or send_string queue I never would have thunk of it.
  • Spire_JeffSpire_Jeff Posts: 1,917
    vining wrote: »
    Yeah, that's not bad at all. I think throwing an error might be better than over writing the oldest since queues are generally sized larger than ever should be necassary so if it's backing up there's probably something wrong.

    I have gone both ways on this. In the situation that queue was written for, I started by throwing errors, but I recently switched to overwrite. It doesn't happen often, but when there is a little lag, it makes more sense to overwrite old messages. It also does not make sense to dump the queue when the device drops offline for a second or two (IP device).

    Jeff
  • bobbob Posts: 296
    Jeff,
    possible to show more of your code, definition and Time_Line? Thanks much!
  • amdpoweramdpower Posts: 110
    This is very interesting. Do you still get a performance boost if you check a simple variable? i.e. I don't like to use a timeline or definitive wait period when controlling devices. I like to send out as soon as the device responds with an OK or timeout after 1 Sec. So if you simply had...
    If (nWait!=1){
         nWait=1
         //Do Spire's Magic
         WAIT 'wFail' 10{
              nWait=0
         }
    }
    
    Would you still get the performance boost?
  • amdpoweramdpower Posts: 110
    Although, I guess you could just run the function on the incoming 'OK' just as easily huh? If it's failsafe.
  • PhreaKPhreaK Posts: 966
    amdpower wrote: »
    Although, I guess you could just run the function on the incoming 'OK' just as easily huh? If it's failsafe.

    I generally use a combination of the timed based cueing and feedback. If you set up your timeline which fires the commands out to do so at the interval specified as the minimum consecutive command gap (if your lucky it may be in the protocol, otherwise 40ms seems to be about right for a lot of devices) but combine it with an ACK timeout (say 1 second). That way each iteration of the timeline will fire of the next command in the cue if the previous command has been an acknowledged, otherwise it will skip it and wait until the next timeline event until the timeout has been reached. If the ACK times out it will try and send the command again up to 3 times, after 3 connsecutive time outs it will bin the command and flag an error - either in RMS if applicable or just send it to 0:0:0 so I can see it as I debug. That way you're verifying all commands for devices that give you nice feedback (for the ones that don't you can always set up some seperate polling logic and make sure its always in the state your code is expecting it to be so no one can play with those damn hardware buttons), and making sure your system doesn't completely lock up in the event of something going wrong. Additionally it will always be firing out the commands as fast as the device can accept them.
  • Spire_JeffSpire_Jeff Posts: 1,917
    Here is an example of the queue implementation:
    DEFINE_FUNCTION INTEGER SEND_CMD (DEV dvOUT, CHAR sCMND[260])
    {
    	IF(TIMELINE_ACTIVE(TL_CMDS))
    		{
    			IF(!(nCURRENT_COMMAND == 1 + ( nCURRENT_QUEUE % AP_QUEUE_SIZE)))
    			{
    				nCURRENT_QUEUE = 1 + ( nCURRENT_QUEUE % AP_QUEUE_SIZE)  
    				uAP_CMD[nCURRENT_QUEUE].CMD = sCMND
    				uAP_CMD[nCURRENT_QUEUE].dvDEV = dvOUT
    				 
    				 
    			}  
    			ELSE
    			{
    				nCURRENT_COMMAND = 1 + ( nCURRENT_COMMAND % AP_QUEUE_SIZE) 
    				nCURRENT_QUEUE = 1 + ( nCURRENT_QUEUE % AP_QUEUE_SIZE)
    				uAP_CMD[nCURRENT_QUEUE].CMD = sCMND
    				uAP_CMD[nCURRENT_QUEUE].dvDEV = dvOUT
    			}
    					
    		} 
    	ELSE
    		{
    			nCURRENT_QUEUE = 1 + ( nCURRENT_QUEUE % AP_QUEUE_SIZE) 
    			uAP_CMD[nCURRENT_QUEUE].CMD = sCMND
    			uAP_CMD[nCURRENT_QUEUE].dvDEV = dvOUT
    			TIMELINE_CREATE(TL_CMDS,lAP_COMM_TL,1,TIMELINE_RELATIVE, TIMELINE_REPEAT)
    			
    		}  
    
    RETURN 0   
    
    }
    
    ....
    
    
    TIMELINE_EVENT[TL_CMDS]
    {
       nCURRENT_COMMAND = 1 + (nCURRENT_COMMAND % AP_QUEUE_SIZE)   
       SEND_STRING uAP_CMD[nCURRENT_COMMAND].dvDEV,uAP_CMD[nCURRENT_COMMAND].CMD
    	 IF(DEBUG>2)
          SEND_STRING 0,"'COMMAND SENT: ',LEFT_STRING(uAP_CMD[nCURRENT_COMMAND].CMD,25)"
       IF(nCURRENT_COMMAND == nCURRENT_QUEUE)          
          TIMELINE_KILL(TL_CMDS)
    } 
    
    

    Jeff
  • John GonzalesJohn Gonzales Posts: 609
    Here's mine...

    Here's one that I use - similar to Jeff's. It uses two functions, One to queue the command, the second to send the command. It also uses a timeline, array, and a structure. This one's not so fancy though, as I don't wait for ACK responses from the device. It could be modified to test for ACK and resend if an error message is received, but this has worked well enough for me. One caveat is that if you fire enough commands at it to wrap the array around so that the queuing position passes the sending position, you can cause problems. One advantage of this method though is that it's simple to send commands to multiple devices and the Queue keeps track of which msg goes to which device.
    (***********************************************************)
    (*  FILE CREATED ON: 08/02/2008  AT: 11:04:56              *)
    (***********************************************************)
    (***********************************************************)
    (***********************************************************)
    (*  FILE_LAST_MODIFIED_ON: 08/02/2008  AT: 11:31:30        *)
    (***********************************************************)
    
    
    (***********************************************************)
    (*          DEVICE NUMBER DEFINITIONS GO BELOW             *)
    (***********************************************************)
    DEFINE_DEVICE
    dvMaster = 	5001:1:0
    dvTP 	=	10001:1:0 
    
    
    (***********************************************************)
    (*               CONSTANT DEFINITIONS GO BELOW             *)
    (***********************************************************)
    DEFINE_CONSTANT //fnQueueTheCommand, fnSendtheCommand
    INTEGER TL_Queue = 1
    (***********************************************************)
    (*              DATA TYPE DEFINITIONS GO BELOW             *)
    (***********************************************************)
    DEFINE_TYPE
    
    STRUCTURE _sCmdQueue
    {
      DEV		dvSendtoDevice
      CHAR	cCmdtoSend[50]
    }
    
    (***********************************************************)
    (*               VARIABLE DEFINITIONS GO BELOW             *)
    (***********************************************************)
    DEFINE_VARIABLE //fnQueueTheCommand, fnSendtheCommand
    _sCmdQueue _CmdQueue[50]  
    VOLATILE LONG nSendSpacing[1]=  {200} //Time between commands
    
    PERSISTENT INTEGER nCurrentQSendingPosition
    PERSISTENT INTEGER nCurrentQueueingPosition
    
    
    
    (***********************************************************)
    (*               LATCHING DEFINITIONS GO BELOW             *)
    (***********************************************************)
    DEFINE_LATCHING
    
    (***********************************************************)
    (*       MUTUALLY EXCLUSIVE DEFINITIONS GO BELOW           *)
    (***********************************************************)
    DEFINE_MUTUALLY_EXCLUSIVE
    
    (***********************************************************)
    (*        SUBROUTINE/FUNCTION DEFINITIONS GO BELOW         *)
    (***********************************************************)
    (* EXAMPLE: DEFINE_FUNCTION <RETURN_TYPE> <NAME> (<PARAMETERS>) *)
    (* EXAMPLE: DEFINE_CALL '<NAME>' (<PARAMETERS>) *)
    DEFINE_FUNCTION fnQueueTheCommand(DEV dvDevice,CHAR cMsg[50])
    {
      _CmdQueue[nCurrentQueueingPosition].dvSendtoDevice 	= 	dvDevice
      _CmdQueue[nCurrentQueueingPosition].cCmdtoSend		=	cMsg
      nCurrentQueueingPosition++
      IF (nCurrentQueueingPosition>50)
    	 nCurrentQueueingPosition=1
      IF(!TIMELINE_ACTIVE(TL_Queue))
    	 TIMELINE_CREATE(TL_Queue, nSendSpacing, 1, TIMELINE_ABSOLUTE, TIMELINE_REPEAT)  
    }
    
    
    DEFINE_FUNCTION fnSendtheCommand()
    {
      SEND_STRING _CmdQueue[nCurrentQSendingPosition].dvSendtoDevice,"_CmdQueue[nCurrentQSendingPosition].cCmdtoSend"
      _CmdQueue[nCurrentQSendingPosition].dvSendtoDevice = 0:0:0	//Clear the Buffer
      _CmdQueue[nCurrentQSendingPosition].cCmdtoSend = "''"			//Clear the Buffer
      nCurrentQSendingPosition++
      IF(nCurrentQSendingPosition>50)
    	 nCurrentQSendingPosition=1
      IF(nCurrentQSendingPosition==nCurrentQueueingPosition)	//If the current sending position is the same as the current queue position then the Queue is empty, kill the timeline
    	 TIMELINE_KILL (TL_Queue)
    }
    
    (***********************************************************)
    (*                STARTUP CODE GOES BELOW                  *)
    (***********************************************************)
    
    DEFINE_START
    nCurrentQSendingPosition=1
    nCurrentQueueingPosition=1
    
    (***********************************************************)
    (*                THE EVENTS GO BELOW                      *)
    (***********************************************************)
    DEFINE_EVENT
    
    DEFINE_EVENT
    TIMELINE_EVENT[TL_Queue]
    {
      fnSendtheCommand()
    }
    (***********************************************************)
    (*            THE ACTUAL PROGRAM GOES BELOW                *)
    (***********************************************************)
    DEFINE_PROGRAM
    
    (***********************************************************)
    (*                     END OF PROGRAM                      *)
    (*        DO NOT PUT ANY CODE BELOW THIS COMMENT           *)
    (***********************************************************)
    
    

    --John
  • PhreaKPhreaK Posts: 966
    Has anyone used the following escape secence (from AMX-PI) to impliment 'internal' queing?
    "27,19,<time>"

    Insert a time delay before transmitting the next character.



    Syntax:

    SEND_COMMAND <DEV>,"27,19,<time>"



    Variable:

    time = 1 - 255. Measured in 1 millisecond increments.



    Example:

    SEND_COMMAND RS232_1,"27,19,10"

    Inserts a 10 millisecond delay before transmitting characters to the

    RS232_1 device.
  • JohnMichnrJohnMichnr Posts: 279
    Spire_Jeff wrote: »
    Here is an example of the queue implementation:

    Jeff

    Jeff - couple of questions.
    nCurrent_command is the command that just was sent correct?
    nCurrent_Queue is the location of the last command in the Q?
    You have one Q for all the devices in the system?

    Do you run into situations where your volume control "stutters" because a command for another device falls into the Q between volume ramping commands? (say you are monitoring the projector(s) power status every 5 seconds and the user just happens to be changing the volume during that time.)

    I know that I tend to use separate Q's for each device, As well as triggering the next command from the ACK or NAK from the device. (I too will throw out error codes for NAKs and try up to 5 times for each command - mostly for the RMS apps), so one Q for all the devices with a set timeline makes me a little nervous.
  • bobbob Posts: 296
    PhreaK wrote: »
    Has anyone used the following escape secence (from AMX-PI) to impliment 'internal' queing?
    SEND_COMMAND RS232_1,"27,19,10"
    // Inserts a 10 millisecond delay before transmitting characters to the RS232_1 device.
    
    Kim, is your example not something like
    WAIT 10 SEND_COMMAND RS232_1,"27,19"
    

    I think it would be more useful if there is a way to insert 10 miliseconds into the outgoing buffer *after* the command has been sent so that the next command is being executed properly by the device. This will save implementing any ques, I think!?
  • PhreaKPhreaK Posts: 966
    I'm not actually sure if it works, I just noticed those escape sequences when browsing AMX-PI and was curious as to whether anyone was using them.
  • I had to use an escape sequence for a production switcher (video). THe old Grass Valley protocol requires sending a break in between commands. The command I was using was the "27,17,21" command.

    But I think the command listed above will just insert spacing between each character sent. good for slowing down a serial data stream, butnot for queing commands going to the device. I think.
  • viningvining Posts: 4,368
    "27,19,<time>"
    
          Insert a time delay before transmitting the next character.
    
    
    Reading this line would make me think this is a one time deal and where it's inserted in affect creates a wait before the strings that follow are sent, but...
    Example:
    
                SEND_COMMAND RS232_1,"27,19,10"
    
                Inserts a 10 millisecond delay before transmitting characters to the 
    
                 RS232_1 device.
    
    Reading this line makes me think it may work like JohnMichnr describe and insert a delay between all subsequent characters.

    Hmmm, which way does it actually work? If it's the 1st method then it could be usefull to delay individual commands similar to queue timing except you're using the output buffer as the queue and you would then have to send this delay to the buffer after every command to insert this delay. I don't really see a reason for the second 2nd interpretation since if you have to slow down every character it should be running at a lower baud unless there's some funky timing requirements.
  • JohnMichnr wrote: »
    so one Q for all the devices with a set timeline makes me a little nervous.
    Queuing multiple devices in a single queue is sometimes helpful in large systems. I use the similar one that I posted for sending text to multiple panels in large systems. As an example, if for some reason you have 20 panels that need text updates, it keeps you from slowing the master down by spacing the send ^txt commands out. That's actually the reason I wrote my particular queue (and why it doesn't bother to test for ACK).

    I don't use one central queue for all the devices on the system but I think you're right, using one queue for a lot of devices could potentially hurt you on real-time stuff like volume control, PTZ, etc.

    --John
  • vining wrote: »
    "27,19,<time>"
    
          Insert a time delay before transmitting the next character.
    
    
    Reading this line would make me think this is a one time deal and where it's inserted in affect creates a wait before the strings that follow are sent, but...
    Example:
    
                SEND_COMMAND RS232_1,"27,19,10"
    
                Inserts a 10 millisecond delay before transmitting characters to the 
    
                 RS232_1 device.
    
    Reading this line makes me think it may work like JohnMichnr describe and insert a delay between all subsequent characters.

    Hmmm, which way does it actually work? If it's the 1st method then it could be usefull to delay individual commands similar to queue timing except you're using the output buffer as the queue and you would then have to send this delay to the buffer after every command to insert this delay. I don't really see a reason for the second 2nd interpretation since if you have to slow down every character it should be running at a lower baud unless there's some funky timing requirements.

    If we're all guessing, I think it's the first one, it puts in a one-time delay. I think the example is if you send that naked string, and immediately send a second send_command, there will be a 10 ms delay before transmitting characters.
    e.g.
    SEND_COMMAND RS232_1,"27,19,10"
    SEND_COMMAND RS232_1,"2,xx,xx,xx,xx,xx,13,10"
    
    //Inserts a 10 millisecond delay before transmitting characters to the device
    

    I'll try to test it this weekend if no one posts a definitive answer.

    --John
  • viningvining Posts: 4,368
    The PI examples show send commands but at the top of this section in PI it says they are escape sequences for send strings?
    This device has some special SEND_STRING escape sequences:
    
     
    
    If any of the 3 character combinations below are found anywhere within
    
     a SEND_STRING program instruction, they will be treated as a command
    
     and not the literal characters.
    
    
    I decided to try and see what this escape actually does and from what I've been able to determine it doesn't do much regardless of type of command used (string or command). My test master is an old NI-4000 running v3.21.343 and I didn't bother checking if this is current for non duet masters.

    Here's the code I was using if anyone else wants to fiddle about. I just open the debug window and change the variables to see what happens and like I said I don't see it doing anything reliably so I wouldn't use this at all for anything. Maybe it's me but....

    Code used:
    DEFINE_DEVICE
    
    dvRS232_1     	        = 5001:1:0 ;
    dvRS232_2     	        = 5001:2:0 ;
    
    DEFINE_VARIABLE   
    
    VOLATILE INTEGER nRS232_1_SendStr = 0 ;
    VOLATILE INTEGER nRS232_1_PreCmd = 0 ;
    VOLATILE INTEGER nRS232_1_PostCmd = 0 ;
    VOLATILE INTEGER nRS232_1_PreStr = 0 ;
    VOLATILE INTEGER nRS232_1_PostStr = 0 ;
    VOLATILE INTEGER nRS232_1_NumStrSend = 10 ;
    VOLATILE INTEGER nRS232_1_NumMilliSec = 10 ;
    
    DEFINE_FUNCTION fnSend_RS232_Str(INTEGER iIndx)
         
         {
         if(nRS232_1_PreCmd)
    	  {
    	  SEND_COMMAND dvRS232_1,"27,19,nRS232_1_NumMilliSec" ;
    	  }
         if(nRS232_1_PreStr)
    	  {
    	  SEND_STRING dvRS232_1,"27,19,nRS232_1_NumMilliSec" ;
    	  }
    
         SEND_STRING dvRS232_1,"'Test String ',itoa(iIndx),' Out RS232_1'" ;
    
         if(nRS232_1_PostCmd)
    	  {
    	  SEND_COMMAND dvRS232_1,"27,19,nRS232_1_NumMilliSec" ;
    	  }
         if(nRS232_1_PostStr)
    	  {
    	  SEND_STRING dvRS232_1,"27,19,nRS232_1_NumMilliSec" ;
    	  }
         }
    
    DATA_EVENT [dvRS232_1]    //SET BAUD 9600,N,8,1 485 DISABLE  and gets zone status
         
         {
         ONLINE:
    	  {
    	  SEND_COMMAND dvRS232_1,'SET BAUD 9600,N,8,1 485 DISABLE' ;
    	  }
         STRING:
    	  {
    	  SEND_STRING 0,"'RS232_1 RCV''D, ',DATA.TEXT" ;
    	  }
         }
    
    DATA_EVENT [dvRS232_2]    //SET BAUD 9600,N,8,1 485 DISABLE  and gets zone status
         
         {
         ONLINE:
    	  {
    	  SEND_COMMAND dvRS232_2,'SET BAUD 9600,N,8,1 485 DISABLE' ;
    	  }
         STRING:
    	  {
    	  SEND_STRING 0,"'RS232_2 RCV''D, ',DATA.TEXT" ;
    	  }
         }
    
    DEFINE_PROGRAM
    
    if(nRS232_1_SendStr)
         {
         STACK_VAR INTEGER i ;
         
         nRS232_1_SendStr = 0 ;
         for(i = 1 ; i <= nRS232_1_NumStrSend ; i++)
    	  {
    	  fnSend_RS232_Str(i) ;
    	  }
         }
    
    FYI, I used a crossover cable from port1 to port 2.
    I tried about every combination of string vs commands, before the string and after the string, a couple strings vs 10 strings, 10 milliseconds vs various milliseconds and nothing I did gave anything near the result I was hoping for.

    Oh well.
  • etmannetmann Posts: 1
    QUE CODE THAT I USE

    This is a que that I use frequently. It doesn't use a lot of overhead and you can set the time between commands by adjusting the wait time at the bottom. It works great for me;
    DEFINE_FUNCTION AP_DISCONNECT(INTEGER OUTPUT)
    {
       AP_COMM("'DL0O',ITOA(OUTPUT),'T'")
    }
    
    DEFINE_FUNCTION AP_COMM(CHAR COMM_STR[15])	
    {
    	AP_COMM_WAITING++
    	IF(AP_COMM_WAITING > nAP_BUFFER_SIZE)  //if at the end, loop around to the start
    		AP_COMM_WAITING = 1
    	cAP_BUFFER[AP_COMM_WAITING] = COMM_STR
    }
    
    
    DEFINE_PROGRAM
    
    IF((AP_COMM_WAITING > 0) && AP_COMM_READY)    //IF WE HAVE A COMMAND AND IT'S READY TO SEND,
    {
    	OFF[AP_COMM_READY]
    	AP_COMM_SENDING++
    	IF(AP_COMM_SENDING > nAP_BUFFER_SIZE)	//IF AT THE END OF THE BUFFER, LOOP AROUND TO THE FIRST INDEX
    		AP_COMM_SENDING = 1
    	SEND_STRING DvAP,cAP_BUFFER[AP_COMM_SENDING]
    	cAP_BUFFER[AP_COMM_SENDING] = ''   
    	IF(cAP_BUFFER[AP_COMM_WAITING] = '')		//LAST COMMAND IN BUFFER
    	{
    		OFF[AP_COMM_WAITING]
    		OFF[AP_COMM_SENDING]
    	}
    	WAIT 2
    		ON[AP_COMM_READY]
    }
    
    
  • bobbob Posts: 296
    Thanks for sharing!
Sign In or Register to comment.