Home AMX User Forum NetLinx Studio

Dynamic device creation

Hi all

I'm looking for a way to dynamically create a (virtual) device by code without having to enter anything into the DEFIINE_DEVIICE section. I couldn't find any solution so far. Is this impossible or am I searching at the wrong places?

Thanks for any suggestions,
Patrick

Comments

  • viningvining Posts: 4,368
    I know you can dynamically change a dev associated with an event table but I don't know about actual creation of a dev, how would know via code that its DPS hasn't already been used.

    This may have some useful info: http://www.amxforums.com/showthread.php?8297-Dynamically-change-D-P-S&highlight=Dynamically+change+device
  • ericmedleyericmedley Posts: 4,177
    wengerp wrote: »
    Hi all

    I'm looking for a way to dynamically create a (virtual) device by code without having to enter anything into the DEFIINE_DEVIICE section. I couldn't find any solution so far. Is this impossible or am I searching at the wrong places?

    Thanks for any suggestions,
    Patrick

    It might be more helpful if you describe what you wish to do. Quite often there's another way to approach the solution. While there are ways to create devices on the fly they are not emigrant and don't work as one might think. But, in most cases there really. Is no need to do this anyway and you can accomplish the same thing with more conventional methods.
  • wengerpwengerp Posts: 29
    ericmedley wrote: »
    It might be more helpful if you describe what you wish to do. Quite often there's another way to approach the solution. While there are ways to create devices on the fly they are not emigrant and don't work as one might think. But, in most cases there really. Is no need to do this anyway and you can accomplish the same thing with more conventional methods.

    I have my own framework which allows me to quickly setup a new system mainly by configuration. Since there are a lot of device id necessary and they all follow my own naming convention, it would be handy to generate them at first place. However, since this doesn't seem to be (reliably) possible, I know found another solution: I wrote a SUB that generates the necessary program code and writes it to a file on the controller (see sample below). I can then simply include this file in the master file.

    Thanks,
    Patrick

    Sample of generated code for a minimal system:
    DEFINE_DEVICE
    dv_comm_THEATER1=0:0:0
    vdv_comm_THEATER1=33011:1:0
    dv_ui1_THEATER1_pg1=10001:11:0
    dv_ui2_THEATER1_pg1=10002:11:0
    vdv_ui1_THEATER1_pg1=35111:1:0
    vdv_ui2_THEATER1_pg1=35112:1:0
    dv_ui1_THEATER1_pg2=10001:12:0
    dv_ui2_THEATER1_pg2=10002:12:0
    vdv_ui1_THEATER1_pg2=35121:1:0
    vdv_ui2_THEATER1_pg2=35122:1:0
    dv_ui1_THEATER1_pg3=10001:13:0
    dv_ui2_THEATER1_pg3=10002:13:0
    vdv_ui1_THEATER1_pg3=35131:1:0
    vdv_ui2_THEATER1_pg3=35132:1:0
    dv_ui1_THEATER1_pg4=10001:14:0
    dv_ui2_THEATER1_pg4=10002:14:0
    vdv_ui1_THEATER1_pg4=35141:1:0
    vdv_ui2_THEATER1_pg4=35142:1:0
    dv_comm_PLAYER1=5001:2:0
    vdv_comm_PLAYER1=33031:1:0
    dv_ui1_PLAYER1_pg1=10001:31:0
    dv_ui2_PLAYER1_pg1=10002:31:0
    vdv_ui1_PLAYER1_pg1=35311:1:0
    vdv_ui2_PLAYER1_pg1=35312:1:0
    DEFINE_START
    DEFINE_COMBINE ( vdv_ui1_THEATER1_pg1, dv_ui1_THEATER1_pg1 )
    DEFINE_COMBINE ( vdv_ui2_THEATER1_pg1, dv_ui2_THEATER1_pg1 )
    DEFINE_COMBINE ( vdv_ui1_THEATER1_pg2, dv_ui1_THEATER1_pg2 )
    DEFINE_COMBINE ( vdv_ui2_THEATER1_pg2, dv_ui2_THEATER1_pg2 )
    DEFINE_COMBINE ( vdv_ui1_THEATER1_pg3, dv_ui1_THEATER1_pg3 )
    DEFINE_COMBINE ( vdv_ui2_THEATER1_pg3, dv_ui2_THEATER1_pg3 )
    DEFINE_COMBINE ( vdv_ui1_THEATER1_pg4, dv_ui1_THEATER1_pg4 )
    DEFINE_COMBINE ( vdv_ui2_THEATER1_pg4, dv_ui2_THEATER1_pg4 )
    DEFINE_COMBINE ( vdv_ui1_PLAYER1_pg1, dv_ui1_PLAYER1_pg1 )
    DEFINE_COMBINE ( vdv_ui2_PLAYER1_pg1, dv_ui2_PLAYER1_pg1 )
    DEFINE_VARIABLE
    DEV vdv_ui_THEATER1_pg1[] = { vdv_ui1_THEATER1_pg1, vdv_ui2_THEATER1_pg1 }
    DEV vdv_ui_THEATER1_pg2[] = { vdv_ui1_THEATER1_pg2, vdv_ui2_THEATER1_pg2 }
    DEV vdv_ui_THEATER1_pg3[] = { vdv_ui1_THEATER1_pg3, vdv_ui2_THEATER1_pg3 }
    DEV vdv_ui_THEATER1_pg4[] = { vdv_ui1_THEATER1_pg4, vdv_ui2_THEATER1_pg4 }
    DEV vdv_ui_PLAYER1_pg1[] = { vdv_ui1_PLAYER1_pg1, vdv_ui2_PLAYER1_pg1 }
    DEV dv_ui_THEATER1_pg1[] = { dv_ui1_THEATER1_pg1, dv_ui2_THEATER1_pg1 }
    DEV dv_ui_THEATER1_pg2[] = { dv_ui1_THEATER1_pg2, dv_ui2_THEATER1_pg2 }
    DEV dv_ui_THEATER1_pg3[] = { dv_ui1_THEATER1_pg3, dv_ui2_THEATER1_pg3 }
    DEV dv_ui_THEATER1_pg4[] = { dv_ui1_THEATER1_pg4, dv_ui2_THEATER1_pg4 }
    DEV dv_ui_PLAYER1_pg1[] = { dv_ui1_PLAYER1_pg1, dv_ui2_PLAYER1_pg1 }
    
  • PhreaKPhreaK Posts: 966
    I hate to burst your bubble but 'include' is a compiler directive. As such, your target include file will need to be available at compile time and cannot be dynamically referenced.
  • wengerpwengerp Posts: 29
    PhreaK wrote: »
    I hate to burst your bubble but 'include' is a compiler directive. As such, your target include file will need to be available at compile time and cannot be dynamically referenced.

    Hi PhreaK

    I'm totally aware of the fact that "include" is a compiler directive. As I said: since the dynamic creation of device definitions is NOT possible I had to find a workaround: first my program generates the necessary program code (at this time the icnlude file is emtpy) and writes it to a file on the controller. Second I drag&drop the code from the generated file to the include file (this is a manual action. I'm not aware of any way to let the program write its output directly to a file, which could be inlcuded by the compiler - after all the compiler runs on my local host while the program runs on the netlinx controller but there might be some creative way using shares or something). Finally I have to recompile the code (now with the correct instructions in the include file) and voilà.

    This has nothing to do with "dynamic", I know ;-) But as I explained I was aiming for comfort and ease of use - and this is achived.

    Patrick
  • PhreaKPhreaK Posts: 966
    Ah cool, if you are just wanting a pre-compiler to take some form of meta syntax and generate NetLinx code you may find it a little simpler (or waaaaaay simpler, depending on what you are doing) to roll this in python / perl / ruby / C / java / node / brainf*ck / cobal / whatever the hell your choice of language is. You can then add your pre-compiler into the tools menu of NetLinx Studio (Settings -> Customize -> Tools) and even assign a keyboard shortcut if you like. The entry you define in there can be passed arguments such as the current file, directory etc.

    This basically enables you to have a tool that runs locally, taking your NetLinx code that is sprinkled with whatever additionally syntactical sugar you desire, compile this down to valid NetLinx then pass the output of this to the nlrc.exe (the NetLinx command line compiler) for final compilation. Once it's set up its a single key press, you can even override F7 so all compiles run through this tool without you having to think about it.
  • AuserAuser Posts: 506
    wengerp wrote: »
    Hi all

    I'm looking for a way to dynamically create a (virtual) device by code without having to enter anything into the DEFIINE_DEVIICE section. I couldn't find any solution so far. Is this impossible or am I searching at the wrong places?

    I quite often use a strategy whereby I specify a pool of virtual devices and modules in code (at compile time) and then read in a configuration file which tells the control system what devices it should know about (what device type, so it knows which module to attach to the device and the physical port that it is connected to).

    The drawback with this approach is that you have to pre-allocate a heap of memory for all of the virtual devices available in the pool, which may or may not be used. Unfortunately I have to live with the lack of ability to dynamically load NetLinx modules as there are a number of developers in our organisation and Duet isn't mature enough IMHO to get all of them licenses and train them up so that they can all support each other's work.

    It occurs to me that it should be possible to create the virtual devs at run time instead of pre-defining them, but memory has never been an issue for me so I haven't bothered trying. Consider the following:
    PROGRAM_NAME='Main'
    
    DEFINE_DEVICE
    
    DEFINE_CONSTANT
    char MODULES_MAX = 32
    
    DEFINE_VARIABLE
    volatile dev ModuleDevicePool_Virtual[MODULES_MAX]
    volatile dev ModuleDevicePool_Physical[MODULES_MAX]
    volatile char ModulesCount
    
    DEFINE_MODULE
    'RandomModule' mdl1(ModuleDevicePool_Virtual[1], ModuleDevicePool_Physical[1])
    'RandomModule' mdl2(ModuleDevicePool_Virtual[2], ModuleDevicePool_Physical[2])
    //[...]
    
    DEFINE_START
    // Read in config file...
    //foreach(ModuleDefinedInConfigFile)
    //{
    //  ModulesCount++
    //  ModuleDevicePool_Virtual[ModulesCount] = DYNAMIC_VIRTUAL_DEVICE
    //  ModuleDevicePool_Physical[ModulesCount] = DeviceSpecifiedInConfigFile
    //}
    set_length_array(ModuleDevicePool_Virtual, ModulesCount)
    set_length_array(ModuleDevicePool_Physical, ModulesCount)
    rebuild_event()
    
    MODULE_NAME='RandomModule' (dev VirtualDevice, dev PhysicalDevice)
    DEFINE_CONSTANT
    char MODULE_DEVICE_VIRTUAL = 1
    char MODULE_DEVICE_PHYSICAL = 2
    
    DEFINE_VARIABLE
    volatile dev ModuleDevices[2]
    volatile char ModuleIsActive = FALSE
    
    define_function InitialiseModule()
    {
      ModuleIsActive = TRUE
      Rebuild_Event()
    }
    
    DEFINE_START
    ModuleDevices[MODULE_DEVICE_VIRTUAL] = VirtualDevice
    ModuleDevices[MODULE_DEVICE_PHYSICAL] = PhysicalDevice
    wait 300
    {
      if
      (
        (ModuleDevices[MODULE_DEVICE_VIRTUAL] <>  VirtualDevice) ||
        (ModuleDevices[MODULE_DEVICE_PHYSICAL] <> PhysicalDevice)
      )
      InitialiseModule()
    }
    

    It compiles, but I haven't tried to see if it works as expected. It is my guess that while memory is allocated for all of the D:P:S addresses of the devices in the pool variables which are declared uninitialised, storage for all of the buttons/channels, etc associated with the devices is not (happy for someone to write some test code to find out) as the devices are not created until they are allocated.

    Whilst my example is based around the use of modules, the key thing is that there is no requirement having to produce individual definitions for all of the virtual devices including names. I always find that manually declaring dozens if not hundreds of devices every time I produce a new system (such as below) is time consuming and prone to error. Better to let the control system do it itself at runtime...
    dv_ui1_THEATER1_pg1=10001:11:0
    dv_ui2_THEATER1_pg1=10002:11:0
    vdv_ui1_THEATER1_pg1=35111:1:0
    vdv_ui2_THEATER1_pg1=35112:1:0
    dv_ui1_THEATER1_pg2=10001:12:0
    dv_ui2_THEATER1_pg2=10002:12:0
    vdv_ui1_THEATER1_pg2=35121:1:0
    vdv_ui2_THEATER1_pg2=35122:1:0
    dv_ui1_THEATER1_pg3=10001:13:0
    dv_ui2_THEATER1_pg3=10002:13:0
    vdv_ui1_THEATER1_pg3=35131:1:0
    vdv_ui2_THEATER1_pg3=35132:1:0
    dv_ui1_THEATER1_pg4=10001:14:0
    dv_ui2_THEATER1_pg4=10002:14:0
    vdv_ui1_THEATER1_pg4=35141:1:0
    vdv_ui2_THEATER1_pg4=35142:1:0
    
  • JasonSJasonS Posts: 229
    It has been my experience that DYNAMIC_VIRTUAL_DEVICE only works inside a module, and it only works once per module. I use it with TRANSLATE_DEVICE in all my modules. If I have a module that uses multiple devices, or passes an array of devices into the module, I will use DYNAMIC_VIRTUAL_DEVICE and then use SET_VIRTUAL_PORT_COUNT on the dynamic device to match the number of devices required. Then a loop defines the dynamic devices in a DEV array, and then runs TRANSLATE_DEVICE if required. This is handy for modules for DSPs that can have multiple units chained together, or for a switcher with configurable switching planes.
  • AMXJeffAMXJeff Posts: 450
    Auser wrote: »
    Duet isn't mature enough IMHO

    I have been building configurable AMX systems for years. If you are not using Duet to solve these issues than you are sorely missing out.

    Duet was designed for this, and is far from being immature. I may not do my implementation in the "AMX way" but duet framework provides for all the tools needed to do this correctly.

    Just my two cents.
  • wengerpwengerp Posts: 29
    AMXJeff wrote: »
    ... duet framework provides for all the tools needed to do this correctly.

    I used to do a lot with duet but due to the lack of proper documentation I finally gave it up. Here in Switzerland it seems to be impossible to get support for duet nor did I find any useful tutorials or other helpful stuff. That's why I changed all my code back to netlinx. It's a shame, isn't it?
  • wengerpwengerp Posts: 29
    JasonS wrote: »
    It has been my experience that DYNAMIC_VIRTUAL_DEVICE only works inside a module, and it only works once per module. I use it with TRANSLATE_DEVICE in all my modules.

    This sounds very promising. However, I can't find any documentation about DYNAMIC_VIRTUAL_DEVICE or TRANSLATE_DEVICE in the netlinx documentation. What are these keywords doing exactly? Where can I found more information?
  • JasonSJasonS Posts: 229
    wengerp wrote: »
    This sounds very promising. However, I can't find any documentation about DYNAMIC_VIRTUAL_DEVICE or TRANSLATE_DEVICE in the netlinx documentation. What are these keywords doing exactly? Where can I found more information?

    Both are "unsupported" by AMX. I learned about them in Programmer 3 class. Since DYNAMIC_VIRTUAL_DEVICE only works inside a module I think it is primarily intended for use with TRANSLATE_DEVICE.

    DYNAMIC_VIRTUAL_DEVICE basically gives you a virtual device that is not being used, but it only works inside a module once.

    TRANSLATE_DEVICE makes writing modules worth doing. It takes the virtual device you pass into the module and your DYNAMIC_VIRTUAL_DEVICE and cross-connects there inputs and outputs. Any Strings or Commands sent to the virtual device come out the DYNAMIC device and vice versa. Channels commands sent to the VIRTUAL device affect the DYNAMIC device and vice versa.
    MODULE_NAME='Example'(DEV vdvVirtual_from_Main_Code, DEV RS232)
    
    DEFINE_DEVICE
    
        vdvInside_Module = DYNAMIC_VIRTUAL_DEVICE
    
    DEFINE_START
    
        TRANSLATE_DEVICE (vdvVirtual_from_Main_Code, vdvInside_Module)
    
    DEFINE_EVENT
    
        DATA_EVENT [vdvInside_Module]
        {
            STRING:
            {
                //Strings sent to vdvVirtual_from_Main_Code in Main code will be received here
                SEND_STRING vdvInside_Module, 'This string will only be seen by vdvVirtual_from_Main_Code'
            }
        }
    

    There have been several threads about TRANSLATE_DEVICE, I would search the forums.
  • AMXJeffAMXJeff Posts: 450
    wengerp wrote: »
    shame, isn't it?

    Yes It is! BTW... Location does not help with duet support, support from the states are equally absent. But AMX uses publically available support libraries, like OSGi, and there are a good deal of documents from the providers. There are a few of us that have dug in and learned what we could, and making it work. It works well, too. AMX is still the only control manufacturer that has provided a system that can build configurable systems natively. They been doing it for years too!

    My 60 seconds are up! Good luck with your design...
  • wengerp wrote: »
    Hi all

    I'm looking for a way to dynamically create a (virtual) device by code without having to enter anything into the DEFIINE_DEVIICE section. I couldn't find any solution so far. Is this impossible or am I searching at the wrong places?

    Thanks for any suggestions,
    Patrick

    Pass the D:P:S into a module as a command parameter (ADDDEVICE-10001:4:1), parse the string into a DEV array and issue REBUILD_EVENT(). The device does not need to be in DEFINE_DEVICE. You do need LEVEL_EVENT[DEVARRAY[],0] in the module for REBUILD_EVENT to process and need to manipluate the DEV array before REBUILD_EVENT(), SET_LENGTH_ARRAY(DEVARRAY,MAX_VALUE + 1) SET_LENGTH_ARRAY(DEVARRAY,actualLengthArray) works as well as anything.
  • wengerpwengerp Posts: 29
    icraigie wrote: »
    Pass the D:P:S into a module as a command parameter (ADDDEVICE-10001:4:1), parse the string into a DEV array and issue REBUILD_EVENT(). The device does not need to be in DEFINE_DEVICE.

    This allows you to _change_ an existing D:P:S but not _create_. You must register the module D:P:S in question (the one you are going to send the commnd to) in the DEFINE_DEVICE section and that's exactly what I was trying to avoid. But thanks anyway.
  • JasonSJasonS Posts: 229
    Did some quick testing this morning, results do not look promising...
    DEFINE_DEVICE
        Defined = 33001:1:0
    
    DEFINE_VARIABLE
        DEV Dynamic
    
        DEV_INFO_STRUCT sDefinedInfo
        DEV_INFO_STRUCT sDynamicInfo
    
    DEFINE_START
    
        Dynamic = 33002:1:0
        DEVICE_INFO(Defined, sDefinedInfo)
        SEND_STRING 0,"'Defined MANUFACTURER_STRING=', sDefinedInfo.MANUFACTURER_STRING"
        SEND_STRING 0,"'Defined MANUFACTURER=',ITOA(sDefinedInfo.MANUFACTURER)"
        SEND_STRING 0,"'Defined DEVICE_ID_STRING=', sDefinedInfo.DEVICE_ID_STRING"
        SEND_STRING 0,"'Defined DEVICE_ID=',ITOA(sDefinedInfo.DEVICE_ID)"
        SEND_STRING 0,"'Defined VERSION=', sDefinedInfo.VERSION"
        SEND_STRING 0,"'Defined FIRMWARE_ID=',ITOA(sDefinedInfo.FIRMWARE_ID)"
        SEND_STRING 0,"'Defined SERIAL_NUMBER=', sDefinedInfo.SERIAL_NUMBER"
        SEND_STRING 0,"'Defined SOURCE_TYPE=',ITOA(sDefinedInfo.SOURCE_TYPE)"
        SEND_STRING 0,"'Defined SOURCE_STRING=', sDefinedInfo.SOURCE_STRING"
    
        SEND_STRING 0,"$0D"
        SEND_STRING 0,"$0D"
        
        DEVICE_INFO(Dynamic, sDynamicInfo)
        SEND_STRING 0,"'Dynamic MANUFACTURER_STRING=', sDynamicInfo.MANUFACTURER_STRING"
        SEND_STRING 0,"'Dynamic MANUFACTURER=',ITOA(sDynamicInfo.MANUFACTURER)"
        SEND_STRING 0,"'Dynamic DEVICE_ID_STRING=', sDynamicInfo.DEVICE_ID_STRING"
        SEND_STRING 0,"'Dynamic DEVICE_ID=',ITOA(sDynamicInfo.DEVICE_ID)"
        SEND_STRING 0,"'Dynamic VERSION=', sDynamicInfo.VERSION"
        SEND_STRING 0,"'Dynamic FIRMWARE_ID=',ITOA(sDynamicInfo.FIRMWARE_ID)"
        SEND_STRING 0,"'Dynamic SERIAL_NUMBER=', sDynamicInfo.SERIAL_NUMBER"
        SEND_STRING 0,"'Dynamic SOURCE_TYPE=',ITOA(sDynamicInfo.SOURCE_TYPE)"
        SEND_STRING 0,"'Dynamic SOURCE_STRING=', sDynamicInfo.SOURCE_STRING"
    
    

    Here is the output:

    (0000024525) Defined MANUFACTURER_STRING=AMX LLC
    (0000024527) Defined MANUFACTURER=1
    (0000024531) Defined DEVICE_ID_STRING=Virtual
    (0000024531) Defined DEVICE_ID=65534
    (0000024532) Defined VERSION=v4.1.400
    (0000024532) Defined FIRMWARE_ID=380
    (0000024532) Defined SERIAL_NUMBER=210515xXXXXXXX
    (0000024533) Defined SOURCE_TYPE=0
    (0000024533) Defined SOURCE_STRING=
    (0000024533)
    (0000024534)
    (0000024534) Dynamic MANUFACTURER_STRING=3$CC7$CC3$CC3$CC3$CC3$CC3$103$CC3$CC3$C
    C3$CC3$CC3$CC3$CC3
    (0000024535) Dynamic MANUFACTURER=0
    (0000024535) Dynamic DEVICE_ID_STRING=3$CC7$CC3$CC3$CC3$CC3$CC3$103$CC3$CC3$CC3$
    CC3$CC3$CC3$CC3
    (0000024535) Dynamic DEVICE_ID=0
    (0000024537) Dynamic VERSION=3$CC7$CC3$CC3$CC3$CC3$CC3$103$CC3$CC3$CC3$CC3$CC3$C
    C3$CC3
    (0000024540) Dynamic FIRMWARE_ID=0
    (0000024546) Dynamic SERIAL_NUMBER=3$CC7$CC3$CC3$CC3$CC3$CC3$103$CC
    (0000024549) Dynamic SOURCE_TYPE=0
    (0000024552) Dynamic SOURCE_STRING=
Sign In or Register to comment.