Home AMX User Forum NetLinx Studio

Die, buffer data... DIE!

I can't kill my buffer data! Every time I submit something to a virtual device with a buffer tied to it (create_buffer vdv_cobis,cobisbuffout), all the data that was ever in the buffer seems to come back to haunt me. I can get the length right before the send_data, and the length will report back as 0. I then send ~200 characters of text to the virtual, and suddenly my buffer has over 3,000 characters in it. The sending process will chew on all the data in the buffer, sending it all out until buffer length goes back down to 0. Then when I send another chunk of data (~200 characters) the buffer is right back to 3,000+ characters in it.

I'm not defining the buffer container as persistent. I'm explicitly clearing it / setting it to "". Yet my zombie data keeps re-appearing once I add something to the buffer by sending it to the virtual.

If I manually add to the buffer (cobisbuffout = "cobisbuffout,this_string") it works as expected.

I've got dozens of buffers running in my controllers, and have never seen this before. What's up?

Comments

  • a_riot42a_riot42 Posts: 1,624
    I can't kill my buffer data! Every time I submit something to a virtual device with a buffer tied to it (create_buffer vdv_cobis,cobisbuffout), all the data that was ever in the buffer seems to come back to haunt me. I can get the length right before the send_data, and the length will report back as 0. I then send ~200 characters of text to the virtual, and suddenly my buffer has over 3,000 characters in it. The sending process will chew on all the data in the buffer, sending it all out until buffer length goes back down to 0. Then when I send another chunk of data (~200 characters) the buffer is right back to 3,000+ characters in it.

    I'm not defining the buffer container as persistent. I'm explicitly clearing it / setting it to "". Yet my zombie data keeps re-appearing once I add something to the buffer by sending it to the virtual.

    If I manually add to the buffer (cobisbuffout = "cobisbuffout,this_string") it works as expected.

    I've got dozens of buffers running in my controllers, and have never seen this before. What's up?

    Post your code.
  • A virtual device on String (and Command) level works a little different from a regular RS232.

    In general, if you send a string to a virtual device, you will immediately get back that string from the device. So if you do a CREATE_BUFFER to the virtual device and do a SEND_STRING, the device will "echo back" the string received - and if a buffer was created, the buffer will be filled with the echo. This again will give a echo etc.
    If you have a look tio the device notification of the virtual device, you will see that you always get a STRING TO and a STRING FROM with the same content.

    This is why the Netlinx code based modules in general use SEND_COMMANDs to getinstructions into the module thru the virtual device, but data from the module comes back as STRING thru the virtual device.
  • DHawthorneDHawthorne Posts: 4,584
    What you are seeing is not unique to NetLinx. String data is stored in a separate area of memory, at whatever size you allocated. It is never actually cleared, values are just reassigned. If I create a string by declaring CHAR sGretting[]="'Hello'", I have allocated a 5 CHAR array, and the length of that array is set in the program as 5. If I subsequently fire a statement that reassigns values, sGretting = "''" (which is all the CLEAR_BUFFER function does, by the way), the program then records the length as zero ... but if I were to peek at that same memory location, I would still see the characters 'Hello'. You even track this by watching the variables and setting the debug window to show the total length. Only the portions you specifically overwrite will actually change. If I reassign my sGreeting to "'!'", then subsequently set the array length to 5, it will go to saying '!ello', even though I explicitly set it to a single character earlier. So all strings have three parameters: the actual data comprising the string, the length of the string, and the maximum length of the string. Anything beyond the length is ignored by functions intended to deal with stings, but even so, you can still reference them. If I reassign "Hello" to '!', then call for the value of sGreeting[5], I'll still get 'o'. But if I ask for "sGreeting", I'll just get the '!'.

    So then, something in your code is resetting your buffer length to the max value. It's seeing all the old, previously processed data, because somewhere along the line it has been notified it is once again part of the string. That's what you need to be looking for, whatever is changing how the string functions determine the end point.
  • Here's the code. I've added comments in the DEFINE_CALL part detailing what I'm getting with 2 different methods:

    DEFINE_DEVICE //Physical Devices
    dvip_cobis = 0:54:0 //COBIS Ticket API

    DEFINE_DEVICE //Virtual Devices
    vdvip_cobis = 33052:1:0 //cobis server api

    integer help_btns[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30} //Help Screen Buttons

    long logtimeout[1] = {10000} //timeout for cobis connection

    structure helpfile {
    char btn[100]
    char url[100]
    }

    DEFINE_VARIABLE

    char cobisbuffout[10000] //help ticket buffers & data
    char cobisbuffin[1024]

    persistent helpfile help[30]
    persistent integer helpcount

    (* SUBROUTINE/FUNCTION DEFINITIONS GO BELOW *)

    define_call 'submittroubleticket' (this_room,char this_pawprint[64]) {
    stack_var char room[10]
    stack_var char user[30]
    stack_var char problem[1024]
    stack_var char httpheader[2048]
    stack_var char httpcontent[4096]
    room = rm[this_room].name
    user = right_string(this_pawprint,length_string(this_pawprint)-5)

    call 'debug' ("'Ticket Process - Room:',room,' User:',user")

    for(tmp_idx3=1;tmp_idx3<=helpcount;tmp_idx3++) {
    if(rm[this_room].helpbtns[tmp_idx3]==1) {
    problem = "problem,help[tmp_idx3].url,','" //add to help string
    call 'debug' ("'Added Prob:',problem")
    }
    }

    //format content stream
    httpcontent = "'room=',room,'&user=',user,'&problem=',problem"

    call 'debug' ("'HTTP Content: ',httpcontent")

    //format data to send to buffer
    httpheader = "'POST /cobisapi.php HTTP/1.1',13,10";
    httpheader = "httpheader,'Connection: close',13,10";
    httpheader = "httpheader,'User-Agent: HTTPTool/1.0',13,10";
    httpheader = "httpheader,'Host: localhost',13,10"
    httpheader = "httpheader,'Content-Type: text/plain',13,10";
    httpheader = "httpheader,'Content-Length: ',ITOA(LENGTH_STRING(httpcontent)),13,10";
    httpheader = "httpheader,13,10" // Extra Line Between Content

    //submit data to buffer
    call 'debug' ("'Buffer Len: ',itoa(length_string(cobisbuffout))")
    // ABOVE CALL SHOWS BUFFER LENGTH AT 0

    call 'debug' ("'Data Len: ',itoa(length_string(httpheader)+length_string(httpcontent)+3)")
    // ABOVE CALL SHOWS DATA LENGTH ~220

    // HERES THE PART THAT DOESN'T WORK
    // AS SOON AS THE STRING IS SENT, BUFFER SIZE JUMPS TO 3000 CHARS +
    // WITH DOZENS OF INSTANCES IN BUFFER

    // send_string vdvip_cobis,"httpheader,httpcontent,'|||'" //using triple pipe for delimiter


    // HERES WHAT DOES WORK INSTEAD TO GET ONLY THE ~220 NEW CHARACTERS TO SHOW UP IN BUFFER

    cobisbuffout = "cobisbuffout,httpheader,httpcontent,'|||'"

    call 'debug' ("'Buffer Len: ',itoa(length_string(cobisbuffout))")
    // ABOVE CALL SHOWS 3200+ CHARACTERS IN BUFFER WHEN USING THE SEND_STRING,
    // BUT ONLY THE ONE ~200 CHAR MESSAGE WHEN USING MANUAL BUFFER ADDITION

    for(tmp_idx3=1;tmp_idx3<helpcount;tmp_idx3++) { //clear button selections
    rm[this_room].helpbtns[tmp_idx3] = 0
    }
    }

    define_call 'cobisresponseparser' (char this_data[1024]) {
    call 'debug' ("'COBIS Response received: ',this_data")
    }


    (***********************************************************)
    (* STARTUP CODE GOES BELOW *)
    (***********************************************************)
    DEFINE_START

    create_buffer vdvip_cobis,cobisbuffout
    create_buffer dvip_cobis,cobisbuffin

    cobisbuffout = ""
    cobisbuffin = ""

    (***********************************************************)
    (* THE EVENTS GO BELOW *)
    (***********************************************************)
    DEFINE_EVENT


    data_event[dvip_cobis] { //COBIS
    onerror: {
    call 'debug' ("'COBIS Port ERROR=',ITOA(Data.Number)")
    }
    online: {
    cobisipconnect = 1
    call 'debug' ("'COBIS Port Open... '")
    }
    offline: {
    cobisipconnect = 0
    call 'debug' ("'COBIS Port CLOSED... '")
    }
    string: {
    call 'debug' ("'Data From COBIS:',itoa(data.device.number),':',itoa(data.device.port),' - ',data.text")
    // Commands from unit should automatically go into cobisbuffin
    }
    }
    data_event[vdvip_cobis] { //COBIS virtual
    online: { }
    offline: { }
    onerror: { }
    string: {
    //data should automatically be appended to cobisbuffout, picked up by program processing
    }
    }

    data_event[dvtp_all] { //Room panel initialization
    online: {
    stack_var integer this_pid
    this_pid = getpid(data.device.number)
    send_command data.device,"'@PPX'"
    if(rm[this_pid].deck==1) {
    send_command data.device,"'PAGE-HomeDeck'"
    }
    else {
    send_command data.device,"'PAGE-Home'"
    }
    SEND_COMMAND data.device,"'!F',250,'1'"
    SEND_COMMAND data.device,"'TEXT250-Cornell Hall RM ',rm[this_pid].name,' Panel'"
    SEND_COMMAND data.device,"'!F',251,'1'"
    SEND_COMMAND data.device,"'TEXT251-Compiled = ',__FILE__,', ',DATE,', ',TIME"
    SEND_COMMAND data.device,"'!F',252,'1'"
    SEND_COMMAND data.device,"'TEXT252-Code Version = ',__VERSION__"
    SEND_COMMAND data.device,"'!F',253,'1'"
    (* Must fill this (Master Ver) *)
    SEND_COMMAND data.device,'TEXT253-'
    SEND_COMMAND data.device,"'!F',254,'1'"
    (* Must fill this (Panel File) *)
    SEND_COMMAND data.device,'TEXT254-Panel Type = NXT-CV7'
    SEND_COMMAND data.device,"'!F',255,'1'"
    (* Must fill this (Dealer Info) *)
    SEND_COMMAND data.device,'TEXT255-University of Missouri'
    send_command data.device,"'^TXT-1,0,ROOM ',rm[this_pid].name"
    call 'extronvolfeedback' (this_pid)
    }
    offline: {
    //what here?
    }
    onerror: {
    //what here?
    }
    string: { //Trouble ticket coming in!
    stack_var integer this_pid
    this_pid = getpid(data.device.number)
    call 'debug' ("'Input from Panel: ',data.text")
    if(find_string(data.text,"'KEYB-'",1)) {
    call 'debug' ("'KBD INPUT, PROCESS TICKET...'")
    call 'submittroubleticket' (this_pid,data.text)
    }
    }
    }



    (***********************************************************)
    (* THE ACTUAL PROGRAM GOES BELOW *)
    (***********************************************************)
    DEFINE_PROGRAM

    //COBIS API
    while(!timeline_active(98)&&find_string(cobisbuffout,"'|||'",1)) {
    //start timeline to timeout connection - NOT IN RUNNING PROGRAM YET
    timeline_create(98,logtimeout,1,timeline_once,timeline_absolute)

    call 'debug' ("'COBIS Trap Len:',itoa(length_string(cobisbuffout)),' Data:',cobisbuffout")
    cobisticket = remove_string(cobisbuffout,"'|||'",1)
    cobisticket = left_string(cobisticket,length_string(cobisticket)-3)
    call 'debug' ("'COBIS HTTP Len:',itoa(length_string(cobisbuffout)),' Data:',cobisticket")

    ip_client_open(dvip_cobis.port,cobisipaddress,80,1)//80=port,1=TCP

    wait_until(cobisipconnect==1) { //set to 1 in online data event, 0 in offline event
    send_string dvip_cobis,"cobisticket"
    call 'debug' ("'COBIS SUBMIT: ',cobisticket")
    }
    }
    while(find_string(cobisbuffin,"$0D",1)) {
    call 'cobisresponseparser' (remove_string(cobisbuffin,"$0D",1))
    }
  • DHawthorneDHawthorne Posts: 4,584
    I think Marc's evaluation is correct. Because your are using SEND_STRING to the virtual, it is echoing and creating something like the programming version of a feedback loop. Your SEND_STRING gets added to the buffer, it echoes the string, and that gets added, etc., until your buffer is full.
  • I would agree, except for 2 things:

    1) I have this exact same buffer methodology running for about 60 other communications buffers in this very program. This is the only one that's having zombie data. However, all the other buffer variables are part of data structures; this is the only one that's just a plain declared CHAR variable.

    2) The variable size is currently set to 10,000, and as I've been testing it, I can see that the zombie data really is all the previous sends, plus the latest one. It doesn't completely fill the buffer, but the size of the data in the buffer grows by the size of the submitted data on every SEND.

    This is an old 2100 controller BTW; I've updated to the latest firmware, etc., but could the old controller have issues?
  • DHawthorneDHawthorne Posts: 4,584
    I'm into hunches now then. I suspect the internal mechanism by which CREATE_BUFFER appends the CHAR array is getting confused by something and using the old array length instead of what has been created by the REMOVE_STRING routines in your sending subroutine. Whatever that mechanism is, it's not confused by the way you are doing it in other portions of the code, most likely due to the variable type. Could be you've found a bug. It may also be a timing issue if the internal buffering is adding the string before it gets "notified" that the array has been modified elsewhere, thus overriding the modification. If it's something like that, you might try taking the parsing routine out of DEFINE_PROGRAM and making it a subroutine that is called on the STRING event. That should resolve any timing jinks.

    If it were me, I would have given up already and just stuck with the string concatenation method you have commented as working and abandoned trying to use SEND_STRING.
  • I am sticking with the "other" method for now. At this point it's just a curiosity I'd like to identify. I'll try moving the buffer to a structure and see if that makes a difference, but I also have other projects that use the same "plain jane" CHAR variable for buffer processing and no problems there.

    This is the only codebase like this I have running on a 2100; all the other ones are 3100's or 3101's. I've updated to the latest firmware in the 2100, but... who knows. I'm calling some kind of goofy bug in the length indexing of the variable.
  • a_riot42a_riot42 Posts: 1,624
    This is why I never use create_buffer. I prefer data.text since it just never goes wrong.

    I don't know what the problem is but I have to ask about this code:

    //format data to send to buffer
    httpheader = "'POST /cobisapi.php HTTP/1.1',13,10";
    httpheader = "httpheader,'Connection: close',13,10";
    httpheader = "httpheader,'User-Agent: HTTPTool/1.0',13,10";
    httpheader = "httpheader,'Host: localhost',13,10"
    httpheader = "httpheader,'Content-Type: text/plain',13,10";
    httpheader = "httpheader,'Content-Length: ',ITOA(LENGTH_STRING(httpcontent)),13,10";
    httpheader = "httpheader,13,10" // Extra Line Between Content

    I would have written it like this (with white space so the text lines up):

    //format data to send to buffer
    httpheader = "'POST /cobisapi.php HTTP/1.1',13,10",
    'Connection: close',13,10",
    'User-Agent: HTTPTool/1.0',13,10",
    'Host: localhost',13,10",
    'Content-Type: text/plain',13,10",
    'Content-Length: ',ITOA(LENGTH_STRING(httpcontent)),13,10,13,10"

    Its easier to read and gets rid of all the assignments and processing required to concatenate all the small strings. Just a thought.
    Paul
  • DHawthorneDHawthorne Posts: 4,584
    a_riot42 wrote: »
    This is why I never use create_buffer. I prefer data.text since it just never goes wrong.

    I respectfully beg to differ. DATA.TEXT may never go wrong for virtual devices, but it frequently does for real ones, especially RS-232. A string event is triggered by a data burst punctuated with a pause ... and if the device pauses for any reason at all except that it is actually done, you don't have all the data, just the bit before it paused. Likewise, if the pause isn't long enough, you might get two events in the same DATA.TEXT. And If you are going to use a construct ilike sMyBuffer = "sMyBuffer, DATA.TEXT", then it is identical to CREATE_BUFFER anyway.
  • @a_riot: Partly because it was code I copied from another post on this forum, and partly because using .= in PHP is normal. It would be a stretch, but I sudddenly wonder if doing that many concats right before doing send_string to the virtual and it's buffer has anything to do with my original failure? Nah, couldn't be. Could it?
  • ericmedleyericmedley Posts: 4,177
    DHawthorne wrote: »
    I respectfully beg to differ. DATA.TEXT may never go wrong for virtual devices, but it frequently does for real ones, especially RS-232. A string event is triggered by a data burst punctuated with a pause ... and if the device pauses for any reason at all except that it is actually done, you don't have all the data, just the bit before it paused. Likewise, if the pause isn't long enough, you might get two events in the same DATA.TEXT. And If you are going to use a construct ilike sMyBuffer = "sMyBuffer, DATA.TEXT", then it is identical to CREATE_BUFFER anyway.

    I concur. Back when we we all switched over to Netlinx, Data.Text was baffling in how it dealt with older pokey RS-232 devices. I kinda got gun shy using it. So, while (early on) everyone was bashing CREATE_BUFFER as being old skool, I was secretly still using it. I'd still keep trying data.text and figuring out how it worked. Nowadays, how it works or does not work is fairly well documented and its quirks are well known.

    To me, as Dave so eligantly pooints out, both methods are 'what they are' and how one uses one or the other depends upon the situation. I do notice a lot of newer coders don't use C_Buff but basically recreate it with buffer=" buffer,data.text" anyway.

    Personally, I use both.
Sign In or Register to comment.