Adding the perl bindings example.
This commit is contained in:
@@ -0,0 +1,348 @@
|
||||
<?xml version="1.0" ?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>API Documentation</title>
|
||||
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
|
||||
<link rev="made" href="mailto:rurban@x-ray.at" />
|
||||
</head>
|
||||
|
||||
<body style="background-color: white">
|
||||
|
||||
|
||||
<!-- INDEX BEGIN -->
|
||||
<div name="index">
|
||||
<p><a name="__index__"></a></p>
|
||||
|
||||
<ul>
|
||||
|
||||
<li><a href="#name">NAME</a></li>
|
||||
<li><a href="#description">DESCRIPTION</a></li>
|
||||
<li><a href="#options">OPTIONS</a></li>
|
||||
<li><a href="#this_tool_s_api">This tool's API</a></li>
|
||||
<ul>
|
||||
|
||||
<li><a href="#readproperty">ReadProperty</a></li>
|
||||
<ul>
|
||||
|
||||
<li><a href="#inputs_to_readproperty">Inputs to ReadProperty</a></li>
|
||||
<li><a href="#outputs_from_readproperty">Outputs from ReadProperty</a></li>
|
||||
<li><a href="#example_of_readproperty">Example of ReadProperty</a></li>
|
||||
</ul>
|
||||
|
||||
<li><a href="#readpropertymultiple">ReadPropertyMultiple</a></li>
|
||||
<ul>
|
||||
|
||||
<li><a href="#inputs_to_readpropertymultiple">Inputs to ReadPropertyMultiple</a></li>
|
||||
<li><a href="#outputs_from_readpropertymultiple">Outputs from ReadPropertyMultiple</a></li>
|
||||
<li><a href="#example_of_readpropertymultiple">Example of ReadPropertyMultiple</a></li>
|
||||
</ul>
|
||||
|
||||
<li><a href="#writeproperty">WriteProperty</a></li>
|
||||
<ul>
|
||||
|
||||
<li><a href="#inputs_to_writeproperty">Inputs to WriteProperty</a></li>
|
||||
<li><a href="#outputs_from_writeproperty">Outputs from WriteProperty</a></li>
|
||||
<li><a href="#example_of_writeproperty">Example of WriteProperty</a></li>
|
||||
</ul>
|
||||
|
||||
<li><a href="#timesync">TimeSync</a></li>
|
||||
<ul>
|
||||
|
||||
<li><a href="#inputs_to_timesync">Inputs to TimeSync</a></li>
|
||||
<li><a href="#outputs_from_timesync">Outputs from TimeSync</a></li>
|
||||
<li><a href="#example_of_timesync">Example of TimeSync</a></li>
|
||||
</ul>
|
||||
|
||||
<li><a href="#log">Log</a></li>
|
||||
<ul>
|
||||
|
||||
<li><a href="#inputs_to_log">Inputs to Log</a></li>
|
||||
<li><a href="#example_of_log">Example of Log</a></li>
|
||||
</ul>
|
||||
|
||||
<li><a href="#silencelog">SilenceLog</a></li>
|
||||
<ul>
|
||||
|
||||
<li><a href="#inputs_to_silencelog">Inputs to SilenceLog</a></li>
|
||||
<li><a href="#outputs_from_silencelog">Outputs from SilenceLog</a></li>
|
||||
<li><a href="#example_of_silencelog">Example of SilenceLog</a></li>
|
||||
</ul>
|
||||
|
||||
<li><a href="#retry">Retry</a></li>
|
||||
<ul>
|
||||
|
||||
<li><a href="#inputs_to_retry">Inputs to Retry</a></li>
|
||||
<li><a href="#outputs_from_retry">Outputs from Retry</a></li>
|
||||
<li><a href="#example_of_retry">Example of Retry</a></li>
|
||||
</ul>
|
||||
|
||||
</ul>
|
||||
|
||||
</ul>
|
||||
|
||||
<hr name="index" />
|
||||
</div>
|
||||
<!-- INDEX END -->
|
||||
|
||||
<p>
|
||||
</p>
|
||||
<h1><a name="name">NAME</a></h1>
|
||||
<p>bacnet.pl - Scriptable BACnet communications</p>
|
||||
<p>
|
||||
</p>
|
||||
<hr />
|
||||
<h1><a name="description">DESCRIPTION</a></h1>
|
||||
<p>This is a tool for scriptable BACnet communication. Users can write their own
|
||||
scripts using standard Perl syntax and API defined in this tool to perform desired
|
||||
execution sequences. For details on this tool's API, see Documentation.html. For other
|
||||
Perl documentation, see <a href="http://perldoc.perl.org">http://perldoc.perl.org</a></p>
|
||||
<link href="syntax.css" rel="stylesheet" type="text/css">
|
||||
<script src="jquery.js"></script>
|
||||
<script src="syntax.js"></script><p>
|
||||
</p>
|
||||
<hr />
|
||||
<h1><a name="options">OPTIONS</a></h1>
|
||||
<p>Usage: bacnet.pl [program_options] [-- script_args]</p>
|
||||
<p>This program executes a script in perl syntax to perform BACnet/IP operations.</p>
|
||||
<pre>
|
||||
|
||||
Possible program options:
|
||||
--script=s The script to execute.
|
||||
--log=s The file to log all output.
|
||||
--help This help message.</pre>
|
||||
<pre>
|
||||
Possible environment variables are:
|
||||
BACNET_IFACE - set this value to dotted IP address of the interface (see
|
||||
ipconfig) for which you want to bind. Default is the interface which
|
||||
Windows considers to be the default (how???). Hence, if there is only a
|
||||
single network interface on Windows, the applications will choose it, and
|
||||
this setting will not be needed.
|
||||
BACNET_IP_PORT - UDP/IP port number (0..65534) used for BACnet/IP
|
||||
communications. Default is 47808 (0xBAC0).
|
||||
BACNET_APDU_TIMEOUT - set this value in milliseconds to change the APDU
|
||||
timeout. APDU Timeout is how much time a client waits for a response from
|
||||
a BACnet device.
|
||||
BACNET_BBMD_PORT - UDP/IP port number (0..65534) used for Foreign Device
|
||||
Registration. Defaults to 47808 (0xBAC0).
|
||||
BACNET_BBMD_TIMETOLIVE - number of seconds used in Foreign Device
|
||||
Registration (0..65535). Defaults to 60000 seconds.
|
||||
BACNET_BBMD_ADDRESS - dotted IPv4 address of the BBMD or Foreign Device
|
||||
Registrar.</pre>
|
||||
<p>
|
||||
</p>
|
||||
<hr />
|
||||
<h1><a name="this_tool_s_api">This tool's API</a></h1>
|
||||
<p>In addition to having all standard Perl flow control, functions, and modules,
|
||||
the this tool provides an API for performing BACnet communication functions.</p>
|
||||
<p>
|
||||
</p>
|
||||
<h2><a name="readproperty">ReadProperty</a></h2>
|
||||
<p>This function implements the ReadProperty service. There are no built in retry
|
||||
mechanisms. NOTE: all enumerations are defined in <em class="file">bacenum.h</em></p>
|
||||
<p>
|
||||
</p>
|
||||
<h3><a name="inputs_to_readproperty">Inputs to ReadProperty</a></h3>
|
||||
<ul>
|
||||
<li><b>devideInstance</b> - the instance number of the device we are reading</li>
|
||||
<li><b>objectName</b> - the enumeration for the object name we are reading</li>
|
||||
<li><b>objectInstance</b> - the instance number of the object we are reading</li>
|
||||
<li><b>propertyName</b> - the enumeration for the property name we are reading</li>
|
||||
<li><b>index</b> - Optional (default -1): the index number we are reading from. -1 if not applicable</li>
|
||||
</ul><p>
|
||||
</p>
|
||||
<h3><a name="outputs_from_readproperty">Outputs from ReadProperty</a></h3>
|
||||
<ul>
|
||||
<li><b>result</b> - the sting result (value or error) for ReadProperty</li>
|
||||
<li><b>isFailure</b> - zero means no failure, non-zero means failure</li>
|
||||
</ul><p>
|
||||
</p>
|
||||
<h3><a name="example_of_readproperty">Example of ReadProperty</a></h3>
|
||||
<p>The following example will read AV0.PresentValue from device 1234</p>
|
||||
<pre>
|
||||
my ($res, $failed) = ReadProperty(1234, 'OBJECT_ANALOG_VALUE', 0, 'PROP_PRESENT_VALUE');</pre>
|
||||
<p>
|
||||
</p>
|
||||
<h2><a name="readpropertymultiple">ReadPropertyMultiple</a></h2>
|
||||
<p>This function implements the ReadPropertyMultiple service. There are no built in retry
|
||||
mechanisms. NOTE: all enumerations are defined in <em class="file">bacenum.h</em></p>
|
||||
<p>
|
||||
</p>
|
||||
<h3><a name="inputs_to_readpropertymultiple">Inputs to ReadPropertyMultiple</a></h3>
|
||||
<ul>
|
||||
<li><b>devideInstance</b> - the instance number of the device we are reading</li>
|
||||
<li><b>r_answerList</b> - reference to a list where to store the answers</li>
|
||||
<li><b>list</b> - a list of ReadAccessSpecifications</li>
|
||||
<ul>
|
||||
<li><b>objectType</b> - the enumeration for the object name to read from</li>
|
||||
<li><b>objectInstance</b> - the instance number of the object we are reading</li>
|
||||
<li><b>propertyName</b> - the enumeration for the property name we are reading</li>
|
||||
<li><b>index</b> - the index number we are reading from. Use -1 if not applicable</li>
|
||||
</ul>
|
||||
</ul><p>
|
||||
</p>
|
||||
<h3><a name="outputs_from_readpropertymultiple">Outputs from ReadPropertyMultiple</a></h3>
|
||||
<ul>
|
||||
<li><b>result</b> - the 'QQQ' delimited concatenated sting result (value or error) for ReadPropertyMultiple. The parsed out result is returned in r_answerList</li>
|
||||
<li><b>isFailure</b> - zero means no failure, non-zero means failure</li>
|
||||
</ul><p>
|
||||
</p>
|
||||
<h3><a name="example_of_readpropertymultiple">Example of ReadPropertyMultiple</a></h3>
|
||||
<p>The following example will read AV0.PresentValue and AV1.PresentValue from device 1234</p>
|
||||
<pre>
|
||||
my @RPM_request = ();
|
||||
my @RPM_answer = ();
|
||||
my $failed;
|
||||
push @RPM_request, ['OBJECT_ANALOG_VALUE', 0, 'PROP_PRESENT_VALUE', -1];
|
||||
push @RPM_request, ['OBJECT_ANALOG_VALUE', 1, 'PROP_PRESENT_VALUE', -1];
|
||||
(undef, $failed) = ReadPropertyMultiple(1234, \@RPM_answer, @RPM_request);</pre>
|
||||
<p>
|
||||
</p>
|
||||
<h2><a name="writeproperty">WriteProperty</a></h2>
|
||||
<p>This function implements the WriteProperty service. There are no built in retry
|
||||
mechanisms. NOTE: all enumerations are defined in <em class="file">bacenum.h</em></p>
|
||||
<p>
|
||||
</p>
|
||||
<h3><a name="inputs_to_writeproperty">Inputs to WriteProperty</a></h3>
|
||||
<ul>
|
||||
<li><b>devideInstance</b> - the instance number of the device we are writing</li>
|
||||
<li><b>objectName</b> - the enumeration for the object name we are writing</li>
|
||||
<li><b>objectInstance</b> - the instance number of the object we are writing</li>
|
||||
<li><b>propertyName</b> - the enumeration for the property name we are writing</li>
|
||||
<li><b>tagName</b> - the enumeration for the type of value we are writing. To specify context tags, prepend the tag name with "Cn:" where 'n' is the context number.</li>
|
||||
<li><b>value</b> - the value we are writing</li>
|
||||
<li><b>priority</b> - Optional (default 0): the priority within Priority Array to write at. Use 1-16 when specify priority, 0 to not specify priority.</li>
|
||||
<li><b>index</b> - Optional (default -1): the index within an array we are writing to. Use positive number to indicate index, -1 to not specify index.</li>
|
||||
</ul><p>
|
||||
</p>
|
||||
<h3><a name="outputs_from_writeproperty">Outputs from WriteProperty</a></h3>
|
||||
<ul>
|
||||
<li><b>result</b> - the sting result (value or error) for WriteProperty</li>
|
||||
<li><b>isFailure</b> - zero means no failure, non-zero means failure</li>
|
||||
</ul><p>
|
||||
</p>
|
||||
<h3><a name="example_of_writeproperty">Example of WriteProperty</a></h3>
|
||||
<p>The following example will write 1.0 to AV0.PresentValue in device 1234</p>
|
||||
<pre>
|
||||
my ($res, $failed) = WriteProperty(1234, 'OBJECT_ANALOG_VALUE', 0, 'PROP_PRESENT_VALUE', 'BACNET_APPLICATION_TAG_REAL', 1.0);</pre>
|
||||
<p>
|
||||
</p>
|
||||
<h2><a name="timesync">TimeSync</a></h2>
|
||||
<p>This function implements the TimeSync and UTCTimeSync services</p>
|
||||
<p>
|
||||
</p>
|
||||
<h3><a name="inputs_to_timesync">Inputs to TimeSync</a></h3>
|
||||
<ul>
|
||||
<li><b>deviceInstanceNumber</b> - the instance number of the device we are reading</li>
|
||||
<li><b>year</b> - Year (i.e. 2011)</li>
|
||||
<li><b>month</b> - Month (i.e. 11 for November)</li>
|
||||
<li><b>day</b> - Day (i.e. 1 for first of month)</li>
|
||||
<li><b>hour</b> - Hour (i.e. 23 for 11pm)</li>
|
||||
<li><b>minute</b> - Minute (i.e. 0-59)</li>
|
||||
<li><b>second</b> - Second (i,e. 0-59)</li>
|
||||
<li><b>utcOffset</b> - Optional: if specified defines the UTC offset and forces UTCTimeSync</li>
|
||||
</ul><p>
|
||||
</p>
|
||||
<h3><a name="outputs_from_timesync">Outputs from TimeSync</a></h3>
|
||||
<ul>
|
||||
<li><b>isFailure</b> - zero means no failure, non-zero means failure</li>
|
||||
</ul><p>
|
||||
</p>
|
||||
<h3><a name="example_of_timesync">Example of TimeSync</a></h3>
|
||||
<pre>
|
||||
$isFailure = TimeSync($deviceInstance, $1, $2, $3, $4, $5, $6) unless $isFailure;</pre>
|
||||
<p>
|
||||
</p>
|
||||
<h2><a name="log">Log</a></h2>
|
||||
<p>This function prints out to the desired method of logging (STDOUT or file).
|
||||
NewLine characters are not required when making calls to this function. If any
|
||||
NewLine characters are specified, they will be stripped out. To print an empty
|
||||
line, pass in a space as the message. NOTE: This function will honor previous
|
||||
requests to silence the log (see SilcenseLog for details)</p>
|
||||
<p>
|
||||
</p>
|
||||
<h3><a name="inputs_to_log">Inputs to Log</a></h3>
|
||||
<ul>
|
||||
<li><b>msg</b> - the message to output
|
||||
</ul><p>
|
||||
</p>
|
||||
<h3><a name="example_of_log">Example of Log</a></h3>
|
||||
<p>The following example will print out "hello world"</p>
|
||||
<pre>
|
||||
Log("Hello World");</pre>
|
||||
<p>
|
||||
</p>
|
||||
<h2><a name="silencelog">SilenceLog</a></h2>
|
||||
<p>This function requests that all future log messages be either suppressed or
|
||||
enabled.</p>
|
||||
<p>
|
||||
</p>
|
||||
<h3><a name="inputs_to_silencelog">Inputs to SilenceLog</a></h3>
|
||||
<ul>
|
||||
<li><b>logIsQuiet</b> - zero means print to log, non-zero means supress log
|
||||
</ul><p>
|
||||
</p>
|
||||
<h3><a name="outputs_from_silencelog">Outputs from SilenceLog</a></h3>
|
||||
<p>The previous value of whether or not the log was silenced before caling this
|
||||
function.</p>
|
||||
<p>
|
||||
</p>
|
||||
<h3><a name="example_of_silencelog">Example of SilenceLog</a></h3>
|
||||
<p>The following example will print out "hello", but not "world"</p>
|
||||
<pre>
|
||||
Log("Hello");
|
||||
SilenceLog(1);
|
||||
Log("World");</pre>
|
||||
<p>
|
||||
</p>
|
||||
<h2><a name="retry">Retry</a></h2>
|
||||
<p>This function will try to execute the requested command up to specified number
|
||||
of times, awaiting the requested answer, with a specified pause between
|
||||
retries. NOTE: the only functions which can be executed by this function are
|
||||
ones which return two parameres in the form of ($response, $isFailure)</p>
|
||||
<p>
|
||||
</p>
|
||||
<h3><a name="inputs_to_retry">Inputs to Retry</a></h3>
|
||||
<ul>
|
||||
<li><b>r_func</b> - The reference to the function which is to be retried</li>
|
||||
<li><b>r_funcArgs</b> - A reference to an array of arguments for the function to be executed</li>
|
||||
<li><b>desiredOutput</b> - The condition which will terminate the retrying. Can be either a number or a regexp to patch against the $response return of the function</li>
|
||||
<li><b>maxTries</b> - The maximum number of retry attempts before calling it quits</li>
|
||||
<li><b>sleepSeconds</b> - The number of seconds (could be fractional) to wait between retries</li>
|
||||
</ul><p>
|
||||
</p>
|
||||
<h3><a name="outputs_from_retry">Outputs from Retry</a></h3>
|
||||
<ul>
|
||||
<li><b>$resp</b> - The response from the last execution of requested function</li>
|
||||
<li><b>isFailure</b> - zero means no failure, non-zero means failure</li>
|
||||
</ul><p>
|
||||
</p>
|
||||
<h3><a name="example_of_retry">Example of Retry</a></h3>
|
||||
<p>The following example will execute the ReadProperty function to read a property
|
||||
from an object (see ReadProperty for details on those arguments) with up to
|
||||
$maxRetries retries (with $retryDelay delay between retries) or unitl the
|
||||
desired answer of 42 is received.</p>
|
||||
<pre>
|
||||
my ($resp, $isFailure) = Retry(
|
||||
\&ReadProperty, [$deviceInstance, 'OBJECT_ANALOG_VALUE', 0, 'PROP_PRESENT_VALUE'],
|
||||
42, $maxRetries, $retryDelay
|
||||
);
|
||||
if ($isFailure)
|
||||
{
|
||||
die "Value was not 42. Last response was '$resp'";
|
||||
}</pre>
|
||||
<p>The following example will try to execute a WriteProperty (see that function for
|
||||
details on its arguments) until the write succeeds.</p>
|
||||
<pre>
|
||||
my ($resp, $isFailure) = Retry(
|
||||
\&WriteProperty, [$deviceInstance, 'OBJECT_ANALOG_VALUE', 0, 'PROP_PRESENT_VALUE', 'BACNET_APPLICATION_TAG_REAL', 42.0],
|
||||
"Acknowledged", $maxRetries, $retryDelay
|
||||
);
|
||||
if ($isFailure)
|
||||
{
|
||||
die "Could not write 42. Last response was '$resp'";
|
||||
}</pre>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
+4
File diff suppressed because one or more lines are too long
+75
@@ -0,0 +1,75 @@
|
||||
pre{
|
||||
font-family: "Courier New", Courier, monospace, sans-serif;
|
||||
text-align: left;
|
||||
line-height: 1.6em;
|
||||
font-size: 11px;
|
||||
padding: 0.1em 0.5em 0.3em 0.7em;
|
||||
border: 2px solid #888;
|
||||
margin: 1.7em 0 1.7em 0.3em;
|
||||
overflow: auto;
|
||||
width: 93%;
|
||||
background: #EEEEEE;
|
||||
}
|
||||
h1 {
|
||||
font-size: 20pt;
|
||||
counter-increment: counter-h1;
|
||||
counter-reset: counter-h2;
|
||||
}
|
||||
h2 {
|
||||
font-size: 17pt;
|
||||
counter-increment: counter-h2;
|
||||
counter-reset: counter-h3;
|
||||
}
|
||||
h3 {
|
||||
font-size: 14pt;
|
||||
counter-increment: counter-h3;
|
||||
counter-reset: counter-h4;
|
||||
}
|
||||
h1:before {
|
||||
content: counter(counter-h1) ". ";
|
||||
}
|
||||
h2:before {
|
||||
content: counter(counter-h1) "." counter(counter-h2) ". ";
|
||||
}
|
||||
h3:before {
|
||||
content: counter(counter-h1) "." counter(counter-h2) "." counter(counter-h3) ". ";
|
||||
}
|
||||
ul {
|
||||
list-style-type: circle;
|
||||
}
|
||||
.quotedString
|
||||
{
|
||||
color: #0000FF;
|
||||
}
|
||||
.comment
|
||||
{
|
||||
color: #999999;
|
||||
}
|
||||
.operator
|
||||
{
|
||||
color: #00CCCC;
|
||||
}
|
||||
.builtinVariable
|
||||
{
|
||||
color: #CCCC00;
|
||||
}
|
||||
.variableSpecifier
|
||||
{
|
||||
color: #FF0000;
|
||||
}
|
||||
.keyword
|
||||
{
|
||||
color: #AA0033;
|
||||
}
|
||||
.builtinFunction
|
||||
{
|
||||
color: #AA00AA;
|
||||
}
|
||||
.identifier
|
||||
{
|
||||
color: #009900;
|
||||
}
|
||||
.number
|
||||
{
|
||||
color: #9999FF;
|
||||
}
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
var ie = document.all != null;
|
||||
var moz = !ie && document.getElementById != null && document.layers == null;
|
||||
function emulateHTMLModel()
|
||||
{
|
||||
// copied from http://www.webfx.nu/dhtml/ieemu/htmlmodel.html
|
||||
|
||||
// This function is used to generate a html string for the text properties/methods
|
||||
// It replaces '\n' with "<BR"> as well as fixes consecutive white spaces
|
||||
// It also repalaces some special characters
|
||||
function convertTextToHTML(s) {
|
||||
s = s.replace(/\&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/\n/g, "<BR>").replace(/\t/g, " "); //tachyon
|
||||
while (/\s\s/.test(s))
|
||||
s = s.replace(/\s\s/, " ");
|
||||
return s.replace(/\s/g, " ");
|
||||
}
|
||||
|
||||
|
||||
HTMLElement.prototype.__defineSetter__("innerText", function (sText) {
|
||||
this.innerHTML = convertTextToHTML(sText);
|
||||
return sText;
|
||||
});
|
||||
|
||||
var tmpGet;
|
||||
HTMLElement.prototype.__defineGetter__("innerText", tmpGet = function () {
|
||||
var r = this.ownerDocument.createRange();
|
||||
r.selectNodeContents(this);
|
||||
return r.toString();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
if (moz)
|
||||
emulateHTMLModel();
|
||||
|
||||
|
||||
// Regular Expressions largely copied from Cory Johns (darkness@yossman.net) excellent Syntax::Highlight::Perl module (see http://search.cpan.org/~johnsca/)
|
||||
|
||||
var re;
|
||||
var RE = new Array;
|
||||
|
||||
// quoted string
|
||||
re = /('|"|`).*?\1/;
|
||||
RE[0] = new RegExp(re);
|
||||
|
||||
// comment
|
||||
re = /\#.*?([\r\n]+|$)/; //tachyon
|
||||
RE[1] = new RegExp(re);
|
||||
|
||||
// operator
|
||||
re = /xor|\.\.\.|and|not|\|\|\=|cmp|\>\>\=|\<\<\=|\<\=\>|\&\&\=|or|\=\>|\!\~|\^\=|\&\=|\|\=|\.\=|x\=|\%\=|\/\=|\*\=|\-\=|\+\=|\=\~|\*\*|\-\-|\.\.|\|\||\&\&|\+\+|\-\>|ne|eq|\!\=|\=\=|ge|le|gt|lt|\>\=|\<\=|\>\>|\<\<|\,|\=|\:|\?|\^|\||x|\%|\/|\*|\<|\&|\\|\~|\!|\>|\.|\-|\+ /;
|
||||
RE[2] = new RegExp(re);
|
||||
|
||||
// builtin variables
|
||||
re = /\$\#?_|\$(?:\^[LAECDFHIMOPRSTWX]|[0-9&`'+*.\/|,\\";#%=\-~^:?!@\$<>()\[\]])|\$\#?ARGV(?:\s*\[)?|\$\#?INC\s*\[|\$(?:ENV|SIG|INC)\s*\{|\@(?:_|ARGV|INC)|\%(?:INC|ENV|SIG)/;
|
||||
RE[3] = new RegExp(re);
|
||||
|
||||
// variable class specifiers
|
||||
re = /(?:(?:[\@\%\*]|\$\#?)\$*)/;
|
||||
RE[4] = new RegExp(re);
|
||||
|
||||
// keyword
|
||||
re = /(continue|foreach|require|package|scalar|format|unless|local|until|while|elsif|next|last|goto|else|redo|sub|for|use|no|if|my)\b/;
|
||||
RE[5] = new RegExp(re);
|
||||
|
||||
// builtin function
|
||||
re = /(getprotobynumber|getprotobyname|getservbyname|gethostbyaddr|gethostbyname|getservbyport|getnetbyaddr|getnetbyname|getsockname|getpeername|setpriority|getprotoent|setprotoent|getpriority|endprotoent|getservent|setservent|endservent|sethostent|socketpair|getsockopt|gethostent|endhostent|setsockopt|setnetent|quotemeta|localtime|prototype|getnetent|endnetent|rewinddir|wantarray|getpwuid|closedir|getlogin|readlink|endgrent|getgrgid|getgrnam|shmwrite|shutdown|readline|endpwent|setgrent|readpipe|formline|truncate|dbmclose|syswrite|setpwent|getpwnam|getgrent|getpwent|ucfirst|sysread|setpgrp|shmread|sysseek|sysopen|telldir|defined|opendir|connect|lcfirst|getppid|binmode|syscall|sprintf|getpgrp|readdir|seekdir|waitpid|reverse|unshift|symlink|dbmopen|semget|msgrcv|rename|listen|chroot|msgsnd|shmctl|accept|unpack|exists|fileno|shmget|system|unlink|printf|gmtime|msgctl|semctl|values|rindex|substr|splice|length|msgget|select|socket|return|caller|delete|alarm|ioctl|index|undef|lstat|times|srand|chown|fcntl|close|write|umask|rmdir|study|sleep|chomp|untie|print|utime|mkdir|atan2|split|crypt|flock|chmod|BEGIN|bless|chdir|semop|shift|reset|link|stat|chop|grep|fork|dump|join|open|tell|pipe|exit|glob|warn|each|bind|sort|pack|eval|push|keys|getc|kill|seek|sqrt|send|wait|rand|tied|read|time|exec|recv|eof|chr|int|ord|exp|pos|pop|sin|log|abs|oct|hex|tie|cos|vec|END|ref|map|die|\-C|\-b|\-S|\-u|\-t|\-p|\-l|\-d|\-f|\-g|\-s|\-z|uc|\-k|\-e|\-O|\-T|\-B|\-M|do|\-A|\-X|\-W|\-c|\-R|\-o|\-x|lc|\-w|\-r)\b/;
|
||||
RE[6] = new RegExp(re);
|
||||
|
||||
// identifier (variable, subroutine, packages)
|
||||
re = /(?:(?:[A-Za-z_]|::)(?:\w|::)*)/;
|
||||
RE[7] = new RegExp(re);
|
||||
|
||||
// number
|
||||
re = /0x[\da-fA-F]+|[_.\d]+([eE][-+]?\d+)?/;
|
||||
RE[8] = new RegExp(re);
|
||||
|
||||
|
||||
var classes = new Array("quotedString", "comment", "operator", "builtinVariable", "variableSpecifier", "keyword", "builtinFunction", "identifier", "number");
|
||||
|
||||
|
||||
/* This is the actual highlighting function.
|
||||
* Takes an html object as argument
|
||||
* returns nothing
|
||||
* replaces the text inside the html object with colored text using <span>'s
|
||||
* css is defined separately. See the array classes to find out the css class names.
|
||||
*/
|
||||
function HighlightCode(object)
|
||||
{
|
||||
codeText = object.innerText; //HTML.replace(/<.*?>/g, "");
|
||||
object.innerHTML = '';
|
||||
var left;
|
||||
var match;
|
||||
var right;
|
||||
while (codeText.length > 0)
|
||||
{
|
||||
var mode = -1 ;
|
||||
var index = 999999999;
|
||||
for (var i = 0; i < RE.length; i++)
|
||||
{
|
||||
if ((codeText.match(RE[i])) && (RegExp.leftContext.length < index))
|
||||
{
|
||||
left = RegExp.leftContext;
|
||||
match = RegExp.lastMatch;
|
||||
right = RegExp.rightContext;
|
||||
index = RegExp.leftContext.length;
|
||||
mode = i;
|
||||
}
|
||||
}
|
||||
if (mode == -1)
|
||||
{
|
||||
object.appendChild(document.createTextNode(codeText)); //.replace(/\r\n/g, "\r")));
|
||||
codeText = '';
|
||||
}
|
||||
else
|
||||
{
|
||||
// append the plain text to the <code> block
|
||||
object.appendChild(document.createTextNode(left)); //.replace(/\r\n/g, "\r")));
|
||||
|
||||
// create a new <span> with the current code
|
||||
var span = document.createElement("span");
|
||||
span.setAttribute("className", classes[mode]); // ie
|
||||
span.setAttribute("class", classes[mode]); //mozilla
|
||||
span.appendChild(document.createTextNode(match));
|
||||
object.appendChild(span);
|
||||
|
||||
codeText = right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// little bit of JQuery to highlight code in all pre elements
|
||||
$(document).ready(function(){
|
||||
$("pre").each(function(i){
|
||||
HighlightCode(this);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,869 @@
|
||||
use warnings;
|
||||
use strict;
|
||||
use Getopt::Long;
|
||||
use Convert::Binary::C;
|
||||
use Hash::Util qw/lock_hash/;
|
||||
use English;
|
||||
use Scalar::Util qw/looks_like_number/;
|
||||
use File::Basename;
|
||||
use File::Spec;
|
||||
use Pod::Usage;
|
||||
use Carp;
|
||||
|
||||
=head1 NAME
|
||||
|
||||
bacnet.pl - Scriptable BACnet communications
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This is a tool for scriptable BACnet communication. Users can write their own
|
||||
scripts using standard Perl syntax and API defined in this tool to perform desired
|
||||
execution sequences. For details on this tool's API, see Documentation.html. For other
|
||||
Perl documentation, see http://perldoc.perl.org
|
||||
|
||||
=begin html
|
||||
<link href="syntax.css" rel="stylesheet" type="text/css">
|
||||
<script src="jquery.js"></script>
|
||||
<script src="syntax.js"></script>
|
||||
|
||||
=end html
|
||||
|
||||
=head1 OPTIONS
|
||||
|
||||
Usage: bacnet.pl [program_options] [-- script_args]
|
||||
|
||||
This program executes a script in perl syntax to perform BACnet/IP operations.
|
||||
|
||||
Possible program options:
|
||||
--script=s The script to execute.
|
||||
--log=s The file to log all output.
|
||||
--help This help message.
|
||||
|
||||
Possible environment variables are:
|
||||
BACNET_IFACE - set this value to dotted IP address of the interface (see
|
||||
ipconfig) for which you want to bind. Default is the interface which
|
||||
Windows considers to be the default (how???). Hence, if there is only a
|
||||
single network interface on Windows, the applications will choose it, and
|
||||
this setting will not be needed.
|
||||
BACNET_IP_PORT - UDP/IP port number (0..65534) used for BACnet/IP
|
||||
communications. Default is 47808 (0xBAC0).
|
||||
BACNET_APDU_TIMEOUT - set this value in milliseconds to change the APDU
|
||||
timeout. APDU Timeout is how much time a client waits for a response from
|
||||
a BACnet device.
|
||||
BACNET_BBMD_PORT - UDP/IP port number (0..65534) used for Foreign Device
|
||||
Registration. Defaults to 47808 (0xBAC0).
|
||||
BACNET_BBMD_TIMETOLIVE - number of seconds used in Foreign Device
|
||||
Registration (0..65535). Defaults to 60000 seconds.
|
||||
BACNET_BBMD_ADDRESS - dotted IPv4 address of the BBMD or Foreign Device
|
||||
Registrar.
|
||||
|
||||
=cut
|
||||
|
||||
############################################
|
||||
# Steps to prepare for execution
|
||||
############################################
|
||||
|
||||
# This is the relative path to get to the base directory cotaining the BACnet
|
||||
# Stack sources from the directory containing this file and the directory
|
||||
# within which InlineC code is built. The reason for delaring it here and
|
||||
# setting the value in a BEGIN block is so that the variable gets its value at
|
||||
# compile time before Inline::C tries to use that variable.
|
||||
my $relSourcePath;
|
||||
my $inlineCFile;
|
||||
my $inlineBuildDir;
|
||||
my $libDir;
|
||||
my $incDir1;
|
||||
my $incDir2;
|
||||
my $incDir3;
|
||||
BEGIN {
|
||||
# the Perl source file is in the same directory as in the InlineC file
|
||||
# this path should not contain any spaces
|
||||
$relSourcePath = File::Spec->rel2abs(dirname($0));
|
||||
die "Install path must not have spaces.\n" if $relSourcePath =~ /\s/;
|
||||
my @dirs = ();
|
||||
push @dirs, $relSourcePath;
|
||||
$inlineCFile = File::Spec->catfile(@dirs, "perl_bindings.c");
|
||||
|
||||
# all Inline C sources shall be contained in ./.Inline
|
||||
push @dirs, ".Inline";
|
||||
$inlineBuildDir = File::Spec->catdir(@dirs);
|
||||
pop @dirs;
|
||||
|
||||
# to properly link, need to reference ./../../lib
|
||||
push @dirs, "..";
|
||||
push @dirs, "..";
|
||||
push @dirs, "lib";
|
||||
$libDir = File::Spec->catdir(@dirs);
|
||||
pop @dirs;
|
||||
|
||||
# to properly build, need to reference ./../../include
|
||||
push @dirs, "include";
|
||||
$incDir1 = File::Spec->catdir(@dirs);
|
||||
pop @dirs;
|
||||
|
||||
# we will use the demo handlers, need to reference ./../../demo/object
|
||||
push @dirs, "demo";
|
||||
push @dirs, "object";
|
||||
$incDir2 = File::Spec->catdir(@dirs);
|
||||
pop @dirs;
|
||||
pop @dirs;
|
||||
|
||||
# TODO: This should be done in a more universal way
|
||||
# to properly build Win32 ports, need to refrence ./../../ports/win32
|
||||
push @dirs, "ports";
|
||||
push @dirs, "win32";
|
||||
$incDir3 = File::Spec->catdir(@dirs);
|
||||
}
|
||||
|
||||
use Inline (
|
||||
C => Config =>
|
||||
LIBS => "-L$libDir -lbacnet -liphlpapi",
|
||||
INC => ["-I$incDir1", "-I$incDir2", "-I$incDir3"],
|
||||
DIRECTORY => $inlineBuildDir,
|
||||
);
|
||||
|
||||
# this is the C source file for interfacing to the library. Yes, this could be
|
||||
# done natively in Perl, but this is just as easy (and probably faster to
|
||||
# execute).
|
||||
use Inline C => "$inlineCFile";
|
||||
|
||||
|
||||
my $ask_help = 0;
|
||||
my $script;
|
||||
my $log;
|
||||
my $logTo = \*STDOUT;
|
||||
my $logIndent = 0;
|
||||
my $logIsQuiet = 0;
|
||||
my $errorMsg;
|
||||
my $answer = '';
|
||||
|
||||
($ask_help = 1) unless GetOptions(
|
||||
'help|?' => \$ask_help,
|
||||
'script=s' => \$script,
|
||||
'log=s' => \$log,
|
||||
);
|
||||
|
||||
if (!defined($script) || !(-f $script))
|
||||
{
|
||||
print "Bad or no script file scpecified.\n";
|
||||
$ask_help = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
# Add the script's location to @INC so that they can include other scripts
|
||||
# using relative paths
|
||||
my $scriptdir = File::Spec->rel2abs(dirname($script));
|
||||
push @INC,$scriptdir;
|
||||
}
|
||||
|
||||
if ($ask_help) {
|
||||
print "============================\n\n";
|
||||
pod2usage(
|
||||
-exitval => 0,
|
||||
-verbose => 99,
|
||||
-sections => "NAME|DESCRIPTION|OPTIONS"
|
||||
);
|
||||
}
|
||||
|
||||
if (defined($log))
|
||||
{
|
||||
open(LOG, ">$log") || croak "Cannot open $log for writing: $!\n";
|
||||
$logTo = \*LOG;
|
||||
}
|
||||
|
||||
# Pull in the BACnet enumerations from the C header file
|
||||
my %C_ENUMS;
|
||||
eval {
|
||||
my $pwd = File::Spec->rel2abs(File::Spec->curdir());
|
||||
|
||||
# let's get into the directory so that we can pull in the bacnet enumerations
|
||||
my @dirs = ();
|
||||
push @dirs, dirname($0);
|
||||
push @dirs, "../../include";
|
||||
chdir(File::Spec->catdir(@dirs));
|
||||
my $c = Convert::Binary::C->new->parse_file('bacenum.h');
|
||||
foreach my $typedef ($c->typedef)
|
||||
{
|
||||
if (ref($$typedef{type}) eq "HASH")
|
||||
{
|
||||
my $enumeration = \%{$C_ENUMS{$$typedef{declarator}}};
|
||||
foreach my $enum_name (keys %{$$typedef{type}{enumerators}})
|
||||
{
|
||||
${$C_ENUMS{$$typedef{declarator}}}{$enum_name} = ${$$typedef{type}{enumerators}}{$enum_name};
|
||||
}
|
||||
}
|
||||
}
|
||||
lock_hash(%C_ENUMS);
|
||||
chdir($pwd);
|
||||
};
|
||||
if ($EVAL_ERROR)
|
||||
{
|
||||
croak "Error pulling in the enumerations. $@\n";
|
||||
}
|
||||
|
||||
# Prepare things for communication
|
||||
BacnetPrepareComm();
|
||||
|
||||
# Execute the user specified script
|
||||
Log("Executing $script - start time " . scalar(localtime(time())) );
|
||||
unless (my $return = do $script)
|
||||
{
|
||||
croak "could not parse $script: $@" if $@;
|
||||
croak "could not pull in $script: $!" unless defined $return;
|
||||
croak "could not execute $script" unless $return;
|
||||
}
|
||||
Log("Finished executing $script - end time " . scalar(localtime(time())) );
|
||||
|
||||
=head1 This tool's API
|
||||
|
||||
In addition to having all standard Perl flow control, functions, and modules,
|
||||
the this tool provides an API for performing BACnet communication functions.
|
||||
|
||||
=cut
|
||||
|
||||
##########################################
|
||||
# This block is the external API
|
||||
##########################################
|
||||
|
||||
=head2 ReadProperty
|
||||
|
||||
This function implements the ReadProperty service. There are no built in retry
|
||||
mechanisms. NOTE: all enumerations are defined in F<bacenum.h>
|
||||
|
||||
=head3 Inputs to ReadProperty
|
||||
|
||||
=begin html
|
||||
<ul>
|
||||
<li><b>devideInstance</b> - the instance number of the device we are reading</li>
|
||||
<li><b>objectName</b> - the enumeration for the object name we are reading</li>
|
||||
<li><b>objectInstance</b> - the instance number of the object we are reading</li>
|
||||
<li><b>propertyName</b> - the enumeration for the property name we are reading</li>
|
||||
<li><b>index</b> - Optional (default -1): the index number we are reading from. -1 if not applicable</li>
|
||||
</ul>
|
||||
|
||||
=end html
|
||||
|
||||
=head3 Outputs from ReadProperty
|
||||
|
||||
=begin html
|
||||
<ul>
|
||||
<li><b>result</b> - the sting result (value or error) for ReadProperty</li>
|
||||
<li><b>isFailure</b> - zero means no failure, non-zero means failure</li>
|
||||
</ul>
|
||||
|
||||
=end html
|
||||
|
||||
=head3 Example of ReadProperty
|
||||
|
||||
The following example will read AV0.PresentValue from device 1234
|
||||
|
||||
my ($res, $failed) = ReadProperty(1234, 'OBJECT_ANALOG_VALUE', 0, 'PROP_PRESENT_VALUE');
|
||||
|
||||
=cut
|
||||
|
||||
sub ReadProperty {
|
||||
my $deviceInstance = shift;
|
||||
my $objectName = shift;
|
||||
my $objectInstance = shift;
|
||||
my $propertyName = shift;
|
||||
my $index = shift;
|
||||
my $isFailure = BindToDevice($deviceInstance);
|
||||
|
||||
# Loop for early exit
|
||||
while(1)
|
||||
{
|
||||
last if $isFailure;
|
||||
|
||||
my ($objectPrintName, $objectValue) = LookupEnumValue('BACNET_OBJECT_TYPE', $objectName);
|
||||
my ($propertyPrintName, $propertyValue) = LookupEnumValue('BACNET_PROPERTY_ID', $propertyName);
|
||||
|
||||
my $msg = "ReadProperty $objectPrintName" . '[' . $objectInstance . "].$propertyPrintName";
|
||||
if (defined($index))
|
||||
{
|
||||
$msg .= ".$index";
|
||||
} else {
|
||||
$index = -1;
|
||||
}
|
||||
$msg .= " from Device" . '[' . $deviceInstance . "] ==> ";
|
||||
|
||||
LogAnswer('', 0);
|
||||
if ( BacnetReadProperty($deviceInstance, $objectValue, $objectInstance, $propertyValue, $index) )
|
||||
{
|
||||
BacnetGetError($errorMsg);
|
||||
$msg .= "Problem: $errorMsg";
|
||||
$isFailure = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
$msg .= $answer;
|
||||
$isFailure = 0;
|
||||
}
|
||||
Log($msg);
|
||||
last;
|
||||
}
|
||||
|
||||
return ($answer, $isFailure);
|
||||
}
|
||||
|
||||
=head2 ReadPropertyMultiple
|
||||
|
||||
This function implements the ReadPropertyMultiple service. There are no built in retry
|
||||
mechanisms. NOTE: all enumerations are defined in F<bacenum.h>
|
||||
|
||||
=head3 Inputs to ReadPropertyMultiple
|
||||
|
||||
=begin html
|
||||
<ul>
|
||||
<li><b>devideInstance</b> - the instance number of the device we are reading</li>
|
||||
<li><b>r_answerList</b> - reference to a list where to store the answers</li>
|
||||
<li><b>list</b> - a list of ReadAccessSpecifications</li>
|
||||
<ul>
|
||||
<li><b>objectType</b> - the enumeration for the object name to read from</li>
|
||||
<li><b>objectInstance</b> - the instance number of the object we are reading</li>
|
||||
<li><b>propertyName</b> - the enumeration for the property name we are reading</li>
|
||||
<li><b>index</b> - the index number we are reading from. Use -1 if not applicable</li>
|
||||
</ul>
|
||||
</ul>
|
||||
|
||||
=end html
|
||||
|
||||
=head3 Outputs from ReadPropertyMultiple
|
||||
|
||||
=begin html
|
||||
<ul>
|
||||
<li><b>result</b> - the 'QQQ' delimited concatenated sting result (value or error) for ReadPropertyMultiple. The parsed out result is returned in r_answerList</li>
|
||||
<li><b>isFailure</b> - zero means no failure, non-zero means failure</li>
|
||||
</ul>
|
||||
|
||||
=end html
|
||||
|
||||
=head3 Example of ReadPropertyMultiple
|
||||
|
||||
The following example will read AV0.PresentValue and AV1.PresentValue from device 1234
|
||||
|
||||
my @RPM_request = ();
|
||||
my @RPM_answer = ();
|
||||
my $failed;
|
||||
push @RPM_request, ['OBJECT_ANALOG_VALUE', 0, 'PROP_PRESENT_VALUE', -1];
|
||||
push @RPM_request, ['OBJECT_ANALOG_VALUE', 1, 'PROP_PRESENT_VALUE', -1];
|
||||
(undef, $failed) = ReadPropertyMultiple(1234, \@RPM_answer, @RPM_request);
|
||||
|
||||
=cut
|
||||
|
||||
sub ReadPropertyMultiple
|
||||
{
|
||||
my $deviceInstanceNumber = shift;
|
||||
my $r_answerList = shift;
|
||||
my @list = @ARG;
|
||||
my @modifiedList = ();
|
||||
my $msg = '';
|
||||
my $isFailure = BindToDevice($deviceInstanceNumber);
|
||||
|
||||
# loop for early exit
|
||||
while(1)
|
||||
{
|
||||
last if $isFailure;
|
||||
|
||||
Log("ReadPropertyMultiple:");
|
||||
$logIndent += 4;
|
||||
|
||||
foreach my $r_prop (@list)
|
||||
{
|
||||
my @tmpList = ();
|
||||
push @tmpList, $$r_prop[$_] for (0 .. 3);
|
||||
(undef, $tmpList[0]) = LookupEnumValue('BACNET_OBJECT_TYPE', $$r_prop[0]);
|
||||
(undef, $tmpList[2]) = LookupEnumValue('BACNET_PROPERTY_ID', $$r_prop[2]);
|
||||
push @modifiedList, \@tmpList;
|
||||
}
|
||||
|
||||
LogAnswer('', 0);
|
||||
@{$r_answerList} = ();
|
||||
if (BacnetReadPropertyMultiple($deviceInstanceNumber, @modifiedList))
|
||||
{
|
||||
BacnetGetError($errorMsg);
|
||||
Log("Problem: $errorMsg");
|
||||
$isFailure = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
my $i = 0;
|
||||
foreach (split('QQQ', $answer))
|
||||
{
|
||||
my ($objectPrintName, undef) = LookupEnumValue('BACNET_OBJECT_TYPE', $list[$i][0]);
|
||||
my ($propertyPrintName, undef) = LookupEnumValue('BACNET_PROPERTY_ID', $list[$i][2]);
|
||||
my $msg = $objectPrintName . '.[' . $list[$i][1] . '].' . $propertyPrintName;
|
||||
if ($list[$i][3] != -1)
|
||||
{
|
||||
$msg .= '.[' . $list[$i][3] . ']';
|
||||
}
|
||||
$msg .= " ==> $_";
|
||||
Log($msg);
|
||||
push @{$r_answerList}, $_;
|
||||
$i++;
|
||||
}
|
||||
$isFailure = 0;
|
||||
}
|
||||
|
||||
$logIndent -= 4;
|
||||
last;
|
||||
}
|
||||
|
||||
return ($answer, $isFailure);
|
||||
}
|
||||
|
||||
=head2 WriteProperty
|
||||
|
||||
This function implements the WriteProperty service. There are no built in retry
|
||||
mechanisms. NOTE: all enumerations are defined in F<bacenum.h>
|
||||
|
||||
=head3 Inputs to WriteProperty
|
||||
|
||||
=begin html
|
||||
<ul>
|
||||
<li><b>devideInstance</b> - the instance number of the device we are writing</li>
|
||||
<li><b>objectName</b> - the enumeration for the object name we are writing</li>
|
||||
<li><b>objectInstance</b> - the instance number of the object we are writing</li>
|
||||
<li><b>propertyName</b> - the enumeration for the property name we are writing</li>
|
||||
<li><b>tagName</b> - the enumeration for the type of value we are writing. To specify context tags, prepend the tag name with "Cn:" where 'n' is the context number.</li>
|
||||
<li><b>value</b> - the value we are writing</li>
|
||||
<li><b>priority</b> - Optional (default 0): the priority within Priority Array to write at. Use 1-16 when specify priority, 0 to not specify priority.</li>
|
||||
<li><b>index</b> - Optional (default -1): the index within an array we are writing to. Use positive number to indicate index, -1 to not specify index.</li>
|
||||
</ul>
|
||||
|
||||
=end html
|
||||
|
||||
=head3 Outputs from WriteProperty
|
||||
|
||||
=begin html
|
||||
<ul>
|
||||
<li><b>result</b> - the sting result (value or error) for WriteProperty</li>
|
||||
<li><b>isFailure</b> - zero means no failure, non-zero means failure</li>
|
||||
</ul>
|
||||
|
||||
=end html
|
||||
|
||||
=head3 Example of WriteProperty
|
||||
|
||||
The following example will write 1.0 to AV0.PresentValue in device 1234
|
||||
|
||||
my ($res, $failed) = WriteProperty(1234, 'OBJECT_ANALOG_VALUE', 0, 'PROP_PRESENT_VALUE', 'BACNET_APPLICATION_TAG_REAL', 1.0);
|
||||
|
||||
=cut
|
||||
|
||||
sub WriteProperty {
|
||||
my $deviceInstance = shift;
|
||||
my $objectName = shift;
|
||||
my $objectInstance = shift;
|
||||
my $propertyName = shift;
|
||||
my $tagName = shift;
|
||||
my $value = shift;
|
||||
my $priority = shift;
|
||||
my $index = shift;
|
||||
my $isFailure = BindToDevice($deviceInstance);
|
||||
|
||||
# loop for early exit
|
||||
while(1)
|
||||
{
|
||||
last if $isFailure;
|
||||
|
||||
my ($objectPrintName, $objectValue) = LookupEnumValue('BACNET_OBJECT_TYPE', $objectName);
|
||||
my ($propertyPrintName, $propertyValue) = LookupEnumValue('BACNET_PROPERTY_ID', $propertyName);
|
||||
|
||||
my $tagValue = '';
|
||||
if ($tagName =~ /^(C\d+):(.*)$/)
|
||||
{
|
||||
$tagName = $2;
|
||||
$tagValue = "$1 ";
|
||||
}
|
||||
my ($tagPrintName, $tagNewValue) = LookupEnumValue('BACNET_APPLICATION_TAG', $tagName);
|
||||
$tagValue .= $tagNewValue;
|
||||
|
||||
my $msg = "WriteProperty $tagPrintName:$value to $objectPrintName" . '[' . $objectInstance . "].$propertyPrintName";
|
||||
if (defined($index))
|
||||
{
|
||||
$msg .= '[' . $index . ']';
|
||||
}
|
||||
else
|
||||
{
|
||||
# an index of -1 means that we are not writing to an array
|
||||
$index = -1;
|
||||
}
|
||||
if (defined($priority))
|
||||
{
|
||||
$msg .= '@' . $priority
|
||||
}
|
||||
else
|
||||
{
|
||||
# a priority of 0 means we are not writing to a priority array
|
||||
$priority = 0;
|
||||
}
|
||||
$msg .= " in Device" . '[' . $deviceInstance . "] ==> ";
|
||||
|
||||
LogAnswer('', 0);
|
||||
if ( BacnetWriteProperty($deviceInstance, $objectValue, $objectInstance, $propertyValue, $priority, $index, $tagValue, $value) )
|
||||
{
|
||||
BacnetGetError($errorMsg);
|
||||
$msg .= "Problem: $errorMsg\n";
|
||||
$isFailure = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
$msg .= $answer;
|
||||
$isFailure = 0;
|
||||
}
|
||||
Log($msg);
|
||||
last;
|
||||
}
|
||||
|
||||
return ($answer, $isFailure);
|
||||
}
|
||||
|
||||
=head2 TimeSync
|
||||
|
||||
This function implements the TimeSync and UTCTimeSync services
|
||||
|
||||
=head3 Inputs to TimeSync
|
||||
|
||||
=begin html
|
||||
<ul>
|
||||
<li><b>deviceInstanceNumber</b> - the instance number of the device we are reading</li>
|
||||
<li><b>year</b> - Year (i.e. 2011)</li>
|
||||
<li><b>month</b> - Month (i.e. 11 for November)</li>
|
||||
<li><b>day</b> - Day (i.e. 1 for first of month)</li>
|
||||
<li><b>hour</b> - Hour (i.e. 23 for 11pm)</li>
|
||||
<li><b>minute</b> - Minute (i.e. 0-59)</li>
|
||||
<li><b>second</b> - Second (i,e. 0-59)</li>
|
||||
<li><b>utcOffset</b> - Optional: if specified defines the UTC offset and forces UTCTimeSync</li>
|
||||
</ul>
|
||||
|
||||
=end html
|
||||
|
||||
=head3 Outputs from TimeSync
|
||||
|
||||
=begin html
|
||||
<ul>
|
||||
<li><b>isFailure</b> - zero means no failure, non-zero means failure</li>
|
||||
</ul>
|
||||
|
||||
=end html
|
||||
|
||||
=head3 Example of TimeSync
|
||||
|
||||
$isFailure = TimeSync($deviceInstance, $1, $2, $3, $4, $5, $6) unless $isFailure;
|
||||
|
||||
=cut
|
||||
|
||||
sub TimeSync
|
||||
{
|
||||
my $deviceInstanceNumber = shift;
|
||||
my $year = shift;
|
||||
my $month = shift;
|
||||
my $day = shift;
|
||||
my $hour = shift;
|
||||
my $minute = shift;
|
||||
my $second = shift;
|
||||
my $utcOffset = shift;
|
||||
my $isUTC;
|
||||
|
||||
my $isFailure = BindToDevice($deviceInstanceNumber);
|
||||
|
||||
# loop for early exit
|
||||
while(1)
|
||||
{
|
||||
last if $isFailure;
|
||||
|
||||
# be a pessimist. Assume things will fail
|
||||
$isFailure = 1;
|
||||
|
||||
if (defined($utcOffset))
|
||||
{
|
||||
$isUTC = 1;
|
||||
Log("UTC Time Sync not yet supported.");
|
||||
last;
|
||||
}
|
||||
else
|
||||
{
|
||||
$utcOffset = 0;
|
||||
$isUTC = 0;
|
||||
}
|
||||
|
||||
if ($year < 1900 || $year > 2099)
|
||||
{
|
||||
Log("Year '$year' is invalid.");
|
||||
last;
|
||||
}
|
||||
|
||||
if ($month <= 0 || $month > 12)
|
||||
{
|
||||
Log("Month '$month' is invalid.");
|
||||
last;
|
||||
}
|
||||
|
||||
if ($day <= 0 || $day > 31)
|
||||
{
|
||||
Log("Day '$day' is invalid.");
|
||||
last;
|
||||
}
|
||||
|
||||
if ($hour < 0 || $hour > 23)
|
||||
{
|
||||
Log("Hour '$hour' is invalid.");
|
||||
last;
|
||||
}
|
||||
|
||||
if ($minute < 0 || $minute > 59)
|
||||
{
|
||||
Log("Minute '$minute' is invalid.");
|
||||
last;
|
||||
}
|
||||
|
||||
if ($second < 0 || $second > 59)
|
||||
{
|
||||
Log("Second '$second' is invalid.");
|
||||
last;
|
||||
}
|
||||
|
||||
Log("TimeSync: Device[$deviceInstanceNumber] $year/$month/$day $hour:$minute:$second");
|
||||
|
||||
$isFailure = BacnetTimeSync($deviceInstanceNumber, $year, $month, $day, $hour, $minute, $second, $isUTC, $utcOffset);
|
||||
last;
|
||||
}
|
||||
|
||||
return $isFailure;
|
||||
}
|
||||
|
||||
=head2 Log
|
||||
|
||||
This function prints out to the desired method of logging (STDOUT or file).
|
||||
NewLine characters are not required when making calls to this function. If any
|
||||
NewLine characters are specified, they will be stripped out. To print an empty
|
||||
line, pass in a space as the message. NOTE: This function will honor previous
|
||||
requests to silence the log (see SilcenseLog for details)
|
||||
|
||||
=head3 Inputs to Log
|
||||
|
||||
=begin html
|
||||
<ul>
|
||||
<li><b>msg</b> - the message to output
|
||||
</ul>
|
||||
|
||||
=end html
|
||||
|
||||
=head3 Example of Log
|
||||
|
||||
The following example will print out "hello world"
|
||||
|
||||
Log("Hello World");
|
||||
|
||||
=cut
|
||||
|
||||
###############################################################################
|
||||
# Global Variables affecting this function
|
||||
# logIsQuiet do not print anytihng if the log was qieted
|
||||
# logIndent how many spaces to put in front of every logged line
|
||||
###############################################################################
|
||||
sub Log {
|
||||
my $msg = shift;
|
||||
|
||||
if (defined($msg) && !$logIsQuiet)
|
||||
{
|
||||
my @last = split('', substr($msg, -2));
|
||||
|
||||
# if there is nothing to print, then don't do it
|
||||
return if (scalar(@last) == 0);
|
||||
|
||||
# if there are newline-like characters, get rid of them.
|
||||
while ($msg =~/^(.*)[\r\n]+(.*)$/)
|
||||
{
|
||||
$msg = $1 . $2;
|
||||
}
|
||||
|
||||
local $OUTPUT_RECORD_SEPARATOR = "\n";
|
||||
print $logTo ' ' x $logIndent . $msg;
|
||||
}
|
||||
}
|
||||
|
||||
=head2 SilenceLog
|
||||
|
||||
This function requests that all future log messages be either suppressed or
|
||||
enabled.
|
||||
|
||||
=head3 Inputs to SilenceLog
|
||||
|
||||
=begin html
|
||||
<ul>
|
||||
<li><b>logIsQuiet</b> - zero means print to log, non-zero means supress log
|
||||
</ul>
|
||||
|
||||
=end html
|
||||
|
||||
=head3 Outputs from SilenceLog
|
||||
|
||||
The previous value of whether or not the log was silenced before caling this
|
||||
function.
|
||||
|
||||
=head3 Example of SilenceLog
|
||||
|
||||
The following example will print out "hello", but not "world"
|
||||
|
||||
Log("Hello");
|
||||
SilenceLog(1);
|
||||
Log("World");
|
||||
|
||||
=cut
|
||||
|
||||
sub SilenceLog {
|
||||
my $prevValue = $logIsQuiet;
|
||||
$logIsQuiet = shift;
|
||||
return $prevValue;
|
||||
}
|
||||
|
||||
=head2 Retry
|
||||
|
||||
This function will try to execute the requested command up to specified number
|
||||
of times, awaiting the requested answer, with a specified pause between
|
||||
retries. NOTE: the only functions which can be executed by this function are
|
||||
ones which return two parameres in the form of ($response, $isFailure)
|
||||
|
||||
=head3 Inputs to Retry
|
||||
|
||||
=begin html
|
||||
<ul>
|
||||
<li><b>r_func</b> - The reference to the function which is to be retried</li>
|
||||
<li><b>r_funcArgs</b> - A reference to an array of arguments for the function to be executed</li>
|
||||
<li><b>desiredOutput</b> - The condition which will terminate the retrying. Can be either a number or a regexp to patch against the $response return of the function</li>
|
||||
<li><b>maxTries</b> - The maximum number of retry attempts before calling it quits</li>
|
||||
<li><b>sleepSeconds</b> - The number of seconds (could be fractional) to wait between retries</li>
|
||||
</ul>
|
||||
|
||||
=end html
|
||||
|
||||
=head3 Outputs from Retry
|
||||
|
||||
=begin html
|
||||
<ul>
|
||||
<li><b>$resp</b> - The response from the last execution of requested function</li>
|
||||
<li><b>isFailure</b> - zero means no failure, non-zero means failure</li>
|
||||
</ul>
|
||||
|
||||
=end html
|
||||
|
||||
=head3 Example of Retry
|
||||
|
||||
The following example will execute the ReadProperty function to read a property
|
||||
from an object (see ReadProperty for details on those arguments) with up to
|
||||
$maxRetries retries (with $retryDelay delay between retries) or unitl the
|
||||
desired answer of 42 is received.
|
||||
|
||||
my ($resp, $isFailure) = Retry(
|
||||
\&ReadProperty, [$deviceInstance, 'OBJECT_ANALOG_VALUE', 0, 'PROP_PRESENT_VALUE'],
|
||||
42, $maxRetries, $retryDelay
|
||||
);
|
||||
if ($isFailure)
|
||||
{
|
||||
die "Value was not 42. Last response was '$resp'";
|
||||
}
|
||||
|
||||
The following example will try to execute a WriteProperty (see that function for
|
||||
details on its arguments) until the write succeeds.
|
||||
|
||||
my ($resp, $isFailure) = Retry(
|
||||
\&WriteProperty, [$deviceInstance, 'OBJECT_ANALOG_VALUE', 0, 'PROP_PRESENT_VALUE', 'BACNET_APPLICATION_TAG_REAL', 42.0],
|
||||
"Acknowledged", $maxRetries, $retryDelay
|
||||
);
|
||||
if ($isFailure)
|
||||
{
|
||||
die "Could not write 42. Last response was '$resp'";
|
||||
}
|
||||
|
||||
=cut
|
||||
sub Retry {
|
||||
my $r_func = shift;
|
||||
my $r_funcArgs = shift;
|
||||
my $desiredOutput = shift;
|
||||
my $maxTries = shift;
|
||||
my $sleepSeconds = shift;
|
||||
|
||||
my ($resp, $failed);
|
||||
|
||||
my $i;
|
||||
for ($i=0; $i<$maxTries; $i++)
|
||||
{
|
||||
($resp, $failed) = &{$r_func}(@{$r_funcArgs});
|
||||
unless ($failed)
|
||||
{
|
||||
if (looks_like_number($desiredOutput))
|
||||
{
|
||||
last if (looks_like_number($resp) && ($resp == $desiredOutput));
|
||||
}
|
||||
else
|
||||
{
|
||||
last if ($resp =~ /$desiredOutput/);
|
||||
}
|
||||
}
|
||||
select(undef, undef, undef, $sleepSeconds);
|
||||
}
|
||||
|
||||
return ($resp, ($i == $maxTries));
|
||||
}
|
||||
|
||||
|
||||
##########################################
|
||||
# These are the supporting functions
|
||||
##########################################
|
||||
|
||||
sub LookupEnumValue {
|
||||
my $enumType = shift;
|
||||
my $enumName = shift;
|
||||
my $printName;
|
||||
|
||||
if (!exists($C_ENUMS{$enumType}{$enumName}))
|
||||
{
|
||||
print "Requested enumeration '$enumName' does not exist within '$enumType'.\n";
|
||||
exit -1;
|
||||
}
|
||||
|
||||
# lookup the value
|
||||
my $value = $C_ENUMS{$enumType}{$enumName};
|
||||
|
||||
# reformat the OBJECT name style
|
||||
my %reformat = (
|
||||
'BACNET_PROPERTY_ID' => 'PROP',
|
||||
'BACNET_OBJECT_TYPE' => 'OBJECT',
|
||||
'BACNET_APPLICATION_TAG' => 'BACNET_APPLICATION_TAG',
|
||||
);
|
||||
|
||||
if (exists($reformat{$enumType}))
|
||||
{
|
||||
if ($enumName =~ /$reformat{$enumType}_(.*)/)
|
||||
{
|
||||
$printName = '';
|
||||
$printName .= ucfirst lc $_ foreach (split('_', $1));
|
||||
}
|
||||
}
|
||||
|
||||
return ($printName, $value);
|
||||
}
|
||||
|
||||
sub BindToDevice {
|
||||
my $deviceInstance = shift;
|
||||
my $isFailure = 0;
|
||||
|
||||
if ( BacnetBindToDevice($deviceInstance) )
|
||||
{
|
||||
BacnetGetError($errorMsg);
|
||||
Log("Problem binding to deivce $deviceInstance: $errorMsg\n");
|
||||
$isFailure = 1;
|
||||
}
|
||||
|
||||
return $isFailure;
|
||||
}
|
||||
|
||||
sub LogAnswer {
|
||||
my $newAnswer = shift;
|
||||
my $append = shift;
|
||||
|
||||
$answer = '' unless $append;
|
||||
$answer .= $newAnswer;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
use warnings;
|
||||
use strict;
|
||||
|
||||
if (scalar(@ARGV) == 1)
|
||||
{
|
||||
my $device = $ARGV[0];
|
||||
my ($resp, $failed) = ReadProperty($device, 'OBJECT_ANALOG_VALUE', 0, 'PROP_PRESENT_VALUE');
|
||||
|
||||
print "status was '$failed' and the response was '$resp'\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
print "Must specify device instance number as an argument to this script\n";
|
||||
}
|
||||
|
||||
1;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,14 @@
|
||||
The BACnet Scriptable (using Perl) Tool.
|
||||
|
||||
* Running this tool assumes that the library has been already built. Currently,
|
||||
the tool assumes only win32 port, but should be easily modifiable for any
|
||||
port build. The library has to be built with BBMD_DEFINE=-DBBMD_ENABLED\=1
|
||||
* This tool has to be run from a path without any spaces. The presence of the
|
||||
.Inline directory is required.
|
||||
* Run the tool without any arguments to see usage instructions
|
||||
* To run the example ReapProperty script (which reads Analog Value 0 Present
|
||||
Value) for Device at instance 1234 run the following command
|
||||
|
||||
perl bacnet.pl --script example_readprop.pl -- 1234
|
||||
|
||||
|
||||
Reference in New Issue
Block a user