Home AMX User Forum NetLinx Studio
Options

Kaleidescape Module 7.20

Has anyone had issues with the SATP with the new version of the module?

I am not getting any kind of feedback on the SATP but I have some control.


here is my include file
PROGRAM_NAME='KPLAYERS'
/*******************************************************************************************
 * File Information
 *******************************************************************************************/

/*
 * (c) 2004-2009 Kaleidescape Inc.  Please see legal notice below.
 * 
 * Version 7.2.0
 * Dated Jan 16, 2009
 * 
 * This module has been created and supported by Kaleidescape Inc.
 * This module is supported directly by Kaleidescape Inc.  Please
 * direct all inquiries regarding this module to  Kaleidescape support
 * at (650) 625-6160.  AMX does not support this module itself.
 * 
 * ----------------------------------------------------------------------
 * 
 * LEGAL NOTICE
 *     (c) 2004-2009 Kaleidescape Inc. This Source Code is the intellectual 
 * property of Kaleidescape, Inc. Kaleidescape grants the non-exclusive 
 * right to modify and/or compile and/or compress the Source Code, and 
 * upload such modified and/or compiled and/or compressed Source Code 
 * to control systems that control at least one licensed Kaleidescape 
 * System. No right is granted to otherwise copy, reproduce, modify, 
 * upload, download, transmit, or distribute the Source Code, or 
 * derivative works, in any way. The Source Code, and derivative works, 
 * are protected by copyright, trade secret, and other intellectual 
 * property laws and by international treaties, which provide rights 
 * and obligations in addition to the rights and obligations set forth here.
 */
 
/*******************************************************************************************
 * Device Definitions
 *******************************************************************************************/
DEFINE_DEVICE

/********************************************************
 *  Create a virtual device per KPlayer controlled.     *
 ********************************************************/
KPLAYER1	 = 33005:1:0
KPLAYER2	 = 33006:1:0
KPLAYER3	 = 33007:1:0
KPLAYER4	 = 33008:1:0
KPLAYER5	 = 33009:1:0
KPLAYER6	 = 33010:1:0
KPLAYER7	 = 33011:1:0
KPLAYER8	 = 33012:1:0
/*******************************************************************************************
 * Global Variables
 *******************************************************************************************/
DEFINE_VARIABLE

/********************************************************
 *  Put all Kplayer virtual devs into one array.        *
 ********************************************************/
dev Kplayer[] = //Add any additional controlled players here
{
    KPLAYER1,
    KPLAYER2,
    KPLAYER3,
    KPLAYER4,
    KPLAYER5,
    KPLAYER6,
    KPLAYER7,
    KPLAYER8
}

/********************************************************
 *  Define all the touch panels that will control a     *
 *  Kaleidescape Player and which player they will      *
 *  control.                                            *
 ********************************************************/
//An array of all the touch panels that will control the K Players
dev KTouchPanels[] = 
{ 
    dvFoyer_Kal,
    dvWineCellar_Kal,
    dvBedroom1_Kal,
    dvHall7_Kal,
    dvGarage_Kal,
    dvExercise_Kal,
    dvDining_Kal,
    dvKitchen_Kal,
    dvHerVanity_Kal,
    dvLoggia_Kal,
    dvSnackBar_Kal,
    dvTheater_Kal,
    dvCabana_Kal,
    dvSafe_Kal
}

//This array determines what player each touch panel controls
integer controlledKplayer[] = //for each touch panel, add an entry to this array
{
    0,0,0,0,0,
    0,0,0,0,0,
    0,0,0,0,0
    //First TP to control Player 1
    //2,       //Second TP to control Player 2
    //3,       //Third TP to control Player 3
}

/********************************************************
 *  Create an array of the Kaleidescape TP page names.  *
 *  These pages are called when the player flips to the *
 *  indicated page.                                     *
 ********************************************************/    
char KTouchpanelPages[20][20] //page names on the touch panel for automatic page flip
   
/********************************************************
 *  Create an array of the Kaleidescape TP popup names. *
 *  These popups are called at various points.          *
 ********************************************************/    
char KTouchpanelPopups[10][30] //popup names on the touch panel
   
/***********************************************************
 *  Create an array of the Kaleidescape TP resource names. *
 *  These resources are updated with the URL of the        * 
 *  appropriate images.                                    *
 ***********************************************************/    
char KTouchpanelResources[3][20] //resource names on the touch panel

/**********************************************************
 *  Create an array of the Kaleidescape TP bitmap names.  *   
 *  These are used in SATP to indicate the status of each *
 *  line shown on the touch panel text window.            *
 *********************************************************/
char KTouchpanelIcons[3] = //icons on the touch panel
{
    3, //play
    2, //pause
    1 //checkmark
}
   
/********************************************************
 *  Create an array of the Variable Text fields to      *
 *  populate on the touch panel.                        *
 ********************************************************/
integer KVariableTextFields[30] = 
{
    1,			//Movie Title
    2,			//Chapter number
    3,			//Total track time
    4,			//Remaining track time 
    5,            	//Current track time 
    6,			//Keyboard & Keypad prompt
    7,			//Keyboard & Keypad entry field
    0,	//unused	//Player friendly name
    8,			//Details title
    9,			//Details info
    11,			//Song Name
    12,			//Artist Name
    13,			//Album Name
    14,			//SATP Search Button
    15,			//SATP Keyboard Text feedback
    16,			//SATP Search Mini Result
    17,			//Now Playing Item
    18,			//PRESET 1 Label
    19,			//PRESET 2 Label
    20,			//PRESET 3 Label
    21,			//PRESET 4 Label
    22,			//PRESET 5 Label
    23,			//PRESET 6 Label
    24,			//PRESET 7 Label
    25,			//PRESET 8 Label
    26,			//PRESET 9 Label
    27			//PRESET 10 Label
}                             
 
/********************************************************
 *  The following two arrays are only needed under      *
 *  special circumstances.  If the keyboard strings come*
 *  from a different device than the button and level   *
 *  events, enable these arrays and define the          *
 *  information just as above.                          *
 ********************************************************/
//An array of all the keyboard devices that will control the K Players
/*dev keyboardDev[] = 
{ 
    TP_ONE_KEYB  //Note that this is different than the TP_ONE above.
    //TP_TWO_KEYB,
    //TP_THREE_KEYB,
}

//This array determines what player each touch panel keyboard controls
integer controlledKplayerKeyboard[] =
{
    1          //First TP Keyboard to control Player 1
    //2,       //Second TP Keyboard to control Player 2
    //1,       //Third TP Keyboard to control Player 1
}
*/

