Queuing Methods
Spire_Jeff
Formerly Caffeinated Programmer
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:
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
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
-
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? -
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. -
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.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. -
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. -
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.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.
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. -
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 -
Jeff,
possible to show more of your code, definition and Time_Line? Thanks much! -
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? -
Although, I guess you could just run the function on the incoming 'OK' just as easily huh? If it's failsafe.
-
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. -
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 -
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 -
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. -
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. -
Kim, is your example not something likeHas 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.
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!? -
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. -
"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. -
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).JohnMichnr wrote: »so one Q for all the devices with a set timeline makes me a little nervous.
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 -
"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 -
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. -
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] } -
Thanks for sharing!
Leave a Comment
Categories
- All Categories
- 2.5K AMX General Discussion
- 922 AMX Technical Discussion
- 514 AMX Hardware
- 502 AMX Control Products
- 3 AMX Video Distribution Products
- 9 AMX Networked AV (SVSI) Products
- AMX Workspace & Collaboration Products
- 3.4K AMX Software
- 151 AMX Resource Management Suite Software
- 386 AMX Design Tools
- 2.4K NetLinx Studio
- 135 Duet/Cafe Duet
- 248 NetLinx Modules & Duet Modules
- 57 AMX RPM Forum
- 228 MODPEDIA - The Public Repository of Modules for Everyone
- 943 AMX Specialty Forums
- 2.6K AMXForums Archive
- 2.6K AMXForums Archive Threads
- 1.5K AMX Hardware
- 432 AMX Applications and Solutions
- 249 Residential Forum
- 182 Tips and Tricks
- 146 AMX Website/Forums

