Home AMX User Forum AMX General Discussion
Options

Clean programming habits?

How do you guys do it? I have been messing around with netlinx for a while now and can get it to do most anything I want, however I still have a million BUTTON_EVENTs for each TP button in the mainline and no real type of organization. What is the standard way of organizing things? I was thinking about having an include for different rooms and such, but then found out that the devices that were included in the mainline couldn't be used in the includes without defining them there. Then of course they were "double defined" if they are in the mainline too. So I'm curious, what is the correct way to go about this? It's such a pain in the neck to scroll through 5000 lines of code just to find something.

Interested to hear what you do..

Dan

Comments

  • Options
    TurnipTruckTurnipTruck Posts: 1,485
    Refer to similar buttons as an array. Assume buttons with channel codes 1-10
    nButtons[]={1,2,3,4,5,6,7,8,9,10}
    

    Then in the button event, you just make reference to the array
    BUTTON_EVENT [dvTP,nButtons]
    {
    PUSH:
      {
      nButton=GET_LAST(nButtons)
      //Now you do whatever with nButton which could be worth anything from one to ten
      }
    }
    

    Or you can use the wildcard
    BUTTON_EVENT [dvTP,0]
    {
    PUSH:
      {
      nButton=BUTTON.INPUT.CHANNEL
      //Now nButton equals the channel code of any button pushed on the TP.
      }
    }
    
  • Options
    TonyAngeloTonyAngelo Posts: 315
    In addition to using arrays, you can have as many DEFINE_EVENT calls as you want. For that matter you can have as many DEFINE_VARIABLE and DEFINE_CONSTANT, etc.

    I usually have separate DEFINE_EVENT's for Data_Events, Timeline_Events, Channel_Events and Button_Events. This makes it easy to fold everything and then find exactly the DEFINE_EVENT section you want.

    I also usually put all the code for specific devices in an include or module.
  • Options
    Spire_JeffSpire_Jeff Posts: 1,917
    You do not have to declare variables in an include for access to them in an include. You just have to make sure they are declared before the #Include statement is issued. Include files are simply like pasting the code in right where the #include statement is. Here is an example:

    Include file named Example_Include
    Program_Name='Example_Include'
    
    Button_event[dvTp, nBtnArray]{
      Push:{
         //Do something.
      }
    }
    

    And now the mainline:
    Program_Name='Main'
    #Include = 'Example_Include'  // #1
    
    Define_Devices
    dvTP = 10001:1:0;
    
    Define_Variable
    Integer nBtnArray[] = {1,2,3,4,5,6,7,8,9}
    
    #Include = 'Example_Include'  // #2
    
    Define_Event
    #Include = 'Example_Include'  // #3
    
    

    #1 - This will cause a problem because the variables/devices are not defined, and it's not within a DEFINE_EVENTS section.

    #2 - This will still not work because a Button_Event can only occur in the Define_Events section.

    #3 - This should work fine.

    Hope this helps,
    Jeff
  • Options
    TurnipTruckTurnipTruck Posts: 1,485
    One more example for you. This is probably one of the most dramatic simplifications from one-event-per-button programming. It's the one-line IR control technique.
    BUTTON_EVENT [dvTP,0]
    {
    PUSH:
      {
      TO [dvIRDevice,BUTTON.INPUT.CHANNEL]
      }
    }
    

    In the above example, you make the channel codes on your touchpanel correspond to whatever IR output channel you want to control.

    You can add
    TO [BUTTON.INPUT]
    
    to the push handler section for true feedback on the touchpanel.
  • Options
    Lots of documentation in the code - helps me think about what I'm doing if I have to document it, and then there's the benefit of the documentation when you're done. Lots of toilet paper helps too. Sad to say I can't seem to write entirely soil-free code all the time, so I need to keep notes along the way and be intimately familiar with troubleshooting tools as well.
  • Options
    TonyAngeloTonyAngelo Posts: 315
    To piggy back off of that, you can further combine events by using a device array for IR control.
    DEFINE_DEVICE
    
    dvTP_DVD = 10001:2:0
    dvTP_Cable1 = 10001:3:0
    dvTP_Cable2 = 10001:4:0
    
    dvDVD = 5001:9:0
    dvCable1 = 5001:10:0
    dvCable2 = 5001:11:0
    
    DEFINE_VARIABLE
    
    DEV vdvIR_TP[] = {dvTP_DVD,dvTP_Cable1,dvTP_Cable2}
    DEV vdvIRDevices[] = {dvDVD,dvCable1,dvCable2}
    
    DEFINE_EVENT
    
    BUTTON_EVENT [vdvIR_TP,0]
    {
    PUSH:
      {
      TO [vdvIRDevices[GET_LAST(vdvIR_TP)],BUTTON.INPUT.CHANNEL]
      }
    }
    

    You can go beyond that by using channel events and virtual devices to achieve the same effect for 232 devices.
  • Options
    chillchill Posts: 186
    dmurray14 wrote: »
    How do you guys do it? I have been messing around with netlinx for a while now and can get it to do most anything I want, however I still have a million BUTTON_EVENTs for each TP button in the mainline and no real type of organization. What is the standard way of organizing things? I was thinking about having an include for different rooms and such, but then found out that the devices that were included in the mainline couldn't be used in the includes without defining them there. Then of course they were "double defined" if they are in the mainline too. So I'm curious, what is the correct way to go about this? It's such a pain in the neck to scroll through 5000 lines of code just to find something.

    Interested to hear what you do..

    Dan

    One approach I've taken at times is to start with comments for each type of thing I need the system to do. For example,
    define_variable
    
    // system housekeeping
    // scheduled events
    // display power
    // source selection
    // video conference
    // recording
    // volume controls
    

    Then go back and define variables, device and/or button arrays etc. under each comment, using names that make sense to me, like
    // source selection
    volatile integer src_b[] =    // source select buttons
    {
      101,102,103,104,105,106,107,108    // switcher inputs 1 thru 8
    }
    
    // volume controls
    volatile float biamp_vol[6]    // volume per zone as received from audia
    
    ...and so on. Then you can paste the whole define_variable section into define_event and edit as needed to make stuff happen.

    The advantage of this is that you have all the 'sections' of your program in the same order in both define_variable and define_event; this makes it easier (for me) to find things later. Also it ensures that names are visually identical throughout the program. I know the compiler is not case sensitive so it doesn't matter, but it irritates me to see foobar, Foobar and FooBar wandering around in the same program together. Gotta watch that OCD.

    Anyway, I don't do this all the time or even most of the time, but it's one approach to getting more organized.

    As for different rooms running the same program, I generally differentiate them (if I need to) by looking at the system's IP address or hostname in define_start, followed by
    switch(room_no)
    {
      case 1 :  // do something
      case 2 :  // do something else
    }
    
    ...at any point where system behavior needs to be different from room to room.

    Finally, regarding includes, I don't use them much except for some stupid functions I've written that are generic enough to be portable. This would be stuff like volume level scale conversion, date and time manipulation, etc.

    HTH.
  • Options
    annuelloannuello Posts: 294
    dmurray14 wrote: »
    I was thinking about having an include for different rooms and such, but then found out that the devices that were included in the mainline couldn't be used in the includes without defining them there. Then of course they were "double defined" if they are in the mainline too.

    An approach that has worked well for me is:
    1. In all .axs and .axi files, put the following at the top & bottom:
    program_name='my_filename'
    #if_not_defined MY_FILENAME
    #define MY_FILENAME
    .
    //Your code goes here
    .
    #end_if
    

    2. In your main file (which is "Set as Master"), declare your devices before you #include your .axi files.

    Using this approach means that my .axi files can refer to the devices declared in the .axs, since the device is declared before the #include statement. There is no need to declare the device in the .axi files.

    In my situation I have a Global.axi file which many .axi files rely on. The #if_not_defined statement ensures that the code in the Global.axi only gets compiled once. For good measure, I take the same approach for all .axi files and even my master .axs file. Some of my other .axi files get included multiple times, depending on the context.

    This approach works for me. I don't know if it is considered good form or not, but it certainly allows my .axi files to be more portable across multiple projects. The only thing I have to watch out for is consistent device-naming conventions, but that's not a huge issue.

    Roger McLean
    Swinburne University
  • Options
    TonyAngeloTonyAngelo Posts: 315
    dmurray14 wrote: »
    I was thinking about having an include for different rooms and such, but then found out that the devices that were included in the mainline couldn't be used in the includes without defining them there. Then of course they were "double defined" if they are in the mainline too.

    I have not found this to be the case. I usually define all my devices in the main code and then refer to those definitions in my includes. As long as the include is called out in the main program after the device has been defined you'll be okay.
  • Options
    dmurray14dmurray14 Posts: 80
    You guys are awesome...lots of great information to work off of here, and I've picked up a few tricks too. I'm probably going to start fresh now that I have a pretty good handle on things and go from there.

    I really appreciate the feedback guys - this really is a great community.

    Thanks again,
    Dan
  • Options
    viningvining Posts: 4,368
    You'll also want to use multiple DEFINE sections to seperate your code in to logical working segments so you can keep the stuff you're not working w/ folded and the stuff you are working w/ open. In the attachment you can see multiple DEFINE_DEVICEs, DEFINE_CONSTANTs, DEFINE_VARIABLEs, etc. This is very helpful way to maintain sanity when dealing with medium to large systems and a good practice to start using on small systems as well. Even if you're using dozens of includes and modules which this example does you will still want multiples to keep things seperated and easy to find.
  • Options
    dmurray14dmurray14 Posts: 80
    vining wrote: »
    You'll also want to use multiple DEFINE sections to seperate your code in to logical working segments so you can keep the stuff you're not working w/ folded and the stuff you are working w/ open. In the attachment you can see multiple DEFINE_DEVICEs, DEFINE_CONSTANTs, DEFINE_VARIABLEs, etc. This is very helpful way to maintain sanity when dealing with medium to large systems and a good practice to start using on small systems as well. Even if you're using dozens of includes and modules which this example does you will still want multiples to keep things seperated and easy to find.

    Makes sense...multiple DEFINE_EVENTs too?
  • Options
    jjamesjjames Posts: 2,908
    dmurray14 wrote: »
    Makes sense...multiple DEFINE_EVENTs too?

    Yes. See attached.

    Just no multiple DEFINE_PROGRAMs. ;)
  • Options
    a_riot42a_riot42 Posts: 1,624
    I use the right-click goto too much to create multiple define_* sections. What happens if you have a lot of define_variables and then use the goto function? Does it just go to the first one in the file?
    Paul
  • Options
    viningvining Posts: 4,368
    a_riot42 wrote:
    I use the right-click goto too much to create multiple define_* sections. What happens if you have a lot of define_variables and then use the goto function? Does it just go to the first one in the file?
    Yep. I generally just keep most things folded and scroll up or down but if you use the goto feature it jumps to the top of the section selected. .
  • Options
    Joe HebertJoe Hebert Posts: 2,159
    vining wrote:
    a_riot42 wrote:
    I use the right-click goto too much to create multiple define_* sections. What happens if you have a lot of define_variables and then use the goto function? Does it just go to the first one in the file?
    Yep. I generally just keep most things folded and scroll up or down but if you use the goto feature it jumps to the top of the section selected. .
    As a clarification and using the question posed by a_riot42, the ?Goto Section? jumps to the first DEFINE_VARIABLE it finds after the cursor so you can jump to successive DEFINE_VARIABLE sections by performing multiple Goto commands. If it doesn?t find anything after the cursor then it jumps back up to the top to look for the first instance. It works in the same fashion as any other ?Find?, at least that?s how it is with NS 2.5.

    I too am in the multiple DEFINE_x/code folding camp and so I don?t really use the ?Goto Section.? It?s easier for me to scroll through folded code.

    I do like the right click menu options but I do wish we could right click on a function call and have a menu item appear that performs a ?Find Definition...? I for one would find that extremely useful.
  • Options
    Chip MoodyChip Moody Posts: 727
    My opinion on code commenting
    Lots of documentation in the code
  • Options
    a_riot42a_riot42 Posts: 1,624
    Chip Moody wrote: »
    My opinion on code commenting

    Its a misconception that code should have a large quantity of commenting. Ideally, the code should be self-commenting. Only very obscure/difficult code should be heavily commented, but then the issue arises that if your code requires a lot of comments to be understood, perhaps it needs a rewrite/redesign.
    Paul
  • Options
    Spire_JeffSpire_Jeff Posts: 1,917
    So you're saying I should use more than 1 or 2 letters for my variable names? Doesn't everyone know what I mean when I use x, y, xx, xy, and z? :p

    Jeff
  • Options
    a_riot42a_riot42 Posts: 1,624
    Here is a quote about comments I think is good:

    When commenting, there are a few essential rules to believe in:

    The value of a comment is directly proportional to the distance between the comment and the code. Good comments stay as close as possible to the code they're referencing. As distance increases, the odds of developers making an edit without seeing the comment that goes with the code increases. The comment becomes misleading, out of date, or worse, incorrect. Distant comments are unreliable at best.

    Comments with complex formatting cannot be trusted. Complex formatting is a pain to edit and a disincentive to maintenance. If it is difficult to edit a comment, it's very likely a developer has avoided or postponed synchronizing his work with the comments. I view complex comments with extreme skepticism.

    Don't include redundant information in the comments. Why have a Revision History section-- isn't that what we use source control for? Besides the fact that this is totally redundant, the odds of a developer remembering, after every single edit, to update that comment section at the top of the procedure are.. very low.

    The best kind of comments are the ones you don't need. The only "comments" guaranteed to be accurate 100% of the time-- and even that is debatable-- is the body of the code itself. Endeavor to write self-documenting code whenever possible. An occasional comment to illuminate or clarify is fine, but if you frequently write code full of "tricky parts" and reams of comments, maybe it's time to refactor.
  • Options
    jjamesjjames Posts: 2,908
    a_riot42 wrote: »
    Its a misconception that code should have a large quantity of commenting. Ideally, the code should be self-commenting. Only very obscure/difficult code should be heavily commented, but then the issue arises that if your code requires a lot of comments to be understood, perhaps it needs a rewrite/redesign.
    Paul
    I pray to the Programming Gods that I never have to deal with Paul's code.

    In my very humble opinion, commenting is very important and should be done as much as possible. Unlike Paul, I realize that I will not be coding for the company I'm with for the rest of my life for one reason or another. Either I'll get shuffled out, I may end up leaving to take up crocheting, or the worst of all scenarios: I die. *GASP!* Either way, someone else is going to look at my code at some point in time, and I don't wanna be a jerk and leave a mystery to them to figure out. It just makes sense to comment your own code, I find myself using certain function in every single job that aren't commented, and if I had to modify them, it took me a minute to figure out what's going on. I've since commented them so I know what to change. Bad programming? Needs to be rewritten or redesigned? I think not. There will probably be at least 8,000 lines of code in your program, do you expect to remember everything?

    I believe this article is a good read: http://particletree.com/features/successful-strategies-for-commenting-code/
  • Options
    a_riot42a_riot42 Posts: 1,624
    Oh for crying out loud...
  • Options
    DHawthorneDHawthorne Posts: 4,584
    I try to name my functions and variables so it's clear what they do. It's hard to misunderstand what "ParseResponse()" does, after all. However, I also find that even after only a few weeks, I can't look at even my own code without scratching my head ... so I put enough comments in there so that the basic flow is at least documented. But I can't say I have the patience to comment heavily; I just put enough so the rest can be figured out more or less easily.
  • Options
    TurnipTruckTurnipTruck Posts: 1,485
    Variable naming is key. There is a school that likes to make variables names as short and seemingly cryptic as possible, but I see no reason. The better your variables are named, the more your code will read like sentences that you can understand.

    nUI - which UI caused the event in get last
    nBut - which button was pressed in get last
    cStringIn - data from a device
    cStringOut - data to a device

    etc......

    Further, the more mature your programming becomes, the less variables you'll need. A few nice structures used with the embedded event objects and your programs will become leaner, meaner and sexier! Just flash some code around at the local pub, you'll see!!
  • Options
    jjamesjjames Posts: 2,908
    Uh-oh! You opened up the dreaded Hungarian notation can of worms . . . (* queue dramatic music *)

    ;)
  • Options
    Further, the more mature your programming becomes, the less variables you'll need. A few nice structures used with the embedded event objects and your programs will become leaner, meaner and sexier! Just flash some code around at the local pub, you'll see!!
    If they don't cheer, they're just jealous, mate.

    Admittedly, I've often wondered why structures aren't used more often. It's as if paper were one day were introduced, and people thought it was marvelous. But then Mr. Joe Everyman realized a problem incurred when stacks of paper started to pile up around him, so he created the folder. Everyone was so proud of Joe. Some time later, Joe came up with another brilliant idea concerning the sorting of paper, but his colleague Bill was a complete douche and stole his idea before he could present it - thus the binder was born, with the capability of essentially holding multiple folders at once. Finally, out of desperation, Joe invented the filing cabinet as a means of containing all such stuff and more! He even made this little section at the top of each drawer that fits a small piece of a index card with a poorly written name on it. But for some reason, people were never quite certain about this "filing cabinet", as they may as well just put all the binders and folders in a bookshelf...

    Well anyway, I think Joe was brilliant, and I like my filing cabinets...er...structures!
  • Options
    annuelloannuello Posts: 294
    But for some reason, people were never quite certain about this "filing cabinet", as they may as well just put all the binders and folders in a bookshelf...

    Well anyway, I think Joe was brilliant, and I like my filing cabinets...er...structures!

    To continue the analogy, would an XML file be a compactus? Great for storing lots of data (especially structures) which you don't have to access frequently. ;)

    Roger McLean
    Swinburne University
  • Options
    a_riot42a_riot42 Posts: 1,624
    Admittedly, I've often wondered why structures aren't used more often.

    Because they cant be passed to modules.
    Paul
  • Options
    DHawthorneDHawthorne Posts: 4,584
    annuello wrote: »
    To continue the analogy, would an XML file be a compactus? Great for storing lots of data (especially structures) which you don't have to access frequently. ;)

    Roger McLean
    Swinburne University

    An analogy is like a car ... drive it too far, and it breaks down. But XML is closer to the Dewey Decimal System ... it's a way to organize and access data, not really a way to store it. Storage is the back end, and XML doesn't really care if it's a file cabinet or a bookshelf, it will work with both.
Sign In or Register to comment.