Home AMX User Forum NetLinx Studio

#if_not_defined ?

In a module I am pesently working on I decided to use the #DEFINE, #IF_NOT_DEFINED and #END_IF to control my diagnostic SEND_STRING 0, messages instead of using a Virtual channel in a IF statement to determine whether or not to bombard the terminal with all the diagnostic messages all the time. My thinking was that these line of codes, IF's conditions and SEND_STRING's don't need to be there 99.999% of the time the code is running but only when developing or troubleshooting. So I figured I'd use the #DEFINE DEBUG and put the #IF_NOT_DEFINED DEBUG in front of all diagnostics based code and send_strings with obviously the #END_IF after and when wanting to use the DEBUG ) I would uncomment the line #DEFINE DEBUG and re-compile. That way all that debug stuff isn't compiled and running with the actual working code.

That seems to work fine in the INCLUDE .axi but how does that work in the associated module when thats compiled before the MAIN .axs file does. I tried to use a different #DEFINE in the module with an #INCLUDE there after to the .axi file but it doesn't appear to like #IF_NOT_DEFINED XXXXXX and #END_IF with in a different set of #IF_NOT_DEFINED YYYYYY and #END_IF.

Now with just the #DEFINED DEBUG in the .axi that seems to halt the send_string 0 from the module but I can't imagine how. It's already compiled when I do the MAIN.axs so it has no way of knowing if its "DEFINED". So is it working like a typical if condition? I'm trying to eliminate thos section from compiling for normal operation and only use it when I need to. I also just want to control this abilitiy from the .axi so I don't have to include the source code of the module. Actually that can't work because the module would have to be compile for it to take affect. So I guess the simplest thing to do is just put a #DEFINE DEBUG in the module as well.

For my own edification is it possible to put these #IF_NOT_DEFINED statement with in other #IF_NOT_DEFINED statements. Can the compiler figure it out and works its way outward from the middle set?

