Home AMX User Forum NetLinx Studio

Incoming string from over IP

Hi,

I am controlling some devices through and extron serial controller over IP. When I am receiving strings from the remote devices and viewing them from the Diagnostics tab in Netlinx, i am only getting back the response one byte at a time instead on the entire string in one line. I created string buffers and am still getting the issue.
DEFINE_DEVICE //-------------------------------------------

dvMASTER = 0:1:13  //NI-900 controller

(*  EXTRON IPL T S6 IP SERIAL CONTROLLER  *)
dvPROJ1  = 0:4:13 //238 Projector
dvMIXER1 = 0:5:13 //238 Mixer
dvPROJ2  = 0:6:13 //239 Projector
dvMIXER2 = 0:7:13 //239 Mixer

DEFINE_VARIABLE //-----------------------------------------

(*  PROJECTOR VARIABLES  *)
PERSISTENT INTEGER nPROJ1_PWR
PERSISTENT INTEGER nPROJ2_PWR
VOLATILE INTEGER nPROJ1_MUTE
VOLATILE INTEGER nPROJ2_MUTE
DEVCHAN dcPROJ_STATUS[]={ {dvTP1,4},{dvTP1,5},
			  {dvTP2,4},{dvTP2,5}
			}
DEVCHAN dcVIDEO[]={ {dvTP1,6},{dvTP1,7},{dvTP1,8},{dvTP1,9},  //TP1
		    {dvTP2,6},{dvTP2,7},{dvTP2,8},{dvTP2,9}   //TP2
		  }
DEVCHAN dcAUDIO[]={ {dvTP1,10},{dvTP1,11},  //TP1
                    {dvTP2,10},{dvTP2,11}   //TP2
		  }

(*  MIXER VARIABLES  *)
NON_VOLATILE INTEGER nMIXER1_VOL
NON_VOLATILE INTEGER nMIXER2_VOL
NON_VOLATILE INTEGER nMIXER1_MUTE
NON_VOLATILE INTEGER nMIXER2_MUTE
DEVCHAN dcVOL[]={ {dvTP1,12},{dvTP1,13},{dvTP1,14},
		  {dvTP2,12},{dvTP2,13},{dvTP2,14}
		}

(*  OTHER VARIABLES  *)
VOLATILE INTEGER nCLIENT_ONLINE[4]
VOLATILE CHAR sDEV4[10]
DEV dIP_DEV[]={ dvPROJ1,dvMIXER1,dvPROJ2,dvMIXER2 }

DEFINE_MUTUALLY_EXCLUSIVE //-------------------------------

//--------SUBROUTINE/FUNCTION DEFINITIONS GO BELOW-------//

DEFINE_FUNCTION fnCLIENT_OPEN()
{
    IF(nCLIENT_ONLINE[4] == FALSE){ IP_CLIENT_OPEN(dIP_DEV[4].PORT,IPTLS6_ADDR,COM4,1) }
    WAIT 1 {}
}

DEFINE_FUNCTION CHAR fnVOL_CTRL(INTEGER nCASE){
    SWITCH(nCASE){
	CASE 1:{ //TP1 vol up
	    IF(nMIXER1_VOL < MAX_VOL_LVL OR nMIXER1_VOL == MIN_VOL_LVL){
		nMIXER1_VOL++
		SEND_STRING dIP_DEV[2],"ITOA(nMIXER1_VOL),'V'"
		SEND_LEVEL dvTP1,1,nMIXER1_VOL
	    }
	}
	CASE 2:{ //TP1 vol down
	    IF(nMIXER1_VOL > MIN_VOL_LVL OR nMIXER1_VOL == MAX_VOL_LVL){
		nMIXER1_VOL--
		SEND_STRING dIP_DEV[2],"ITOA(nMIXER1_VOL),'V'"
		SEND_LEVEL dvTP1,1,nMIXER1_VOL
	    }
	}
	CASE 3:{ //TP1 mute
	    IF(nMIXER1_MUTE == FALSE){

		nMIXER1_MUTE = TRUE
		SEND_STRING dIP_DEV[2],"sMIXER_CMDS[OUT_MUTE]"
	    }
	    ELSE{
		nMIXER1_MUTE = FALSE
		SEND_STRING dIP_DEV[2],"sMIXER_CMDS[OUT_UNMUTE]"
		SEND_LEVEL dvTP1,1,nMIXER1_VOL
	    }
	}
	CASE 4:{ //TP2 vol up
	    IF(nMIXER2_VOL < MAX_VOL_LVL OR nMIXER2_VOL == MIN_VOL_LVL){
		nMIXER2_VOL++
		SEND_STRING dIP_DEV[4],"ITOA(nMIXER2_VOL),'V'"
		SEND_LEVEL dvTP2,1,nMIXER2_VOL
	    }
	}
	CASE 5:{ //TP2 vol down
	    IF(nMIXER2_VOL > MIN_VOL_LVL OR nMIXER2_VOL == MAX_VOL_LVL){
		nMIXER2_VOL--
		SEND_STRING dIP_DEV[4],"ITOA(nMIXER2_VOL),'V'"
		SEND_LEVEL dvTP2,1,nMIXER2_VOL
	    }
	}
	CASE 6:{ //TP2 mute
	    IF(nMIXER2_MUTE == FALSE){
		nMIXER2_MUTE = TRUE
		SEND_STRING dIP_DEV[4],"sMIXER_CMDS[OUT_MUTE]"
	    }
	    ELSE{
		nMIXER2_MUTE = FALSE
		SEND_STRING dIP_DEV[4],"sMIXER_CMDS[OUT_UNMUTE]"
	    }
	}
    }
}

