Home AMX User Forum AMXForums Archive Threads Tips and Tricks

Using Channels for Feedback

Read on only if you want to know about a bizarre programming experience I'm having right now.

I keep remembering that someone on the forum said never to test for something based on the status of a button. Something odd is happening around the office to make me believe that advice is more valuable than I thought. Consider this code, for a mute toggle button:
BUTTON_EVENT[TouchPanels,18]{ // muting
    PUSH:{
	if ([BUTTON.INPUT.DEVICE,BUTTON.INPUT.CHANNEL]) send_command vdv_ADA_Suite7, "'MUTE=0:1'"
	else send_command vdv_ADA_Suite7, "'MUTE=1:1'"
    }
}

The channel state is determined by this:
DATA_EVENT[vdv_ADA_Suite7]{ // ada suite7 feedback
    COMMAND: {
	SELECT{
	    ACTIVE (data.text == 'MUTE=1:1'):
	    {
		ON[TouchPanels[1],18]
		ON[TouchPanels[2],18]
	    }
	    ACTIVE (data.text == 'MUTE=0:1' OR LEFT_STRING(data.text,3) == 'VOL'):
	    {
		OFF[TouchPanels[1],18]
		OFF[TouchPanels[2],18]
	    }
	}
    }
}

Now... this code had worked perfectly for about a month. This weekend we had some power outages, but nothing fatal to anything on our network. When I came in monday, I noticed that the Mute button, when pressed, would flicker but would not change the volume. At first I blamed the ADA Bus but I realized that the ADA Bus was working fine because other buttons on the system that communicated with the ADA Bus were working. In fact, all of the devices in my system seemed okay.

Running some tests, I found that the Mute button would throw 'MUTE=0:1' every time I pressed it. I used NS to set channel 18 of the touchpanel to ON, and I pushed the button again. Still 'MUTE=0:1'. The code had not changed at all, the devices in the system were all fine, I rebooted the master and the panels a couple times, this problem persisted. It's like all of a sudden my system decided to call me on this unhealthy programming practice.

That wasn't the only piece of code in the system that was messing up either.
BUTTON_EVENT[TouchPanels,TP_ADA_Inputs]{ // ADA inputs
    PUSH:{
	if ([BUTTON.INPUT.DEVICE,BUTTON.INPUT.CHANNEL] == 1) TOTAL_OFF[BUTTON.INPUT.DEVICE,TP_ADA_Inputs]
	else{
	    ON[BUTTON.INPUT.DEVICE,BUTTON.INPUT.CHANNEL]
	    if ( ([BUTTON.INPUT.DEVICE,61] OR [BUTTON.INPUT.DEVICE,62]) OR [BUTTON.INPUT.DEVICE,63]){
		// if an output is selected
		CALL 'ADA_Zone_Change' (BUTTON.INPUT.DEVICE)
	    }
	}
	SEND_COMMAND vdv_ADA_TUNER_ARRAY[BUTTON.INPUT.CHANNEL-64], 'TUNE?'
    }
}

Again, the buttons would sort of flicker when pressed, the channels would not stay on as intended. I plan to fix this by inserting variables for each button of which I want to keep track the state. But if this code is wrong, why did it work for so long and after so many reboots? The whole thing seems very.. odd to me.

EDIT:
Chip Moody wrote:
A very wizened programmer (or two or three) at AMX told me back when I was first starting to program that I shouldn't use channels on a touch panel on the right side of a boolean equation like you would with other devices, like a com port, or a variable. (I.E., asign to the channels, but don't use them as reference)

I don't remember the explanation if there was one, but it's just been one of those "you just shouldn't do it" type things that's stuck with me for most of the past decade...