/*********************************************************
 *  Define the module with the created variables/devices.*
 *********************************************************/
DEFINE_MODULE 'Kaleidescape' Kplayers (
	    dvKalPort, 		//port (232 or TCP/IP) used for communication with 
					//the Kaleidescape System
	    Kplayer, 			//virtual device array for communicating with one 
					//or more Kaleidescape Players
	    KVariableTextFields,	//Variable text fields to use on the touch panel
	    KTouchpanelPages, 		//Pages to flip to on the touch panel for 
					//various OSD states
	    KTouchpanelPopups, 		//Popup pages to turn on/off on the touch panel
	    KTouchpanelResources, 	//Touch panel resources (cover art) to update
	    KTouchpanelIcons) 		//Bitmaps to use


/******************************************************************************************
 *                Startup code                                                            *
 ******************************************************************************************/
DEFINE_START

//Populate the TP page names array
/*KTouchpanelPages[1] = 'K MovieList'		//Movie List control page
KTouchpanelPages[2] = 'K Collections'		//Movie Collections control page
KTouchpanelPages[3] = 'K CoverArt'		//Movie Covers control page
KTouchpanelPages[4] = 'K ParentalControl'	//Parental controls page
KTouchpanelPages[5] = ''			//unused - reserved for future use 
KTouchpanelPages[6] = ''			//Screen Saver enabled
KTouchpanelPages[7] = 'K Movie Playing'		//Playing a movie
KTouchpanelPages[8] = 'K StatusScreen'		//System status page
KTouchpanelPages[9] = 'K MusicList'		//Music List control page
KTouchpanelPages[10]= 'K MusicCovers'		//Music Covers control page
KTouchpanelPages[11]= 'K MusicCollections'	//Music Collections control page
KTouchpanelPages[12]= 'K MusicNowPlaying'	//Music Now Playing control page



KTouchpanelPopups[1] = 'K Keyboard'		//Keyboard popup page
KTouchpanelPopups[2] = 'K Keypad'		//Numeric keypad popup page
KTouchpanelPopups[3] = 'K Music Playing'	//Music Playing info popup
KTouchpanelPopups[4] = 'K Details'		//Details popup (only used for OSD w/o Video control)
KTouchpanelPopups[5] = ''			//SATP Now Playing popup
KTouchpanelPopups[6] = 'K SATP Keyboard Search'	//SATP Keyboard Search

KTouchpanelResources[1] = 'K Now Playing Cover'	//Cover art for the currently playing item
KTouchpanelResources[2] = 'K Details Cover'	//Cover art for the currently showing details
KTouchpanelResources[3] = ''			//unused - reserved for future use
*/

/*********************************************************
 *  Set up interface information.                        *
 *********************************************************/
 
/*
 * tell the module the what this virtual device should control:
 *   send_command <virtual device>, '<ID>[.<Zone>][/<sequence>]'
 * 
 * <ID> is set on the player as the
 * "Control Protocol Device ID" within the installer web 
 * utility.  If the IP addresse defined below (in the 'IP' command)
 * is the IP address of the player, you can simply use an ID of '01'
 * and the module will figure out the correct setting.
 * 
 * example:
 * send_command KPLAYER1, 'ID 05' will make the virtual device
 *                                KPLAYER1 control the player 
 *                                whose Control Protocol Device ID
 *                                is set to 5.
 *
 * if using SATP, you need to define which zone you want to 
 * control.  Zone (output) 1 of the Kaleidescape Music Player
 * is defined as '.01' Zone 2 is '.02' etc.  If you want to set 
 * the virtual device to use SATP with a Kaleidescape Movie
 * Player, set the Zone to '.01'
 *
 * examples:
 * send_command KPLAYER1, 'ID 05.03' will make the virtual device
 *                                   KPLAYER1 control output 3 on
 *                                   player 5 using SATP.
 *
 * send_command KPLAYER1, 'ID 05.01' will make the virtual device
 *                                   KPLAYER1 control output 1 on
 *                                   player 5 using SATP. (the only
 *                                   output for a movie player)
 *
 * if you need to control the same player's Zone with multiple, different
 * sized touch panels or you want to allow each touch panel to browse
 * independently, you need to define separate virtual devices
 * that use the same ID and Zone, but use different sequence numbers.
 * The sequence number is a single digit between 1 and 9.
 * 
 *  example:
 *   send_command KPLAYER1, 'ID 05.03/1' 
 *   send_command KPLAYER2, 'ID 05.03/2'
 * 				    both KPLAYER1 and KPLAYER2 will
 * 				    control output 3 on player 5,
 * 				    but will have independant browse
 * 				    trees, and can show a different
 * 				    number of lines on the touch panel.
 */

/*
 * sometimes there are problems with the startup sequence.
 * this wait will clear up those problems by ensuring these
 * commands are only sent after the event handler starts.
 */

Send_Command KPLAYER1,'DEBUG'

