Home AMX User Forum NetLinx Studio

Variable passing in modules and module triggering

Here's a little quandry for everyone. What's the best way to extract variables from a module to be used in your main source?

for example: Say we have a variable iARM_MODE_STAT embedded in a module which indicates the arm status of a security system. Now I want to do something like

IF((iARM_MODE_STAT=32) || (iARM_MODE_STAT=64))
{
//do something
}

but the above is in my main source. How would I approach this? Right now what I'd do is rewrite the module to include some kind of feedback on a virtual device so that in the module...

iARM_MODE_STAT=32
ON[vdvMDL_FEEDBACK,32]

Then a seperate variable in main source would be based of the feedback on [vdvMDL_FEEDBACK,32]

Isn't there a better way?

Comments

  • Variable Passing in Modules

    There are several ways to do this with each way having positives and negatives.

    1. For a simple variable or array, declare it in the main module and pass it as an argument to the module(s) that need to use it or change it. Modifying it in the module makes the modified value available to the main module as well as any modules that share it.

    2. Virtual device channels - as you noted.

    3. Virtual device levels - since levels are arbitrary byte, integer, or other data type values, this is a handy way to pass data since it does not require polling to see if a value changes - use a LEVEL_EVENT.

    4. Use VARIABLE_TO_STRING or XML_TO_STRING to encode structures or other data types that can not be passed directly to a module and then SEND_STRING the values when they change to a virtual device declared in the main program or another module. In the STRING handler of the DATA_EVENT for the virtual device, convert the string to the variable and use the values. This is tedious and I have seen some cases where the encodings do not work on all embedded data types in a structure but for the most part this is how you marshal data back and forth between modules when structures and large blocks of data are involved.

    There are probably other ways as well but these are few techniques I commonly use. Hope it helps.
  • Re: Variable passing in modules and module triggering
    Originally posted by Irvine_Kyle
    So basically I was somewhat on the right track. If I want multiple thigns to interact via my security module I would do something to the effect of


    IF((iARM_MODE_STAT=32) || (iARM_MODE_STAT=64))
    {
    //do something
    SEND_LEVEL(vdvMDL_FEEDBACK,1)
    }
    ELSE
    {
    //do something else
    SEND_LEVEL(vdvMDL_FEEDBACK,2)
    }

    then in my main source

    DEFINE_VARIALBE
    DEVLEV lvMDL_FEEDBACK[ ]=
    {vdvMDL_FEEDBACK,1},{vdvMDL_FEEDBACK,2}


    LEVEL_EVENT[lvMDL_FEEDBACK]
    {
    SWITCH(LEVEL.VALUE)
    {
    CASE 1:
    {
    //DO SOMETHING
    }
    CASE 2:
    {
    //DO SOMETHING ELSE
    }
    }
    }

    Am I on the right track?
  • Also if you wouldn't mind could you further explain the VARIABLE_TO_XML SEND_STRING option as well?
  • Variable Passing in Modules

    You could use the LEVEL approach in your example to 'pass' an integer state value between your main program and your module. In this case, there is a simpler solution that could be used if you were sampling the variable in question in Mainline. If you declared something like:

    VOLATILE INTEGER nArmState

    in the Main Module and then performed a SWITCH statement on the various values of it you are interested in, then simply pass nArmState as an INTEGER argument to any module that needs visibility to the value or that needs to change it. Then, as the value is changed, its value will be reflected in the other locations where it is used. This depends on having code in Mainline that samples the value (a polling approach) and is therefore somewhat inefficient. This is where using an EVENT such as a LEVEL event or a STRING event in the marshalling example excels - it provides notification of a value change and therefore is as efficient as possible. Another option is to pass the variable as an argument and then use an event such as a CHANNEL_EVENT on a virtual device to signal to other modules that the value has changed and to therefore examine it. This eliminates the need to poll the variable for changes in value and is more efficient.

    To elaborate on the STRING/XML approach, if you have a lot of data to pass between modules and particularly if the data is in the form of a structure (which can not be an argument to a module), routines exist for converting the structure to either STRING or XML format. You would use VARIABLE_TO_STRING() or VARIABLE_TO_XML() to create a CHAR data type representation of the structure. You can then send the resulting CHAR array (STRING) to any module you wish by doing a SEND_STRING of the converted value. In the receiving module, in your DATA_EVENT handler, you define a STRING handler and when you receive the string, you convert it back to the variable/structure using the inverse function (either STRING or XML depending on how it was converted in the sender). Of course, your variable or structure definition must be identical between the sender and receiver or it will not work. This technique is commonly used for passing large amounts of data typically data contained in structures with mixed data types.
  • frthomasfrthomas Posts: 176
    Re: Variable Passing in Modules
    Originally posted by Reese Jacobs
    You would use VARIABLE_TO_STRING() or VARIABLE_TO_XML()

    In practice XML is way more verbose than binary (what VARIABLE_TO_STRING does), and there is little or no benefit I can think of to do such exchanges using XML *within* netlinx modules. The only one I can think of is the data is human readable which can help in debugging maybe...

    Now if you use that method to store data on the DOC of Netlinx (for persistent storage) then using XML would allow you to upload an updated version (easy to do again since it is human readable).

    And if you want to exchange data with a non Netlinx host, then using XML has the important benefit of being decodable/encodable by the receiver, whereas binary may not be (very OS/CPU related).

    So I'd use VARIABLE_TO_STRING between modules, maybe VARIABLE_TO_XML for disk saves and certainly for communication with non netlinx servers.

    Fred
  • frthomasfrthomas Posts: 176
    Re: Variable Passing in Modules
    Originally posted by Reese Jacobs
    2. Virtual device channels - as you noted.

    3. Virtual device levels - since levels are arbitrary byte, integer, or other data type values, this is a handy way to pass data since it does not require polling to see if a value changes - use a LEVEL_EVENT.

    I tend to use the rule of thumb that if more than, say, 10 values are of external interest for the particular variable, I use a level, otherwise a set of channels.

    The problems with levels is that you can't read their value. That is, either you recreate the variable inside main code and use the level_event to update its value, or you are limited on acting upon changes.

    A channel, however, can be tested at any time using [Dev, channel] *AND* generates an event. More efficient IMHO.

    So in your case Irvine, since you seem to be only interested in 2 values of iARM_MODE_STAT (32 and 64), I'd create two channels to indicate each value. In fact, your code seems to indicate you are really worried about the case where iARM_MODE_STAT is 32 OR 64 so you could create a single channel iARM_MODE_STAT_IS_32_OR_64 and act on that in main code.

    Fred
  • DHawthorneDHawthorne Posts: 4,584
    You can "read" a level by putting a CREATE_LEVEL statement in your START secton and assigning a variable to the virtual level. This somewhat de-modularizes some aspects; but this entire topic is one of the big plusses of the Duet architecture - callable functions internal to the Java modules that cold return your data.

    Another thing I do sometimes is I put a string handler in a module that parses a simple command set and sends back a string with a response. But then you have to parse that too in your main program, so I limit it to really simple data.
  • Variable Passing in Modules

    Dave,

    Thanks for your point on obtaining the value of a level. I frequently hear Netlinx programmers talk about the limitation of not being able to read the level - the lack of a GET_LEVEL or similar command. However, with LEVEL_EVENTS providing notification when a value changes, you could get the level from the event if that is the only time you really care about the value. However, as you point out, the CREATE_LEVEL method allows you to always have access to the latest value for a level. Since the Master updates the level value in the specified variable for you automatically, this is certainly more efficient than using a GET_LEVEL command even if one existed. The problem with reading a level value is that you would have to know when to do it (when it changes) and this is exactly what the level event was created to accomplish.

    I also agree with you regarding callable functions in Java and the ability to obtain data from other modules. This will be a nice extension to have for the language.
  • frthomasfrthomas Posts: 176
    Re: Variable Passing in Modules
    Originally posted by Reese Jacobs
    I frequently hear Netlinx programmers talk about the limitation of not being able to read the level - the lack of a GET_LEVEL or similar command. [...] Since the Master updates the level value in the specified variable for you automatically, this is certainly more efficient than using a GET_LEVEL command even if one existed. The problem with reading a level value is that you would have to know when to do it (when it changes) and this is exactly what the level event was created to accomplish.

    I agree CREATE_LEVEL avoids to add to the level event the line
    my_level_variable = Level.value;

    But I think you are missing the point. Let's imagine you have a level that indicates the amount of outside light (in lux). Now there are things you'd want to do when that goes below some level, for example, turning on the lights, lowering blinds, etc. For that LEVEL events are perfect.
    Now for example in the home theater, blinds are lowered whenever the projector is on, but you'd like to have an intelligent program that only raises the blinds when switching off the projector if there's light outside. Now the EVENT is the projector/theater switch off, and in there I'd like to do something like:
    if (there's light outside) raise blinds;

    "there's light outside" is level>somevalue. But in this BUTTON_EVENT I can't read the level. So I need a variable to keep track of its changes and cache the last LEVEL_EVENT.

    And this is inefficient because whatever device updates the level has the variable (or at least access to the value somehow). NetLinx has it as well because otherwise it would'nt know if the event must be sent or not (if you send twice the same level value, only one event gets sent). And the "user" program must have it again just to read it. So that is 3 copies of the same variable.
    If levels where "readable", we could do with a single one (the level).

    That's what NetLinx programmers mean when complaining about the level limitation, and I think it is a very valid one.

    Fred
  • Variable Passing/Levels etc.

    Fred,

    Let's just agree to disagree on this one. I understand your point regarding the second use of a level value. I see it as more of a variable scope issue than a level issue.

    In your example regarding the limitations of not being able to read a level, you could:

    1. Declare a LEVEL_EVENT in multiple modules and cache the value locally - this is not the most efficient method of course

    2. Use CREATE_LEVEL in multiple modules to have Netlinx track the value for you - this is more efficient than (1) but as you noted, it is less than the perfect case of having the level defined and updated in only place

    3. My solution to your problem would be to define the variable in the Main Program, pass that variable as an argument to any routine that requires it, and then define your LEVEL_EVENT in whatever module you prefer. This way, there is only one LEVEL_EVENT handler, one CREATE_LEVEL if you chose that route, and multiple modules are free to share the value of the level. This approach addresses your issue of efficiency - one copy of the variable, one event handler or one variable for Netlinx to update, and the value can be freely shared between modules

    Also, as Dave pointed out in an earlier post, the ability to have callable modules will also make this easier to accomplish since a callable module that tracks and maintains the LEVEL value could be called from any module. In this case, you would be creating your own READ_LEVEL or GET_LEVEL function using a callable module.

    I do agree with you that at times it has seemed very counterintuitive to not have the ability to read the value of a level but I guess I have spent so much time working around it that I don't think about it much anymore. If we had it as an option, it certainly would make certain coding situations easier to handle but I do think we have adequate constructs to work around the lack of it. Perhaps AMX will add it to Netlinx so we can't debate the topic any longer. :-)
  • Lots of great info here guys, and thanks for the replies / ideas! Just thought that I might mention to the un-informed that upon consulting AMX about how they trigger Main code from a module they suggest the following.

    create a virtual device such as vdvMDL_FB or something
    then pass this to the module. Within the module you can use a SEND_COMMAND vdvMDL_FB, "'command-do_something_different"

    then in main source you create a DATA_EVENT[vdvMDL_FB] and use the COMMAND: handler to parse out the data and act appropriately. So far this has worked great for my instance. Makes things easily readable and unlimited in scope.
  • DHawthorneDHawthorne Posts: 4,584
    Originally posted by Irvine_Kyle
    Lots of great info here guys, and thanks for the replies / ideas! Just thought that I might mention to the un-informed that upon consulting AMX about how they trigger Main code from a module they suggest the following.

    create a virtual device such as vdvMDL_FB or something
    then pass this to the module. Within the module you can use a SEND_COMMAND vdvMDL_FB, "'command-do_something_different"

    then in main source you create a DATA_EVENT[vdvMDL_FB] and use the COMMAND: handler to parse out the data and act appropriately. So far this has worked great for my instance. Makes things easily readable and unlimited in scope.
    One thing about this I have discovered the hard way. I have a project with multiple masters...7 to be exact, and at least two more planned. You can set and read channels and levels on a virtual device across masters, but SEND_COMMAND and SEND_STING won't make it. For example, I create a virtual on master 1, and have code on master 1 to parse a command string. If I define the same virtual on master 2, I can turn channels on and off from master 2 and master 1 recognizes the changes. But if I send a command or a string from master 2, the COMMAND or SRING event on master 1 never sees it.

    So you have to be careful about this with multiple master systems (heh, unless it changed on a recent firmware update and I haven't realized it). My work-around has been to use channels on virtuals as a flag, set the channel with master 2, and when master 1 sees the channel come on, it acts and then turns it off.
  • alexanboalexanbo Posts: 282
    That seems a bit odd I use multiple masters quite a bit and send commands and strings back and forth all the time.

    I define the virtual device on the other master on the master where i'm sending the command from.

    For example in the code for system 1 I would do something like:

    vdvREMOTE_DEVICE = 34001:1:2

    And then Send_Command vdvREMOTE_DEVICE,"'POWER=1'"

    Then on system 2 I would have:

    vdvDEVICE = 34001:1:0

    and then:

    DATA_EVENT[vdvDEVICE]
    {
    COMMAND:
    {
    }
    }

    Would pick off the command.

    Maybe I'm just misunterstanding what you want to do.
  • DHawthorneDHawthorne Posts: 4,584
    Nope, that's exactly the kind of thing I was trying to accomplish and I could not make it work. I even Telnetted into the masters individually...sending the command on the master that had the EVENT defined worked fine, but sending it to the same device, but connected to another master, it did not. Maybe I was having a brain-dead moment and was using SEND_COMMAND, but parsing a STRING event...hmm, I'll have to take another look at it.
  • alexanboalexanbo Posts: 282
    Hmm weird I think I have been doing that sort of thing since firmware .117 if that sheds any light on it.
  • Master to Master Send String/Command

    Dave,

    When I first saw your post about problems with SEND_STRING and SEND_COMMAND in master to master mode, I was concerned that if it was broken, it needed to get fixed! Like Alex, I have done a lot of LEVELs and CHANNELs in master to master without problems. I wrote a quick test program this morning to send a command to one master under a button PUSH and for the other master to respond to the command with a string response using some of the data in the command. Thankfully, it worked as expected on various firmware levels certainly back to 117.

    Is it possible you did not make a URL entry either programmatically or using Studio? Without the URL entry, the masters certainly won't be able to communicate and it is easy to overlook this detail sometimes.
  • DHawthorneDHawthorne Posts: 4,584
    No, these were otherwise communicating masters, adn I wound up using channel flags to get the job done. I almost certainly was having a brain-dead moment when I first had the issue, and subsequently based any coding after that on wrong deductions. Probably I was parsing for commands and sending strings, then got myself confused while troubleshooting it. I have a very bad habit of working through breaks when trying to resolve a problem and I get kinda fuzzy when my blood sugars get too low...heh, and I don't always realize it is happening. Looks like I just plain got it wrong this time.
Sign In or Register to comment.