Home AMX User Forum NetLinx Studio

Need a little help understanding arrays.

undrtkrundrtkr Junior MemberPosts: 35
I'm testing some new approaches to my programming. Specifically using a DEV array for multiple touchpanels instead of using DEFINE_COMBINE and such. Here is an example of my code.

DEFINE_DEVICE
dvPnla = 10001:1:0
dvPnlb = 10002:1:0

DEFINE_CONSTANT
cMAX_PNLS = 2

DEFINE_TYPE
STRUCTURE _uPnlInfo
{
INTEGER Device //Tracks Source Selection on Panel
}

DEFINE_VARIABLE
CONSTANT DEV dvPnls[] = {dvPnla,dvPnlb}
PERSISTENT _uPnlInfo uPnlInfo[cMAX_PNLS]

DEFINE_FUNCTION fnSetSysPwrOff(INTEGER nPnlIdx)
{
uPnlInfo.Device = 0
SEND_COMMAND dvPnls,"'@PPX'" //Deactivate All Popups
SEND_COMMAND dvPnls,"'@PPN-Logo'"
}

(*
*)
(*- Yes/No Prompt -*)
(*
*)
BUTTON_EVENT[dvPnls,2] //No
BUTTON_EVENT[dvPnls,3] //Yes
{
PUSH:
{
STACK_VAR
INTEGER nPnlIdx
nPnlIdx = GET_LAST(dvPnls)

SEND_COMMAND dvPnls[nPnlIdx],"'@PPF-Yes-No Prompt'"

SELECT
{
ACTIVE(BUTTON.INPUT.CHANNEL = 2):{}
ACTIVE(BUTTON.INPUT.CHANNEL = 3):{fnSetSysPwrOff(nPnlIdx)}
}
}
}

The code works beautifully, when the user presses the system power button the prompt only appears on that particular panel. When they press YES to shutdown the popups happen simultaneous on both panels like they are supposed to.

The problem I'm having is the uPnlInfo.Device = 0 gives me a Dimension mismatch [1] vs. [0]. Why is it giving me that for the structure but NOT for the SEND_COMMAND dvPnls,"'@PPX'"?

The only way to get it to compile is to do this:
uPnlInfo[1].Device = 0
uPnlInfo[2].Device = 0

I would like to be able to set the structure.device globally instead of having to declare the two arrays. Is that even possible?