wait 50
{
    
    send_command KPLAYER1, 'ID 02'		//OSD control
    send_command KPLAYER2, 'ID 03'		//OSD control
    send_command KPLAYER3, 'ID 04'		//OSD control
    send_command KPLAYER4, 'ID 05'		//OSD control
    send_command KPLAYER5, 'ID 06.01'		//SATP control
    send_command KPLAYER6, 'ID 06.02'		//SATP control
    send_command KPLAYER7, 'ID 06.03'		//SATP control
    send_command KPLAYER8, 'ID 06.04'		//SATP control
    
    //send_command KPLAYER2, 'ID 01.01'		//SATP control

    //send_command KPLAYER2, 'ID 04'
    //send_command KPLAYER3, 'ID 05'

/*
 * If Kaleidescape_Port is a network port, tell the module 
 * the ip address to connect to.  This only needs to be set
 * once for the entire module and can be sent through 
 * any one of the defined virtual devices.
 */

/*
 * Kaleidescape recommends using the server as the single
 * point of connection.  That way if any player is turned off,
 * the rest of the system will still function.
 */

    send_command KPLAYER1, 'IP 192.168.4.150'    
    //send_command KPLAYER2, 'IP 192.168.4.150'    
//    send_command KPLAYER3, 'IP 192.168.4.150'    
//    send_command KPLAYER4, 'IP 192.168.4.150'    
//    send_command KPLAYER5, 'IP 192.168.4.150'    
//    send_command KPLAYER6, 'IP 192.168.4.150'    
//    send_command KPLAYER7, 'IP 192.168.4.150'    
//    send_command KPLAYER8, 'IP 192.168.4.150'    

//Tell the module how many SATP lines of text to populate on this interface

    send_command KPLAYER5, 'LINES 6'		//AXT-CV6 and CP4
    send_command KPLAYER6, 'LINES 6'		//CV10, MVP-8400 and NXT-1700
    send_command KPLAYER7, 'LINES 6'
    send_command KPLAYER8, 'LINES 6'

/*
 * This section sets up the keypad control of the module.
 * Keypads can be used to start music playing without requring the user
 * to use either a video display or a text-based touch panel.
 */

/* 
 * There are two methods for keypad control.  The first is a set of buttons
 * to cycle through any collection.  Each virtual device can be instructed
 * to control up to three collections.  There are 3 channels per collection -
 * first, next and previous.
 */

    //define the first set of channels to cycle through albums
    //send_command KPLAYER1, 'COLLECTION 1 Albums' 
    //first = 131, next = 132, previous = 133

    //define the second set of channels to cycle through genres
    //send_command KPLAYER1, 'COLLECTION 2 Genres' 
    //first = 134, next = 135, previous = 136

    //define the third set of channels to cycle through mix albums
    //send_command KPLAYER1, 'COLLECTION 3 Mix Albums' 
    //first = 137, next = 138, previous = 139

/* 
 * For keypad control, all virtual devices are set up independantly.  If KPLAYER2 is defined,
 * and a keypad is to be used to control it, copy the above commands and define which collections
 * it will cycle through.
 */

/* 
 * The second method for keypad control is using presets.
 * The 10 channels for presets are 121-130
 * Every virtual device needs a Preset ID to identify which set of presets to use.
 * This ID is used to identify a set of presets to the player, and may one day
 * be visible to the user.
 *
 * The Preset ID is used to create the tag that is ultimately used per preset.
 * If a Preset ID of 'test' is used, the first preset (channel 121) will use the tag
 * 'test1', the second preset (channel 122) will use the tag 'test2', etc.
 */

    //send_command KPLAYER1, 'PRESET ID test'
 
/* 
 * if two different players are to share a set of presets, give them the same Preset ID.
 * To save a preset, press and hold the channel (use a "to" statement) and the preset will save
 * See below for sample code.
 */

} //end of wait


/**************************
 * End of interface setup *
 **************************/

/*
 * Use this call to match user defined events.  User defined events can be used
 * inside scripts to make the control system do something.  Examples may be to
 * change the lighting level, open or close curtains, or whatever else the end-user
 * may want to happen automatically from a script that they write.
 * 
 * A good suggestion is to give the user a mechanism to disable the screen masking
 * changes.  This way, when they've got a script that plays a series of favorite 
 * scenes, they can leave the masking set to a specific position.  You may also 
 * want to provide a command to re-enable the auto masking within a script
 * so that if the user wanted to create a script that played a series of favorite
 * scenes, then a movie - they could freeze the mask when desired (scenes) then 
 * allow the mask to automatically move at the appropriate time (during the movie).
 */
/*DEFINE_CALL 'Kaleidescape User Commands' (integer kplayerReference, char userCommand[])
{
    userCommand = upper_string(userCommand) //make all text case insensitive
    
    switch (userCommand)
    {
	case 'OPEN CURTAINS':	{}
	case 'DISABLE MASK':	{}
	case 'ENABLE MASK':	{}
	case 'START POPCORN':	{}
    }
}*/
/*******************************************************************************************
 * Event Handlers
 *******************************************************************************************/
DEFINE_EVENT

/*
 * if the TP is controlling multiple players or other devices with the same
 * channel, level or VT numbers, put a conditional on the events below
 * and only send controls or feedback when the Player is selected on the TP.
 *
 * In such a case, the UI may be out of sync.  Issue the following command:
 * send_command KPLAYER1, 'refresh'
 * and the TP page flips, VT data and levels will be resent.
 */

DATA_EVENT[KTouchPanels]
{
    online:
    {
	stack_var integer touchpanelRef
	stack_var integer kplayerRef
	
	//determine which touch panel triggered the event
	touchpanelRef = get_last(KTouchPanels)
	
	//determine which player this panel is controlling
	kplayerRef = controlledKplayer[touchpanelRef]
	
	//tell the controlled player to refresh the signals
	if(kplayerRef > 0) send_command Kplayer[kplayerRef], 'refresh'
    }
    
    /*
     * keyboard input can be accomplished by placing text within the buttons on the 
     * touch panel. Set key "a" to "KAL-A", "b" to "KAL-B", etc.  Any single character 
     * command sent to the interface will be processed as if it were a keyboard key.    
     * the "space" and "backspace" buttons are handled with the button event below.  
     * Space is channel 66 and backspace is 67
     */
    
    string:
    {
	stack_var char 		kCharacter[10]
	stack_var integer 	touchpanelRef
	stack_var integer 	kplayerRef
	
	//determine which touch panel triggered the event
	touchpanelRef = get_last(KTouchPanels) 
	
	//determine which player this panel is controlling
	kplayerRef = controlledKplayer[touchpanelRef]
	
	//only proceed if the ref is valid
	if(kplayerRef > 0)
	{	    	
	    if(find_string(data.text,'KAL-', 1)) // when our custom header is found
	    {
		remove_string(data.text, 'KAL-', 1) // remove the header
		kCharacter = "get_buffer_char(data.text)" // remove the keyboard character
		
		if(kCharacter[1] = '[') // if this is a fuzzy set (for instance [abc2] )
		{
		    //pull out the remaining characters
		    kCharacter = "'[', remove_string(data.text,']',1)" 
		}
		// send that character (or set) to the player
		send_command Kplayer[kplayerRef], "kCharacter"
	    }
	    
	    if(find_string(data.text,'WAKE',1)) // when the TP wakes up
	    {	
		// clear the screen saver from the kplayer (if active)
		pulse [Kplayer[kplayerRef],79] 
	    }
	}
    }
}

