Home AMX User Forum NetLinx Studio

Where do you put your INCLUDEs?

Curious to know the consensus for placement of INCLUDEs? I've put them at the beginning of my program file, in the Define_Start and in the mainline.

My includes are mostly a series of functions rolled together, but when I put them in the define_Start (which seems to make sense to load the file only once), strange things sometimes happen like DEFINE_START looping.

I also use INCLUDEs to separate portions of my program into smaller files for things like Monitor control, Camera control, etc.

Comments

  • The files do not get LOADED.

    The code in the include is compiled into the the program.

    Where you put the includes in your program depends on what is in them.

    If all that is in there is functions that you call from your program, I would put them at the top of the Program Source "Master.axs".
  • DHawthorneDHawthorne Posts: 4,584
    I try to design my INCLUDES so I can put them right at the top of the main code, before DEFINE_DEVICE. I prefer to have them all in one spot. But, as mentioned, they are inline code insertions, so you have to be careful. So, if an INCLUDE, for example, by necessity must have references in it to a device that wasn't declared yet, it has to come after DEFINE_DEVICE. I do not like to define devices in an INCLUDE myself, again, I like to have them all spelled out in one spot to avoid confusion, so when I write the include myself I set it up so that works. Third-party includes don't always have that option, so those I'll put right before DEFINE_START, so all their dependencies are intact.

    But all that is just my preference. It depends on what you are doing with them. I generally only use includes to define variables and constants needed for modules; keeping them in a separate file just makes them easier to maintain. If you are going to put code blocks in them, you have to place them where the actual code would be going if you wrote it out.
  • ericmedleyericmedley Posts: 4,177
    I try to design my includes so that they fall into two catagories.
    1) stuff that always changes from project to project. They are usually called 'config'
    2) stuff that never changes at all. they are usually named with the specific thing they deal with.

    the configs are declared at the top for ease.
    the non-changing ones usually go at the bottom of the configs so you don't have to think about them other than to make sure they're added to the project. Also, since most of what they use is actually declared in the config file above.
  • JasonSJasonS Posts: 229
    I hate the way includes work in NetLinx they are practically useless for anything more than utility functions IMHO. I put all of my Includes and Define_Module statements at the end of my Define_Variable section before my Define_Functions. I would like to be able to organize template code in includes but you always run into reference ordering problems. The only way around it is to split your includes into sections (ie Define_Variable, Define_Event, etc.) and include each one in the proper section, I tried it once and gave up, it kind of defeats the purpose as far as I am concerned. I got the Duet bug now and I don't want to go back to NetLinx.
  • ericmedleyericmedley Posts: 4,177
    JasonS wrote: »
    I hate the way includes work in NetLinx they are practically useless for anything more than utility functions IMHO. I put all of my Includes and Define_Module statements at the end of my Define_Variable section before my Define_Functions. I would like to be able to organize template code in includes but you always run into reference ordering problems. The only way around it is to split your includes into sections (ie Define_Variable, Define_Event, etc.) and include each one in the proper section, I tried it once and gave up, it kind of defeats the purpose as far as I am concerned. I got the Duet bug now and I don't want to go back to NetLinx.
    Ah yes..
    another opportunity for me to trot out my annoying plea for a Netlinx 'Super Include' basically an include that doesn't compile linearly with the rest of the code. That way, functions, variable, constants, devices that haven't appeared yet in the linear compile don't throw and error until the complier has made it all the way through all the code. That would solve soooooo many issues that make using includes so annoying.

    squeak squeak squeak...
  • JasonSJasonS Posts: 229
    Ha, Ha, Ha don't hold your breath. I complained so much to my rep that they put me in contact with "someone that could help". All they did was tell me that was how every programming language worked and that I just had to program around it, they could give pointers on how to do it. Once you get to a certain point the only thing AMX Tech Support is good for is telling you about the screw ups they will admit to and that they are "working on it". I get so tired of having to hound them for follow up information on an issue it is ridiculous.

    Sorry I blew up...
  • a_riot42a_riot42 Posts: 1,624
    ericmedley wrote: »
    Ah yes..
    another opportunity for me to trot out my annoying plea for a Netlinx 'Super Include' basically an include that doesn't compile linearly with the rest of the code. That way, functions, variable, constants, devices that haven't appeared yet in the linear compile don't throw and error until the complier has made it all the way through all the code. That would solve soooooo many issues that make using includes so annoying.

    squeak squeak squeak...

    This is why I put mine farther down the axs file. I declare mine between DEFINE_LATCHING and DEFINE_MUTUALLY_EXCLUSIVE and then DEFINE_MODULE after that. I don't really have any methodology, but its worked out fine so far.
    Paul
  • GregGGregG Posts: 251
    I use 3 different location for my includes.

    My debugging functions include and any job related constant includes go right before DEFINE_CONSTANT.
    My "data" includes (with variables and lookup functions) go after DEFINE_VARIABLE, but before any function defs.
    My device control and module/subsystem includes all go right before DEFINE_START.

    The main thing I try to remember is always put includes on the line before a DEFINE_...something line - because the includes can sometimes have define sections in them that you forget about over time and whatever section the include leaves open or active is what the next line of your main code will be in.

    so an include like this ("ex1.axi"):
    DEFINE_CONSTANT
    NULL = 0
    DEFINE_PROGRAM
    

    would cause counter-intuitive results when used in a main code file like this:
    //.... stuff above
    
    DEFINE_START
    Include 'ex1.axi'
    CALL 'INIT'
    
    DEFINE_EVENT
    
    // ... stuff continues
    

    Now you essentially have CALL 'INIT' defined in the DEFINE_PROGRAM section, and that will run it over and over.
  • PhreaKPhreaK Posts: 966
    I've recently switched to the habit of placing them all up the top so that I can see all the dependancies of a file when I open it. The only thing that goes above them is compiler directives that handle conditional compilation components (e.g. RMS callbacks). I also get around the device definition / include order dance by using an include purely for all my device definition to. This the ends up with something like the following:

    My 'master' source:
    PROGRAM_NAME='FooBar'
    
    #INCLUDE 'Devices';
    #INCLUDE 'FunctionalityBlockA';
    #INCLUDE 'FunctionalityBlockB';
    #INCLUDE 'FunctionalityBlockC';
    

    Devices.axi:
    PROGRAM_NAME='Devices'
    
    
    DEFINE_DEVICE
    
    // Base devices
    dvMaster = 0:1:0;
    
    dvTp = 10001:1:0;
    dvADevice = 5001:1:0;
    dvAnotherDevice = 5001:2:0;
    
    // RMS core devices
    vdvRms = 41001:1:0;
    vdvRmsGui = 41002:1:0;
    ...
    

    A module example:
    MODULE_NAME='ModuleThatDoesSomeRmsThings'(dev vdvRMS)
    
    #DEFINE INCLUDE_SCHEDULING_NEXT_ACTIVE_RESPONSE_CALLBACK
    #DEFINE INCLUDE_SCHEDULING_ACTIVE_RESPONSE_CALLBACK
    #DEFINE INCLUDE_SCHEDULING_NEXT_ACTIVE_UPDATED_CALLBACK
    #DEFINE INCLUDE_SCHEDULING_ACTIVE_UPDATED_CALLBACK
    #DEFINE INCLUDE_SCHEDULING_EVENT_ENDED_CALLBACK
    #DEFINE INCLUDE_SCHEDULING_EVENT_STARTED_CALLBACK
    #DEFINE INCLUDE_SCHEDULING_CREATE_RESPONSE_CALLBACK
    
    
    #INCLUDE 'RmsSchedulingApi'
    #INCLUDE 'RmsSchedulingEventListener'
    
    
    // some code that does interesting things here
    ...
    

    A 'singleton' include:
    PROGRAM_NAME='ThisShouldOnlyEverBeUsedOnceInEachScope'
    
    
    #IF_DEFINED __SOME_UNIQUE_IDENTIFIER__
    #WARN 'ThisShouldOnlyEverBeUsedOnceInEachScope already in use for this scope'
    #ELSE
    #DEFINE __SOME_UNIQUE_IDENTIFIER__
    
    // Put some code here that can only be used once
    
    #END_IF // __SOME_UNIQUE_IDENTIFIER__
    

    And finally, an include that can be used repeatedly (such as a common functions library)
    PROGRAM_NAME='ThisIsAReusableLibrary'
    
    
    #IF_NOT_DEFINED __A_LIBRARY__
    #DEFINE __A_LIBRARY__
    
    // library code here
    
    #END_IF // __A_LIBRARY__
    
  • JasonSJasonS Posts: 229
    My problem is that the way I want to use includes is for complex sub systems that reference each other. I always end up running into issues with definition order. I briefly experimented with splitting my includes by section, but I felt like it defeated the purpose of making the sub sections easier to maintain / customize per project. Now I just copy the sub systems I need into the main axs file per project. I still use the occasional include for the more complex modules. After doing my first all Duet system I am completely sold on it. Now all I have to do is get management and the other programmers on board.
  • chillchill Posts: 186
    I use includes for just a few things:

    0. General rule: put #includes just before a DEFINE_*. Rationale: your include may contain several DEFINE_SOMETHING sections. By putting it just before one in your main program, you ensure that the main program is back on the correct DEFINE after the include is... well, included.

    1. Device definitions, which I put at the start of define_device (breaking Rule Zero). These includes are written per type of controller, to allow me to do my define_device section with reference to concepts like "the I/O port" without having to remember that it's 5001:17:0 or 5001:4:0. So I have files like 'ni3kdev' and 'ni700dev'. For example, my 'ni3kdev' contains
    sio1 = 5001:1:0
    sio2 = 5001:2:0
    // and so forth
    
    ...and my main program's define_device might be:
    #include'ni3kdev'
    vtc = sio1
    cam = sio2
    // and so forth
    

    2. Utility functions, as JasonS says. In these cases the include contains one or more define_function()s. These are general-purpose things that I use often, but I don't use all of them for every project. I put them at the beginning of the define_function / define_call area.

    My philosophy is that my function libraries, like my modules, should each do one thing and do it well. I try to create robust, portable building blocks that can be reused again and again. On that note, why would anyone use an include that is customized for a specific main program? If you're going to do that, you might as well just put that code in the main program and ditch the include; if nothing else it will be more readable.

    Finally, and off topic: does anyone really call their programs Master.axs? I like the program_name to at least give a small hint as to the nature of the system. Like 12th_floor_command_center.axs. Unlike includes, this really is customized to a specific system.

    Just my 1.8 cents.
    .
  • JasonSJasonS Posts: 229
    chill wrote: »
    On that note, why would anyone use an include that is customized for a specific main program? If you're going to do that, you might as well just put that code in the main program and ditch the include; if nothing else it will be more readable.
    .

    Readability is exactly why I customize those includes to the specific project. The sub systems are contained in their own include, this makes it much quicker and easier to navigate the main program. These includes generally contain structures that require customization for the particular project, room names, source destination names, security levels, etc. The includes are designed to to be customized, code that should not be edited is notated as such as well as areas where customization needs to happen. This makes the main program much clearer because it is not full of implementation details.
  • GregGGregG Posts: 251
    JasonS wrote: »
    Readability is exactly why I customize those includes to the specific project. The sub systems are contained in their own include, this makes it much quicker and easier to navigate the main program.

    I completely agree with this. After you hit about 5000 or so lines in main code, you really have to start breaking things down into functional units or it will be impossible to follow the program flow.

    The job I'm working on today has only 2000 lines of code in the define_event section of main code.
    But that section defines all the pushable user interface buttons so I can quickly see where to look to diagnose any action that is not happening properly.

    The functions that are referenced there are scattered across 1.5 Meg of axi and axs files and would have made for a main code file in the primary master with nearly 30,000 lines if they were all inline. I definitely prefer a hierarchical overview.
  • PhreaKPhreaK Posts: 966
    JasonS wrote: »
    My problem is that the way I want to use includes is for complex sub systems that reference each other. I always end up running into issues with definition order.

    There are a few tricks you can use to get around this. Although the compiler is not multipass there's a bit of an oddity with functions - these can be referenced before they appear in the compile order. This enables you to then do things like build getter and setter functions (albeit without any actual privacy for the actual variables) as well as callbacks to keep things a little more modular.
  • JasonSJasonS Posts: 229
    The only idea that I have for a real solution is an external program. This program would take the main program and all the includes and generate a combined AXS file to compile.
  • ericmedleyericmedley Posts: 4,177
    JasonS wrote: »
    The only idea that I have for a real solution is an external program. This program would take the main program and all the includes and generate a combined AXS file to compile.

    I honestly don't see a big issue to fix the problem. The major errors that result from trying to use INCLUDES that are not syntax related (or other programming issues) is that something or other is not yet declared. During compile all one would have to do is essentially say, " Hmmm.. This function call/variable/Constant/ etc... Hasn't been declared yet... I'll just put a sticky note on it and keep going until the end of my first run through and see if it shows up. If not, I'll throw the error."

    The first compiler pass wouldn't need to actually generate any machine code. It would essentially be a pass through to make a general outline/roadmap of the full program. If all the mojor pieces are correct, then you can run through again using the first pass as a road map to locate all the prices and parts needed to do a full compile.

    I've heard the argument about loosing the linearity by having a super include but if this is true, why then does it not throw an error if you call a function on line 100 if the actual function declare is on line 200?

    And while it is true that other programming languages don't do it either, it's kind of non sequitur because INCLUDES in other SDKs don't look or behave at all like includes in Netlinx.
  • ericmedley wrote: »
    I've heard the argument about loosing the linearity by having a super include but if this is true, why then does it not throw an error if you call a function on line 100 if the actual function declare is on line 200?

    Bingo. If you reference include file variables indirectly through function calls (Set_Variable(x), Get_Variable()) you don't need to worry so much as to the where a particular include file is the compile order.
  • JasonSJasonS Posts: 229
    ericmedley wrote:
    I honestly don't see a big issue to fix the problem. The major errors that result from trying to use INCLUDES that are not syntax related (or other programming issues) is that something or other is not yet declared. During compile all one would have to do is essentially say, " Hmmm.. This function call/variable/Constant/ etc... Hasn't been declared yet... I'll just put a sticky note on it and keep going until the end of my first run through and see if it shows up. If not, I'll throw the error."

    I agree it doesn't seem like it would take much for AMX to fix this. However what I have been told by an AMX Engineer is that they are very reluctant to touch the compliler unless there is a major bug.
    icraigie wrote:
    Bingo. If you reference include file variables indirectly through function calls (Set_Variable(x), Get_Variable()) you don't need to worry so much as to the where a particular include file is the compile order.

    I never seem to have problems with variables across multiple includes, it is only function calls.
Sign In or Register to comment.