DEFINE_START //--------------------------------------------

(*  STARTUP SETTINGS  *)
fnCLIENT_OPEN()

nMIXER2_VOL = DEF_VOL_LVL
SEND_STRING dIP_DEV[4],"ITOA(nMIXER2_VOL),'V'"
SEND_LEVEL dvTP2,1,nMIXER2_VOL

WAIT 1 {
    IP_CLIENT_CLOSE(dIP_DEV[4].PORT)
}

CREATE_BUFFER dIP_DEV[4],sDEV4[10]

DEFINE_EVENT //--------------------------------------------

(*  CLIENT STATUS  *)
DATA_EVENT[dIP_DEV[4]]{
    ONLINE:{
	nCLIENT_ONLINE[4] = TRUE
	SEND_STRING 0,"'COM4 ONLINE'"
    }
    OFFLINE:{
	nCLIENT_ONLINE[4] = FALSE
	SEND_STRING 0,"'COM4 OFFLINE'"
    }
    STRING:{
	sDEV4 = DATA.TEXT
	SEND_STRING 0,"sDEV4"
	SEND_COMMAND dvTP2,"'^TXT,59,0,',sDEV4"
    }
    ONERROR:{ SEND_STRING 0,"ITOA(DATA.NUMBER)" }
}

BUTTON_EVENT[dcVOL]{ //audio ramping & mute
    PUSH:{
	STACK_VAR INTEGER nINDEX
	nINDEX = GET_LAST(dcVOL)
	fnCLIENT_OPEN()
	fnVOL_CTRL(nINDEX)
	TO[dcVOL[nINDEX]]
    }
    HOLD[1,REPEAT]:{
	STACK_VAR INTEGER nINDEX
	nINDEX = GET_LAST(dcVOL)
	fnVOL_CTRL(nINDEX)
    }
}

So, if I increment the vol by 1, in Diagnostics i am expecting to get a string response in one line like this:
Line 23:  Vol43$0D

Instead, I am getting this:
Line 23: V
Line 24: o
Line 25: l
Line 25: 4
Line 26: 3
Line 27: $0D

Any suggestions? Sorry if my code might not make sense but I tried to paste only the essential things and avoid having you the entire code. Thanks in advance for any help.

Cristhian