/*
 * Let's wake up the panel when the K screen saver kicks in, then wake up the panel when 
 * it clears. The above DATA_EVENT will clear the K screen saver when the touch panel 
 * wakes up (opposite of this CHANNEL_EVENT)
 *
 * Use caution when changing which player the TP is controlling.  If you change the 
 * controlledKplayer array and send 'refresh' the panel will immediately sleep if the 
 * K screen saver is up.  The best implementation is to clear the screen saver
 * with channel 79 immediately before sending the refresh to prevent this.
 */
/*CHANNEL_EVENT[Kplayer, 1078] //screen saver feedback
{
    on:
    {
	stack_var integer touchpanelRef
	stack_var integer kplayerRef
	
	kplayerRef = get_last(Kplayer) //determine which player triggered the event

	//step through each TP
	for(touchpanelRef = 1; touchpanelRef <= length_array(KTouchPanels); touchpanelRef++)	
	{
	    //if this TP is controlling this player
	    if(controlledKplayer[touchpanelRef] = kplayerRef) 
	    {
		//put the panel to sleep too
		send_command KTouchPanels[touchpanelRef], 'SLEEP' 
	    }
	}
    }
    off:
    {
	stack_var integer touchpanelRef
	stack_var integer kplayerRef
	
	kplayerRef = get_last(Kplayer) //determine which player triggered the event
	
	//step through each TP
	for(touchpanelRef = 1; touchpanelRef <= length_array(KTouchPanels); touchpanelRef++)
	{
	    //if this TP is controlling this player
	    if(controlledKplayer[touchpanelRef] = kplayerRef) 
	    {
		send_command KTouchPanels[touchpanelRef], 'WAKE' //wake up the panel	    
	    }
	}	
    }
}*/

/********************************************************
 *  Pass VT and level information to the touch panel.   *
 ********************************************************/
DATA_EVENT[Kplayer]
{
    string:
    {
	stack_var integer touchpanelRef
	stack_var integer kplayerRef
	
	kplayerRef = get_last(Kplayer) //determine which player triggered the event
	
	//step through each TP
	for(touchpanelRef = 1; touchpanelRef <= length_array(KTouchPanels); touchpanelRef++)
	{
	    //if this TP is controlling this player
	    if(controlledKplayer[touchpanelRef] = kplayerRef)			
	    {	    
		switch (data.text[1]) //evaluate the first character
		{
		    case '~': //string passing flag 
		    {
			switch (data.text[2]) //evaluate the second character
			{
			    case 'U': //user defined command 
			    {
				stack_var char userCommand[256]
				userCommand = right_string(data.text, length_string(data.text) - 2)
				//call 'Kaleidescape User Commands' (kplayerRef, userCommand)
			    }
			}
		    }
		    default: //pass all other commands directly to the touch panel
		    {
			send_command KTouchPanels[touchpanelRef], data.text
		    }
		} //end of 'switch'
	    } //end of 'if'
	} //end of 'for'
    } //end of 'string'
} //end of event

/*********************************************************
 *  Pass progress level information to the touch panel.  *
 *********************************************************/
LEVEL_EVENT[Kplayer, 1]
{
    stack_var integer touchpanelRef
    stack_var integer kplayerRef
    
    kplayerRef = get_last(Kplayer) //determine which virtual device triggered the event
    
    //step through each TP
    for(touchpanelRef = 1; touchpanelRef <= length_array(KTouchPanels); touchpanelRef++)
    {
	//if this TP is controlling this player
	if(controlledKplayer[touchpanelRef] = kplayerRef) 
	{
	    //send the level to the touch panel
	    send_level KTouchPanels[touchpanelRef], 1, level.value
	}
    }
}

/*********************************************************
 *  Pass scroll bar top information to the virtual dev.  *
 *********************************************************/
LEVEL_EVENT[KTouchPanels, 4]
{
    stack_var integer touchpanelRef
    stack_var integer kplayerRef
    
    //determine which touch panel triggered the event
    touchpanelRef = get_last(KTouchPanels) 
    
    //determine which player this panel is controlling
    kplayerRef = controlledKplayer[touchpanelRef]
    
    if(kplayerRef)
    {
	send_level Kplayer[kplayerRef], 4, level.value
    }
}

/*********************************************************
 *  Pass scroll bar top information to the touch panel.  *
 *********************************************************/
LEVEL_EVENT[Kplayer, 5]
{
    stack_var integer touchpanelRef
    stack_var integer kplayerRef
    
    kplayerRef = get_last(Kplayer) //determine which virtual device triggered teh event
    
    //step through each TP
    for(touchpanelRef = 1; touchpanelRef <= length_array(KTouchPanels); touchpanelRef++)
    {
	//if this TP is controlling this player
	if(controlledKplayer[touchpanelRef] = kplayerRef) 
	{
	    //send the level to the touch panel
	    send_level KTouchPanels[touchpanelRef], 5, level.value 
	}
    }
}

/************************************************************
 *  Pass scroll bar bottom information to the touch panel.  *
 ************************************************************/
LEVEL_EVENT[Kplayer, 6]
{
    stack_var integer touchpanelRef
    stack_var integer kplayerRef
    
    kplayerRef = get_last(Kplayer) //determine which virtual device triggered teh event
    
    //step through each TP
    for(touchpanelRef = 1; touchpanelRef <= length_array(KTouchPanels); touchpanelRef++)
    {
	//if this TP is controlling this player
	if(controlledKplayer[touchpanelRef] = kplayerRef) 
	{
	    //send the level to the touch panel
	    send_level KTouchPanels[touchpanelRef], 6, level.value
	}
    }
}

/***********************************************************
 *  Pass all TP presses to module through virtual dev      *
 *  note that virtual dev follows standard IR button order *
 ***********************************************************/
BUTTON_EVENT[KTouchPanels, 0]
{
    push:
    {
	stack_var integer touchpanelRef
	stack_var integer kplayerRef
	
	//determine which touch panel triggered the event
	touchpanelRef = get_last(KTouchPanels)
	
	//determine which player this panel is controlling
	kplayerRef = controlledKplayer[touchpanelRef]	
	if(kplayerRef > 0)
	{
	    to[Kplayer[kplayerRef], push_channel]
	}
    }
}

/****************************************************
 *  An example of how to invoke a script.           *
 ****************************************************/
/*BUTTON_EVENT[KTouchPanels, 201]
{
    push:
    {
	stack_var integer touchpanelRef
	stack_var integer kplayerRef
	
	//determine which touch panel triggered the event
	touchpanelRef = get_last(KTouchPanels)

	//determine which player this panel is controlling
	kplayerRef = controlledKplayer[touchpanelRef]
	if(kplayerRef > 0)
	{
	    send_command Kplayer[kplayerRef], "'PLAY_SCRIPT:Theater Startup:'"
	}
    }
}*/

