Home NetLinx Studio

Module misbehavior?

amxhobbyistamxhobbyist Junior MemberPosts: 90
There appears to be a miscommunication when capturing strings using data_events in modules. This does not work when the device is passed into the module.

Example 1: Device defined in main program. Module is called, and device is passed as an argument. Module picks up argument as: MODULE_NAME='name' (dev devicename). In this example I can perform most operations on the device from within the module. However, capturing a string within a data_event in the module does not work at all.

Example 2: Define the device in both the module and the main program. Same problem.

Example 3: Remove the device's definition from the main program and define it directly in the module. Now everything works just fine.

Is this a problem or a bug? Since I am able to capture channel_events and perform other operations on a device when passed as an argument to a module, then there should be no reason why I can't capture strings. Having to define the device in the module is a bit of a hack, in my opinion. It makes the concept of a reusable and/or "universal" module worthless in this situation.

Comments

  • Reese JacobsReese Jacobs Junior Member Posts: 347
    Module Misbehavior

    You may want to look more closely at your module code since I use modules extensively for both RS232/485 and TCP based devices, all of my modules have COMMAND and STRING handlers for these devices, and I never declare a DEVICE in the module itself - the devices are always passed in to the module from the main program (and the modules work fine). Further, most all of the AMX modules that control devices which return strings have embedded STRING handlers in them and they all require the device definition as part of the MODULE invocation since they have no way of directly defining the device in the module itself (the device is not known ahead of time and they do not provide source code for most of the modules).

    Have you done a CREATE_BUFFER in the module for the device? Are there any other differences between the working and non-working versions other than the device declaration? Not sure what to tell you except that what you are trying to do is perfectly acceptable and in fact is the most common method for doing it. If it did not work, there would be a lot of existing modules from AMX and others that are broken.
  • Reese JacobsReese Jacobs Junior Member Posts: 347
    Module Misbehavior

    I neglected to ask you what kind of device you were trying to control and whether or not the device is local to the system defining the STRING handler or if the device is located on a remote system (different master).

    The reason I ask is that a dealer friend of mine just ran into major (and undocumented) problems using a PLB-AS16 Landmark switcher in this manner and he could never get the STRING handler to work reliably. It turns out it is a known issue even though it was not known to him or a lot of others. Sometimes the problem can be device or usage specific although as I said in my original response, what you are trying to do should work in concept and does work in reality for most devices and applications.
  • amxhobbyistamxhobbyist Junior Member Posts: 90
    Thanks for the reply, Reese. Here's more information:

    I'm using modules to help break my code up into managable chunks. This module is intended to handle my phone system, and I started it with a simple capture of caller ID data from an AXB-DTMF on the axlink bus of my NI-3000. Note that I have a rather small installation here, only the one system. So here's the relevant code:

    Main program:
    DEFINE_DEVICE
    dev_dtmf  = 96:1:1
    DEFINE_MODULE 'PhoneSupport' modPhoneSupport(dev_dtmf);
    

    Module:
    MODULE_NAME='PhoneSupport' (dev dev_dtmf)
    DEFINE_EVENT
    data_event [dev_dtmf] {
        string: {
             // do something
         }
    }
    channel_event [dev_dtmf,36] {	// incoming ring
        on: { 
    	// do something
        }
    }
    

    End result: The channel_event works just fine. However, the data_event doesn't work at all. If I undefine dev_dtmf in the main program, and specifically define it in the module under define_device, then the code works perfect.
  • amxhobbyistamxhobbyist Junior Member Posts: 90
    Have you done a CREATE_BUFFER in the module for the device?
    I didn't know I had to; I thought that was "the old way of doing things" (in Axcess) and that a data_event in Netlinx handles it all for me now.
  • Reese JacobsReese Jacobs Junior Member Posts: 347
    Module Misbehavior

    There is certainly nothing wrong with the module declaration you are using. I generally do not specify a system number for my devices (system number of 0) unless the device is specifically defined on another system. In other words, in your example, I am assuming that the DTMF device is local to the Master on which the code is running and that you are using System number 1. I typically would define my device as 96:1:0 in this case but that should not make any difference.

    Do a couple of things as a test:

    1. Change your device definition to 96:1:0 in the Main program and pass it to the module

    2. Define a character buffer and use CREATE_BUFFER - do this in the module

    If you do not define a buffer and use CREATE_BUFFER, then you have to process all of the STRING data under the STRING handler in the DATA_EVENT using DATA.DATA (which is valid only during the event). You could move the string data to a buffer yourself. There are however many compelling reasons to use CREATE_BUFFER having to do with buffering incoming data (overflow and data buffer issues). There is another thread on the forums currently about COM port issues that discusses some of the virtues of using CREATE_BUFFER and letting Netlinx do the buffering for you.

    Try the suggestions above and see if they make any difference for your module. I can't say that I have done an Axcess device in a module but as I said, conceptually there should not be a problem. Let's see if we can tweak your module to make it work and then try to determine why it did not work as expected with the first effort.

    Reese
  • Joe HebertJoe Hebert Junior Member Posts: 2,159
    Hi amxhobbyist,

    The only thing I can see that might be a problem is where you?ve placed DEFINE_MODULE in your main program. All DEFINE_MODULE statements are supposed to be placed at the very end of DEFINE_START. I don?t see a DEFINE_START in your example code so I thought I would bring it up. I don? know what happens if you place DEFINE_MODULE some place else.

    I happen to have an AMX-DTMF+ at home so I took your code and modified it a tad by adding a couple of variables to capture the STRINGs coming in. It works perfectly fine and I?m declaring the DTMF device with system 1 like you did (although I normally use 0) so that shouldn?t be an issue.

    Like Reese, I?ve never declared the device in the module itself. And also as Reese mentioned you will either need CREATE_BUFFER or you will need to capture the strings with DATA.TEXT (not DATA.DATA)

    The example code posted below shows both techniques (with no regard for buffer overflow) and both work as expected. Give it a try and see if it gets you any further.

    Main Program:
    DEFINE_DEVICE
    dev_dtmf  = 96:1:1
    
    DEFINE_START
    DEFINE_MODULE 'PhoneSupport' modPhoneSupport(dev_dtmf);
    
    Module:
    MODULE_NAME='PhoneSupport' (dev dev_dtmf)
    
    DEFINE_VARIABLE
    
    CHAR cPhoneBuff[1024] 	//filled in with create buffer
    CHAR cMyPhoneBuff[1024]	//filled in with data.text from STRING handler
    
    DEFINE_START
    
    CREATE_BUFFER dev_dtmf, cPhoneBuff
    
    DEFINE_EVENT
    
    DATA_EVENT[dev_dtmf] {
    
       ONLINE: {
          SEND_COMMAND dev_dtmf, 'ON HOOK'
          SEND_COMMAND dev_dtmf, 'AUDIO-ON'
          SEND_COMMAND dev_dtmf, 'AUTO ON'
          SEND_COMMAND dev_dtmf, "'COUNT-2'"
       }
       STRING: {  //caller ID
          cMyPhoneBuff = "cMyPhoneBuff,DATA.TEXT"
       }
    }
    
    
    CHANNEL_EVENT [dev_dtmf,36] {	// incoming ring
       ON: { 
        
          cMyPhoneBuff = "cMyPhoneBuff,' One Ringy Dingy'"
        }
    }
    
    Hope this helps.

    Joe
  • frthomasfrthomas Junior Member Posts: 176
    IMHO the missing DEFINE_START may be the culprit. NetLinx needs to rebuild the event table AFTER the variable is defined and that is normally all sync'd up within DEFINE_START.
  • ipssheldonipssheldon Registered User Posts: 106
    According to AMX Tech Support, using a Create_Buffer is preferred to processing strings directly in a DATA_EVENT. What they have told me is to include a Create_Buffer statement in DEFINE_START, but process the buffer in the Data_Event. Something like this:

    DEFINE_DEVICE

    dvSomeDev

    DEFINE_VARIABLE

    CHAR cBUFFER[500]

    DEFINE_START

    CREATE_BUFFER dvSomeDev,cBUFFER

    DEFINE_EVENT

    DATA_EVENT[dvSomeDev]
    {
    ONLINE:
    {
    // do something
    }
    STRING:
    {
    Process data in cBuffer.
    }
    }

    But don't use DATA.TEXT in the String event. I believe there are also tech notes about this on AMX.COM.

    Hope this helps.

    Sheldon Samuels
    IPS Resources LLC
  • ipssheldonipssheldon Registered User Posts: 106
    Oops.

    In my last post,

    dvSomeDev should have been something like:

    dvSomeDev = 96:1:1.

    Sorry about that.
  • Joe HebertJoe Hebert Junior Member Posts: 2,159
    Originally posted by ipssheldon
    ...But don't use DATA.TEXT in the String event. I believe there are also tech notes about this on AMX.COM.
    I wouldn?t recommend not using DATA.TEXT. I use DATA.TEXT all the time for strings generated by AMX devices or module virtual devices. If the data coming in is assured to be a complete command then I?ll use DATA.TEXT directly.

    If I?m grabbing web pages or other data via HTTP then I?ll go the CREATE_BUFFER route and use the OFFLINE event of my IP device to trigger processing.

    Sometimes I?ll use a concatenation routine (cBuffer=?cBuffer,DATA.TEXT") and manually buffer the data for serial devices. One point worth mentioning is that there is an undocumented 16000 byte limitation when concatenating arrays so there are cases when CREATE_BUFFER (which can handle the full 64K) is the only way to go. I found out that one the hard way.

    I think CREATE_BUFFER and DATA.TEXT are both useful. It all depends on the job as to which one to pick. CREATE_BUFFER certainly has its advantages but DATA.TEXT is a valuable player also.

    I couldn?t find a tech note advising not to use DATA.TEXT in the STRING handler. I only found this one http://www.amx.com/techsupport/PDNTechNote.asp?id=616 explaining how to do it.

    Joe
  • DHawthorneDHawthorne Junior Member Posts: 4,584
    The difficulty in using DATA.TEXT is not knowing the exact criteria by which such an event is generated. It is obviously not generated on a per-character basis when there is a stream of data, or each character would generate an individual event. It must be generated when the port detects an incoming stream, and then a pause. The timing is not documented anywhere that I know of, nor the criteria by which it determines what constitues a string, and what is simply a part of a string. Therefore, you cannot guarentee with every device that you (1) have your entire input before the STRING event fires, and (2) the STRING event properly segments the incoming data the same way your device intends it to be interpreted (in other words, 2 distinct packets might come in, but the STRING event treats them as one). You can absolutely rely on the STRING and COMMAND events for master-to-master messaging, but you cannot be certain for every device that it will react properly to your data stream.

    Using CREATE_BUFFER overcomes those shortcomings. You can think of it as a BufferedStream object, and you can parse the buffer yourself on either a byte-by-byte basis or tokenize it. My rule-of-thumb is use CREATE_BUFFER for hardware devices, and STRING events for iternal communications.
  • Chip MoodyChip Moody Junior Member Posts: 727
    I thought there was a tech note on this as well, but couldn't find it. They do gloss over it quickly in this one:

    http://www.amxcorp.com/techsupport/PDNTechNote.asp?id=399
    (Using Online Data Events to Configure Devices in Netlinx)

    If you run one program with
    MyBuffer = "MyBuffer,DATA.TEXT"
    
    in a device's STRING: handler and another with
    CREATE_BUFFER This232Port,MyBuffer
    
    (and then only using the DATA_EVENT to trigger a parsing routine) you will technically wind up with MyBuffer having the exact same contents. My favorite "guru" down in Texas (I.E., props to Guy Minervini) explained to me a that the CREATE_BUFFER method is preferred, as it "runs more efficiently under the hood" than the DATA.TEXT concantination method. (That was a long time ago, so yeah - that's a paraphrase)

    I'm kinda anal-retentive, so my mindset is "there is one method that works in all instances, so why not use it in every instance?" I.E., there's no perceiveable benefit I'm aware of using other methods regardless of the application, so why bother?

    - Chip
  • Joe HebertJoe Hebert Junior Member Posts: 2,159
    Originally posted by Chip Moody
    I'm kinda anal-retentive, so my mindset is "there is one method that works in all instances, so why not use it in every instance?" I.E., there's no perceiveable benefit I'm aware of using other methods regardless of the application, so why bother?
    I think anal-retentive is a prerequisite for programmers. :)

    I agree that CREATE_BUFFER is better than concatenating DATA.TEXT. However, I use DATA.TEXT all the time for AXLINK devices (like the AXB-DTMF that started this thread) and for strings that come back from virtual devices. If the string is guaranteed to be complete (which it is with AXLINK devices) then why CREATE_BUFFER when there is nothing to buffer?

    Joe
  • Chip MoodyChip Moody Junior Member Posts: 727
    Originally posted by Joe Hebert
    I think anal-retentive is a prerequisite for programmers. :)

    I agree that CREATE_BUFFER is better than concatenating DATA.TEXT. However, I use DATA.TEXT all the time for AXLINK devices (like the AXB-DTMF that started this thread) and for strings that come back from virtual devices. If the string is guaranteed to be complete (which it is with AXLINK devices) then why CREATE_BUFFER when there is nothing to buffer?

    Joe


    See "anal retentiveness".

    :)

    - Chip
  • Reese JacobsReese Jacobs Junior Member Posts: 347
    Module Misbehavior

    Joe,

    Like you, I used to use DATA.TEXT for virtual devices since the strings always seemed to be complete and further you could reasonably rely on only getting a single string when the event fired. In fact, there are still some programming examples in Programmer II that illustrate and depend on this behavior.

    I don't do a lot of Axlink devices with my Netlinx systems but I do use a lot of virtual devices particularly for intermodule communication. I found that for relatively short strings, I could count on them being intact in DATA.TEXT. However, for more sophisticated modules that used VARIABLE_TO_STRING or VARIABLE_TO_XML to create strings that were then passed to another module I found that for long strings DATA.TEXT was no longer intact. This is probably intuitive but it forced me to use CREATE_BUFFER for even virtual devices and like Chip, once I got into the habit I quit using DATA.TEXT. There seems to be some length restrictions on DATA.TEXT that if exceeded even on virtual device SEND_STRINGs (or SEND_COMMANDs) will force you to use concatenation to make it work. In this case, CREATE_BUFFER becomes the better alternative.

    Dave pointed out some of the problems with DATA.TEXT earlier - notably the lack of a documented set of rules regarding how it treats data. Does anyone have any specific details on DATA.TEXT handling and limitations per some of Dave's points? Although CREATE_BUFFER is a great solution and apparently more efficient, SEND_COMMANDs still require DATA.TEXT handling. In this regard, knowing more about how DATA.TEXT works internally would be useful.
  • Data.Text

    If you ask Tech Support, I believe you will find that Data.Text is limited to about 2000 characters. It has something to do with the maximum character payload of an Ethernet packet.

    Any virtual device communication of 2000 characters or less should arrive in one chunk.
  • Reese JacobsReese Jacobs Junior Member Posts: 347
    Module Misbehavior

    I have actually seen packet fragmentation on strings sent to a local virtual device using SEND_STRING on packet sizes less than 256 bytes in length (but longer than 64). In fact, a module I wrote that used VARIABLE_TO_STRING() to convert a structure into a string about 200 bytes long is where I first encountered the problem. I have asked Tech Support the question before but they did not know off-hand and submitted and RFI to engineering for more information. I never heard back regarding a response to the RFI.
  • amxhobbyistamxhobbyist Junior Member Posts: 90
    Thank you all for your replies. It took me awhile to find time to sit down and troubleshoot this again, so I apologize for the delay in responding.

    I tried everything that was recommended, and various incantations of it. In the end, I don't even know what fixed it. I:

    - Moved define_module under define_start in the main program. No change.

    - Tried creating the buffer & changing the device to system 0, no change.

    - Got fed up and removed EVERYTHING from the module. All variables, everything but (1) added an empty DEFINE_START to the module, and (2) left a basic data_event:
    data_event [dev_dtmf] {
        string: {
        send_command dev_tp_computer,'beep'
        }
    }
    

    And hey, this worked! The panel beeped when a call came in! So I slowly started adding everything else back into the module one little piece at a time. Got it all in and everything worked great. I figured, "Adding the empty define_start to the module did it." So I removed it as a test, recompiled and downloaded. It still worked fine!!! Which doesn't make any sense. (I put it back in anyway)

    In the end I have no idea how it was fixed but it was. My guess is a combination of unknowingly imperfect programming practices (no define_start in the module, putting define_module before define_start in main, etc) contributed to this.

    I can't help but wonder if running the module with the empty define_start once initialized everything somehow, and that information was held on subsequent downloads. This would explain why adding fixed it, but removing didn't break it. It's just a wild guess, though - I don't know enough about the internals of Netlinx to really take an honest stab at this.

    Anyway, I greatly appreciate all of your assistance. Now that I have more detailed information and experience with proper code techniques in regards to modules, I shouldn't have a problem like this again.

    Thank you!
Sign In or Register to comment.