Comments

  • mpullinmpullin Obvious Troll Account, Marked for Deletion Posts: 949
    Looks like you're on a good path to improvement there. Some suggestions.

    1) Don't ever make variables with reserved words like Channel or Device like you have here. This is why your program doesn't work. Call it something like nSRC instead.
    2) Don't use c for constant, you'll find you want to use that letter for CHARs. Just nMAX_PNLS is sufficient.
    3) Not sure why the SELECT-ACTIVE when a SWITCH-CASE or even a single IF would do the job.
    4) Put your button channels in as constants, e.g.
    DEFINE_CONSTANT // button channels
    butSOMETHING = 1;
    butYorN[] = {2,3};
    
    Then your button event can just be BUTTON_EVENT[dvPnls,butYorN]. Use GET_LAST(butYorN) instead of BUTTON.INPUT.CHANNEL. Then if the channels on your panel change you only have to change your program in one place. The constants section.
  • HedbergHedberg Junior Member Posts: 671
    undrtkr wrote: »

    DEFINE_TYPE
    STRUCTURE _uPnlInfo
    {
    INTEGER Device //Tracks Source Selection on Panel
    }
    ...

    DEFINE_VARIABLE
    CONSTANT DEV dvPnls[] = {dvPnla,dvPnlb}
    PERSISTENT _uPnlInfo uPnlInfo[cMAX_PNLS]

    DEFINE_FUNCTION fnSetSysPwrOff(INTEGER nPnlIdx)
    {
    uPnlInfo.Device = 0

    . . .

    Ok, you are defining a data type named "uPnlInfo" and then you are trying to create an array of structures of that data type with the name of the data type. that's like saying:

    integer integer[3]

    define your variable/structure something else like:

    PERSISTENT _uPnlInfo uPnlInfo_distinguishing_Name[cMAX_PNLS]


    Now, you have an array of structures and to assign a value you need to do something like:

    uPnlInfo_distinguishing_Name[1].Device = nSomeIntegerValue
  • mpullinmpullin Obvious Troll Account, Marked for Deletion Posts: 949
    I disagree, Hedberg; the data type name and the variable name are not the same. The data type name has an underscore _right before it. It may seem confusing at first to have a data type name and an variable name that are so similar, but I don't have a problem with it since there is only going to be one variable of that type - an array with an element for each panel, in effect creating a "directory" of information related to each TP. I have used the same notation (data type and variable name differing by only an underscore) in my programs.
  • HedbergHedberg Junior Member Posts: 671
    mpullin wrote: »
    I disagree, Hedberg; the data type name and the variable name are not the same. The data type name has an underscore _right before it. It may seem confusing at first to have a data type name and an variable name that are so similar, but I don't have a problem with it since there is only going to be one variable of that type - an array with an element for each panel, in effect creating a "directory" of information related to each TP. I have used the same notation (data type and variable name differing by only an underscore) in my programs.

    You are right about the naming and I was wrong.

    Also, it appears that I didn't read the original question very well and have answered the question that I wish he had asked rather than the question he did.

    I think the actual question is, why can you refer to an array of devices without referencing the index (as with a send_command to an array of panels) but you can't refer to an array of structures in a parallel manner.
    I think the answer is that the people who created the Netlinx programming language created a special case for arrays of devices.
  • undrtkrundrtkr Junior Member Posts: 35
    mpullin wrote: »
    1) Don't ever make variables with reserved words like Channel or Device like you have here. This is why your program doesn't work. Call it something like nSRC instead.

    Actually, Device for the structure does work. And because it is part of the structure uPnlInfo I find it redundant to label it as nDevice or nSrc. uPnlinfo[nPnlIdx].nDevice for some reason drives me crazy. Doesn't look right to me. I know, I'm weird that way.
    2) Don't use c for constant, you'll find you want to use that letter for CHARs. Just nMAX_PNLS is sufficient.

    I use str for CHARs. Like strBuffer[200]. If I use nMAX_PNLS I'll think it is an integer variable that I can manipulate and not a constant. Trust me, I HAVE to have different designations or I'll keep refering to the beginning of the program and that drives me nuts.
    3) Not sure why the SELECT-ACTIVE when a SWITCH-CASE or even a single IF would do the job.

    SELECT-ACTIVE and SWITCH-CASE essentially do the same thing. It takes the same amount of lines to create them. I don't like using IF-ELSE in a button event. To me SELECT-ACTIVE or SWITCH-CASE looks cleaner. Yeah I know, another querk. :)
    4) Put your button channels in as constants, e.g.
    DEFINE_CONSTANT // button channels
    butSOMETHING = 1;
    butYorN[] = {2,3};
    
    Then your button event can just be BUTTON_EVENT[dvPnls,butYorN]. Use GET_LAST(butYorN) instead of BUTTON.INPUT.CHANNEL. Then if the channels on your panel change you only have to change your program in one place. The constants section.

    I've been doing this for some of my code, just didn't do that for this particular button event. I do agree with you on number 4 though and am getting to that point. One thing at a time. :)
    mpullin wrote: »
    I disagree, Hedberg; the data type name and the variable name are not the same. The data type name has an underscore _right before it. It may seem confusing at first to have a data type name and an variable name that are so similar, but I don't have a problem with it since there is only going to be one variable of that type - an array with an element for each panel, in effect creating a "directory" of information related to each TP. I have used the same notation (data type and variable name differing by only an underscore) in my programs.

    mpullin hit the nail on head. I define data types with _u then define the variable name beginning with u. I find no reason to to name the variable different from the data type unless you're defining two different variables with the same data type.
    Hedberg wrote: »
    I think the actual question is, why can you refer to an array of devices without referencing the index (as with a send_command to an array of panels) but you can't refer to an array of structures in a parallel manner.
    I think the answer is that the people who created the Netlinx programming language created a special case for arrays of devices.

    I figured that might be the case. Well poo on the people who created the Netlinx language then. :)
  • mpullinmpullin Obvious Troll Account, Marked for Deletion Posts: 949
    To be fair, I didn't read his question very well either. Yeah, you cannot make a sweeping assignment on any array other than a DEV array. I think that is just a shortcut NetLinx created because it's not possible in other languages... traversing arrays with loops is the norm rather than the exception.
    --

    As for SWITCH-CASE, it's much less verbose than SELECT-ACTIVE, but can only be used to evaluate one expression at a time. Personally I find that most logical branches in my code are based on the value of one expression. SELECT-ACTIVE is a structure unique to NetLinx as far as I know, whereas you'll find SWITCH-CASE in all modern programming languages. 90% of programmers here will swear by SELECT-ACTIVE in every situation, possibly because it's taught in P1, possibly because of a few NetLinx compiler quirks (read: smear tactics) that one must be wary of.

    Personally, I love SWITCH-CASE. I believe that even to the extent it is implemented in NetLinx it is more elegant than SELECT-ACTIVE. One advantage for example, the expression you're switching on is only evaluated once.
    SWITCH(REMOVE_STRING(DATA.TEXT,'=',1)){ will remove the header of a virtual device string and branch off based on it, leaving the important data in DATA.TEXT. To do this with a SELECT-ACTIVE you'd need to make a STACK_VAR, put the result of the REMOVE_STRING in the STACK_VAR, and each line of your SELECT_ACTIVE would need to test against the STACK_VAR. That's two extra lines right there!
    Same thing with GET_LAST. SWITCH(GET_LAST(butTransports)){ followed by case 1: case 2: etc. No extra variable, no repeatedly calling the GET_LAST function which consumes a mysterious amount of memory that no one exactly knows.

    I could go on. But you should keep trying new stuff like you are now and make your own decision.
  • undrtkrundrtkr Junior Member Posts: 35
    mpullin wrote: »
    To be fair, I didn't read his question very well either. Yeah, you cannot make a sweeping assignment on any array other than a DEV array. I think that is just a shortcut NetLinx created because it's not possible in other languages... traversing arrays with loops is the norm rather than the exception.
    --

    As for SWITCH-CASE, it's much less verbose than SELECT-ACTIVE, but can only be used to evaluate one expression at a time. Personally I find that most logical branches in my code are based on the value of one expression. SELECT-ACTIVE is a structure unique to NetLinx as far as I know, whereas you'll find SWITCH-CASE in all modern programming languages. 90% of programmers here will swear by SELECT-ACTIVE in every situation, possibly because it's taught in P1, possibly because of a few NetLinx compiler quirks (read: smear tactics) that one must be wary of.

    Personally, I love SWITCH-CASE. I believe that even to the extent it is implemented in NetLinx it is more elegant than SELECT-ACTIVE. One advantage for example, the expression you're switching on is only evaluated once.
    SWITCH(REMOVE_STRING(DATA.TEXT,'=',1)){ will remove the header of a virtual device string and branch off based on it, leaving the important data in DATA.TEXT. To do this with a SELECT-ACTIVE you'd need to make a STACK_VAR, put the result of the REMOVE_STRING in the STACK_VAR, and each line of your SELECT_ACTIVE would need to test against the STACK_VAR. That's two extra lines right there!
    Same thing with GET_LAST. SWITCH(GET_LAST(butTransports)){ followed by case 1: case 2: etc. No extra variable, no repeatedly calling the GET_LAST function which consumes a mysterious amount of memory that no one exactly knows.

    I could go on. But you should keep trying new stuff like you are now and make your own decision.

    Most of the new ideas I've been getting have come from an AMX programmer we sometimes outsource to. He's been programming AMX for 11 years and when I first had to dig into his programs it was WAY over my head. After looking some more on this forum and back at his programs I'm starting to SLOWLY understand these concepts, and like them.

    I have noticed on his newest program he started using SWITCH(GET_LAST(nBtnTransports)). Before he was creating a STACK_VAR nBtnIdx the a nBtnIdx = GET_LAST(nBtnTransports).

    I can certainly understand why he would for go the STACK_VAR. Like I said in my previous posts, one concept at a time. :)

    The reason why I've adopted SELECT-ACTIVE in my button events is because if I need to I can create an OR/AND condition very quickly. In my view it's all about what your comfortable with.
  • DHawthorneDHawthorne Old Timer Posts: 4,584
    Nothing to do with tht original question, but I had to throw in a comment on the last post.

    I only put a GET_LAST inside a SWITCH parameter if it is the only time I am going to use it. If I need that value elsewhere in the block, I assign it to a STACK_VAR. The reason is simple efficiency: why call a function over and over again to get the same value? Call it once, and it's now a memory operation, not a function call.
  • jjamesjjames Just another dude Posts: 2,906
    To the OP: congrats on learning arrays. They are extremely powerful and can make things much easier. Sometimes it's difficult to get it nailed down - it takes a certain kind of thinking. But, once you have it set, they're very useful in making a flexible system, so when the client comes back and wants to add 10 touch panels because he loves his original 2 so much - it won't be difficult to do.

    Enjoy and have fun!
  • a_riot42a_riot42 AMX Wizard Posts: 1,619
    I have reduced the amount of code running on the master by an order of magnitude doing things similar to what you talking about. One hiccup is that get_last doesn't behave properly in a hold event and there are some other gotchas as well, so beware.

    One thing that I feel compelled to mention as I see it so often is why do people abbreviate variable names to the point where they are meaningless to anyone but the writer?

    CONSTANT DEV dvPnls[] = {dvPnla,dvPnlb}
    PERSISTENT _uPnlInfo uPnlInfo[cMAX_PNLS]

    Is there some limit on how many vowels you are allowed in a variable name? Why not this instead?

    constant dev dvPanels[] = {dvPanelA,dvPanelB}
    persistent _uPanelInfo uPanelInfo[cMaxPanels]

    To me, that is much easier to read, write, and if I need the variable somewhere else, I don't have to try and guess how I abbreviated it in the declaration (was it cnLvgPnl, cnLivingPnl, cnLvgPanel, cnLivingPanel, etc).
    Paul
  • undrtkrundrtkr Junior Member Posts: 35
    a_riot42 wrote: »
    One thing that I feel compelled to mention as I see it so often is why do people abbreviate variable names to the point where they are meaningless to anyone but the writer?

    CONSTANT DEV dvPnls[] = {dvPnla,dvPnlb}
    PERSISTENT _uPnlInfo uPnlInfo[cMAX_PNLS]

    Is there some limit on how many vowels you are allowed in a variable name? Why not this instead?

    constant dev dvPanels[] = {dvPanelA,dvPanelB}
    persistent _uPanelInfo uPanelInfo[cMaxPanels]

    To me, that is much easier to read, write, and if I need the variable somewhere else, I don't have to try and guess how I abbreviated it in the declaration (was it cnLvgPnl, cnLivingPnl, cnLvgPanel, cnLivingPanel, etc).
    Paul

    Yeah I would agree it could get cryptic. However, when you scan the device numbers like this:
    dvPnla = 10001:1:0
    dvPnlb = 10002:1:0
    
    constant dev dvPnls[] = {dvPnla, dvPnlb}
    persistent _uPnlInfo uPnlInfo[cMaxPnls]
    

    It's pretty easy to deduce that dvPnla and dvPnlb are touch panels based off the device number. I also comment as much as I can when I program which lets the reader know what the particular variable, constant or structure name means.

    Also, I use Netlinx Studios auto complete incessantly which is why I designate constants with 'c', variables with an 'n' or 'str' etc. Helps in situations where I have a constant labeled cSystemPower_Icon_Off_Inactive. All I need to remember is cSys and let the auto complete popup a list for me to choose from.

    I'd like to think there is a method to my madness. :)
  • a_riot42a_riot42 AMX Wizard Posts: 1,619
    undrtkr wrote: »
    It's pretty easy to deduce that dvPnla and dvPnlb are touch panels based off the device number. I also comment as much as I can when I program which lets the reader know what the particular variable, constant or structure name means.

    Different strokes for different folks I guess. I try to keep my comments to an absolute minimum, since they often don't get updated as the code changes due to forgetfulness, time etc. The only thing worse than obfuscated code with no comments, is obfuscated code with incorrect comments :)

    I try hard to make my code self-commenting, and have found it to be much easier to read later on after I have forgotten the project,
    if (sHouseMusic.isRunning || statusChanged)
    {
      updateUI(ui, currentStatus)
    }
    .
    .
    .
    

    Maybe not a great example but you see what I mean. If you were to comment this code, the comment would likely be:
    // if House Music is running or the status has changed, update the touch panel
    which is obviously rather superfluous.
    Paul
Sign In or Register to comment.