/****************************************************
 *  Pass TP XY levels to module through virtual dev *
 ****************************************************/
LEVEL_EVENT[KTouchPanels, 7]
{
    stack_var integer touchpanelRef
    stack_var integer kplayerRef
    
    //determine which touch panel triggered the event
    touchpanelRef = get_last(KTouchPanels) 
    
    //determine which player this panel is controlling
    kplayerRef = controlledKplayer[touchpanelRef]
    if(kplayerRef > 0) 
    {
	send_level Kplayer[kplayerRef], 50, level.value
    }
}

LEVEL_EVENT[KTouchPanels, 8]
{
    stack_var integer touchpanelRef
    stack_var integer kplayerRef
    
    //determine which touch panel triggered the event
    touchpanelRef = get_last(KTouchPanels)
    
    //determine which player this panel is controlling
    kplayerRef = controlledKplayer[touchpanelRef]
    if(kplayerRef > 0) send_level Kplayer[kplayerRef], 51, level.value
}

/********************************************************
 *  The following events should be copied/pasted for    *
 *  use by the different players, if needed.            *
 ********************************************************/

/********************************************************
 *  Receive events from KPlayer for lighting cues       *
 ********************************************************/
/*CHANNEL_EVENT[KPLAYER1, 1090] //lights movie
{
    on:
    {
//	pulse[IR_LIGHTS,5] //instruct the lights to turn off
    }
}

CHANNEL_EVENT[KPLAYER1, 1091] //lights intermission
CHANNEL_EVENT[KPLAYER1, 1092] //lights credits
CHANNEL_EVENT[KPLAYER1, 1093] //lights non-movie
{
    on:
    {
//	pulse[IR_LIGHTS,1] //instruct the lights to turn on
    }
}*/


/********************************************************
 *  Receive events from KPlayer for masking information *
 ********************************************************/
/*LEVEL_EVENT[KPLAYER1, 11] //Screen masking data
{
    switch (level.value)
    {
	case 0: {} //unknown masking - open wide 
	case 1: {} //image is 1.33 (4x3)
	case 2: {} //image is 1.66
	case 3: {} //image is 1.78 (16x9)
	case 4: {} //image is 1.85
	case 5: {} //image is 2.35
    }
}*/

/***********************************************************
 *  Receive events from KPlayer for video mode information *
 ***********************************************************/
 
/*
 * Information about the video mode is returned via the levels
 * 20 (Composite & S-video), 21 (Component) and 22 (HDMI).
 * These values may be used to set a scaler or change inputs
 * on a projector that wants different video modes on different
 * inputs.
 */

//LEVEL_EVENT[KPLAYER1, 20]	//Composite & Svideo mode
//LEVEL_EVENT[KPLAYER1, 22]	//HDMI vide mode
/*LEVEL_EVENT[KPLAYER1, 21]	//Component video mode
{
    switch (level.value)
    {
	case 00: {}          //  no output
	case 01: {}          //  480i60 4x3   NTSC
	case 02: {}          //  480i60 16x9
	case 03: {}          //  480p60 4x3
	case 04: {}          //  480p60 16x9
	case 05: {}          //  576i50 4x3   PAL
	case 06: {}          //  576i50 16x9
	case 07: {}          //  576p50 4x3
	case 08: {}          //  576p50 16x9
	case 09: {}          //  720p60       NTSC
	case 10: {}          //  720p50       PAL
	case 11: {}          //  1080i60      NTSC
	case 12: {}          //  1080i50      PAL
	case 13: {}          //  1080p60      NTSC
	case 14: {}          //  1080p50      PAL
    }
}*/

BUTTON_EVENT[KTouchPanels,2000] //select none
BUTTON_EVENT[KTouchPanels,2001] //select KPLAYER1
BUTTON_EVENT[KTouchPanels,2002] //select KPLAYER2
BUTTON_EVENT[KTouchPanels,2003] //select KPLAYER3
BUTTON_EVENT[KTouchPanels,2004] //select KPLAYER4
BUTTON_EVENT[KTouchPanels,2005] //select KPLAYER5
BUTTON_EVENT[KTouchPanels,2006] //select KPLAYER6
BUTTON_EVENT[KTouchPanels,2007] //select KPLAYER7
BUTTON_EVENT[KTouchPanels,2008] //select KPLAYER8
{
    push:
    {
	stack_var integer touchpanelRef
	stack_var integer interfaceRef
	
	touchpanelRef = get_last(KTouchPanels)		//determine the TP that generated this event
	interfaceRef = button.input.channel - 2000	//determine the Player Ref that is requested
	
	controlledKplayer[touchpanelRef] = interfaceRef	//set the new player reference
	if(interfaceRef > 0)
	{
	    pulse[Kplayer[interfaceRef], 79] //Clear the screen saver.
	    send_command Kplayer[interfaceRef], 'REFRESH' //resend any strings to the touch panel
	}
    }
}

/********************************************************
 *  End of events to be copied/pasted per player        *
 ********************************************************/

/********************************************************
 ********************************************************
 ***                                                  ***
 ***                   SAMPLE CODE                    ***
 ***                                                  ***
 ********************************************************
 ********************************************************/


/********************************************************
 *  Change which TP controls which vdev at runtime.     *
 ********************************************************/

/*
 * This code snippet assumes that you've got 5 different virtual 
 * devices to control, and the buttons to pick which player to
 * control are channels 501 - 505.  
 * 
 * As configured above, these virtual devices may represent
 * individual Movie Players, Zones of a Music Player, or
 * even the same player controlled in different ways.  Make sure
 * that the touch panel does an appropriate page flip to the
 * proper UI pages needed to control the selected KPLAYER.
 * (SATP if a zone is defined, OSD otherwise)
 */

