Home AMX User Forum AMX Technical Discussion

Sanyo data projector buffers

Hi,

It seems that a well known fact is that the data buffer sent from the Sanyo data projectors have to be really managed to obtain the correct meaning e.g. lamp hours, projector status etc... So I crafted this code:

//DEFINE DEVICE
PROJ = 5001:1:0 // SANYO WTC500L


//DEFINE VARIABLE
INTEGER nLampHourCounter //accumulates lamp hours

CHAR cProjectorStringBuffer[50] //input datastream from data projector
CHAR cProjectorLampTimeBuffer[5] //store lamp time


//DEFINE START

//Create the data projector input stream / buffer
CREATE_BUFFER PROJ, cProjectorLampTimeBuffer
CREATE_BUFFER PROJ, cProjectorStringBuffer

//Read Projector Status CR0
DATA_EVENT[PROJ]
{
ONLINE:
{
//clear buffer
CLEAR_BUFFER cProjectorStringBuffer

SEND_STRING PROJ, "'CR0',$0D"

//Perform active action depending on projector status
SELECT
{
//Projector is in STANDBY
ACTIVE(FIND_STRING(cProjectorStringBuffer,'80',1)):
{}

//Power ON
ACTIVE(FIND_STRING(cProjectorStringBuffer,'00',1)):
{
//Query for lamp time
SEND_STRING PROJ, "'CR3',$0D"

//Get the lamp time
nLampHourCounter = ATOI(GET_BUFFER_STRING(cProjectorLampTimeBuffer,5))

//notify RMS server
RMSSETLAMPHOURS(PROJ,nLampHourCounter)
}

//Countdown in process i.e. WARMING
ACTIVE(FIND_STRING(cProjectorStringBuffer,'40',1)):
{
//Query for lamp time
SEND_STRING PROJ, "'CR3',$0D"

//Get the lamp time
nLampHourCounter = ATOI(GET_BUFFER_STRING(cProjectorLampTimeBuffer,5))

//notify RMS server
RMSSETLAMPHOURS(PROJ,nLampHourCounter)
}

//Countdown in process i.e. COOLING
ACTIVE(FIND_STRING(cProjectorStringBuffer,'20',1)):
{}

//Power FAILURE
ACTIVE(FIND_STRING(cProjectorStringBuffer,'10',1)):
{}

//Cooling Down in process due to Abnormal Temperature
ACTIVE(FIND_STRING(cProjectorStringBuffer,'28',1)):
{}


//Standby after Cooling Down due to Abnormal Temperature
ACTIVE(FIND_STRING(cProjectorStringBuffer,'88',1)):
{}

//Power-Save Cooling Down in process
ACTIVE(FIND_STRING(cProjectorStringBuffer,'24',1)):
{}

//Cooling Down in process after OFF due to Lamp Failure
ACTIVE(FIND_STRING(cProjectorStringBuffer,'21',1)):
{}

//Standby after Cooling Down due to Lamp Failure
ACTIVE(FIND_STRING(cProjectorStringBuffer,'81',1)):
{}

//Cooling Down in process after Power Off due to Shutter management
ACTIVE(FIND_STRING(cProjectorStringBuffer,'2C',1)):
{}

//Standby after Cooling Down due to Shutter management
ACTIVE(FIND_STRING(cProjectorStringBuffer,'8C',1)):
{}
}

}

}

QUESTION: what wrong with this code? Any pointers will be really appreciated.