Comments

  • buttons

    Strange thing,
    I never use channels like that. Most of the time i use a channel on the device port or virtual device. I remember a issue long ago. It was on a axcent 2. I made a var and forget to reset it when the system reboots. Normally you do something like Var=0 in DEFINE_START. In this case i forget that and at one time there was a power failure and the system reboots. The var was used to read a buffer of a vcr. It was set (var=1) as long as the buffer was reading. The power failure came at the moment that the var was set. A reboot doesn't changed is so it stays 1 and the buffer was never read again. The only way to solve this was to download the software again so every var was reset. I did that and everything worked fine (i put of course the var in the DEFINE_START section and reset it as the system was rebooting).
    So my advice DOWNLOAD the software again and check if things are working fine.
    Good luck
  • mpullinmpullin Posts: 949
    Thanks for the advice. I had already powered the whole system down, put blank code into it, powered it up, put the original code back into it, etc. Still doesn't work as it once did. That's why this is so bizarre.
  • DHawthorneDHawthorne Posts: 4,584
    I've always used variables for that kind of tracking, never a button channel. I don't even like using virtual device channels, but those are better than button channels. Button channels seem to have their own set of rules, and those rules are not very well documented.

    I suspect your problem is the OFF statement on the buttons. Replace it with a TOTAL_OFF. Your OFF statement only shuts the input channel off, not the feedback channel. I always thought the BUTTON.INPUT.CHANNEL value reflected the input channel just by the way it's named, but there may be circumstances where it reflects the feedback channel instead. As far as I'm concerned, the whole area is a bit murky, which is why I use variables.

    Of course, this does not explain why it was fine for a while, then suddenly stopped working. Different version of Studio?
  • Unless there's some reason to use a variable, I've taken to using channels on a device's 232 port to track boolean states... IF ([RS232port,1]) {do something} works great.

    But yeah, don't trust those TP buttons. :)

    - Chip
  • maxifoxmaxifox Posts: 209
    To Chip: I was always wondering (and still) what is the best approach to tracking status of RS-232 devices. So far I stick with variables, but you mentioned channels on RS-232 port... What are the advantages comparing with variables? BTW, you are talking about channels on virtual device not real, right?

    I think the thread is worth to go to Tips & Tricks section, is not it?
  • DHawthorneDHawthorne Posts: 4,584
    No, you can use real channels on an RS-232 port, just stay under 250. They aren't doing anything else. I use them for simple tracking, like transport states or a debug flag.

    I suppose I should amend my earlier statement about using variables. I use them for system and general tracking; if it's device specific, it makes far more sense to use a channel or level on that device, or a virtual connected to it.
  • Yeah, if it's device specific for an RS232 controlled item, and it's an on/off-true/false boolean state, the channels seem like a perfect choice in place of an integer variable. If we have a matrix mixer going, perhaps with an audio conference unit in it, I might have channels set up like this:

    [TP,1] = [Mixer,Mic1Mute]
    [TP,2] = [Mixer,Mic2Mute]
    ...etc
    [TP,21] = [Mixer,ATCOffHook]
    [TP,22] = [Mixer,ATCSendMute]

    Where the [Mixer,xxxxx] channels get turned on/off during the parsing of the mixer's string DATA_EVENTs.

    And yes, you can use channels on a virtual device, but the ones on the physical device aren't doing anything, and the fact that it's a physical device over a non-existent one appeals to my anal-retentiveness. :)

    - Chip
  • mpullinmpullin Posts: 949
    Using the RS232 port channels was a great idea.

    I built a DEV array like this: DEV BOOLEAN[] = {5001:1:0,5001:2:0}

    and I went though each instance where I'm setting a channel in order to read it later, and I replaced BUTTON.INPUT.DEVICE with BOOLEAN[GET_LAST(TouchPanels)]. It worked like a charm!

    The only thing was, I some rapid flicker when I changed outputs on the ADA. I realized however that I had a mutually exclusive set defined for the TouchPanel buttons but not for the BOOLEAN channels :-p
  • Interesting, but not bizarre.

    Greetings all.

    I would like to weigh in on this subject becasue it has brought on much thought during many years of programming and an eventual technique for storing conditions.

    Part 1) Typical Use of Variables

    I typically use variables for state management when the value goes beyond a simple On or Off (zero or one.) For example, if I am tracking which source is selected I might use the variable nSource.
    Define_Device
    dvTP = 10001:1:0
    
    Define_Constant
    INTEGER nSource_Btns[] =
    {
       81, // 1 - VCR
       82, // 2 - DVD
       83, // 3 - DSS
       84, // 4 - PC
       85  // 5 - Laptop
    }
    
    Define_Variable
    INTEGER nSource
    VOLATILE INTEGER nCount
    
    Define_Event
    Button_Event[dvTP,nSource_Btns] // Source Select
    {
       Push:
       {
          nSource=Get_Last(nSource_Btns)
       }
    }
    
    Define_Program
    // Button Feedback - nCount loops from 1-5 (size of the array nSource_Btns)
    For(nCount=1;nCount<=Length_Array(nSource_Btns);nCount++)
       [dvTP,nSource_Btns[nCount]] = (nSource=nCount)
    
    If you notice, I pretty much use button arrays exclusively to take advantage of the Get_Last object which is the index of the array nSource_Btns. This provides easy scalability in the number of source select buttons without changing the Button_Event defintion or button feedback code.

    You can debate whether feedback should go into a Timeline or Define_Program. Define_Program works great until a program becomes very large.
  • Part 2) Typical Use of Channel

    I typically use channels for state management when the value is a simple On or Off (zero or one.) For example, if I am tracking power status. This was discussed previously, but I will illustrate with an example.
    Define_Device
    dvPDP = 5000:1:0   // Plasma
    dvTP = 10001:1:0
    
    Define_Constant
    INTEGER nOff = 0
    INTEGER nOn = 1
    INTEGER nPwr = 9
    INTEGER nPwrBtn = 9
    
    Define_Event
    Data_Event[dvPDP]
    {
       Online:
          Send_Command Data.Device ''SET BAUD 9600,N,8,1' 
    }
    
    Button_Event[dvTP,nPwrBtn] // Toggle Plasma Power
    {
       Push:
       {
          [dvPDP,nPwr] = ![dvPDP,nPwr] // Toggle Channel State
          IF ([dvPDP,nPwr] = nOn)
             Send_String dvPDP," 'PON',$0D" // Power on string
          ELSE
             Send_String dvPDP," 'POF',$0D" // Power off string
       }
    }
    
    Define_Program
    // Button Feedback 
       [dvTP,nPwrBtn] = ([dvPDP,nPwr])
    
    Suppose you have 5 touch panels controlling 5 different plasmas.
    Define_Device
    dvPDP1 = 5000:1:0   // Plasma 1
    dvPDP2 = 5000:2:0   // Plasma 2
    dvPDP3 = 5000:3:0   // Plasma 3
    dvPDP4 = 5000:4:0   // Plasma 4
    dvPDP5 = 5000:5:0   // Plasma 5
    
    dvTP1 = 10001:1:0
    dvTP2 = 10002:1:0
    dvTP3 = 10003:1:0
    dvTP4 = 10004:1:0
    dvTP5 = 10005:1:0
    
    Define_Constant
    INTEGER nOff = 0
    INTEGER nOn = 1
    INTEGER nPwr = 9
    INTEGER nPwrBtn = 9
    VOLATILE INTEGER nCount
    
    Define_Variable
    DEV dv_PDP[] = // Array of Plasma Panels
    {
       dvPDP1, // 1 - 
       dvPDP2, // 2 - 
       dvPDP3, // 3 - 
       dvPDP4, // 4 - 
       dvPDP5  // 5 - 
    }
    DEV dv_TP[] = // Array of Touch Panels
    {
       dvTP1, // 1 - 
       dvTP2, // 2 - 
       dvTP3, // 3 - 
       dvTP4, // 4 - 
       dvTP5  // 5 - 
    }
    
    
    Define_Event
    Data_Event[dv_PDP] // Set Baud for all plasma panels
    {
       Online:
          Send_Command Data.Device ''SET BAUD 9600,N,8,1' 
    }
    
    Button_Event[dv_TP,nPwrBtn] // Toggle Plasma Power
    {
       Push:
       {
          Stack_Var INTEGER nINDEX
          nINDEX = Get_Last(dv_TP)
          
          // Toggle Channel State
          [dv_PDP[nINDEX],nPwr] = ![dv_PDP[nINDEX],nPwr] 
          IF ([dv_PDP[nINDEX],nPwr] = nOn)
             Send_String dv_PDP[nINDEX]," 'PON',$0D" // Power on string
          ELSE
             Send_String dv_PDP[nINDEX]," 'POF',$0D" // Power off string
       }
    }
    
    Define_Program
    // Button Feedback - nCount loops from 1-5 (size of the array dv_TP)
    For(nCount=1;nCount<=Length_Array(dv_TP);nCount++)
       [dv_TP[nCount],nPwrBtn] = ([dv_PDP[nCount],nPwr])
    

    It is never a good idea to use a channel in a touch panel for state evalutions because the panel can go offline. I either use a physical device in the NIxxx or a virtual.

    Keep in mind, if you use a virtual device you must set the upper channel limit if you use channels above 255. Use the command Set_Virtual_Channel_Count (vdvDEV,x) in the Define_Start section where x represent the upper channel limit up 4000.
  • totally of the wall

    Hi there I work with ships which dont have real ground we find often on reboot things go wierd what we thought (and untill the event) worked fine now doesent
    in many cases it appears that the PS can cause more issues than you would think possible
    before to change all you belive to be correct have you looked at the ps?
Sign In or Register to comment.