Comments

  • a_riot42a_riot42 Posts: 1,624
    Not sure why that is going on with an Extron, but your code should be able to deal with incomplete strings. You have no guarantee of a complete string so if you don't code for this you will miss strings.
    Paul
  • jjamesjjames Posts: 2,908
    NetLinx Basics 101

    Agreed.

    If you know the string will always end with a carriage return, you should buffer the text until you have one and then remove it and continue to parse.
  • arbelaezcarbelaezc Posts: 12
    Shouldn't DATA.TEXT show the entire string in the Diagnostics? Not every byte from my response has a carriage return on it, just at the end of the string.
  • jjamesjjames Posts: 2,908
    Depends how quickly/slowly the string comes in.


    Sent from my iPhone using Tapatalk
  • excerpt:
    arbelaezc wrote: »
    DEFINE_START //--------------------------------------------
    
    (*  STARTUP SETTINGS  *)
    fnCLIENT_OPEN()
    
    nMIXER2_VOL = DEF_VOL_LVL
    SEND_STRING dIP_DEV[4],"ITOA(nMIXER2_VOL),'V'"
    SEND_LEVEL dvTP2,1,nMIXER2_VOL
    
    WAIT 1 {
        IP_CLIENT_CLOSE(dIP_DEV[4].PORT)
    }
    
    CREATE_BUFFER dIP_DEV[4],sDEV4[10]
    
    DEFINE_EVENT //--------------------------------------------
    
    (*  CLIENT STATUS  *)
    DATA_EVENT[dIP_DEV[4]]{
        ONLINE:{
    	nCLIENT_ONLINE[4] = TRUE
    	SEND_STRING 0,"'COM4 ONLINE'"
        }
        OFFLINE:{
    	nCLIENT_ONLINE[4] = FALSE
    	SEND_STRING 0,"'COM4 OFFLINE'"
        }
        STRING:{
    	sDEV4 = DATA.TEXT
    	SEND_STRING 0,"sDEV4"
    	SEND_COMMAND dvTP2,"'^TXT,59,0,',sDEV4"
        }
        ONERROR:{ SEND_STRING 0,"ITOA(DATA.NUMBER)" }
    }
    

    So, if I increment the vol by 1, in Diagnostics i am expecting to get a string response in one line like this:
    Line 23:  Vol43$0D
    

    Instead, I am getting this:
    Line 23: V
    Line 24: o
    Line 25: l
    Line 25: 4
    Line 26: 3
    Line 27: $0D
    

    There is documentation of this somewhere but I do not feel like looking it up. Buffer parsing from IP devices is a little different than for RS232. Try it this way. Keep the string data event, but leave it empty. This may seem odd but it is necessary. In the OFFLINE data event, do your buffer parsing, from the buffer variable you created in DEFINE_START and not from data.text. And if you are expecting a $0D at the end of every complete string than do it like:

    while(find_string(sDEV4,"$0D",1))
    {
    char sParseData[100];

    sParseData = remove_string(sDEV4,"$0D",1)
    // do what you need to do with sParseData
    }
  • jjamesjjames Posts: 2,908
    In the OFFLINE data event, do your buffer parsing, from the buffer variable you created in DEFINE_START and not from data.text.
    That's if your device goes offline - some do not, such as the an Integra receiver, or some devices go offline after a few seconds of no communication. I'm not familiar with the Extron Serial->IP device and don't know if it keeps a connection or not. I would think that it keeps a connection or else it'd be a synchronous device, not good acceptable if you're trying to keep of unsolicited feedback.

    I'd recommend not parsing in the offline event.
  • ericmedleyericmedley Posts: 4,177
    Your processor is outrunning the incoming strung. It's a problem as old as the hills. Do not try to evaluate the string until you're sure you have it all. I almost always build the buffer myself when I can.

    Something along the lines of
    MyBuff="MyBuff,data.text"
    If(find_string(MyBuff,<whatever the end delimiter is>,1)
      {
      //now go parse the string...
      }
    

    If there is no end delimiter , I'll put in a slight delay before evaluating, copy the chunk of string that I think might be right and not delete the whole message until I'm sure I got it straight.

    That's my opinion. If you don't like it, I have others...
  • viningvining Posts: 4,368
    ericmedley wrote: »
    Your processor is outrunning the incoming strung. It's a problem as old as the hills. Do not try to evaluate the string until you're sure you have it all. I almost always build the buffer myself when I can.

    Something along the lines of
    MyBuff="MyBuff,data.text"
    If(find_string(MyBuff,<whatever the end delimiter is>,1)
      {
      //now go parse the string...
      }
    

    If there is no end delimiter , I'll put in a slight delay before evaluating, copy the chunk of string that I think might be right and not delete the whole message until I'm sure I got it straight.

    That's my opinion. If you don't like it, I have others...
    I would basically do it this way too but instead of
    If(find_string(MyBuff,<whatever the end delimiter is>,1)
    
    I would do:
    If(find_string(data.text,<whatever the end delimiter is>,1)
    
    I would still use MyBuff to hold the incoming data but doing a find string on it means you're constantly looking through a larger and larger string to find your delimeter (end point). If you only search in data.text for this it's a little easier on the system since your only looking in the last segment (chunk) that came it and this string can't be larger than 2048 which is the max length of data.text. Actaully the string is more likely less than 1500 which is the standard ethernet MTU (max transmission unit(?)). If you're parsing large returns and looking for a delimeter in MyBuffer you could be searching in a strings up to 15999. Like finding a needle in a bale of hay vs finding a needle in a haystack. I'd rather look through the bale. Once you find your delimeter in data.text parse MyBuffer.

    On the downside if your delimeter spans more than 1 data event you're screwed if you do it this way and in that case you have to find_string in MyBuffer.
  • arbelaezcarbelaezc Posts: 12
    jjames wrote: »
    That's if your device goes offline - some do not, such as the an Integra receiver, or some devices go offline after a few seconds of no communication. I'm not familiar with the Extron Serial->IP device and don't know if it keeps a connection or not. I would think that it keeps a connection or else it'd be a synchronous device, not good acceptable if you're trying to keep of unsolicited feedback.

    I'd recommend not parsing in the offline event.

    I am keeping all devices online and when the connections times out, they will go offline. I really only need to parse the string for the projector for lamp hours, current input, and power status. I will review my code and make the edits per the posters input. I appreciate the info and will keep everyone posted.
  • arbelaezcarbelaezc Posts: 12
    ericmedley wrote: »
    Your processor is outrunning the incoming strung. It's a problem as old as the hills. Do not try to evaluate the string until you're sure you have it all. I almost always build the buffer myself when I can.

    Something along the lines of
    MyBuff="MyBuff,data.text"
    If(find_string(MyBuff,<whatever the end delimiter is>,1)
      {
      //now go parse the string...
      }
    

    If there is no end delimiter , I'll put in a slight delay before evaluating, copy the chunk of string that I think might be right and not delete the whole message until I'm sure I got it straight.

    That's my opinion. If you don't like it, I have others...

    Thanks ericmedley! My strings are now coming in one line at a time. Appreciate the help!
Sign In or Register to comment.