Comments

  • ericmedleyericmedley Posts: 4,177
    Wow, I'd guess thers quite a bit wrong here. But, it would help to know the projector protocol before passing any judgement.
  • viningvining Posts: 4,368
    I would start by putting your select active in a string handle on the the data event not the online handler.

    Next i would find out if the devices sends a delimiter so you know when you've received a full command, then grab that string and process it. You also have to empty your buffer upon every complete string.

    Personally i wouldn't use a created buffer but a local var appened to itself fed from data.text. I'd post an example but i'm on an iPad.
  • ericmedley wrote: »
    Wow, I'd guess thers quite a bit wrong here. But, it would help to know the projector protocol before passing any judgement.

    The biggest issue with the Sanyo (Eiki, Christie, whoever else OEM's from Sanyo) is that the projector doesn't reply with anything that says to which command it is replying. Whenever I work with these projectors, I just keep track of the last command I sent and parse the response from there. For instance, I recently programmed a Christie DHD800, which is OEM'd from Sanyo and uses this same protocol. When you send the CR3 command (query for lamp hours), it sends back something similar to 00034 00034 [CR]. This says that the projector has 2 lamps, and both lamps have been on for 34 hours. I then parsed the response based on what the last command sent to the projector was. See the example below for more details.
    DEFINE_FUNCTION fnSendProjCommand(char cCMD[24]){
    	
    	if(nProjOnline = 0) {
    		IP_CLIENT_OPEN(dvFrontProj.PORT,proj_ip,proj_port,IP_TCP)
    	}
    	wait_until(nProjOnline = 1 && nProjOKtoSend = 1) {
    		send_string 0,"'to proj: ',cCMD,13"
    		send_string dvFrontProj,"cCMD,13"
    	}
    	
    	cLastProjCommand = cCMD
    }
    data_event[dvFrontProj]
    //cut out the online/offline stuff, just for clarity
    	string:{
    		stack_var char cTemp[255]
    		
    		send_string 0,"'from proj: ',data.text"
    		if(find_string(data.text,'PASSWORD:',1)){
    			send_string 0,"'to proj:',13,10"
    			send_string data.device,"13,10"
    		}
    		if(find_string(data.text,'Hello',1)){
    			nProjOktoSend = 1
    		}
    		while(find_string(data.text,"13",1) && nProjOktoSend = 1){
    			cTemp = remove_string(data.text,"13",1)
    			
    			select {
    				active(find_string(cLastProjCommand,"'CR3'",1)):{
    					nLampHours[1] = atoi(remove_string(cTemp,' ',1))
    					
    					nLampHours[2] = atoi(remove_string(cTemp,"13",1))
    					
    					send_command dvIpadProj,"'^TXT-1,0,Lamp 1:',itoa(nLampHours[1]),' hrs Lamp 2:',itoa(nLampHours[2]),' hrs'"
    				}
    				active(find_string(cLastProjCommand,"'CR0'",1)):{
    					if(find_string(cTemp,'00',1))
    						nProjStatus = 1
    					else if(find_string(cTemp,'80',1))
    						nProjStatus = 0
    					else if(find_string(cTemp,'40',1))
    						nProjStatus = 2 //warming
    					else if(find_string(cTemp,'20',1))
    						nProjStatus = 3 //cooling
    					else
    						send_string 0, 'Projector error. Please check Projector for issues'
    					
    					if(nProjStatus = 2 || nProjStatus = 3 && !timeline_active(TL_BLINK))
    						timeline_create(TL_BLINK,lTL_BLINK,length_array(lTL_BLINK),timeline_relative,timeline_repeat)
    					else if(nProjStatus = 0 || nProjStatus = 1 && timeline_active(TL_BLINK))
    						timeline_kill(TL_BLINK)
    				}
    			}
    		}
    	}
    
    

    I know I'm not following good procedure by doing the variable that concatenates from data.text, but this works with the projector I'm using. Hope that helps.
  • regallionregallion Posts: 95
    	wait_until(nProjOnline = 1 && nProjOKtoSend = 1) {
    		send_string 0,"'to proj: ',cCMD,13"
    		send_string dvFrontProj,"cCMD,13"
    	}
    

    Does this actually work? Does WAIT_UNTIL work differently to WAIT with regards to referring to variables that may have expired (cCMD in this case) ?
  • regallion wrote: »
    	wait_until(nProjOnline = 1 && nProjOKtoSend = 1) {
    		send_string 0,"'to proj: ',cCMD,13"
    		send_string dvFrontProj,"cCMD,13"
    	}
    

    Does this actually work? Does WAIT_UNTIL work differently to WAIT with regards to referring to variables that may have expired (cCMD in this case) ?

    Yes, it works.
  • regallionregallion Posts: 95
    Can I just clarify then, WAIT_UNTIL is not like WAIT? The code effectively halts until the condition is true?

    As I understand WAIT - the processor will create another thread somewhere else in the ether which is why it loses all access to local variables. I thought WAIT_UNTIL was the same? Am I wrong?

    Or can WAIT also still maintain access to passed parameter variables after the function has exited?
  • viningvining Posts: 4,368
    Waits only loose access to stack vars, locals & globals are fine.

    There are no additional threads. When the code comes across a wait the wait is put into a queue structure that holds the wait id or name, the time and a pointer to the code segment to execute, either 1 line or brace to brace immediately following the wait pointer. The wait queue is regularly checked and their times decremented and when their time is reached they execute the code refenced by the pointer. Since stacks don't hold their value or even exist after after the code that created them completes they can't be referenced later when the wait executes a sub section of that code. Not a problem with locals or globals though. Of course those values may not be what you expected by the time the wait executes.
  • regallionregallion Posts: 95
    My main query was the visibility of cCMD.

    As long as it's declared global or local from the calling section of code then it's all good.

    I forgot parameters are passed by reference instead of value (that is right isn't it? lol).
  • HedbergHedberg Posts: 671
    Seen bunches of these and parsing the response to a status query really isn't that tough. The responses are all easily identifiable and are terminated with $0D so just look for a particular string. For example, if data.text is "'20',$0d", then you know that the projector has responded to a status request and that it is in the cool down cycle. Responses to lamp hours requests will be five character strings ($0D as the last character). To the best of my knowledge, these projectors only send a five character string in response to a lamp hours request. Some of these projectors will sometimes give crap responses, but I've never seen one that actually caused a problem because of a crap string. That is, a string may be missed, but never interpreted as something else. Typically, the projector status is queried once per second and the responses are just evaluated with no special logic at all.
Sign In or Register to comment.