Comments

  • Two thoughts:

    If you want a given define or flag or constant value or similar to be available in all modules, put it with all of its friends in a special include and include it at the beginning of every source file. I call mine "iBuildConstants.axi".

    I didn't follow your description of your code; could you please post an example?
  • viningvining Posts: 4,368
    I like that idea of a single INCLUDE .axi. I can put all the #DEFINE's in it create a different #DEFINE for each .axi or .axs file and comment out selectively what I want when I want to see the debug SEND_STRIN 0, in terminal.


    Here's a piece of the code:
    (*VAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVVVAVAVAV*)
    (* DEBUG CONTROL SEND_STRING 0, COMPILE WITH OR WITH OUT *)
    (*VAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVVVAVAVAV*)
    
    //#DEFINE VAV_NP_DEBUG        // <<---------Comment out to compile w/o send_string 0 feedback!!
    
    (* if you change the comment you must recompile the      *)
    (* module for this to take effect in the module as well! *)
    (* nothing else goes between the above #END_IF and the   *)
    (* below #IF_NOT_DEFINED VAV_NP_DEBUG_INCLUDE !! or else!*)
    
    (*VAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVVVAVAVAV*)
    (*   UNCOMMENT TO COMPILE WITH DEBUG SEND_STRING 0, FB   *)
    (*VAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVVVAVAVAV*)
    

    if (nReadFHandle > 0) 
    	  {
    	  (*  nReadFHandle = fnFfile_Seek(nReadFHandle,0,icReadFile) ; *)  //leave in, for for future needs 		     
    	  nReadFResult = FILE_READ(nReadFHandle,cReadFileBuf,inReadFStrLen) ;
    	  #IF_DEFINED VAV_NP_DEBUG ;
    	  if (nReadFResult > 0) 
    	       {
    	      // cAMX_NPReaderbuf = cReadFileBuf ;
    	       SEND_STRING 0,"'AMX_NotePad fnReadFile. File_Read OK. Rcvd: a ',
    					     itoa(length_string(cReadFileBuf)),
    					     ' CHAR String!  line-<',ITOA(__LINE__),'>',crlf" ; 
    	       (*   fnFdeleteFile(icReadFile) ;  *)
    	       }
    	  else
    	       {
    	       local_var char cErrorMsg [20] ;
    	       switch (itoa(nReadFResult)) 
    		    {
    		    case '-9': {cErrorMsg = 'end-of-file reached'} ;
    		    case '-6': {cErrorMsg = 'invalid parameter'} ;
    		    case '-5': {cErrorMsg = 'disk I/O error'} ;
    		    case '-1': {cErrorMsg = 'invalid file handle'} ;
    		    case '-0': {cErrorMsg = 'zero bits returned?'} ;
    		    }
    	       SEND_STRING 0,"'AMX_NotePad fnReadFile. Bad Read_File: ',cErrorMsg,
    						  '! line-<',ITOA(__LINE__),'>',crlf" ;
    	       }
    	  #END_IF ;
    	  file_close(nReadFHandle) ;
    	  }
    
  • I may be missing your desired result here,

    But I think you are complicating matters.

    If your goal is to be able to turn on/off your debug send_string 0's, you could just define a virtual device and a debug flag for each element you want to debug. Then you could just do a SEND_Command to the virtual device "DEBUG=1" or "DEBUG=0" from telnet.
    For example...

    DEFINE_DEVICE
    
    vdvVAV_NP_DEBUG  33001:1:0
    
    DEFINE_VARIABLE
    
    integer nVAV_NP_DEBUG
    
    DEFINE_EVENT
    
    DATA_EVENT[vdvVAV_NP_DEBUG]
    {
            Command:
             {
                    Switch(DATA.TEXT)
                     {
                            CASE 'DEBUG=0':{ nVAV_NP_DEBUG = 0}
                             CASE 'DEBUG=1':{ nVAV_NP_DEBUG = 1}
                     }
              }
    }
    

    Then....
    if (nReadFHandle > 0) 
    	  {
    	  (*  nReadFHandle = fnFfile_Seek(nReadFHandle,0,icReadFile) ; *)  //leave in, for for future needs 		     
    	  nReadFResult = FILE_READ(nReadFHandle,cReadFileBuf,inReadFStrLen) ;
    	  if(nVAV_NP_DEBUG)                 //#IF_DEFINED VAV_NP_DEBUG ;
                      {
    	  if (nReadFResult > 0) 
    	       {
    	      // cAMX_NPReaderbuf = cReadFileBuf ;
    	       SEND_STRING 0,"'AMX_NotePad fnReadFile. File_Read OK. Rcvd: a ',
    					     itoa(length_string(cReadFileBuf)),
    					     ' CHAR String!  line-<',ITOA(__LINE__),'>',crlf" ; 
    	       (*   fnFdeleteFile(icReadFile) ;  *)
    	       }
    	  else
    	       {
    	       local_var char cErrorMsg [20] ;
    	       switch (itoa(nReadFResult)) 
    		    {
    		    case '-9': {cErrorMsg = 'end-of-file reached'} ;
    		    case '-6': {cErrorMsg = 'invalid parameter'} ;
    		    case '-5': {cErrorMsg = 'disk I/O error'} ;
    		    case '-1': {cErrorMsg = 'invalid file handle'} ;
    		    case '-0': {cErrorMsg = 'zero bits returned?'} ;
    		    }
    	       SEND_STRING 0,"'AMX_NotePad fnReadFile. Bad Read_File: ',cErrorMsg,
    						  '! line-<',ITOA(__LINE__),'>',crlf" ;
    	       }
    	  }               //#END_IF ;
    	  file_close(nReadFHandle) ;
    	  }
    
    [/QUOTE]

    Now all you would need to do is the following from telnet...

    SEND_COMMAND vdvVAV_NP_DEBUG,"'DEBUG=1'"

    or

    SEND_COMMAND vdvVAV_NP_DEBUG,"'DEBUG=0'"

    No new recompiles/downloads needed
  • DHawthorneDHawthorne Posts: 4,584
    I used that method in the Axcent days, but nowadays, I use a variable flag like Kenny suggests.

    #DEFINE and the associated #IF's that go with it are pre-compiler statements. They are evaluated before the code is compiled. If used in an #INCLUDE (which is also a pre-compiler operation), the compiler session is going to have no way to distinguish one from the other if they are similarly named. If you really must use them, give your #DEFINEs a unique name for every component - so your main code can use DEBUG, but your modules can call it MODULE_1_DEBUG, or something like that.
  • viningvining Posts: 4,368
    I was doing the same with a virtual channel.

    if (dvVirtual,255])
    {
    send_string 0, stuff .......
    }

    I would set the channel when I wanted to see the debug. The problem is, although not a big problem, is that the debug code is always running for no reason the only difference is whether or nor we send it to view in terminal. My thinking is why even compile it in the code for normal day to day operation. If you need it re-compile with the #DEFINE whatever un-commented, debug, and when finished, re-compile with it commented out effectively removing all debug code from running in the normal ay to day operation. There's no need for it running when no one is viewing terminal and the send_string 0, is only part of the running debug code.
  • viningvining Posts: 4,368
    Of course what KennyP did is more to my liking where everything other than the one "IF" condition is overlooked although still compiled.

    The way I was previously doing this was just around the send_string itself and didn't include the rest of the debug code. Which was sort of me being simple/stupid.

    I'll wrestle with which method is the best all around one to use but your (Dave & KennyP) preferences are well noted.

    The #IF_NOT_DEFINE or #IF_DEFINE can they be used with in each other?

    like:

    #IF_NOT_DEFINED X

    some code stuff.


    #IF_NOT_DEFINED Y

    some code stuff.

    #END_IF



    some other code stuff.


    #END_IF


    can the compiler handle this.
  • DHawthorneDHawthorne Posts: 4,584
    Yes, you can nest the preprocessor statements like that. I do it all the time.
  • viningvining Posts: 4,368
    OK, after some thinking I've come to the conclusion that although it would be beneficial not to have unnecessary code compiled, taking up space and using up system resources it would be beneficial to be able to turn on the debug feedback with out the need to shut down and re-compile. The extra time to evaluate all the "IF" conditions and the extra memory used is negligible when compared it's run time benefit. The use of a systems page to be able to set these dvVirtual channels or global variable for each module or include file would be handy for determining which channel or flag to set for a particular feedback that is instead of trying to look through notes or the source code to figure out what variable or channel to set for a particular section of code.

    Although on the size and type of systems I've been working on, shutting down to re-compile isn't an issue, the systems alot of you guys work on, shutting down to re-compile might not be a viable option or an option at all in order to see what's going on.
  • Multiple levels of debug

    I discovered a while ago by experimentation with an NXC-ME260 that send_string 0 slugs the processor massively. I don't know why and I don't know if it does the same on an NI. Nevertheless I write my code to maximise the debug information but then to be able to reduce it to little or nothing as the code matures.

    When I first write new code, I litter it profusely with verbose calls to a debug function - most functions will send a list of all arguments, for example. (If every module did this all the time the result would not be nice.)

    The debug function decides whether to send_string it according to the state of a variable (one for each module).

    As the code matures and the debug stream begins to get tedious, I comment out all the (a) trivial (reliable) stuff I never want to see again - but leave it there just in case I hit a problem.

    Major functions (c) that I always want to see in the debug stream eg button pushes or significant system state changes get left in the code, and always get sent when the debug variable is on.

    In between (b) is a large class of debug call that I only want to see when I'm debugging that module, and that I don't want to fatten the code and the processor load in other circumstances. These I put inside an "#if_defined FullDebug" bracket, with the "#define FullDebug" right up at the top of the module in clear view.

    The last thing I do before leaving a site is to search for "#define FullDebug" and comment them all out and set the default values of all the debug variables to False, then recompile and reload a minimal size / minimal load program.

    I'm very careful to ensure that no non-debug code creeps into the debug bracket as this would be Not Good.
  • viningvining Posts: 4,368
    In the "Don't compile debug when no longer needed" version I ended up with a seperate include file as shown below:
    PROGRAM_NAME='VAV_DEBUG_FEEDBACK'
    (***********************************************************)
    (*  FILE CREATED ON: 09/13/2006  AT: 15:04:08              *)
    (***********************************************************)
    (*  FILE_LAST_MODIFIED_ON: 09/13/2006  AT: 15:51:11        *)
    (***********************************************************)
    
    
                    (*  INSTRUCTIONS   *)
    (*VAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVVVAVAVAV*)
    (* THIS INCLUDES ALL THE DEBUG FOR THE SYSTEM            *)
    (* UN-COMMENT A SECTION TO ENABLE FEEDBACK               *)
    (*VAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVVVAVAVAV*)
    
    (*VAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVVVAVAVAV*)
    (* AFTER CHANGES RE-SAVE THIS FILE                       *)
    (* IF THE CHANGES EFFECT MODULES THEY MUST BE RE-COMPILED*)
    (*VAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVVVAVAVAV*)
    
    (*VAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVVVAVAVAV*)
    (* ONLY UN-COMMENT SECTIONS THAT YOU DESIRE FEEDBACK FROM*)
    (*                                                       *)
    (*VAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVVVAVAVAV*)
    
    
    
    (* EFFECTS 'VAV_NotePad_UpdaterMOD_v1' & 'VAV_NotePad_AutoUpdater' )//re-compile module
    (*
    #IF_NOT_DEFINED VAV_NP_DEBUG
    #DEFINE VAV_NP_DEBUG
    #END_IF
    *)
    
    
    
    (* EFFECTS 'VAV_!MyWeatherMod1' & 'VAV_Weather_Settings' *)//re-compile module
    (*
    #IF_NOT_DEFINED VAV_RSSWEATEHER_DEBUG
    #DEFINE VAV_RSSWEATEHER_DEBUG
    #END_IF
    *)
    
    
    
    (* EFFECTS 'VAV_Roku_SoundBridge' *)
    (*
    #IF_NOT_DEFINED VAV_SB_DEBUG
    #DEFINE VAV_SB_DEBUG
    #END_IF
    *)
    
    keep adding for all files with send_string feedback features!!
    


    I include this at the beginning of all files as shown below:
    MODULE_NAME = 'VAV_NotePad_UpdaterMOD_v1' (DEV  vdvFTPmoduleDiag,
    					DEV  dvFTPSourceLocalSock,
    					DEV  dvFTPDestLocalSock,
    					CHAR cFTPModSourceIP[],
    					CHAR cFTPModSourceUser[],
    					CHAR cFTPModSourcePass[],
    					CHAR cFTPModDestIP[],
    					CHAR cFTPModDestUser[],
    					CHAR cFTPModDestPass[],
    					INTEGER nFTPNUM_FILES_TO_CHECK)
    
    
    (*VAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVVVAVAVAV*)
    (* DEBUG CONTROL SEND_STRING 0, COMPILE WITH OR WITH OUT *)
    (* GO TO VAV_DEBUG_FEEDBACK TO TURN ON OR OFF ! ! ! ! ! !*)
    (*VAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVVVAVAVAV*)
    
    #INCLUDE 'VAV_DEBUG_FEEDBACK' 
    

    Then in all areas of code that related to feed back (send_string 0, stuff) I do this:
    if (cFTPdoWhat == '<:CHECK:>')
    	  {
    	  SWITCH (ulMessageType)
    	       {
    	       (* USER LOGIN STUFF *)
    	       CASE 220:   // server ready
    		    {
    		    send_string dvLocalSocket,"'user ',cUser,13,10" ;
    		    #IF_DEFINED VAV_NP_DEBUG 
    		    send_string 0,"'FTPModule: READY (220): SENDING: user ',cUser,', --line-<',ITOA(__LINE__),'>',13,10" ;
    		    #END_IF 
    		    }
    	       CASE 221:   // goodbye!
    		    {
    		    #IF_DEFINED VAV_NP_DEBUG 
    		    send_string 0,"'FTPModule: GOODBYE! --line-<',ITOA(__LINE__),'>',13,10" ;
    		    #END_IF 
    		    }
    	       CASE 530:   // not logged in
    		    {
    		    send_string dvLocalSocket,"'user ',cUser,13,10" ;
    		    #IF_DEFINED VAV_NP_DEBUG 
    		    send_string 0,"'FTPModule: NOT LOGGED IN (530): SENDING: user ',cUser,', --line-<',ITOA(__LINE__),'>',13,10" ;
    		    #END_IF 
    		    }
    	       CASE 331:   // password required
    		    {
    		    send_string dvLocalSocket,"'pass ',cPass,13,10" ;
    		    #IF_DEFINED VAV_NP_DEBUG 
    		    send_string 0,"'FTPModule: PASSWORD REQUIRED (331): SENDING: pass ',cPass,', --line-<',ITOA(__LINE__),'>',13,10" ;
    		    #END_IF 
    		    }
    	       CASE 550:   // file not found
    		    {
    		    if (find_string(cFTPModSFileName,';',1))
    			 {
    			 #IF_DEFINED VAV_NP_DEBUG 
    			 send_string 0,"'FTPModule:(550)<:CHECK_NOT_FOUND:>',cNP_CheckFileName,'</NP>',13,10" ;
    			 #END_IF 
    			 send_string vdvFTPmoduleDiag,"'FTPModule:(550)<:CHECK_NOT_FOUND:>',cNP_CheckFileName,'</NP>',13,10" ;
    			 fnFTPCheckAllFiles(550,cServerMsg) ;
    			 }
    		    else
    			 {
    			 send_string dvFTPSourceLocalSock,"'quit',13,10" ;
    			 send_string vdvFTPmoduleDiag,"'FTPModule:(550)<:CHECK_NOT_FOUND:>',cNP_CheckFileName,'</NP>',13,10" ;
    			 send_string vdvFTPmoduleDiag,"'FTPModule:(550)<:CHECK_FINISHED:>',13,10" ;
    			 #IF_DEFINED VAV_NP_DEBUG 
    			 send_string 0,"'FTPModule:(550)<:CHECK_NOT_FOUND:><:CHECK_FINISHED:></NP>',13,10" ;
    			 #END_IF 
    			 }
    		    }
    		    ............
    

    This works real well but requires re-compiling associated files if modules and then the main.axs but changes to debug operation are only made in the dubug include .axi which I place references to which files are effected and other notes as needed. I just put this together today so it's not complete.

    Plus I think the simpler [dvVirtual,channel] or global VAR flagging method may be more beneficial as I can enable or disable feedback during runtime which so far isn't a big deal to me now but because it may be in the future I want to pick the right path for the long haul not just immediate satisfaction. Although that's good to!

    I thought I was in favor of the flagging method but I like this one too!
  • DHawthorneDHawthorne Posts: 4,584
    I have pretty much abandoned the idea that I needed to be niggardly about system resources and code size. I had a lot of habits left over from the early days of having to write 64k .com programs; but even if you go completely ape with a NetLinx system (bad code, like endless loops or recursion excepted), you are unlikely to have memory issues, or processor loading due to code operations. What lags a NetLinx system is connections and message transfers between the master and devices, not the stuff that happens on the master and stays there.

    That siad, this is my (latest) standard procedure for debugging:

    I create a vritual device and use channel one as a debug flag. I have a regular debug message function that will send debug messages if the channel is set on, and the function identifies the originating master (or device, if it's a module). I call this function liberally throughout my program; it provides any kind of status I like, and does nothing at all if my debug channel is not on. Out of sheer laziness and ease of typing, I will usually have a variable that folows the channel state, and I can test against that instead of the channel. The vritual device also has a command handler that I can use to query my system if telnetted in from a remote site. I can turn the channel on and off via telnet, and it doesn't depend on me being at a location that has Studio installed for debugging; I can also walk another tech through the procedure if I am driving or otherwise not in a position to do it myself.
  • viningvining Posts: 4,368
    Dave wrote:
    I have pretty much abandoned the idea that I needed to be niggardly about system resources and code size.

    After looking up the meaning of the word "niggardly", I agree. As long as the evaluation of the "IF" condition stays with in the processor itself the time to evaluate, say a thousand send_string related IFs probably wouldn't take a millisecond to complete.

    Unfortunately being anal in conjunction with my OCD makes me reluctant to undo something I've done after writing it. My systems are comparatively small and the compiled code is around 1 meg compiled so there's plenty of space available. Part of the anal OCD thing requires me to finish something before I throw it out and do something diferent, which makes no sense at all but that's the way I am.

    I stated previously the benefits of channel or var flagging with "IF" conditions far out weigh the minuscule gains of the #DEFINE/#IF_NOT_DEFINE method and that will be the method I will implement in all my code. I just had to take the #DEFINE/#IF_NOT_DEFINE method to a level of completeness before moving on.

    Hopefully in time I will grasp some of the other things you are doing and implement something similar as well.
Sign In or Register to comment.