代码拉取完成,页面将自动刷新
<!--
MIT License Copyright 2021, 2022 - Bitpool Pty Ltd
-->
<!-- PrimeVue -->
<link href="resources/@bitpoolos/edge-bacnet/primevue-saga-blue-theme.css" rel="stylesheet" />
<link href="resources/@bitpoolos/edge-bacnet/primevue.min.css" rel="stylesheet" />
<link href="resources/@bitpoolos/edge-bacnet/primeflex.min.css" rel="stylesheet" />
<link href="resources/@bitpoolos/edge-bacnet/primeicons.css" rel="stylesheet" />
<script>
//custom script loader to ensure dependencies load every time
(function () {
LoadScripts();
function LoadScripts(async) {
if (async === undefined) {
async = false;
}
var scripts = [];
var _scripts = [
"resources/@bitpoolos/edge-bacnet/vue.global.prod.js",
"resources/@bitpoolos/edge-bacnet/core.min.js",
"resources/@bitpoolos/edge-bacnet/confirmdialog.min.js",
"resources/@bitpoolos/edge-bacnet/confirmationservice.min.js",
];
if (async) {
LoadScriptsAsync(_scripts, scripts);
} else {
LoadScriptsSync(_scripts, scripts);
}
}
// what you are looking for :
function LoadScriptsSync(_scripts, scripts) {
var x = 0;
var loopArray = function (_scripts, scripts) {
// call itself
loadScript(_scripts[x], scripts[x], function () {
// set x to next item
x++;
// any more items in array?
if (x < _scripts.length) {
loopArray(_scripts, scripts);
}
});
};
loopArray(_scripts, scripts);
}
// async load as in your code
function LoadScriptsAsync(_scripts, scripts) {
for (var i = 0; i < _scripts.length; i++) {
loadScript(_scripts[i], scripts[i], function () {});
}
}
// load script function with callback to handle synchronicity
function loadScript(src, script, callback) {
script = document.createElement("script");
script.onerror = function () {
// handling error when loading script
console.log("Error - could not load BACnet node HTML dependencies");
};
script.onload = function () {
callback();
};
script.src = src;
document.getElementsByTagName("head")[0].appendChild(script);
}
})();
</script>
<script type="text/javascript">
class NodeService {
getNetworkData() {
return fetch("/bitpool-bacnet-data/getNetworkTree").then((res) => res.json());
}
rebuildDataModel() {
return fetch("/bitpool-bacnet-data/rebuildDataModel").then((res) => res.json());
}
clearBacnetServerPoints() {
return fetch("/bitpool-bacnet-data/clearBacnetServerPoints").then((res) => res.json());
}
getBacnetServerPoints() {
return fetch('/bitpool-bacnet-data/getBacnetServerPoints').then(res => res.json());
}
}
RED.nodes.registerType("Bacnet-Gateway", {
category: "Bitpool BACnet",
color: "#00aeef",
defaults: {
name: { value: "" },
local_device_address: { value: "", required: true },
apduTimeout: { value: 6000 },
roundDecimal: { value: 2 },
local_device_port: { value: 47808, required: true },
apduSize: { value: "5", required: true },
maxSegments: { value: "0x50", required: true },
retries: { value: "5", required: true },
broadCastAddr: { value: "255.255.255.255", required: true },
toLogIam: { value: false },
discover_polling_schedule: { value: "60" },
discover_polling_schedule_value: { value: "1", required: true },
discover_polling_schedule_options: { value: "Minutes", required: true },
deviceId: { value: 817001, required: true },
manual_instance_range_enabled: { value: false },
manual_instance_range_start: { value: 0 },
manual_instance_range_end: { value: 10000 },
logErrorToConsole: { value: false },
serverEnabled: { value: false },
device_read_schedule: { value: "60" },
device_read_schedule_value: { value: "1", required: true },
device_read_schedule_options: { value: "Minutes", required: true },
deviceRangeRegisters: { value: [] },
},
networkInterfaces: [],
inputs: 1,
outputs: 1,
icon: "bitpool.svg",
label: function () {
return this.name || "gateway";
},
paletteLabel: function () {
return "gateway";
},
oneditprepare: function () {
let node = this;
let tabs = RED.tabs.create({
id: "node-input-read-tabs",
onchange: function (tab) {
$("#node-input-tabs-content").children().hide();
$("#" + tab.id).show();
},
});
tabs.addTab({
id: "read-properties-tab",
label: "Gateway",
});
tabs.addTab({
id: "read-discover-tab",
label: "Discovery",
});
tabs.addTab({
id: "read-server-tab",
label: "Server",
});
if (node.networkInterfaces && node.networkInterfaces.length > 0) {
let nicSelector = document.getElementById("node-input-local_device_address");
node.networkInterfaces.forEach(function (option) {
nicSelector.options[nicSelector.options.length] = option;
});
nicSelector.value = node.local_device_address;
}
function queryAdapters() {
let nicSelector = document.getElementById("node-input-local_device_address");
$.ajax({
url: "/bitpool-bacnet-data/getNetworkInterfaces",
success: function (data) {
let keys = Object.keys(data);
for (const key in keys) {
let nicName = keys[key];
let ipAddr = data[keys[key]][0];
let text = nicName + " : " + ipAddr;
if (!node.networkInterfaces) node.networkInterfaces = [];
let found = node.networkInterfaces.findIndex((ele) => ele.text == text && ele.value == ipAddr);
if (found == -1) {
let newOption = new Option(text, ipAddr);
nicSelector.options[nicSelector.options.length] = newOption;
node.networkInterfaces.push(newOption);
}
}
nicSelector.value = node.local_device_address;
},
timeout: 10000,
});
}
queryAdapters();
function setManualInstanceRangeState(state) {
let deviceIdRangeStart = $("#node-input-manual_instance_range_start");
let deviceIdRangeEnd = $("#node-input-manual_instance_range_end");
if (state == true) {
deviceIdRangeStart.removeAttr("readonly");
deviceIdRangeEnd.removeAttr("readonly");
} else if (state == false) {
deviceIdRangeStart.attr("readonly", true);
deviceIdRangeEnd.attr("readonly", true);
}
}
setManualInstanceRangeState(node.manual_instance_range_enabled);
$("#node-input-manual_instance_range_enabled").change(function (e) {
setManualInstanceRangeState(this.checked);
});
if (node.vm1 == undefined) {
const confirmDialog = document.createElement("p-confirm-dialog");
document.getElementById("serverParent").appendChild(confirmDialog);
//document.getElementById("clearServerContainer").appendChild(confirmDialog);
}
const { createApp, ref, onMounted } = Vue;
const ConfirmDialog = primevue.confirmdialog;
const ConfirmationService = primevue.confirmationservice;
const ListBox = primevue.listbox;
//prime vue app
const App = {
data() {
return {
nodeService: ref(new NodeService()),
serverObjects: ref([]),
selectedServerObject: ref()
};
},
mounted() {
this.getServerObjects();
},
methods: {
confirmAll(event) {
let app = this;
this.$confirm.require({
message: "Do you want to clear all the BACnet server points? This action is not reversible",
header: "Delete Confirmation",
icon: "pi pi-info-circle",
acceptClass: "p-button-danger",
accept: () => {
//handle accept
this.nodeService.clearBacnetServerPoints().then(function() {
app.getServerObjects();
});
},
reject: () => {
//handle reject
},
});
},
confirm(json) {
this.$confirm.require({
message: 'Do you want to clear this BACnet server point? This action is not reversible',
header: 'Delete Confirmation',
icon: 'pi pi-info-circle',
acceptClass: 'p-button-danger',
accept: () => {
//handle accept
let app = this;
$.ajax({
type: "POST",
url: '/bitpool-bacnet-data/clearBacnetServerPoint',
dataType: 'json',
contentType: 'application/json',
data: JSON.stringify(json),
success: function (result) {
app.getServerObjects();
},
timeout: 10000
});
},
reject: () => {
//handle reject
}
});
},
getServerObjects() {
let app = this;
this.nodeService.getBacnetServerPoints().then(function (result) {
app.serverObjects = result;
})
},
formatObjectName(name) {
//return shortened point name if longer than 50 characters
if(name.length > 45) {
return name.slice(0, 45).concat("...");
}
return name;
}
},
components: {
"p-button": primevue.button,
"p-confirm-dialog": primevue.confirmdialog,
"p-confirm-popup": primevue.confirmpopup,
},
};
let vueapp = createApp(App);
vueapp.use(primevue.config.default);
vueapp.use(primevue.confirmpopup);
vueapp.use(primevue.confirmationservice);
node.vm1 = vueapp.mount("#serverParent");
$("#file-upload").on("change", function (event) {
const input = event.target.files[0];
const reader = new FileReader();
reader.onload = function (e) {
const text = e.target.result;
let jsonPayload = JSON.parse(text);
$.ajax({
type: "POST",
url: "/bitpool-bacnet-data/updateDeviceList",
dataType: "json",
contentType: "application/json",
data: JSON.stringify(jsonPayload),
success: function (result) {},
timeout: 10000,
});
};
reader.readAsText(input);
});
$("#file-export").click(function (params) {
$.ajax({
url: "/bitpool-bacnet-data/getDeviceList",
success: function (deviceList) {
let data = "text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(deviceList));
let aEle = document.getElementById("exportJSON");
aEle.setAttribute("href", "data:" + data);
aEle.setAttribute("download", "bitpool-bacnet-devices.json");
aEle.click();
},
timeout: 10000,
});
});
//device scan range matrix
$("#node-input-deviceIdRangeMatrix-container")
.css("min-width", "350px")
.editableList({
addItem: function (row, index, data) {
row.css({ overflow: "none", whiteSpace: "nowrap" });
let rowData = {};
if (data && typeof data.enabled == "boolean" && typeof data.start == "string" && typeof data.end == "string") {
if (data.enabled === true) {
rowData.enabled = "checked";
} else if (data.enabled === false) {
rowData.enabled = "";
}
rowData.start = data.start;
rowData.end = data.end;
} else {
rowData.enabled = "";
rowData.start = "";
rowData.end = "";
}
let fragment = document.createDocumentFragment();
let htmlRow = `
<div class="form-row deviceIdRangeEntry" id="node-input-device_id_range">
<label for="node-input-device_id_range_entry"><i class="icon-tag"></i><span data-i18n="bitpool-bacnet.label.device_id_range_entry"></span>Enabled</label>
<input type="checkbox" id="node-input-device_id_range_entry_enabled" style="width: auto;" ${rowData.enabled} />
<a style="padding-left: 60px;">Start: </a><input type="number" id="node-input-device_id_range_entry_start" style="width: 125px;" min="0" max="4194303" value="${rowData.start}" />
<a style="padding-left: 35px;">End: </a><input type="number" id="node-input-device_id_range_entry_end" style="width: 125px;" min="1" max="4194303" value="${rowData.end}" />
</div>`;
$(htmlRow).appendTo(fragment);
row[0].appendChild(fragment);
document.getElementById("node-input-reg-block-count").innerHTML = $(
"#node-input-deviceIdRangeMatrix-container"
).editableList("length");
},
removeItem: function (data) {
document.getElementById("node-input-reg-block-count").innerHTML = $(
"#node-input-deviceIdRangeMatrix-container"
).editableList("length");
},
removable: true,
scrollOnAdd: false,
header: $("<div style='display:flex; padding:10px 10px 0px 5px; column-gap: 10px'>").append(
$.parseHTML(
"<div><p>Count:</p></div><div style='color: gray'><label id='node-input-reg-block-count'>0</label> <span style='padding-left: 85px;'>Device ID Range(s)</span> </div>"
)
),
buttons: [
{
label: "json",
icon: "fa fa-share",
title: "Export tags as JSON to browser",
click: function (evt) {
var deviceRangeRegisters = $("#node-input-deviceIdRangeMatrix-container").editableList("items");
var mapItems = [];
deviceRangeRegisters.each(function (i) {
var registerMap = $(this);
var mapItem = {
enabled: registerMap.find("#node-input-device_id_range_entry_enabled").val(),
start: registerMap.find("#node-input-device_id_range_entry_start").val(),
end: registerMap.find("#node-input-device_id_range_entry_end").val(),
};
mapItems.push(mapItem);
});
var oMyBlob = new Blob(
[
JSON.stringify(mapItems, null, 0)
.replaceAll(/\[{/gi, "[\n{")
.replaceAll(/}\]/gi, "}\n]")
.replaceAll(/},/gi, "},\n")
.replaceAll(/{/gi, " {"),
],
{ type: "text/json" }
);
window.open(URL.createObjectURL(oMyBlob));
},
},
{
label: "delete",
icon: "fa-regular fa-trash-can",
title: "Delete all registers",
click: function (evt) {
$("#node-input-deviceIdRangeMatrix-container").editableList("empty");
document.getElementById("node-input-reg-block-count").innerHTML = 0;
},
},
],
});
if (node.deviceRangeRegisters.length > 0) {
for (var i = 0; i < node.deviceRangeRegisters.length; i++) {
$("#node-input-deviceIdRangeMatrix-container").editableList("addItem", node.deviceRangeRegisters[i]);
}
} else {
$("#node-input-deviceIdRangeMatrix-container").editableList("addItem", {
enabled: true,
start: "0",
end: "4194303",
});
}
document.getElementById("node-input-reg-block-count").innerHTML = node.deviceRangeRegisters.length;
},
oneditsave: function () {
let node = this;
document.getElementById("node-input-discover_polling_schedule").value = getTimePeriodInSeconds(
document.getElementById("node-input-discover_polling_schedule_value").value,
document.getElementById("node-input-discover_polling_schedule_options").value
);
document.getElementById("node-input-device_read_schedule").value = getTimePeriodInSeconds(
document.getElementById("node-input-device_read_schedule_value").value,
document.getElementById("node-input-device_read_schedule_options").value
);
node.deviceRangeRegisters = [];
let deviceRanges = $("#node-input-deviceIdRangeMatrix-container").editableList("items");
deviceRanges.each(function (i) {
let map = $(this);
let mapItem = {
enabled: map.find("#node-input-device_id_range_entry_enabled").is(":checked"),
start: map.find("#node-input-device_id_range_entry_start").val(),
end: map.find("#node-input-device_id_range_entry_end").val(),
};
node.deviceRangeRegisters.push(mapItem);
});
},
});
function getTimePeriodInSeconds(value, interval) {
switch (interval) {
case "Seconds":
return value;
break;
case "Minutes":
return value * 60;
break;
case "Hours":
return value * 3600;
break;
case "Days":
return value * 86400;
break;
default:
// code block
}
}
</script>
<script type="text/html" data-template-name="Bacnet-Gateway">
<style>
.deviceButton {
width: -webkit-fill-available;
background: space;
background-color: transparent !important;
color: inherit;
border: none;
display: flex;
align-items: center;
padding-top: 20px;
}
.point {
padding-left: 50px;
}
.pointButton {
color: inherit;
border: none;
display: flex;
align-items: center;
}
.networkTreeContent {
margin-top: 20px;
margin-left: 50px;
}
.p-confirm-dialog-accept > .p-button-label,
.bacnetServerRebuildSchedule_clearButton > .p-button-label {
color: white;
}
.red-ui-editor label {
font-size: 12px;
}
.bacnetServerRebuildSchedule_clearButton {
box-shadow: 0 0 0 0.2rem #edacac !important;
width: 200px;
border-radius: 3px;
margin-left: 140px !important;
}
.clearServerContainer {
position: absolute;
top: 55px;
margin-left: 280px;
}
.bacnetServerPoint_clearButton {
box-shadow: 0 0 0 0.2rem #edacac !important;
width: 200px;
border-radius: 3px;
margin-left: 420px !important;
margin-top: -75px !important;
}
.bacnetServerPoint_clearButton > .p-button-label {
color: white;
}
.clear_server_span {
width: 0px;
}
.read_server_parent_div {
display: flex;
align-items: flex-start;
justify-content: space-around;
flex-direction: column;
}
button {
height: 34px;
border-radius: 5px !important;
}
.inputStyle {
color: var(--red-ui-workspace-button-color-hover) !important;
background: var(--red-ui-workspace-button-background-hover);
border: 1px solid var(--red-ui-secondary-border-color);
border-radius: 5px !important;
}
input[type="file"] {
display: none !important;
}
.custom-file-upload {
border: 1px solid var(--red-ui-secondary-border-color);
border-radius: 5px !important;
padding: 6px 12px;
cursor: pointer;
}
.uploadButton {
margin-bottom: 5px !important;
margin-left: 30px !important;
}
.point_name {
font-weight: bold;
}
</style>
<div class="form-row node-input-read-tabs-row">
<ul style="min-width:600px;margin-bottom:20px" id="node-input-read-tabs"></ul>
</div>
<div id="node-input-tabs-content">
<div id="read-properties-tab" style="display:none">
<div class="form-row">
<label for="node-input-name"><i class="icon-tag"></i><span data-i18n="bitpool-bacnet.label.name"></span> Name</label>
<input type="text" id="node-input-name" placeholder="Name" />
</div>
<div class="form-row node-input-read-tabs-row">
<ul style="min-width:600px;margin-bottom:20px" id="node-input-read-tabs"></ul>
</div>
<div class="form-row" id="networkInterfaceDiv">
<label for="node-input-local_device_address"
><i class="icon-tag"></i><span data-i18n="bitpool-bacnet.label.local_device_address"></span> Network
Interface</label
>
<select id="node-input-local_device_address" style="width: 70%;"></select>
</div>
<div class="form-row">
<label for="node-input-broadCastAddr"
><i class="icon-tag"></i><span data-i18n="bitpool-bacnet.label.broadCastAddr"></span> Broadcast Address
</label>
<input type="text" id="node-input-broadCastAddr" placeholder="255.255.255.255" />
</div>
<div class="form-row">
<label for="node-input-local_device_port"
><i class="icon-tag"></i><span data-i18n="bitpool-bacnet.label.local_device_port"></span> Local Device Port</label
>
<input type="text" id="node-input-local_device_port" placeholder="47808" />
</div>
<div class="form-row">
<label for="node-input-deviceId"
><i class="icon-tag"></i><span data-i18n="bitpool-bacnet.label.deviceId"></span> Device ID
</label>
<input type="text" id="node-input-deviceId" placeholder="817001" />
</div>
<div class="form-row node-input-deviceIdRangeMatrix-container-row">
<ol id="node-input-deviceIdRangeMatrix-container" style="width: 600px;"></ol>
</div>
</div>
<div id="read-discover-tab" style="display:none">
<div class="form-row">
<label for="node-input-apduTimeout"
><i class="icon-tag"></i> <span data-i18n="bitpool-bacnet.label.apduTimeout"></span>Apdu Timeout</label
>
<input type="text" id="node-input-apduTimeout" placeholder="10000" />
</div>
<div class="form-row">
<label for="node-input-apduSize"
><i class="icon-tag"></i> <span data-i18n="bitpool-bacnet.label.apduSize"></span>Max Apdu Size</label
>
<select id="node-input-apduSize">
<option value="0">0</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
</select>
</div>
<div class="form-row">
<label for="node-input-maxSegments"
><i class="icon-tag"></i> <span data-i18n="bitpool-bacnet.label.maxSegments"></span>Max Segments</label
>
<select id="node-input-maxSegments">
<option value="0">0</option>
<option value="0x10">0x10</option>
<option value="0x20">0x20</option>
<option value="0x30">0x30</option>
<option value="0x40">0x40</option>
<option value="0x50">0x50</option>
<option value="0x60">0x60</option>
<option value="0x70">0x70</option>
</select>
</div>
<div class="form-row">
<label for="node-input-retries"
><i class="icon-tag"></i> <span data-i18n="bitpool-bacnet.label.retries"></span>Number of Retries</label
>
<select id="node-input-retries">
<option value="0">0</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
<option value="7">7</option>
<option value="8">8</option>
<option value="9">9</option>
<option value="10">10</option>
</select>
</div>
<div class="form-row" style="align-items: center; display: flex;">
<label for="node-input-discover_polling_schedule_value"
><i class="icon-tag"></i> <span data-i18n="bitpool-bacnet.label.bacnet_polling_schedule"></span>Device Discover
Frequency</label
>
<p style="margin-right: 5px; margin-bottom: 0px; padding-left: 7px;">Every</p>
<input type="text" id="node-input-discover_polling_schedule" style="display: none;" />
<input
type="text"
id="node-input-discover_polling_schedule_value"
placeholder="5"
style="width: 70px; margin-right: 5px;" />
<select name="timePeriod" id="node-input-discover_polling_schedule_options" style="width: 120px; margin-right: 5px;">
<option value="Seconds">Seconds</option>
<option value="Minutes">Minutes</option>
<option value="Hours">Hours</option>
<option value="Days">Days</option>
</select>
</div>
<div class="form-row deviceIdRange">
<label for="node-input-device_id_range"
><i class="icon-tag"></i><span data-i18n="bitpool-bacnet.label.device_id_range"></span>Point Discovery Range
</label>
<input type="checkbox" id="node-input-manual_instance_range_enabled" style="width: auto;" />
<a style="padding-left: 60px;">Start: </a
><input type="number" id="node-input-manual_instance_range_start" style="width: 125px;" min="0" max="100000" />
<a style="padding-left: 35px;">End: </a
><input type="number" id="node-input-manual_instance_range_end" style="width: 125px;" min="1" max="100000" />
</div>
<div class="form-row" style="align-items: center; display: flex;">
<label for="node-input-device_read_schedule_value"
><i class="icon-tag"></i> <span data-i18n="bitpool-bacnet.label.device_read_schedule"></span>Object Discover
Frequency</label
>
<p style="margin-right: 5px; margin-bottom: 0px; padding-left: 7px;">Every</p>
<input type="text" id="node-input-device_read_schedule" style="display: none;" />
<input
type="text"
id="node-input-device_read_schedule_value"
placeholder="5"
style="width: 70px; margin-right: 5px;" />
<select name="timePeriod" id="node-input-device_read_schedule_options" style="width: 120px; margin-right: 5px;">
<option value="Seconds">Seconds</option>
<option value="Minutes">Minutes</option>
<option value="Hours">Hours</option>
<option value="Days">Days</option>
</select>
</div>
<div class="form-row">
<label for="node-input-toLog"> Log found device: </label>
<input type="checkbox" id="node-input-toLogIam" style="width: auto;" />
</div>
<div class="form-row">
<label for="node-input-logErrorToConsole"> Log BACnet errors to console: </label>
<input type="checkbox" id="node-input-logErrorToConsole" style="width: auto;" />
</div>
<div class="form-row" id="importDeviceList">
<label> Device List: </label>
<label for="file-upload" class="custom-file-upload">
<i class="fa fa-cloud-upload" id="fileLabel"></i> <a id="fileLabelText" style="padding-left: 10px;">Import</a>
</label>
<input type="file" id="file-upload" accept="application/JSON" class="inputStyle" style="width: 258px;" />
<label for="file-export" class="custom-file-upload" style="margin-left: 30px;">
<i class="fa fa-cloud-download" id="fileLabel"></i> <a id="fileLabelText" style="padding-left: 10px;">Export</a>
</label>
<input id="file-export" class="inputStyle" style="width: 258px; display: none;" />
<a id="exportJSON" style="display: none"></a>
</div>
</div>
<div id="read-server-tab" style="display:none">
<div class="form-row">
<label for="node-input-serverEnabled"> Enabled: </label>
<input type="checkbox" id="node-input-serverEnabled" style="width: auto;" />
</div>
<div class="read_server_parent_div" id="serverParent">
<div class="form-row clearServerContainer" id="clearServerContainer" style="align-items: center; display: flex;">
<p-button
class="bacnetServerRebuildSchedule_clearButton p-button-danger"
@click="confirmAll($event)"
label="Reinitialize Server">
</p-button>
</div>
<div class="form-row" id="clearServerPointContainer">
<label>Server Objects</label>
<div v-for="object in serverObjects" :key="object.name">
<div><p class="point_name">[{{ object.type }}{{ object.instance }}] {{ formatObjectName(object.name) }}</p></div>
<p-button class="bacnetServerPoint_clearButton p-button-danger" @click="confirm({
type: object.type,
instance: object.instance,
name: object.name
})" label="Remove Object"></p-button>
</div>
</div>
</div>
</div>
</div>
</script>
<script type="text/html" data-help-name="Bacnet-Gateway">
<p>
This node is the brain of the Bitpool BACnet node collection. It acts are the gateway for all BACnet communications and
functionality for the collection.
</p>
<h3><strong>Gateway Tab</strong></h3>
<ul class="node-ports">
<li>
Network Interface - the desired interface for the bacstack client to bind to. This interface must not have any other
BACnet clients bound to it.
</li>
<li>
Broadcast Address - the desired subnet for global msgs to be broadcast and recieved on. This should be as strict as
possible. Use 255.255.255.255 if unsure.
</li>
<li>Local Device Port - the port to be used for BACnet comms. Default is 47808</li>
</ul>
<h3><strong>Discovery Tab</strong></h3>
<ul class="node-ports">
<li>APDU Timeout - BACnet msg timeout option</li>
<li>Max APDU Size - BACnet max apdu size</li>
<li>Max Segments - BACnet max segments</li>
<li>
Global Discover Frequency - the frequency at which the gateway issues global WhoIs BACnet commands. This should be
limited to the least amount possible, as over-loading a network can be a serious issue with BACnet commmunications.
</li>
<li>
Manual Point Discovery Instance Range - if a BACnet device doesnt have a Object list (BACnet objectType:propertyId -
8:76), the this bacnet client will enter into manual discovery mode, where it iterates through types and instnace
ranges. This range can be used to limit this manual scanning
</li>
<li>Log Device Found - toggles logging of found devices to the node-red debug tab.</li>
<li>Log BACnet Errors to Console - toggles logging of BACnet related errors to the node-red console</li>
</ul>
<h3><strong>Server Tab</strong></h3>
<p>This section provides the ability to simulate a BACnet device and BACnet points using node-red.</p>
<p>
Injecting a msg.topic and msg.payload into the gateway node will create a virtual point that can be discovered by other
devices via BACnet/IP
</p>
<p>
This node only supports 2 BACnet object types, Analog Value - to show numeric data, and a Character String - to show
string data.
</p>
<ul class="node-ports">
<li>Enabled - toggles whether or not the local BACnet server is started or not.</li>
<li>
Clear Server Points - a schedule for the locally generated BACnet points to get cleared from the node object store
</li>
</ul>
<h3><strong>Examples</strong></h3>
<p>
For example flows, please use the examples section for this node. These examples can be found at: Node-red hamburger menu
on top right -> Import -> Examples -> @bitpoolos/edge-bacnet
</p>
<p>
To find captured examples of settings and flows, please go to our wiki
<a href="https://wiki.bitpool.com/en/edge/apps/bitpool-edge/nr-bacnet">here</a>
</p>
<h3>Resources:</h3>
<p><a href="https://youtu.be/4K7mVxfvfbc">Video Walk-through </a></p>
<h4><strong>Online Docs:</strong></h4>
<ul type="1">
<li><a href="https://www.bitpool.com/">bitpool.com</a> - check us out here.</li>
<li><a href="https://app.bitpool.com/">app.bitpool.com</a> - set up your account.</li>
<li><a href="https://wiki.bitpool.com/">wiki.bitpool.com</a> - find more documentation.</li>
<li><a href="https://bacnet.org/">BACnet</a> - find more about the protocol.</li>
</ul>
</script>
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。