/*
BUTTON_EVENT[KTouchPanels,500] //select none
BUTTON_EVENT[KTouchPanels,501] //select KPLAYER1
BUTTON_EVENT[KTouchPanels,502] //select KPLAYER2
BUTTON_EVENT[KTouchPanels,503] //select KPLAYER3
BUTTON_EVENT[KTouchPanels,504] //select KPLAYER4
BUTTON_EVENT[KTouchPanels,505] //select KPLAYER5
{
    push:
    {
	stack_var integer touchpanelRef
	stack_var integer interfaceRef
	
	touchpanelRef = get_last(KTouchPanels)		//determine the TP that generated this event
	interfaceRef = button.input.channel - 500	//determine the Player Ref that is requested
	
	controlledKplayer[touchpanelRef] = interfaceRef	//set the new player reference
	if(interfaceRef > 0)
	{
	    pulse[Kplayer[interfaceRef], 79] //Clear the screen saver.
	    send_command Kplayer[interfaceRef], 'REFRESH' //resend any strings to the touch panel
	}
 
 
    }
}
*/
 
 
/******************************************************** 
 *  Sample keypad code                                  * 
 ********************************************************/

/*
DEFINE_VARIABLE
//define an array to specify the functions the keypad will perform
devchan KeypadActions[] = 
{
    {KPLAYER1, 37},	//play-pause
    {KPLAYER1, 2},	//stop
    {KPLAYER1, 121},	//K preset 1
    {KPLAYER1, 122},	//K preset 2
    {KPLAYER1, 135},	//K next genre
    {KPLAYER1, 136},	//K previous genre
    {KPLAYER1, 4},	//K next track
    {KPLAYER1, 5}	//K previous track
//  {MyTuner, VolUp}
//  {MyTuner, VolDown}
//  {MyTuner, Mute}
//  {MyTuner, Power_Toggle}
};

//define an array to specify what will drive the keypad's feedback
devchan KeypadFeedback[] =
{
    {KPLAYER1, 1037},	//play-pause fb
    {KPLAYER1, 1002},	//stop fb
    {KPLAYER1, 1121},	//K preset 1 fb
    {KPLAYER1, 1122},	//K preset 2 fb
    {KPLAYER1, 135},	//K next genre - momentary
    {KPLAYER1, 136},	//K previous genre - momentary
    {KPLAYER1, 4},	//K next track - momentary
    {KPLAYER1, 5}	//K previous track - momentary
//  {MyTuner, VolUp}
//  {MyTuner, VolDown}
//  {MyTuner, Mute_FB}
//  {MyTuner, Power_FB}
};

DEFINE_EVENT

BUTTON_EVENT[Sample_Keypad, 0] //trap all keypad button events
{
    push:
    {
	stack_var integer channel
	channel = button.input.channel //determine which channel triggered this event
	if(channel <= length_array(KeypadActions)) //if the channel is in the array bounds
	{
	    to[KeypadActions[chn]] //trigger that action
	}
    }
}

CHANNEL_EVENT[KeypadFeedback] //trap all feedback for the keypad
{
    on:
    {
	stack_var integer channel
	channel = get_last(KeypadFeedback) //determine which position in the array triggered this event
	on[SAMPLE_KEYPAD, channel] //turn on that feedback
    }
    off:
    {
	stack_var integer channel
	channel = get_last(KeypadFeedback) //determine which position in the array triggered this event
	off[SAMPLE_KEYPAD,channel] //turn off that feedback
    }
}

*/

/********************************************************
 ********************************************************
 ***                                                  ***
 ***                     end of                       ***
 ***                   SAMPLE CODE                    ***
 ***                                                  ***
 ********************************************************
 ********************************************************/


/*
 * The following event is only necessary under very special circumstances.
 * Sometimes, button events and level events are communicated from a separate device_id
 * than the string events.  For those situations, uncomment this code and the related code above.
 */
/*
DATA_EVENT[keyboardDev]
{
    string: 
    {	
	stack_var char 		kCharacter[10]
	stack_var integer 	keyboardRef
	stack_var integer 	kplayerRef
	
	//determine which touch panel triggered the event
	keyboardRef = get_last(keyboardDev) 
	
	//determine which player this panel is controlling
	kplayerRef = controlledKplayerKeyboard[keyboardRef]
	
	//only proceed if the ref is valid
	if(kplayerRef > 0)
	{	    	
	    if(find_string(data.text,'KAL-', 1)) // when our custom header is found
	    {
		remove_string(data.text, 'KAL-', 1) // remove the header
		kCharacter = "get_buffer_char(data.text)" // remove the keyboard character
		
		if(kCharacter[1] = '[') // if this is a fuzzy set (for instance [abc2] )
		{
		    //pull out the remaining characters
		    kCharacter = "'[', remove_string(data.text,']',1)" 
		}
		// send that character (or set) to the player
		send_command Kplayer[kplayerRef], "kCharacter"
	    }
	    
	    if(find_string(data.text,'WAKE',1)) // when the TP wakes up
	    {	
		// clear the screen saver from the kplayer (if active)
		pulse [Kplayer[kplayerRef],79] 
	    }
	}
    }
}*/

/********************************************************
 *  Handle feedback to the TP.                          *
 *  note that the feedback for any given button is      *
 *  offset by 1000.                                      *
 ********************************************************/
DEFINE_PROGRAM

{
    local_var integer channel
    stack_var integer touchpanelRef
    stack_var integer kplayerRef
    
    channel++ //on each run through mainline, handle the next feedback channel
    if(channel > 142) channel = 1
    
    for(touchpanelRef = 1; touchpanelRef <= length_array(KTouchPanels); touchpanelRef++)
    {
	//determine which player this panel is controlling
	kplayerRef = controlledKplayer[touchpanelRef]	
	if(kplayerRef > 0)
	{
	    [KTouchPanels[touchpanelRef], channel] = [Kplayer[kplayerRef], channel + 1000]
	}
    }
}

I am also using a do_push based on the player to select the correct one
Do_Push(KTouchpanels[panel],2000) // no player
Do_Push(KTouchpanels[panel],2001) /// movie player 1
Do_Push(KTouchpanels[panel],2002) /// movie player 2
Do_Push(KTouchpanels[panel],2003) /// movie player 3
Do_Push(KTouchpanels[panel],2004) /// movie player 4
Do_Push(KTouchpanels[panel],2005) /// music server zone 1
Do_Push(KTouchpanels[panel],2006) /// music server zone 2
Do_Push(KTouchpanels[panel],2007) /// music server zone 3
Do_Push(KTouchpanels[panel],2008) /// music server zone 4


Does anyone have a modified version of the module that does away with the

char m_UIPages[20][],
char m_UIPopups[10][],
char m_UIImages[3][],
char m_UIIcons[3]

as we are using a non standard panel design?

I am also getting the following errors

