NetLinx Programming Conventions
NetLinx Programming Conventions
Description:
Device numbering should remain standard. Recommendations are:
Device: Types: Comments:
1-255 AXCESS Devices Use AXCESS standards
301-3072 NetLinx card frames Start at frame number 25 (frame# * 12) + 1
5001-6000 ICSNet NetLinx devices NXI, NXM-COM2, NXM-IRS4, etc.
6001-7000 ICSNet Landmark devices PLH-VS8, PLH-AS16, PLB-AS16
7001-8000 P3 Devices
8001-10000 Internet Inside applications Database Plus, PCLink, etc.
10001-32000 ICSNet Panels DMS, IMS, Web panels
33001-36863 Virtual devices For simplicity, start at 33001
System Numbering
Description:
Each NetLinx master can be assigned a unique system number (between 1 and 65535). If dealing with a single
NetLinx master system, this system number is not an important factor and could typically be left at 1. However,
when dealing with a multiple NetLinx master system and implementing master-to-master (M2M), this system
number becomes extremely important. Each of these NetLinx masters would need to have a unique system number
assigned. (It should be noted that even when implementing multiple NetLinx masters that do not require M2M, it
would still be a good idea to provide unique system numbers. This way, if M2M is ever needed in the future, it
could be more easily implemented.)
System 0 is not a valid system number to assign to a master. However, with programming the NetLinx system,
system 0 is extremely useful to use. When you define a device as system 0, it implies that this device is on the
master being programmed. Then, the system number for that master could be any value and the code would still
work.
Recommended:
DEFINE_DEVICE
dvRelay = 5001:7:0 // System 0 implies THIS NetLinx master
With this example, if for some reason, the system number of this master had to change, this program would still
work. It is also easier to identify that this device is on my NetLinx system.
Not Recommended:
DEFINE_DEVICE
dvRelay = 5001:7:1 // System 1 refers explicitly to system 1,
// could be me or another master
Network Setup
• In Amx Studio, select the Tools->Master Comm Settings Menu item. Select NetLinx for the platform and then
select either a COM port (if the networking is not already configured) or an IP address (if it is currently on the
network). Select OK when complete.
• Select the Tools->NetLinx Diagnostics Menu item. Select the Networking tab from the tabs along the top. Enter 0
for both System and Device. Then press Get IP Info. Then press Get DNS Info
•Is there a supplied Host name for the IP address assigned to the NetLinx master? If so, enter under Host Name. If
no name is supplied, enter NetLinx.
•Is your IP address dynamic (DHCP) or fixed (Static)? Select the Use DHCP or Specify IP Address according to
your network information.
•If you selected Use DHCP, then press Set IP Info and then press Reboot. When the master restarts, it will contact
the DHCP server to fill in any missing network information. You are now finished with your NetLinx Network
Setup.
•If you selected Specify IP Address, enter the IP address supplied by the network administrator. If you do not have
an IP address, contact the network administrator to obtain one. If you enter the incorrect number, the NetLinx master
and very likely another device on the network will not be able to communicate on the network.
•Enter your Subnet Mask. This value determines the scope of your network. If you did not receive a value for this,
enter 255.255.255.0. It is a very common default. Do not leave this blank.
•Enter your Gateway. This value determines a path to computers not on your network. If you did not receive a value
for this, enter your IP address. Do not leave this blank.
•Enter your Domain Suffix. If you did not receive a value for this, leave it blank.
•Enter up to 3 DNS IP. This values help your master find other network resources. If you did not receive a value for
these, leave them blank.
•Press Set IP Info, followed by Set DNS Info and then press Reboot. When the master restarts, it will use the new
network settings. You are now finished with your NetLinx Network Setup.
It is also possible to have the network administrator provide the NetLinx master’s host name. Then the network
administrator would enter this host name into a host table (Windows NT). This method would require the NetLinx
master to be configured as a dynamic IP. You should contact the network administrator for more details and
configuration settings.
If the network cannot resolve the host name of the NetLinx master, then it will be necessary to obtain a static IP
address to use for the NetLinx master. Then the URL entry would be configured with this static IP.
Description:
When defining a device that will use a port on the master, it is recommended to use the FIRST_LOCAL_PORT
when declaring the device. This will ensure that the NetLinx program will use the first available local port on the
master with future firmware versions. It is also recommended to keep the ports in consecutive order. For example,
using ports FIRST_LOCAL_PORT, FIRST_LOCAL_PORT+1, AND FIRST_LOCAL_PORT+2 would be better
than using FIRST_LOCAL_PORT, FIRST_LOCAL_PORT+1, AND FIRST_LOCAL_PORT+200. Doing it this
way will still allocate ports up to FIRST_LOCAL_PORT+200, even when they are not used. The more ports
defined, the more the master has to keep track of, so keep the numbers low and consecutive.
When writing code for IP devices, it is recommended to use DEFINE_DEVICE just like all other devices. This
way, it is easy to see the IP devices without having to search through the DEFINE_CONSTANT section for master
port numbers. Then when doing the IP_CLIENT_OPEN, you simply use the DEVICE.PORT. This is preferred
over creating a constant to define the master port. Also note that the definition of the DATA_EVENT must
explicitly declare the DEV as 0:dvIP.PORT:0. See the example below.
For example:
DEFINE_DEVICE
dvPcom = 0:FIRST_LOCAL_PORT:0 // Polycom Viewstation (IP Device)
dvARQ = 0:FIRST_LOCAL_PORT+1:0 // Audio Request ARQ1-Pro (IP Device)
DEFINE_START
IP_CLIENT_OPEN(dvPcom.PORT,’255.255.255.200’,24,1)
IP_CLIENT_OPEN(dvARQ.PORT,’255.255.255.201’,3663,1)
DEFINE_EVENT
DATA_EVENT[0:dvPcom.PORT:0] // NOTE: Can’t use [dvPcom] here!
{
ONLINE :
{
}
}
4. IP Ports Reference
IP Ports Reference List
Description:
IP Ports currently being used are:
IP Port: Types: Comments:
1-1024 are assigned port numbers
21 FTP File transfer
23 Telnet Telnet
25 SMTP Send Mail
80 HTTP Web
110 POP3 Receive Mail
A complete listing of IP Port numbers can be found at:
http://www.iana.org/assignments/port-numbers
Description:
Symbols names, such as constants, devices, and variables, have typically been uppercase in AXCESS since symbols
are not case sensitive. NetLinx has not added case-sensitivity to symbols. However, we will move to a new
standard of uppercase and mixed case. When using all uppercase, it is more readable to use underscores, such as
nMY_VAR. When using mixed case, the underscore is not needed, such as nMyVar. Here is a summary, followed
by examples:
Devices
Description:
Devices in AXCESS were always a single integer and could be assigned to variables without type casting problem.
In NetLinx, all devices have a DEV structure to contain all of the device data (D:P:S). All entries in the
DEFINE_DEVICE section are implicitly defined as DEV types. Therefore, we should add the prefix ‘dv’ to all
device definitions. An example would be:
DEFINE_DEVICE
dvVCR = 5001:1:0 // IR #1 Sony SVO-1630 HC:RMV200 s216.irl
// MODE: IR Carrier: Off FG10-006
In NetLinx, the use of virtual devices will become more widely used. Virtual devices have a range of 32768-36863,
but for simplicity, it is recommended to start virtual devices at 33001. The prefix 'vdv' should be added to all virtual
device definitions. Virtual devices will be used in DEFINE_COMBINE, COMBINE_DEVICES, and
UNCOMBINE_DEVICES (keywords). An example would be:
DEFINE_DEVICE
vdvVirtual = 33001:1:0 // Virtual control over this system
DEFINE_COMBINE
(vdvVirtual, dvSomeTP) // Note: first device must be virtual
Constants
Description:
DEFINE_CONSTANT
// Source selections
SRC_VCR = 1
SRC_DVD = 2
// House zones
ZN_MBR = 1 // Master Bedroom
ZN_KITCHEN = 2 // Kitchen
Description:
When defining a structure, use the _s notation as the structure type definition. The actual name of the variable that
uses a structure will carry a Hungarian notation of a prefixed ‘u’, and each data type within the structure will carry
it’s own prefix (see variables). Structures can be easily seen, because they use the dot operator (uUserType.sName
= 'Test'). Here is the recommended notation for NetLinx programming user-defined data types:
DEFINE_TYPE
STRUCTURE _sUSER_TYPE
{
CHAR sName[20] // String array
INTEGER nFlag // Integer
}
DEFINE_VARIABLE
Variables
Description:
Variables in AXCESS were always 1 of 3 types: integers, 8-bit arrays and integer (16-bit) arrays. With NetLinx, we
now have many more variable types including user-defined types. To accommodate these new types, NetLinx
programming standard will adopt Hungarian notation style. Hungarian notation is a method of specifying the
variable type in the name of the variable name itself. This helps to eliminate confusion when writing code.
Variables should be declared as VOLATILE (or defined as STACK_VAR in subroutines/functions) wherever a non-
volatile variable is not needed. The NetLinx master contains more volatile memory than non-volatile and when the
programs use large chunks of memory, you can run out of non-volatile more quickly.
DEFINE_VARIABLE
Description:
The DEFINE_DEVICE section should include as much data about the devices as possible. Each declaration should
include the dv (or vdv) notation. The device names should be descriptive and include any notation for the device's
location (example: dvTPMbr1A…..denotes TP # (1), first device of panel (A), location (master bedroom)).
The comments to the right of the device should include information such as AMX device type, make/model of
equipment being controlled, FG# of cable being used, hand control, dos filename of IR file, baud rate, etc. It is
sometimes necessary to break up all of this information on it’s own line to make it more readable. Remember that
the more information put in this section will greatly increase technical support later down the road when this project
is completed.
Description:
In an Axcess system, it was common practice to DEFINE_COMBINE touch panels. This is still available in
NetLinx, however it is recommended to use DEV arrays of panels instead. There is a sample program below.
The most common problem DEFINE_COMBINE is when all panels are “almost” identical, but one panel needs to
route preview video to this destination, while another panel needs to route preview video to another destination. In
this circumstance, with the panels combined, you would need to assign unique channel codes to each button to make
it work and hope that other button was never added to the other panel.
To eliminate this problem, it is now recommended to put panels in a DEV array. This will provide
DEFINE_COMBINE functionality, as well as let the program determine which panel actually pressed the button to
perform these “almost” identical functions. This is always recommended, even when the system only has one panel,
because if a web panel was ever added (for example), it could simply be added to the DEV array.
DEFINE_VARIABLE
dvPnls[] = {dvTp1a, dvTp2a}
IF(BUTTON.INPUT.DEVICE = dvTp1a)
SetSourcePreview (SRC_VCR, 1)
ELSE IF(BUTTON.INPUT.DEVICE = dvTp2a)
SetSourcePreview (SRC_VCR, 2)
}
}
7. Events Sections
Data Events
Description:
All device configurations should take place in the online event for each individual device. With this, the system
should be configured for correct operation at device online events without any other technician intervention.
For IR devices, setup of mode (IR/Serial) and carrier (on/off) should be configured. It is also customary to provide
any default feedback for IR controlled devices as well. For example, the SYSTEM_CALL 'FUNCTION'
(dvVCR,STOP,0) should be used in the IR Online event.
For 232 devices, setup of baud rate, parity, data bits, stop bits, and 485 status should be configured. With 232
devices, it is recommended to use CREATE_BUFFER in startup. Then parse over the buffer under the STRING
event. All buffer parsing should be done with a separate subroutine, usually passing the device number, the buffer,
and sometimes an index pointer as parameters. A single buffer parsing routine could then be used to parse data for
many different devices of the same type.
DATA_EVENT[dvVideoSwt]
{
ONLINE:
{
SEND_COMMAND Data.Device,'SET BAUD 9600,N,8,1 485 DISABLED'
}
OFFLINE:
{
}
STRING:
{
WHILE(FIND_STRING(VideoSwtBuff,”13,10”,1))
{
CALL 'Parse Video Swt Buff' (dvVideoSwt,1,VideoSwtBuff)
}
}
}
Button Events
Description:
All button presses should incorporate the BUTTON_EVENT. No pushes/releases should occur in
DEFINE_PROGRAM section. Where it is applicable to group common buttons, use a DEVCHAN set. Where it is
not applicable, simply use the [device,channel].
Channel Events
Description:
There are no special requirements for channel events. However, it may require a touch panel bargraph to be updated
with a previous level in a mute on/off condition.
CHANNEL_EVENT[dvVOL,3]
{
ON :
{
SEND_LEVEL dvTpMbr1a,1,0
}
OFF :
{
SEND_LEVEL dvTpMbr1a,1,nPgmLvl
}
}
Level Events
Description:
Under a level event is where you should update the level on a touch panel. This used to be done in Mainline with
Axcess. It will still work in mainline, but it is recommended to move it under the level event.
IF([dvVOL,3])
SEND_LEVEL dvTpMbr1a,1,0
ELSE
SEND_LEVEL dvTpMbr1a,1,nPgmLvl
}
Description:
It is recommended that a function call be used with a switch...case to iterate through all of the available sources
(VCR, DVD, camera, etc) supported in the program. Each of these sources should be declared as a constant to make
the calls easier to read. Although case is ignored, it is recommended the name of the function be mixed case. For
example:
RETURN (SRC_VCR)
}
CASE SRC_CASS :
{
SEND_STRING dvSWT,"'CI2O1T'"
CALL 'SetProjFn' (dvPROJ,1,FN_PWR_OFF)
PULSE[dvRelay,SCREEN_UP]
RETURN (SRC_CASS)
}
}
}
Subroutines
Description:
It is recommended that a subroutine be used with a switch...case to iterate through all functions of this particular
device supported in the program. For example, if a 232-controlled video projector is included in the program, there
may be a subroutine to provide specific functions for that projector such as power on, power off, video mute on,
video mute off, and input selects. All of these functions should be declared in the constant section to make the calls
easier to read. This subroutine should contain feedback and the actual 232 command. It should not contain any wait
statements or logic. This will keep the call generic and useable for many different devices. The logic should be
included where the subroutine is called.
nVidMute[nIDX] = 0
nVidInp[nIDX] = 0
}
CASE FN_PROJ_OFF :
{
OFF[dvPROJ,FN_PWR]
SEND_STRING dvPROJ,"'C00',13"
nVidMute[nIDX] = 0
nVidInp[nIDX] = 0
}
CASE FN_PROJ_INP_VID1 :
{
SEND_STRING dvPROJ,"'C07',13"
nVidMute[nIDX] = 0
nVidInp[nIDX] = FN_PROJ_INP_VID1
}
CASE FN_PROJ_INP_RGB1 :
{
SEND_STRING dvPROJ,"'C05',13"
nVidMute[nIDX] = 0
nVidInp[nIDX] = FN_PROJ_INP_RGB1
}
CASE FN_PROJ_VID_MUTE_ON :
{
SEND_STRING dvPROJ,"'C0N',13"
nVidMute[nIDX] = 1
}
CASE FN_PROJ_VID_MUTE_OFF :
{
SEND_STRING dvPROJ,"'C0F',13"
nVidMute[nIDX] = 0
}
}
}
Description:
With an M2M system, the NetLinx master needs a path to communicate with other masters. This is accomplished
with entries in a NetLinx master’s URL list of the other NetLinx masters. Then when a program defines a device
that is on another system, the NetLinx master will open up communication with that other master to exchange data
such as strings, commands, channels, pushes, etc between each master. Therefore, you as a programmer are not
even aware that the device is on another system.
First, let’s take a low-overhead device such as an Extron SW6 switcher. It is considered low-overhead because it
has a limited number of inputs and outputs and it would be uncommon to try and make several switches back to
back with this device.
Next, a high-overhead device would be something like an Extron Matrix 6400. It is considered high-overhead
because it has a large number of inputs and outputs and it would be common to make several switches back to back
with this device. With this in mind, assume that 5 masters had to make I/O switches on this device, you could run
the risk of simultaneously trying to make several I/O routes on this switcher from all 5 masters. It would be a better
idea to only let the NetLinx master that is physically attached to this device, do all of the communications with it.
Then, you would have all masters add commands to a command queue on this master, where all of the switches
could be made through this queue. How do other masters add commands to this queue? Simple, use a virtual device
and send commands to this virtual device from all of the masters. This virtual device then processes the data.text
and throws this switching command into the queue.
Note, the bottleneck is not an issue with the NetLinx master, but rather an issue with the 232 communications with
the external device. Typically the 232 buffer of an external device is not designed to take on all of the traffic that an
M2M system could send to it.
M2M Inner-Communications
Sometimes it is necessary to communicate between NetLinx programs. With an Axcess system, we did this with a
422 data link between masters. To do this in NetLinx, we use virtual devices defined on each master in the M2M
system. It is a good idea to create a “broadcast” virtual device for each master. That master would “broadcast”
messages out and if another master wanted to listen, that master would create the same virtual device from the other
master’s system. This master would also have it’s own “broadcast” virtual device. NOTE: You have to be careful
that the replies to these commands don’t create other replies. When this happens, your NetLinx master’s input and
output LED will come on solid because these masters are replying to each other’s message. This is an undesirable
condition and should be avoided.
Master #1: Master #2:
“Broadcasts” messages on Listens for #1 messages on
33001:1:0 33001:1:1
For further information regarding M2M, please refer to the “NetLinx Programming Language” instruction manual.
It can be found on the AMX website.
10. Modules
Overview
Modules are NetLinx programs that are to be easily dropped into a NetLinx program to communicate with a
particular device. The module takes all of the complex code and hides it behind the module as a *.tko file. Then, a
simpler API is exposed to the NetLinx program for external control. Note that it is common to label the NetLinx
program as the caller, since that program makes “calls” to the module (via send commands, channels, etc).
When creating modules, they should never define other modules. Instead, all modules should be defined in the same
location.
For further information regarding modules, please refer to the “NetLinx Programming Language” instruction
manual. It can be found on the AMX website.
Module Naming
The naming convention of a module is ‘MFG_MODEL_<type>’ where type would be a classification of module
such as COMM (Communication) or UI (User Interface). There could be different UI <types> based upon each
device being controlled.
Note, some UI modules may allow the caller to enter a NO BUTTON condition, which really means this UI doesn’t
require the functionality of the button. In system calls, NO BUTTON is defined as 0. In NetLinx, using 0 under a
button event provides the functionality that ALL pushes will iterate through the button event, regardless of whether
they are included in the DEVCHAN or INTEGER button array. This could be undesirable, since all pushes would
iterate through any button events that had 0 as a channel. So, instead of using 0 for NO BUTTON, you should
choose a button that is out of the range of 1-255. I recommend using 257 for NO BUTTON.
Note, as of now, all common module definitions should be included one after the other. For example, this is the
proper way to add multiple module definitions:
(*******************************************************)
(* CONSTANT DEFINITIONS GO HERE *)
(*******************************************************)
DEFINE_CONSTANT
NL_NO_BUTTON = 257
(*******************************************************)
(* VARIABLE DEFINITIONS GO HERE *)
(*******************************************************)
DEFINE_VARIABLE
(*******************************************************)
(* STARTUP CODE GOES HERE *)
(*******************************************************)
DEFINE_START
(*******************************************************)
(* MODULE DEFINITIONS GOE HERE *)
(*******************************************************)
DEFINE_MODULE ‘AMX_COMM’ mdlCOMM1(vdvSomeDev1, dvSomeDev1)
DEFINE_MODULE ‘AMX_COMM’ mdlCOMM2(vdvSomeDev2, dvSomeDev2)
(*******************************************************)
(* CONSTANT DEFINITIONS GO HERE *)
(*******************************************************)
DEFINE_CONSTANT
(*******************************************************)
(* VARIABLE DEFINITIONS GO HERE *)
(*******************************************************)
DEFINE_VARIABLE
(*******************************************************)
(* STARTUP CODE GOES HERE *)
(*******************************************************)
DEFINE_START
(*******************************************************)
(* MODULE DEFINITIONS GOE HERE *)
(*******************************************************)
DEFINE_MODULE ‘AMX_COMM’ mdlCOMM1(vdvSomeDev1, dvSomeDev1)
DEFINE_MODULE ‘AMX_UI’ mdlUI1(vdvSomeDev1, dvSomePnl1)
COMM Module
This is the module that will communicate with the external device. It typically requires 2 parameters, the first being
the virtual device and the second being the actual device. This COMM module will do all of the buffer parsing,
device polling, and device control that may be required. This COMM module will also use the virtual device to
expose feedback, levels, send commands (for controlling the external device), and string events (for getting data
back from the module about the device). This is referred to as the custom API for this device. This API will require
a text document regarding how to use the module.
This COMM module should always queue up the commands when it is necessary. That way the caller could stack
several commands and expect the module to execute them without any knowledge of the delays required to make all
of the commands go out.
This UI will be custom for each external device. Some devices may require multiple UI modules. For instance, a
security system may require a UI module for a security control and a separate UI module for thermostat control.
These UI modules will communicate with the COMM module through the API. The idea of the UI module is to
easily drop in a touch panel page along with the UI module for control of this device with little knowledge of how
the device actually works. Of course, if the programmer does not like the functionality of the UI module, they will
be able to communicate with the COMM module using the same API and their own custom interface.
When it comes time to define button lists for the UI module, you should consider whether you will require the caller
to pass in a DEVCHAN set, or a single DEV for the panel and an INTEGER array for the channel numbers. Each
has it’s own advantages. The DEVCHAN method allows all channel codes of the list to be on multiple devices of a
touch panel. But, this method is not good when you need to see an ONLINE data event for a panel to update
variable text. If this is a requirement, the single DEV and an INTEGER array for channel numbers may be a better
solution.
It should be noted that a UI module could contain “hidden” features using NL_NO_BUTTON (similar to system
calls). For example, a button list may contain both an ON and OFF element for channel codes. This would be for a
UI that would require both discrete buttons. However, it is more common that a UI contain a single PWR button
that toggles. This can be accomplished in a module by using the NL_NO_BUTTON channel for the OFF element.
This way, when the ON button is pressed, the module can see if the OFF button is defined as a no button condition,
then the module will make the ON button turn into toggling functionality. You should never assume this is how a
module will work. You should review the API to see if it contains notes about these “hidden” features.
BUTTON_EVENT[dvPnl,nBtnList]
{
PUSH :
{
SWITCH(GET_LAST(nBtnList))
{
CASE 1 : // Button - Power On
{
(*** Provide “hidden” toggling functionality ***)
IF(nBtnList[2] = NL_NO_BUTTON)
{
IF([vdvComm,PWR_FB])
SEND_COMMAND vdvComm,’POF’
ELSE
SEND_COMMAND vdvComm,’PON’
}
ELSE
{
SEND_COMMAND vdvComm,’PON’
}
}
CASE 2 : // Button - Power Off
{
SEND_COMMAND vdvComm,’POF’
}
}
}
}
DEFINE_PROGRAM
Common API
UI Module #1
External
UI COMM Polling Device
Queue Module
UI Module #2
Caller String
Event