Module misbehavior?
amxhobbyist
Posts: 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.
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.
0
Comments
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.
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.
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:
Module:
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.
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
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: Module: Hope this helps.
Joe
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
In my last post,
dvSomeDev should have been something like:
dvSomeDev = 96:1:1.
Sorry about that.
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
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.
http://www.amxcorp.com/techsupport/PDNTechNote.asp?id=399
(Using Online Data Events to Configure Devices in Netlinx)
If you run one program with in a device's STRING: handler and another with (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
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
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.
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.
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.
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:
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!