Line 36 (09:05:03):: Kaleidescape:Sending command "06.01/1/BROWSE:now_playing::1-6:SUGGEST:/76"
Line 37 (09:05:03):: Kaleidescape 5:Received push 911
Line 38 (09:05:03):: Kaleidescape 5:Received push 1086
Line 39 (09:05:03):: Kaleidescape:$0D$0A-- Processing response: 06.01$011$01012$02$04
Line 40 (09:05:03):: Kaleidescape:Error 12: Invalid parameter.
Line 41 (09:05:04):: Kaleidescape 5:Received release 81
Line 42 (09:05:04):: Kaleidescape 5:Received release 911

Comments

  • Options
    a_riot42a_riot42 Posts: 1,624
    ondrovic wrote: »
    Has anyone had issues with the SATP with the new version of the module?

    I implemented the new module the other day and so far no issues, but I haven't given it a real workout yet. Not sure what you mean about doing away with the arrays of popups though. You need them to drive the UI. I modified the module so that it only uses popups though.
    Paul
  • Options
    ericmedleyericmedley Posts: 4,177
    I just put it in a project last week andn found some issues with the feedback. I rewrote it. I'll post the code for you here. In a moment.
    e
  • Options
    ondrovicondrovic Posts: 217
    Cool thanks :)
  • Options
    ericmedleyericmedley Posts: 4,177
    ericmedley wrote: »
    I just put it in a project last week andn found some issues with the feedback. I rewrote it. I'll post the code for you here. In a moment.
    e

    Here ya go. I put the little wait in at the end because this is in a pretty big and chatty program. I don't mind if the fed back lags by a few ticks. You can take it out if you don't want it. I also moved the length_array statement up to startup. I just wanted to see the value in debug. YOu can put it back in the for loop if that bothers you.

    The thing I noticed is that running the loop the previous way just slammed the processor. The output and input lights on the front of the master were pegged on. Also, there was a continual runtime error on line 1004. It was an 'array ref too large error."
    DEFINE_VARIABLE
    volatile integer K_Feedback
    volatile integer KTouchPanel_length
    
    
    DEFINE_START
    KTouchPanel_length=length_array(KTouchPanels)
    
    
    DEFINE_PROGRAM
    (* // old version.
    {
        stack_var integer touchpanelRef
        stack_var integer kplayerRef
        
        K_channel++ //on each run through mainline, handle the next feedback channel
        if(K_channel > 142) K_channel = 1
        
        for(touchpanelRef = 1; touchpanelRef <= length_array(KTouchPanels); touchpanelRef++)
    		{
    		//determine which player this panel is controlling
    		kplayerRef = controlledKplayer[touchpanelRef]	
    		if(kplayerRef > 0)
    			{
    				[KTouchPanels[touchpanelRef], channel] = [Kplayer[kplayerRef], channel + 1000]
    			}
    		} 
    	}*) // end old version
    		
    // fix.
    
    if(K_Feedback=0)
    {
    local_var integer touchpanelRef
    local_var integer kplayerRef
    K_Feedback=1
    for(K_channel=141;K_channel>0;K_channel--)
    {
    for(touchpanelRef = KTouchPanel_length; touchpanelRef>0 ; touchpanelRef--)
    	{
    	//determine which player this panel is controlling
    	kplayerRef = controlledKplayer[touchpanelRef]	
    	if(kplayerRef > 0)
    		{
    		[KTouchPanels[touchpanelRef], K_channel] = [Kplayer[kplayerRef], K_channel + 1000]
    		}
    	}
    if(K_channel=1)
    	{
    	wait 8
    		{
    		K_Feedback=0
    		}
    	}
    } // end for(K_channel=141...
    
    }
    
    
    
  • Options
    ondrovicondrovic Posts: 217
    E,

    did i do this corectly?

    replaced this:
    {
        local_var integer channel
        stack_var integer touchpanelRef
        stack_var integer kplayerRef
        
        channel++ //on each run through mainline, handle the next feedback channel
        if(channel > 142) channel = 1
        
        for(touchpanelRef = 1; touchpanelRef <= length_array(KTouchPanels); touchpanelRef++)
        {
    	//determine which player this panel is controlling
    	kplayerRef = controlledKplayer[touchpanelRef]	
    	if(kplayerRef > 0)
    	{
    	    [KTouchPanels[touchpanelRef], channel] = [Kplayer[kplayerRef], channel + 1000]
    	}
        }
    

    with this:
    If(K_Feedback = 0) {
    
        local_var integer channel
        local_var integer touchpanelRef
        local_var integer kplayerRef
        K_Feedback = 1
        
        For(channel=141;channel>0;channel--) {
        
    	For(touchpanelRef = KTouchPanel_length; touchpanelRef > 0; touchpanelRef--) {
    	
    	    kplayerRef = controlledKplayer[touchpanelRef]
    	    
    	    If(kplayerRef > 0) {
    	    
    		[KTouchPanels[touchpanelRef],Channel] = Kplayer[kplayerRef],Channel + 1000]
    	    }
    	}
    	
    	If(Channel = 1) {
    	
    	    Wait 8 {
    	    
    		K_Feedback = 0
    	    }
    	}
        }
    }
    

    Thanks for the assistance
  • Options
    ericmedleyericmedley Posts: 4,177
    ondrovic wrote: »
    E,

    did i do this corectly?

    replaced this:
    {
        local_var integer channel
        stack_var integer touchpanelRef
        stack_var integer kplayerRef
        
        channel++ //on each run through mainline, handle the next feedback channel
        if(channel > 142) channel = 1
        
        for(touchpanelRef = 1; touchpanelRef <= length_array(KTouchPanels); touchpanelRef++)
        {
    	//determine which player this panel is controlling
    	kplayerRef = controlledKplayer[touchpanelRef]	
    	if(kplayerRef > 0)
    	{
    	    [KTouchPanels[touchpanelRef], channel] = [Kplayer[kplayerRef], channel + 1000]
    	}
        }
    

    with this:
    If(K_Feedback = 0) {
    
        local_var integer channel
        local_var integer touchpanelRef
        local_var integer kplayerRef
        K_Feedback = 1
        
        For(channel=141;channel>0;channel--) {
        
    	For(touchpanelRef = KTouchPanel_length; touchpanelRef > 0; touchpanelRef--) {
    	
    	    kplayerRef = controlledKplayer[touchpanelRef]
    	    
    	    If(kplayerRef > 0) {
    	    
    		[KTouchPanels[touchpanelRef],Channel] = Kplayer[kplayerRef],Channel + 1000]
    	    }
    	}
    	
    	If(Channel = 1) {
    	
    	    Wait 8 {
    	    
    		K_Feedback = 0
    	    }
    	}
        }
    }
    

    Thanks for the assistance


    yes. (and some stuff to make this message long enough....)
  • Options
    a_riot42a_riot42 Posts: 1,624
    Why do you need the if(K_Feedback = 0)? I thought mainline was single-threaded. Also, does this code compile? Channel is a reserved word.

    The original code updates the status of one channel on all currently controlling UIs every pass through mainline. Your code updates all the channels on all currently controlling UIs every pass through mainline. Unless I'm mistaken, this seems like far more updating than the original code.
    Paul
  • Options
    ericmedleyericmedley Posts: 4,177
    a_riot42 wrote: »
    Why do you need the if(K_Feedback = 0)? I thought mainline was single-threaded. Also, does this code compile? Channel is a reserved word.

    The original code updates the status of one channel on all currently controlling UIs every pass through mainline. Your code updates all the channels on all currently controlling UIs every pass through mainline. Unless I'm mistaken, this seems like far more updating than the original code.
    Paul


    It's part of a short wait. the whhole routine only runs every 8 ticks.
     if(K_Feedback = 0)
    {
    K_Feedback = 1
    wait 8
      {
       K_Feedback = 0
      }
    }
    

    Channel is left over from the origianal routine. I wondered about that as well. And since in the origianl it was a local var I was wondering how it would incremet. I left it as is and it compiles. I should probably change the name for the very reason that it is a keyword.

    There is nothing wrong with doing it the other way. I just didn't wat to do a For Loop on every pass through mainlie for 20+ panels. I don't see anyting wrong with updating feedback every 6-8 ticks or so. The eye does't notice it that much. And the conditionals do have to be evaluated on eveyr pass whether anything chaged or not. The 2nd routine only does the 141 evals every 8 ticks.
  • Options
    a_riot42a_riot42 Posts: 1,624
    ericmedley wrote: »
    It's part of a short wait. the whole routine only runs every 8 ticks.
     if(K_Feedback = 0)
    {
    K_Feedback = 1
    wait 8
      {
       K_Feedback = 0
      }
    }
    

    Oh, I think what confused me is would have written it like this and not used a variable:
    wait 8 {
    
        local_var integer channel
        local_var integer touchpanelRef
        local_var integer kplayerRef
        
        For(channel=141;channel>0;channel--) 
        {
    	For(touchpanelRef = KTouchPanel_length; touchpanelRef > 0; touchpanelRef--) 
            {	
    	    kplayerRef = controlledKplayer[touchpanelRef]
    	    If (kplayerRef > 0) 
                {
    		[KTouchPanels[touchpanelRef],Channel] = Kplayer[kplayerRef],Channel + 1000]
    	    }
    	}
        }	
    }
    

    Wouldn't this work with no timeline code?
    channel_event[kPlayers, kButtons]
    {
      on:
      {
        stack_var integer player, btn, i
    
        player = get_last(KPlayers)
        btn = get_last(kButtons)
    
        for(i = iNumTPs; i > 0; i--) 
        {	
          If (controlledKplayer[i] == player) 
          {
    	on[KTouchPanels[i], btn + 1000]
          }
        }
      }
      off:
      {
        stack_var integer player, btn, i
    
        player = get_last(KPlayers)
        btn = get_last(kButtons)
    
        for(i = iNumTPs; i > 0; i--) 
        {	
          If (controlledKplayer[i] == player) 
          {
    	off[KTouchPanels[i], btn+ 1000]
          }
        }
      }
    }
    
  • Options
    ericmedleyericmedley Posts: 4,177
    a_riot42 wrote: »
    Oh, I think what confused me is would have written it like this and not used a variable:
    wait 8 {
    
        local_var integer channel
        local_var integer touchpanelRef
        local_var integer kplayerRef
        
        For(channel=141;channel>0;channel--) 
        {
    	For(touchpanelRef = KTouchPanel_length; touchpanelRef > 0; touchpanelRef--) 
            {	
    	    kplayerRef = controlledKplayer[touchpanelRef]
    	    If (kplayerRef > 0) 
                {
    		[KTouchPanels[touchpanelRef],Channel] = Kplayer[kplayerRef],Channel + 1000]
    	    }
    	}
        }	
    }
    

    Wouldn't this work with no timeline code?
    channel_event[kPlayers, kButtons]
    {
      on:
      {
        stack_var integer player, btn, i
    
        player = get_last(KPlayers)
        btn = get_last(kButtons)
    
        for(i = iNumTPs; i > 0; i--) 
        {	
          If (controlledKplayer[i] == player) 
          {
    	on[KTouchPanels[i], btn + 1000]
          }
        }
      }
      off:
      {
        stack_var integer player, btn, i
    
        player = get_last(KPlayers)
        btn = get_last(kButtons)
    
        for(i = iNumTPs; i > 0; i--) 
        {	
          If (controlledKplayer[i] == player) 
          {
    	off[KTouchPanels[i], btn+ 1000]
          }
        }
      }
    }
    


    Your way is actually better in that it doesn't require mainline looking at the IF statment. The only reason I did what I did is that I actually start out with the flag at '1' and make is zero after 35 seconds after startup. That way it waits for the module to come online.

    But, you know, I should just do it the way you describe. What's the differnece? So wht if for 30 seconds nothing happens.

    Thanks!
    e
  • Options
    a_riot42a_riot42 Posts: 1,624
    ericmedley wrote: »
    Your way is actually better in that it doesn't require mainline looking at the IF statment. The only reason I did what I did is that I actually start out with the flag at '1' and make is zero after 35 seconds after startup. That way it waits for the module to come online.

    But, you know, I should just do it the way you describe. What's the differnece? So wht if for 30 seconds nothing happens.

    Thanks!
    e

    I haven't compiled or tested that code but I can't see why it wouldn't work without using mainline. I will stick it in the module and see. I have noticed that kplayer transport button feedback is too slow to keep up with fast button presses so maybe it will help.
    Paul
  • Options
    ondrovicondrovic Posts: 217
    Paul,

    Keep us updated maybe you found away around some of the slow transport feedback

    Good work and thanks for sharing
Sign In or Register to comment.