--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bacnet/BacnetSlaveEditor.py Fri Jun 08 13:28:00 2018 +0200
@@ -0,0 +1,1029 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# This file is part of Beremiz, a Integrated Development Environment for
+# programming IEC 61131-3 automates supporting plcopen standard.
+# This files implements the bacnet plugin for Beremiz, adding BACnet server support.
+#
+# Copyright (C) 2017: Mario de Sousa (msousa@fe.up.pt)
+#
+# See COPYING file for copyrights details.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+import wx
+from collections import Counter
+from pickle import dump
+from util.BitmapLibrary import GetBitmap
+
+
+
+# Import some libraries on Beremiz code...
+from controls.CustomGrid import CustomGrid
+from controls.CustomTable import CustomTable
+from editors.ConfTreeNodeEditor import ConfTreeNodeEditor
+from graphics.GraphicCommons import ERROR_HIGHLIGHT
+
+
+
+# BACnet Engineering units taken from: ASHRAE 135-2016, clause/chapter 21
+BACnetEngineeringUnits = [
+ ('(Acceleration) meters-per-second-per-second (166)', 166 ),
+ ('(Area) square-meters (0)', 0 ),
+ ('(Area) square-centimeters (116)', 116 ),
+ ('(Area) square-feet (1)', 1 ),
+ ('(Area) square-inches (115)', 115 ),
+ ('(Currency) currency1 (105)', 105 ),
+ ('(Currency) currency2 (106)', 106 ),
+ ('(Currency) currency3 (107)', 107 ),
+ ('(Currency) currency4 (108)', 108 ),
+ ('(Currency) currency5 (109)', 109 ),
+ ('(Currency) currency6 (110)', 110 ),
+ ('(Currency) currency7 (111)', 111 ),
+ ('(Currency) currency8 (112)', 112 ),
+ ('(Currency) currency9 (113)', 113 ),
+ ('(Currency) currency10 (114)', 114 ),
+ ('(Electrical) milliamperes (2)', 2 ),
+ ('(Electrical) amperes (3)', 3 ),
+ ('(Electrical) amperes-per-meter (167)', 167 ),
+ ('(Electrical) amperes-per-square-meter (168)', 168 ),
+ ('(Electrical) ampere-square-meters (169)', 169 ),
+ ('(Electrical) decibels (199)', 199 ),
+ ('(Electrical) decibels-millivolt (200)', 200 ),
+ ('(Electrical) decibels-volt (201)', 201 ),
+ ('(Electrical) farads (170)', 170 ),
+ ('(Electrical) henrys (171)', 171 ),
+ ('(Electrical) ohms (4)', 4 ),
+ ('(Electrical) ohm-meter-squared-per-meter (237)', 237 ),
+ ('(Electrical) ohm-meters (172)', 172 ),
+ ('(Electrical) milliohms (145)', 145 ),
+ ('(Electrical) kilohms (122)', 122 ),
+ ('(Electrical) megohms (123)', 123 ),
+ ('(Electrical) microsiemens (190)', 190 ),
+ ('(Electrical) millisiemens (202)', 202 ),
+ ('(Electrical) siemens (173)', 173 ),
+ ('(Electrical) siemens-per-meter (174)', 174 ),
+ ('(Electrical) teslas (175)', 175 ),
+ ('(Electrical) volts (5)', 5 ),
+ ('(Electrical) millivolts (124)', 124 ),
+ ('(Electrical) kilovolts (6)', 6 ),
+ ('(Electrical) megavolts (7)', 7 ),
+ ('(Electrical) volt-amperes (8)', 8 ),
+ ('(Electrical) kilovolt-amperes (9)', 9 ),
+ ('(Electrical) megavolt-amperes (10)', 10 ),
+ ('(Electrical) volt-amperes-reactive (11)', 11 ),
+ ('(Electrical) kilovolt-amperes-reactive (12)', 12 ),
+ ('(Electrical) megavolt-amperes-reactive (13)', 13 ),
+ ('(Electrical) volts-per-degree-kelvin (176)', 176 ),
+ ('(Electrical) volts-per-meter (177)', 177 ),
+ ('(Electrical) degrees-phase (14)', 14 ),
+ ('(Electrical) power-factor (15)', 15 ),
+ ('(Electrical) webers (178)', 178 ),
+ ('(Energy) ampere-seconds (238)', 238 ),
+ ('(Energy) volt-ampere-hours (239)', 239 ),
+ ('(Energy) kilovolt-ampere-hours (240)', 240 ),
+ ('(Energy) megavolt-ampere-hours (241)', 241 ),
+ ('(Energy) volt-ampere-hours-reactive (242)', 242 ),
+ ('(Energy) kilovolt-ampere-hours-reactive (243)', 243 ),
+ ('(Energy) megavolt-ampere-hours-reactive (244)', 244 ),
+ ('(Energy) volt-square-hours (245)', 245 ),
+ ('(Energy) ampere-square-hours (246)', 246 ),
+ ('(Energy) joules (16)', 16 ),
+ ('(Energy) kilojoules (17)', 17 ),
+ ('(Energy) kilojoules-per-kilogram (125)', 125 ),
+ ('(Energy) megajoules (126)', 126 ),
+ ('(Energy) watt-hours (18)', 18 ),
+ ('(Energy) kilowatt-hours (19)', 19 ),
+ ('(Energy) megawatt-hours (146)', 146 ),
+ ('(Energy) watt-hours-reactive (203)', 203 ),
+ ('(Energy) kilowatt-hours-reactive (204)', 204 ),
+ ('(Energy) megawatt-hours-reactive (205)', 205 ),
+ ('(Energy) btus (20)', 20 ),
+ ('(Energy) kilo-btus (147)', 147 ),
+ ('(Energy) mega-btus (148)', 148 ),
+ ('(Energy) therms (21)', 21 ),
+ ('(Energy) ton-hours (22)', 22 ),
+ ('(Enthalpy) joules-per-kilogram-dry-air (23)', 23 ),
+ ('(Enthalpy) kilojoules-per-kilogram-dry-air (149)', 149 ),
+ ('(Enthalpy) megajoules-per-kilogram-dry-air (150)', 150 ),
+ ('(Enthalpy) btus-per-pound-dry-air (24)', 24 ),
+ ('(Enthalpy) btus-per-pound (117)', 117 ),
+ ('(Entropy) joules-per-degree-kelvin (127)', 127 ),
+ ('(Entropy) kilojoules-per-degree-kelvin (151)', 151 ),
+ ('(Entropy) megajoules-per-degree-kelvin (152)', 152 ),
+ ('(Entropy) joules-per-kilogram-degree-kelvin (128)', 128 ),
+ ('(Force) newton (153)', 153 ),
+ ('(Frequency) cycles-per-hour (25)', 25 ),
+ ('(Frequency) cycles-per-minute (26)', 26 ),
+ ('(Frequency) hertz (27)', 27 ),
+ ('(Frequency) kilohertz (129)', 129 ),
+ ('(Frequency) megahertz (130)', 130 ),
+ ('(Frequency) per-hour (131)', 131 ),
+ ('(Humidity) grams-of-water-per-kilogram-dry-air (28)', 28 ),
+ ('(Humidity) percent-relative-humidity (29)', 29 ),
+ ('(Length) micrometers (194)', 194 ),
+ ('(Length) millimeters (30)', 30 ),
+ ('(Length) centimeters (118)', 118 ),
+ ('(Length) kilometers (193)', 193 ),
+ ('(Length) meters (31)', 31 ),
+ ('(Length) inches (32)', 32 ),
+ ('(Length) feet (33)', 33 ),
+ ('(Light) candelas (179)', 179 ),
+ ('(Light) candelas-per-square-meter (180)', 180 ),
+ ('(Light) watts-per-square-foot (34)', 34 ),
+ ('(Light) watts-per-square-meter (35)', 35 ),
+ ('(Light) lumens (36)', 36 ),
+ ('(Light) luxes (37)', 37 ),
+ ('(Light) foot-candles (38)', 38 ),
+ ('(Mass) milligrams (196)', 196 ),
+ ('(Mass) grams (195)', 195 ),
+ ('(Mass) kilograms (39)', 39 ),
+ ('(Mass) pounds-mass (40)', 40 ),
+ ('(Mass) tons (41)', 41 ),
+ ('(Mass Flow) grams-per-second (154)', 154 ),
+ ('(Mass Flow) grams-per-minute (155)', 155 ),
+ ('(Mass Flow) kilograms-per-second (42)', 42 ),
+ ('(Mass Flow) kilograms-per-minute (43)', 43 ),
+ ('(Mass Flow) kilograms-per-hour (44)', 44 ),
+ ('(Mass Flow) pounds-mass-per-second (119)', 119 ),
+ ('(Mass Flow) pounds-mass-per-minute (45)', 45 ),
+ ('(Mass Flow) pounds-mass-per-hour (46)', 46 ),
+ ('(Mass Flow) tons-per-hour (156)', 156 ),
+ ('(Power) milliwatts (132)', 132 ),
+ ('(Power) watts (47)', 47 ),
+ ('(Power) kilowatts (48)', 48 ),
+ ('(Power) megawatts (49)', 49 ),
+ ('(Power) btus-per-hour (50)', 50 ),
+ ('(Power) kilo-btus-per-hour (157)', 157 ),
+ ('(Power) joule-per-hours (247)', 247 ),
+ ('(Power) horsepower (51)', 51 ),
+ ('(Power) tons-refrigeration (52)', 52 ),
+ ('(Pressure) pascals (53)', 53 ),
+ ('(Pressure) hectopascals (133)', 133 ),
+ ('(Pressure) kilopascals (54)', 54 ),
+ ('(Pressure) millibars (134)', 134 ),
+ ('(Pressure) bars (55)', 55 ),
+ ('(Pressure) pounds-force-per-square-inch (56)', 56 ),
+ ('(Pressure) millimeters-of-water (206)', 206 ),
+ ('(Pressure) centimeters-of-water (57)', 57 ),
+ ('(Pressure) inches-of-water (58)', 58 ),
+ ('(Pressure) millimeters-of-mercury (59)', 59 ),
+ ('(Pressure) centimeters-of-mercury (60)', 60 ),
+ ('(Pressure) inches-of-mercury (61)', 61 ),
+ ('(Temperature) degrees-celsius (62)', 62 ),
+ ('(Temperature) degrees-kelvin (63)', 63 ),
+ ('(Temperature) degrees-kelvin-per-hour (181)', 181 ),
+ ('(Temperature) degrees-kelvin-per-minute (182)', 182 ),
+ ('(Temperature) degrees-fahrenheit (64)', 64 ),
+ ('(Temperature) degree-days-celsius (65)', 65 ),
+ ('(Temperature) degree-days-fahrenheit (66)', 66 ),
+ ('(Temperature) delta-degrees-fahrenheit (120)', 120 ),
+ ('(Temperature) delta-degrees-kelvin (121)', 121 ),
+ ('(Time) years (67)', 67 ),
+ ('(Time) months (68)', 68 ),
+ ('(Time) weeks (69)', 69 ),
+ ('(Time) days (70)', 70 ),
+ ('(Time) hours (71)', 71 ),
+ ('(Time) minutes (72)', 72 ),
+ ('(Time) seconds (73)', 73 ),
+ ('(Time) hundredths-seconds (158)', 158 ),
+ ('(Time) milliseconds (159)', 159 ),
+ ('(Torque) newton-meters (160)', 160 ),
+ ('(Velocity) millimeters-per-second (161)', 161 ),
+ ('(Velocity) millimeters-per-minute (162)', 162 ),
+ ('(Velocity) meters-per-second (74)', 74 ),
+ ('(Velocity) meters-per-minute (163)', 163 ),
+ ('(Velocity) meters-per-hour (164)', 164 ),
+ ('(Velocity) kilometers-per-hour (75)', 75 ),
+ ('(Velocity) feet-per-second (76)', 76 ),
+ ('(Velocity) feet-per-minute (77)', 77 ),
+ ('(Velocity) miles-per-hour (78)', 78 ),
+ ('(Volume) cubic-feet (79)', 79 ),
+ ('(Volume) cubic-meters (80)', 80 ),
+ ('(Volume) imperial-gallons (81)', 81 ),
+ ('(Volume) milliliters (197)', 197 ),
+ ('(Volume) liters (82)', 82 ),
+ ('(Volume) us-gallons (83)', 83 ),
+ ('(Volumetric Flow) cubic-feet-per-second (142)', 142 ),
+ ('(Volumetric Flow) cubic-feet-per-minute (84)', 84 ),
+ ('(Volumetric Flow) million-standard-cubic-feet-per-minute (254)', 254 ),
+ ('(Volumetric Flow) cubic-feet-per-hour (191)', 191 ),
+ ('(Volumetric Flow) cubic-feet-per-day (248)', 248 ),
+ ('(Volumetric Flow) standard-cubic-feet-per-day (47808)', 47808 ),
+ ('(Volumetric Flow) million-standard-cubic-feet-per-day (47809)', 47809 ),
+ ('(Volumetric Flow) thousand-cubic-feet-per-day (47810)', 47810 ),
+ ('(Volumetric Flow) thousand-standard-cubic-feet-per-day (47811)', 47811 ),
+ ('(Volumetric Flow) pounds-mass-per-day (47812)', 47812 ),
+ ('(Volumetric Flow) cubic-meters-per-second (85)', 85 ),
+ ('(Volumetric Flow) cubic-meters-per-minute (165)', 165 ),
+ ('(Volumetric Flow) cubic-meters-per-hour (135)', 135 ),
+ ('(Volumetric Flow) cubic-meters-per-day (249)', 249 ),
+ ('(Volumetric Flow) imperial-gallons-per-minute (86)', 86 ),
+ ('(Volumetric Flow) milliliters-per-second (198)', 198 ),
+ ('(Volumetric Flow) liters-per-second (87)', 87 ),
+ ('(Volumetric Flow) liters-per-minute (88)', 88 ),
+ ('(Volumetric Flow) liters-per-hour (136)', 136 ),
+ ('(Volumetric Flow) us-gallons-per-minute (89)', 89 ),
+ ('(Volumetric Flow) us-gallons-per-hour (192)', 192 ),
+ ('(Other) degrees-angular (90)', 90 ),
+ ('(Other) degrees-celsius-per-hour (91)', 91 ),
+ ('(Other) degrees-celsius-per-minute (92)', 92 ),
+ ('(Other) degrees-fahrenheit-per-hour (93)', 93 ),
+ ('(Other) degrees-fahrenheit-per-minute (94)', 94 ),
+ ('(Other) joule-seconds (183)', 183 ),
+ ('(Other) kilograms-per-cubic-meter (186)', 186 ),
+ ('(Other) kilowatt-hours-per-square-meter (137)', 137 ),
+ ('(Other) kilowatt-hours-per-square-foot (138)', 138 ),
+ ('(Other) watt-hours-per-cubic-meter (250)', 250 ),
+ ('(Other) joules-per-cubic-meter (251)', 251 ),
+ ('(Other) megajoules-per-square-meter (139)', 139 ),
+ ('(Other) megajoules-per-square-foot (140)', 140 ),
+ ('(Other) mole-percent (252)', 252 ),
+ ('(Other) no-units (95)', 95 ),
+ ('(Other) newton-seconds (187)', 187 ),
+ ('(Other) newtons-per-meter (188)', 188 ),
+ ('(Other) parts-per-million (96)', 96 ),
+ ('(Other) parts-per-billion (97)', 97 ),
+ ('(Other) pascal-seconds (253)', 253 ),
+ ('(Other) percent (98)', 98 ),
+ ('(Other) percent-obscuration-per-foot (143)', 143 ),
+ ('(Other) percent-obscuration-per-meter (144)', 144 ),
+ ('(Other) percent-per-second (99)', 99 ),
+ ('(Other) per-minute (100)', 100 ),
+ ('(Other) per-second (101)', 101 ),
+ ('(Other) psi-per-degree-fahrenheit (102)', 102 ),
+ ('(Other) radians (103)', 103 ),
+ ('(Other) radians-per-second (184)', 184 ),
+ ('(Other) revolutions-per-minute (104)', 104 ),
+ ('(Other) square-meters-per-newton (185)', 185 ),
+ ('(Other) watts-per-meter-per-degree-kelvin (189)', 189 ),
+ ('(Other) watts-per-square-meter-degree-kelvin (141)', 141 ),
+ ('(Other) per-mille (207)', 207 ),
+ ('(Other) grams-per-gram (208)', 208 ),
+ ('(Other) kilograms-per-kilogram (209)', 209 ),
+ ('(Other) grams-per-kilogram (210)', 210 ),
+ ('(Other) milligrams-per-gram (211)', 211 ),
+ ('(Other) milligrams-per-kilogram (212)', 212 ),
+ ('(Other) grams-per-milliliter (213)', 213 ),
+ ('(Other) grams-per-liter (214)', 214 ),
+ ('(Other) milligrams-per-liter (215)', 215 ),
+ ('(Other) micrograms-per-liter (216)', 216 ),
+ ('(Other) grams-per-cubic-meter (217)', 217 ),
+ ('(Other) milligrams-per-cubic-meter (218)', 218 ),
+ ('(Other) micrograms-per-cubic-meter (219)', 219 ),
+ ('(Other) nanograms-per-cubic-meter (220)', 220 ),
+ ('(Other) grams-per-cubic-centimeter (221)', 221 ),
+ ('(Other) becquerels (222)', 222 ),
+ ('(Other) kilobecquerels (223)', 223 ),
+ ('(Other) megabecquerels (224)', 224 ),
+ ('(Other) gray (225)', 225 ),
+ ('(Other) milligray (226)', 226 ),
+ ('(Other) microgray (227)', 227 ),
+ ('(Other) sieverts (228)', 228 ),
+ ('(Other) millisieverts (229)', 229 ),
+ ('(Other) microsieverts (230)', 230 ),
+ ('(Other) microsieverts-per-hour (231)', 231 ),
+ ('(Other) millirems (47814)', 47814 ),
+ ('(Other) millirems-per-hour (47815)', 47815 ),
+ ('(Other) decibels-a (232)', 232 ),
+ ('(Other) nephelometric-turbidity-unit (233)', 233 ),
+ ('(Other) pH (234)', 234 ),
+ ('(Other) grams-per-square-meter (235)', 235 ),
+ ('(Other) minutes-per-degree-kelvin (236)', 236 )
+ ] # BACnetEngineeringUnits
+
+
+
+# ObjectID (22 bits ID + 10 bits type) => max = 2^22-1 = 4194303
+# However, ObjectID 4194303 is not allowed!
+# 4194303 is used as a special value when object Id reference is referencing an undefined object
+# (similar to NULL in C)
+BACnetObjectID_MAX = 4194302
+BACnetObjectID_NUL = 4194303
+
+
+
+# A base class
+# what would be a purely virtual class in C++
+class ObjectProperties:
+ # this __init_() function is currently not beeing used!
+ def __init__(self):
+ #nothing to do
+ return
+
+
+class BinaryObject(ObjectProperties):
+ # 'PropertyNames' will be used as the header for each column of the Object Properties grid!
+ # Warning: The rest of the code depends on the existance of an "Object Identifier" and "Object Name"
+ # Be sure to use these exact names for these BACnet object properties!
+ PropertyNames = ["Object Identifier", "Object Name", "Description"]
+ ColumnAlignments = [ wx.ALIGN_RIGHT , wx.ALIGN_LEFT, wx.ALIGN_LEFT]
+ ColumnSizes = [ 40 , 80 , 80 ]
+ PropertyConfig = {"Object Identifier": {"GridCellEditor" : wx.grid.GridCellNumberEditor,
+ "GridCellRenderer" : wx.grid.GridCellNumberRenderer,
+ "GridCellEditorParam": "0,4194302"
+ # syntax for GridCellNumberEditor -> "min,max"
+ # ObjectID (22 bits ID + 10 bits type) => max = 2^22-1
+ },
+ "Object Name" : {"GridCellEditor" : wx.grid.GridCellTextEditor,
+ "GridCellRenderer": wx.grid.GridCellStringRenderer},
+ "Description" : {"GridCellEditor" : wx.grid.GridCellTextEditor,
+ "GridCellRenderer": wx.grid.GridCellStringRenderer}
+ }
+
+class AnalogObject(ObjectProperties):
+ # 'PropertyNames' will be used as the header for each column of the Object Properties grid!
+ # Warning: The rest of the code depends on the existance of an "Object Identifier" and "Object Name"
+ # Be sure to use these exact names for these BACnet object properties!
+ #
+ # NOTE: Although it is not listed here (so it does not show up in the GUI, this object will also
+ # keep another entry for a virtual property named "Unit ID". This virtual property
+ # will store the ID corresponding to the "Engineering Units" currently chosen.
+ # This virtual property is kept synchronised to the "Engineering Units" property
+ # by the function PropertyChanged() which should be called by the OnCellChange event handler.
+ PropertyNames = ["Object Identifier", "Object Name", "Description", "Engineering Units"] #'Unit ID'
+ ColumnAlignments = [ wx.ALIGN_RIGHT , wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_LEFT ]
+ ColumnSizes = [ 40 , 80 , 80 , 200 ]
+ PropertyConfig = {"Object Identifier": {"GridCellEditor" : wx.grid.GridCellNumberEditor,
+ "GridCellRenderer" : wx.grid.GridCellNumberRenderer,
+ "GridCellEditorParam": "0,4194302"
+ # syntax for GridCellNumberEditor -> "min,max"
+ # ObjectID (22 bits ID + 10 bits type) => max = 2^22-1
+ },
+ "Object Name" : {"GridCellEditor" : wx.grid.GridCellTextEditor,
+ "GridCellRenderer" : wx.grid.GridCellStringRenderer},
+ "Description" : {"GridCellEditor" : wx.grid.GridCellTextEditor,
+ "GridCellRenderer" : wx.grid.GridCellStringRenderer},
+ "Engineering Units": {"GridCellEditor" : wx.grid.GridCellChoiceEditor,
+ "GridCellRenderer" : wx.grid.GridCellStringRenderer,
+ # use string renderer with choice editor!
+ "GridCellEditorParam": ','.join([x[0] for x in BACnetEngineeringUnits])
+ # syntax for GridCellChoiceEditor -> comma separated values
+ }
+ }
+
+ # obj_properties should be a dictionary, with keys "Object Identifier", "Object Name", "Description", ...
+ def UpdateVirtualProperties(self, obj_properties):
+ obj_properties["Unit ID"] = [x[1] for x in BACnetEngineeringUnits if x[0] == obj_properties["Engineering Units"]][0]
+
+
+
+class MultiSObject(ObjectProperties):
+ # 'PropertyNames' will be used as the header for each column of the Object Properties grid!
+ # Warning: The rest of the code depends on the existance of an "Object Identifier" and "Object Name"
+ # Be sure to use these exact names for these BACnet object properties!
+ PropertyNames = ["Object Identifier", "Object Name", "Description", "Number of States"]
+ ColumnAlignments = [ wx.ALIGN_RIGHT , wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_CENTER ]
+ ColumnSizes = [ 40 , 80 , 80 , 120 ]
+ PropertyConfig = {"Object Identifier": {"GridCellEditor" : wx.grid.GridCellNumberEditor,
+ "GridCellRenderer" : wx.grid.GridCellNumberRenderer,
+ "GridCellEditorParam": "0,4194302"
+ # syntax for GridCellNumberEditor -> "min,max"
+ # ObjectID (22 bits ID + 10 bits type) => max = 2^22-1
+ },
+ "Object Name" : {"GridCellEditor" : wx.grid.GridCellTextEditor,
+ "GridCellRenderer" : wx.grid.GridCellStringRenderer},
+ "Description" : {"GridCellEditor" : wx.grid.GridCellTextEditor,
+ "GridCellRenderer" : wx.grid.GridCellStringRenderer},
+ "Number of States" : {"GridCellEditor" : wx.grid.GridCellNumberEditor,
+ "GridCellRenderer" : wx.grid.GridCellNumberRenderer,
+ "GridCellEditorParam": "1,255" # syntax for GridCellNumberEditor -> "min,max"
+ # MultiState Values are encoded in unsigned integer
+ # (in BACnet => uint8_t), and can not be 0.
+ # See ASHRAE 135-2016, section 12.20.4
+ }
+ }
+
+
+
+# The default values to use for each BACnet object type
+#
+# Note that the 'internal plugin parameters' get stored in the data table, but
+# are not visible in the GUI. They are used to generate the
+# EDE files as well as the C code
+class BVObject(BinaryObject):
+ DefaultValues = {"Object Identifier":"",
+ "Object Name" :"Binary Value",
+ "Description" :"",
+ # internal plugin parameters...
+ "BACnetObjTypeID" :5,
+ "Ctype" :"uint8_t",
+ "Settable" :"Y"
+ }
+
+class BOObject(BinaryObject):
+ DefaultValues = {"Object Identifier":"",
+ "Object Name" :"Binary Output",
+ "Description" :"",
+ # internal plugin parameters...
+ "BACnetObjTypeID" :4,
+ "Ctype" :"uint8_t",
+ "Settable" :"Y"
+ }
+
+class BIObject(BinaryObject):
+ DefaultValues = {"Object Identifier":"",
+ "Object Name" :"Binary Input",
+ "Description" :"",
+ # internal plugin parameters...
+ "BACnetObjTypeID" :3,
+ "Ctype" :"uint8_t",
+ "Settable" :"N"
+ }
+
+class AVObject(AnalogObject):
+ DefaultValues = {"Object Identifier":"",
+ "Object Name" :"Analog Value",
+ "Description" :"",
+ "Engineering Units":'(Other) no-units (95)',
+ # internal plugin parameters...
+ "Unit ID" :95, # the ID of the engineering unit
+ # will get updated by UpdateVirtualProperties()
+ "BACnetObjTypeID" :2,
+ "Ctype" :"float",
+ "Settable" :"Y"
+ }
+
+class AOObject(AnalogObject):
+ DefaultValues = {"Object Identifier":"",
+ "Object Name" :"Analog Output",
+ "Description" :"",
+ "Engineering Units":'(Other) no-units (95)',
+ # internal plugin parameters...
+ "Unit ID" :95, # the ID of the engineering unit
+ # will get updated by UpdateVirtualProperties()
+ "BACnetObjTypeID" :1,
+ "Ctype" :"float",
+ "Settable" :"Y"
+ }
+
+class AIObject(AnalogObject):
+ DefaultValues = {"Object Identifier":"",
+ "Object Name" :"Analog Input",
+ "Description" :"",
+ "Engineering Units":'(Other) no-units (95)',
+ # internal plugin parameters...
+ "Unit ID" :95, # the ID of the engineering unit
+ # will get updated by UpdateVirtualProperties()
+ "BACnetObjTypeID" :0,
+ "Ctype" :"float",
+ "Settable" :"N"
+ }
+
+class MSVObject(MultiSObject):
+ DefaultValues = {"Object Identifier":"",
+ "Object Name" :"Multi-state Value",
+ "Description" :"",
+ "Number of States" :"255",
+ # internal plugin parameters...
+ "BACnetObjTypeID" :19,
+ "Ctype" :"uint8_t",
+ "Settable" :"Y"
+ }
+
+class MSOObject(MultiSObject):
+ DefaultValues = {"Object Identifier":"",
+ "Object Name" :"Multi-state Output",
+ "Description" :"",
+ "Number of States" :"255",
+ # internal plugin parameters...
+ "BACnetObjTypeID" :14,
+ "Ctype" :"uint8_t",
+ "Settable" :"Y"
+ }
+
+class MSIObject(MultiSObject):
+ DefaultValues = {"Object Identifier":"",
+ "Object Name" :"Multi-state Input",
+ "Description" :"",
+ "Number of States" :"255",
+ # internal plugin parameters...
+ "BACnetObjTypeID" :13,
+ "Ctype" :"uint8_t",
+ "Settable" :"N"
+ }
+
+
+
+
+
+
+
+
+
+
+class ObjectTable(CustomTable):
+ # A custom wx.grid.PyGridTableBase using user supplied data
+ #
+ # This will basically store a list of BACnet objects that the slave will support/implement.
+ # There will be one instance of this ObjectTable class for each BACnet object type
+ # (e.g. Binary Value, Analog Input, Multi State Output, ...)
+ #
+ # The list of BACnet objects will actually be stored within the self.data variable
+ # (declared in CustomTable). Self.data will be a list of dictionaries (one entry per BACnet
+ # object). All of these dictionaries in the self.data list will have entries whose keys actually
+ # depend on the BACnet type object being handled. The keys in the dictionary will be
+ # the entries in the PropertyNames list of one of the following classes:
+ # (BVObject, BOObject, BIObject, AVObject, AOObject, AIObject, MSVObject, MSOObject, MSIObject).
+ #
+ # For example, when handling Binary Value BACnet objects,
+ # self.data will be a list of dictionaries (one entry per row)
+ # self.data[n] will be a dictionary, with keys "Object Identifier", "Object Name", "Description"
+ # for example: self.data[n] = {"Object Identifier":33, "Object Name":"room1", "Description":"xx"}
+ #
+ # Note that this ObjectTable class merely stores the configuration data.
+ # It does not control the display nor the editing of this data.
+ # This data is typically displayed within a grid, that is controlled by the ObjectGrid class.
+ #
+ def __init__(self, parent, data, BACnetObjectType):
+ # parent : the _BacnetSlavePlug object that is instantiating this ObjectTable
+ # data : a list with the data to be shown on the grid
+ # (i.e., a list containing the BACnet object properties)
+ # Instantiated in _BacnetSlavePlug
+ # BACnetObjectType: one of BinaryObject, AnalogObject, MultiSObject
+ # (or a class that derives from them).
+ # This is actually the class itself, and not a variable!!!
+ # However, self.BACnetObjectType will be an instance
+ # of said class as we later need to call methods from this class.
+ # (in particular, the UpdateVirtualProperties() method)
+ #
+ # The base class must be initialized *first*
+ CustomTable.__init__(self, parent, data, BACnetObjectType.PropertyNames)
+ self.BACnetObjectType = BACnetObjectType()
+ self.ChangesToSave = False
+
+ #def _GetRowEdit(self, row):
+ #row_edit = self.GetValueByName(row, "Edit")
+ #var_type = self.Parent.GetTagName()
+ #bodytype = self.Parent.Controler.GetEditedElementBodyType(var_type)
+ #if bodytype in ["ST", "IL"]:
+ #row_edit = True;
+ #return row_edit
+
+ def _updateColAttrs(self, grid):
+ # wx.grid.Grid -> update the column attributes to add the
+ # appropriate renderer given the column name.
+ #
+ # Otherwise default to the default renderer.
+ #print "ObjectTable._updateColAttrs() called!!!"
+ for row in range(self.GetNumberRows()):
+ for col in range(self.GetNumberCols()):
+ PropertyName = self.BACnetObjectType.PropertyNames[col]
+ PropertyConfig = self.BACnetObjectType.PropertyConfig[PropertyName]
+ grid.SetReadOnly (row, col, False)
+ grid.SetCellEditor (row, col, PropertyConfig["GridCellEditor"] ())
+ grid.SetCellRenderer (row, col, PropertyConfig["GridCellRenderer"]())
+ grid.SetCellBackgroundColour(row, col, wx.WHITE)
+ grid.SetCellTextColour (row, col, wx.BLACK)
+ if "GridCellEditorParam" in PropertyConfig:
+ grid.GetCellEditor(row, col).SetParameters(PropertyConfig["GridCellEditorParam"])
+ self.ResizeRow(grid, row)
+
+ def FindValueByName(self, PropertyName, PropertyValue):
+ # find the row whose property named PropertyName has the value PropertyValue
+ # Returns: row number
+ # for example, find the row where PropertyName "Object Identifier" has value 1002
+ # FindValueByName("Object Identifier", 1002).
+ for row in range(self.GetNumberRows()):
+ if int(self.GetValueByName(row, PropertyName)) == PropertyValue:
+ return row
+ return None
+
+ # Return a list containing all the values under the column named 'colname'
+ def GetAllValuesByName(self, colname):
+ values = []
+ for row in range(self.GetNumberRows()):
+ values.append(self.data[row].get(colname))
+ return values
+
+ # Returns a dictionary with:
+ # keys: IDs of BACnet objects
+ # value: number of BACnet objects using this same Id
+ # (values larger than 1 indicates an error as BACnet requires unique
+ # object IDs for objects of the same type)
+ def GetObjectIDCount(self):
+ # The dictionary is built by first creating a list containing the IDs of all BACnet objects
+ ObjectIDs = self.GetAllValuesByName("Object Identifier")
+ ObjectIDs_as_int = [int(x) for x in ObjectIDs] # list of integers instead of strings...
+ # This list is then transformed into a collections.Counter class
+ # Which is then transformed into a dictionary using dict()
+ return dict(Counter(ObjectIDs_as_int))
+
+ # Check whether any object ID is used more than once (not valid in BACnet)
+ # (returns True or False)
+ def HasDuplicateObjectIDs(self):
+ ObjectIDsCount = self.GetObjectIDCount()
+ for ObjName in ObjectIDsCount:
+ if ObjectIDsCount[ObjName] > 1:
+ return True
+ return False
+
+ # Update the virtual properties of the objects of the classes derived from ObjectProperties
+ # (currently only the AnalogObject class had virtua properties, i.e. a property
+ # that is determined/calculated based on the other properties)
+ def UpdateAllVirtualProperties(self):
+ if hasattr(self.BACnetObjectType, 'UpdateVirtualProperties'):
+ for ObjProp in self.data:
+ self.BACnetObjectType.UpdateVirtualProperties(ObjProp)
+
+
+
+class ObjectGrid(CustomGrid):
+ # A custom wx.grid.Grid (CustomGrid derives from wx.grid.Grid)
+ #
+ # Needed mostly to customise the initial values of newly added rows, and to
+ # validate if the inserted data follows BACnet rules.
+ #
+ #
+ # This ObjectGrid class:
+ # Creates and controls the GUI __grid__ for configuring all the BACnet objects of one
+ # (generic) BACnet object type (e.g. Binary Value, Analog Input, Multi State Output, ...)
+ # This grid is currently displayed within one 'window' controlled by a ObjectEditor
+ # object (this organization is not likely to change in the future).
+ #
+ # The grid uses one line/row per BACnet object, and one column for each property of the BACnet
+ # object. The column titles change depending on the specific type of BACnet object being edited
+ # (BVObject, BOObject, BIObject, AVObject, AOObject, AIObject, MSVObject, MSOObject, MSIObject).
+ # The editor to use for each column is also obtained from that class (e.g. TextEditor,
+ # NumberEditor, ...)
+ #
+ # This class does NOT store the data in the grid. It merely controls its display and editing.
+ # The data in the grid is stored within an object of class ObjectTable
+ #
+ def __init__(self, *args, **kwargs):
+ CustomGrid.__init__(self, *args, **kwargs)
+
+ # Called when a new row is added by clicking Add button
+ # call graph: CustomGrid.OnAddButton() --> CustomGrid.AddRow() --> ObjectGrid._AddRow()
+ def _AddRow(self, new_row):
+ if new_row > 0:
+ self.Table.InsertRow(new_row, self.Table.GetRow(new_row - 1).copy())
+ else:
+ self.Table.InsertRow(new_row, self.DefaultValue.copy())
+ self.Table.SetValueByName(new_row, "Object Identifier", BACnetObjectID_NUL) # start off with invalid object ID
+ # Find an apropriate BACnet object ID for the new object.
+ # We choose a first attempt (based on object ID of previous line + 1)
+ new_object_id = 0
+ if new_row > 0:
+ new_object_id = int(self.Table.GetValueByName(new_row - 1, "Object Identifier"))
+ new_object_id += 1
+ # Check whether the chosen object ID is not already in use.
+ # If in use, add 1 to the attempted object ID and recheck...
+ while self.Table.FindValueByName("Object Identifier", new_object_id) is not None:
+ new_object_id += 1
+ # if reached end of object IDs, cycle back to 0
+ # (remember, we may have started at any inital object ID > 0, so it makes sense to cyclce back to 0)
+ # warning: We risk entering an inifinite loop if all object IDs are already used.
+ # The likelyhood of this happening is extremely low, (we would need 2^22 elements in the table!)
+ # so don't worry about it for now.
+ if new_object_id > BACnetObjectID_MAX:
+ new_object_id = 0
+ # Set the object ID of the new object to the apropriate value
+ # ... and append the ID to the default object name (so the name becomes unique)
+ new_object_name = self.DefaultValue.get("Object Name") + " " + str(new_object_id)
+ self.Table.SetValueByName(new_row, "Object Name" , new_object_name)
+ self.Table.SetValueByName(new_row, "Object Identifier", new_object_id)
+ self.Table.ResetView(self)
+ return new_row
+
+
+ # Called when a object ID is changed
+ # call graph: ObjectEditor.OnVariablesGridCellChange() --> this method
+ # Will check whether there is a duplicate object ID, and highlight it if so.
+ def HighlightDuplicateObjectIDs(self):
+ if self.Table.GetNumberRows() < 2:
+ # Less than 2 rows. No duplicates are possible!
+ return
+ IDsCount = self.Table.GetObjectIDCount()
+ # check ALL object IDs for duplicates...
+ for row in range(self.Table.GetNumberRows()):
+ obj_id1 = int(self.Table.GetValueByName(row, "Object Identifier"))
+ if IDsCount[obj_id1] > 1:
+ # More than 1 BACnet object using this ID! Let us Highlight this row with errors...
+ # TODO: change the hardcoded column number '0' to a number obtained at runtime
+ # that is guaranteed to match the column titled "Object Identifier"
+ self.SetCellBackgroundColour(row, 0, ERROR_HIGHLIGHT[0])
+ self.SetCellTextColour (row, 0, ERROR_HIGHLIGHT[1])
+ else:
+ self.SetCellBackgroundColour(row, 0, wx.WHITE)
+ self.SetCellTextColour (row, 0, wx.BLACK)
+ # Refresh the graphical display to take into account any changes we may have made
+ self.ForceRefresh()
+ return None
+
+
+ # Called when the user changes the name of BACnet object (using the GUI grid)
+ # call graph: ObjectEditor.OnVariablesGridCellChange() -->
+ # --> BacnetSlaveEditorPlug.HighlightAllDuplicateObjectNames() -->
+ # --> ObjectEditor.HighlightDuplicateObjectNames() -->
+ # --> (this method)
+ # Will check whether there is a duplicate BACnet object name, and highlight it if so.
+ #
+ # Since the names of BACnet objects must be unique within the whole bacnet server (and
+ # not just among the BACnet objects of the same class (e.g. Analog Value, Binary Input, ...)
+ # to work properly this method must be passed a list of the names of all BACnet objects
+ # currently configured.
+ #
+ # AllObjectNamesFreq: a dictionary using as key the names of all currently configured BACnet
+ # objects, and value the number of objects using this same name.
+ def HighlightDuplicateObjectNames(self, AllObjectNamesFreq):
+ for row in range(self.Table.GetNumberRows()):
+ # TODO: change the hardcoded column number '1' to a number obtained at runtime
+ # that is guaranteed to match the column titled "Object Name"
+ if AllObjectNamesFreq[self.Table.GetValueByName(row, "Object Name")] > 1:
+ # This is an error! Highlight it...
+ self.SetCellBackgroundColour(row, 1, ERROR_HIGHLIGHT[0])
+ self.SetCellTextColour (row, 1, ERROR_HIGHLIGHT[1])
+ else:
+ self.SetCellBackgroundColour(row, 1, wx.WHITE)
+ self.SetCellTextColour (row, 1, wx.BLACK)
+ # Refresh the graphical display to take into account any changes we may have made
+ self.ForceRefresh()
+ return None
+
+
+
+
+class ObjectEditor(wx.Panel):
+ # This ObjectEditor class:
+ # Creates and controls the GUI window for configuring all the BACnet objects of one
+ # (generic) BACnet object type (e.g. Binary Value, Analog Input, Multi State Output, ...)
+ # This 'window' is currenty displayed within one tab of the bacnet plugin, but this
+ # may change in the future!
+ #
+ # It includes a grid to display all the BACnet objects of its type , as well as the buttons
+ # to insert, delete and move (up/down) a BACnet object in the grid.
+ # It also includes the sizers and spacers required to lay out the grid and buttons
+ # in the wndow.
+ #
+ def __init__(self, parent, window, controller, ObjTable):
+ # window: the window in which the editor will open.
+ # controller: The ConfigTreeNode object that controlls the data presented by
+ # this 'config tree node editor'
+ #
+ # parent: wx._controls.Notebook
+ # window: BacnetSlaveEditorPlug (i.e. beremiz.bacnet.BacnetSlaveEditor.BacnetSlaveEditorPlug)
+ # controller: controller will be an object of class
+ # FinalCTNClass (i.e. beremiz.ConfigTreeNode.FinalCTNClass )
+ # (FinalCTNClass inherits from: ConfigTreeNode and _BacnetSlavePlug)
+ # (For the BACnet plugin, it is easier to think of controller as a _BacnetSlavePlug,
+ # as the other classes are generic to all plugins!!)
+ #
+ # ObjTable: The object of class ObjectTable that stores the data displayed in the grid.
+ # This object is instantiated and managed by the _BacnetSlavePlug class.
+ #
+ self.window = window
+ self.controller = controller
+ self.ObjTable = ObjTable
+
+ wx.Panel.__init__(self, parent)
+
+ # The main sizer, 2 rows: top row for buttons, bottom row for 2D grid
+ self.MainSizer = wx.FlexGridSizer(cols=1, hgap=10, rows=2, vgap=0)
+ self.MainSizer.AddGrowableCol(0)
+ self.MainSizer.AddGrowableRow(1)
+
+ # sizer placed on top row of main sizer:
+ # 1 row; 6 columns: 1 static text, one stretchable spacer, 4 buttons
+ controls_sizer = wx.FlexGridSizer(cols=6, hgap=4, rows=1, vgap=5)
+ controls_sizer.AddGrowableCol(0)
+ controls_sizer.AddGrowableRow(0)
+ self.MainSizer.Add(controls_sizer, border=5, flag=wx.GROW|wx.ALL)
+
+ # the buttons that populate the controls sizer (itself in top row of the main sizer)
+ # NOTE: the _("string") function will translate the "string" to the local language
+ controls_sizer.Add(wx.StaticText(self, label=_('Object Properties:')), flag=wx.ALIGN_BOTTOM)
+ controls_sizer.AddStretchSpacer()
+ for name, bitmap, help in [
+ ("AddButton" , "add_element" , _("Add variable" )),
+ ("DeleteButton", "remove_element", _("Remove variable" )),
+ ("UpButton" , "up" , _("Move variable up" )),
+ ("DownButton" , "down" , _("Move variable down"))]:
+ button = wx.lib.buttons.GenBitmapButton(self, bitmap=GetBitmap(bitmap),
+ size=wx.Size(28, 28),
+ style=wx.NO_BORDER)
+ button.SetToolTipString(help)
+ setattr(self, name, button)
+ controls_sizer.Add(button)
+
+ # the variable grid that will populate the bottom row of the main sizer
+ panel = self
+ self.VariablesGrid = ObjectGrid(panel, style=wx.VSCROLL)
+ #self.VariablesGrid.SetDropTarget(VariableDropTarget(self)) # use only to enable drag'n'drop
+ self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.OnVariablesGridCellChange)
+ #self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK, self.OnVariablesGridCellLeftClick)
+ #self.VariablesGrid.Bind(wx.grid.EVT_GRID_EDITOR_SHOWN, self.OnVariablesGridEditorShown)
+ self.MainSizer.Add(self.VariablesGrid, flag=wx.GROW)
+
+ # Configure the Variables Grid...
+ self.VariablesGrid.SetRowLabelSize(0) # do not include a leftmost column containing the 'row label'
+ self.VariablesGrid.SetButtons({"Add": self.AddButton,
+ "Delete": self.DeleteButton,
+ "Up": self.UpButton,
+ "Down": self.DownButton})
+ # The custom grid needs to know the default values to use when 'AddButton' creates a new row
+ # NOTE: ObjTable.BACnetObjectType will contain the class name of one of the following classes
+ # (BVObject, BIObject, BOObject, AVObject, AIObject, AOObject, MSVObject, MSIObject, MSOObject)
+ # which inherit from one of (BinaryObject, AnalogObject, MultiSObject)
+ self.VariablesGrid.SetDefaultValue(self.ObjTable.BACnetObjectType.DefaultValues)
+
+ # self.ObjTable: The table that contains the data displayed in the grid
+ # This table was instantiated/created in the initializer for class _BacnetSlavePlug
+ self.VariablesGrid.SetTable(self.ObjTable)
+ self.VariablesGrid.SetEditable(True)
+ # set the column attributes (width, alignment)
+ # NOTE: ObjTable.BACnetObjectType will contain the class name of one of the following classes
+ # (BVObject, BIObject, BOObject, AVObject, AIObject, AOObject, MSVObject, MSIObject, MSOObject)
+ # which inherit from one of (BinaryObject, AnalogObject, MultiSObject)
+ ColumnAlignments = self.ObjTable.BACnetObjectType.ColumnAlignments
+ ColumnSizes = self.ObjTable.BACnetObjectType.ColumnSizes
+ for col in range( self.ObjTable.GetNumberCols()):
+ attr = wx.grid.GridCellAttr()
+ attr.SetAlignment(ColumnAlignments[col], wx.ALIGN_CENTRE)
+ self.VariablesGrid.SetColAttr(col, attr)
+ self.VariablesGrid.SetColMinimalWidth(col, ColumnSizes[col])
+ self.VariablesGrid.AutoSizeColumn(col, False)
+
+ # layout the items in all sizers, and show them too.
+ self.SetSizer(self.MainSizer) # Have the wondow 'own' the sizer...
+ #self.MainSizer.ShowItems(True) # not needed once the window 'owns' the sizer (SetSizer())
+ #self.MainSizer.Layout() # not needed once the window 'owns' the sizer (SetSizer())
+
+ # Refresh the view of the grid...
+ # We ask the table to do that, who in turn will configure the grid for us!!
+ # It will configure the CellRenderers and CellEditors taking into account the type of
+ # BACnet object being shown in the grid!!
+ #
+ # Yes, I (Mario de Sousa) know this architecture does not seem to make much sense.
+ # It seems to me that the cell renderers etc. should all be configured right here.
+ # Unfortunately we inherit from the customTable and customGrid classes in Beremiz
+ # (in order to maintain GUI consistency), so we have to adopt their way of doing things.
+ #
+ # NOTE: ObjectTable.ResetView() (remember, ObjTable is of class ObjectTable)
+ # calls ObjectTable._updateColAttrs(), who will do the configuring.
+ self.ObjTable.ResetView(self.VariablesGrid)
+
+
+ def RefreshView(self):
+ #print "ObjectEditor.RefreshView() called!!!"
+ # Check for Duplicate Object IDs is only done within same BACnet object type (ID is unique by type).
+ # The VariablesGrid class can handle it by itself.
+ self.VariablesGrid.HighlightDuplicateObjectIDs()
+ # Check for Duplicate Object Names must be done globally (Object Name is unique within bacnet server)
+ # Only the BacnetSlaveEditorPlug can and will handle this.
+ #self.window.HighlightAllDuplicateObjectNames()
+ pass
+
+ #########################################
+ # Event handlers for the Variables Grid #
+ #########################################
+ def OnVariablesGridCellChange(self, event):
+ row, col = event.GetRow(), event.GetCol()
+ #print "ObjectEditor.OnVariablesGridCellChange(row=%s, col=%s) called!!!" % (row, col)
+ self.ObjTable.ChangesToSave = True
+ if self.ObjTable.GetColLabelValue(col) == "Object Identifier":
+ # an Object ID was changed => must check duplicate object IDs.
+ self.VariablesGrid.HighlightDuplicateObjectIDs()
+ if self.ObjTable.GetColLabelValue(col) == "Object Name":
+ # an Object Name was changed => must check duplicate object names.
+ # Note that this must be done to _all_ BACnet objects, and not just the objects
+ # of the same BACnet class (Binary Value, Analog Input, ...)
+ # So we have the BacnetSlaveEditorPlug class do it...
+ self.window.HighlightAllDuplicateObjectNames()
+ # There are changes to save =>
+ # udate the enabled/disabled state of the 'save' option in the 'file' menu
+ self.window.RefreshBeremizWindow()
+ event.Skip()
+
+ def OnVariablesGridCellLeftClick(self, event):
+ row = event.GetRow()
+
+ def OnVariablesGridEditorShown(self, event):
+ row, col = event.GetRow(), event.GetCol()
+
+ def HighlightDuplicateObjectNames(self, AllObjectNamesFreq):
+ return self.VariablesGrid.HighlightDuplicateObjectNames(AllObjectNamesFreq)
+
+
+
+
+
+
+class BacnetSlaveEditorPlug(ConfTreeNodeEditor):
+ # inheritance tree
+ # wx.SplitterWindow-->EditorPanel-->ConfTreeNodeEditor-->BacnetSlaveEditorPlug
+ #
+ # self.Controller -> The object that controls the data displayed in this editor
+ # In our case, the object of class _BacnetSlavePlug
+
+ CONFNODEEDITOR_TABS = [
+ (_("Analog Value Objects"), "_create_AV_ObjectEditor"),
+ (_("Analog Output Objects"), "_create_AO_ObjectEditor"),
+ (_("Analog Input Objects"), "_create_AI_ObjectEditor"),
+ (_("Binary Value Objects"), "_create_BV_ObjectEditor"),
+ (_("Binary Output Objects"), "_create_BO_ObjectEditor"),
+ (_("Binary Input Objects"), "_create_BI_ObjectEditor"),
+ (_("Multi-State Value Objects"), "_create_MSV_ObjectEditor"),
+ (_("Multi-State Output Objects"), "_create_MSO_ObjectEditor"),
+ (_("Multi-State Input Objects"), "_create_MSI_ObjectEditor")]
+
+ def _create_AV_ObjectEditor(self, parent):
+ self.AV_ObjectEditor = ObjectEditor(parent, self, self.Controler, self.Controler.ObjTables["AV_Obj"])
+ return self.AV_ObjectEditor
+
+ def _create_AO_ObjectEditor(self, parent):
+ self.AO_ObjectEditor = ObjectEditor(parent, self, self.Controler, self.Controler.ObjTables["AO_Obj"])
+ return self.AO_ObjectEditor
+
+ def _create_AI_ObjectEditor(self, parent):
+ self.AI_ObjectEditor = ObjectEditor(parent, self, self.Controler, self.Controler.ObjTables["AI_Obj"])
+ return self.AI_ObjectEditor
+
+ def _create_BV_ObjectEditor(self, parent):
+ self.BV_ObjectEditor = ObjectEditor(parent, self, self.Controler, self.Controler.ObjTables["BV_Obj"])
+ return self.BV_ObjectEditor
+
+ def _create_BO_ObjectEditor(self, parent):
+ self.BO_ObjectEditor = ObjectEditor(parent, self, self.Controler, self.Controler.ObjTables["BO_Obj"])
+ return self.BO_ObjectEditor
+
+ def _create_BI_ObjectEditor(self, parent):
+ self.BI_ObjectEditor = ObjectEditor(parent, self, self.Controler, self.Controler.ObjTables["BI_Obj"])
+ return self.BI_ObjectEditor
+
+ def _create_MSV_ObjectEditor(self, parent):
+ self.MSV_ObjectEditor = ObjectEditor(parent, self, self.Controler, self.Controler.ObjTables["MSV_Obj"])
+ return self.MSV_ObjectEditor
+
+ def _create_MSO_ObjectEditor(self, parent):
+ self.MSO_ObjectEditor = ObjectEditor(parent, self, self.Controler, self.Controler.ObjTables["MSO_Obj"])
+ return self.MSO_ObjectEditor
+
+ def _create_MSI_ObjectEditor(self, parent):
+ self.MSI_ObjectEditor = ObjectEditor(parent, self, self.Controler, self.Controler.ObjTables["MSI_Obj"])
+ return self.MSI_ObjectEditor
+
+ def __init__(self, parent, controler, window, editable=True):
+ self.Editable = editable
+ ConfTreeNodeEditor.__init__(self, parent, controler, window)
+
+ def __del__(self):
+ self.Controler.OnCloseEditor(self)
+
+ def GetConfNodeMenuItems(self):
+ return []
+
+ def RefreshConfNodeMenu(self, confnode_menu):
+ return
+
+ def RefreshView(self):
+ self.HighlightAllDuplicateObjectNames()
+ ConfTreeNodeEditor.RefreshView(self)
+ self. AV_ObjectEditor.RefreshView()
+ self. AO_ObjectEditor.RefreshView()
+ self. AI_ObjectEditor.RefreshView()
+ self. BV_ObjectEditor.RefreshView()
+ self. BO_ObjectEditor.RefreshView()
+ self. BI_ObjectEditor.RefreshView()
+ self.MSV_ObjectEditor.RefreshView()
+ self.MSO_ObjectEditor.RefreshView()
+ self.MSI_ObjectEditor.RefreshView()
+
+ def HighlightAllDuplicateObjectNames(self):
+ ObjectNamesCount = self.Controler.GetObjectNamesCount()
+ self. AV_ObjectEditor.HighlightDuplicateObjectNames(ObjectNamesCount)
+ self. AO_ObjectEditor.HighlightDuplicateObjectNames(ObjectNamesCount)
+ self. AI_ObjectEditor.HighlightDuplicateObjectNames(ObjectNamesCount)
+ self. BV_ObjectEditor.HighlightDuplicateObjectNames(ObjectNamesCount)
+ self. BO_ObjectEditor.HighlightDuplicateObjectNames(ObjectNamesCount)
+ self. BI_ObjectEditor.HighlightDuplicateObjectNames(ObjectNamesCount)
+ self.MSV_ObjectEditor.HighlightDuplicateObjectNames(ObjectNamesCount)
+ self.MSO_ObjectEditor.HighlightDuplicateObjectNames(ObjectNamesCount)
+ self.MSI_ObjectEditor.HighlightDuplicateObjectNames(ObjectNamesCount)
+ return None
+
+
+ def RefreshBeremizWindow(self):
+ # self.ParentWindow is the top level Beremiz class (object) that
+ # handles the beremiz window and layout
+ self.ParentWindow.RefreshTitle() # Refresh the title of the Beremiz window
+ # (it changes depending on whether there are
+ # changes to save!! )
+ self.ParentWindow.RefreshFileMenu() # Refresh the enabled/disabled state of the
+ # entries in the main 'file' menu.
+ # ('Save' sub-menu should become enabled
+ # if there are changes to save! )
+ ###self.ParentWindow.RefreshEditMenu()
+ ###self.ParentWindow.RefreshPageTitles()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bacnet/__init__.py Fri Jun 08 13:28:00 2018 +0200
@@ -0,0 +1,26 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# This file is part of Beremiz, a Integrated Development Environment for
+# programming IEC 61131-3 automates supporting plcopen standard and CanFestival.
+#
+# Copyright (c) 2017 Mario de Sousa (msousa@fe.up.pt)
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# This code is made available on the understanding that it will not be
+# used in safety-critical situations without a full and competent review.
+
+
+from bacnet import *
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bacnet/bacnet.py Fri Jun 08 13:28:00 2018 +0200
@@ -0,0 +1,697 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# This file is part of Beremiz, a Integrated Development Environment for
+# programming IEC 61131-3 automates supporting plcopen standard.
+# This files implements the bacnet plugin for Beremiz, adding BACnet server support.
+#
+# Copyright (c) 2017 Mario de Sousa (msousa@fe.up.pt)
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# This code is made available on the understanding that it will not be
+# used in safety-critical situations without a full and competent review.
+
+
+
+import os, sys
+from collections import Counter
+from datetime import datetime
+
+base_folder = os.path.split(os.path.dirname(os.path.realpath(__file__)))[0]
+base_folder = os.path.join(base_folder, "..")
+BacnetPath = os.path.join(base_folder, "BACnet")
+BacnetLibraryPath = os.path.join(BacnetPath, "lib")
+BacnetIncludePath = os.path.join(BacnetPath, "include")
+BacnetIncludePortPath = os.path.join(BacnetPath, "ports")
+BacnetIncludePortPath = os.path.join(BacnetIncludePortPath, "linux")
+
+import wx
+import pickle
+
+from BacnetSlaveEditor import *
+from BacnetSlaveEditor import ObjectProperties
+from ConfigTreeNode import ConfigTreeNode
+from PLCControler import LOCATION_CONFNODE, LOCATION_MODULE, LOCATION_GROUP, LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY
+
+# Parameters to be monkey patched in beremiz customizations
+BACNET_VENDOR_ID = 9999
+BACNET_VENDOR_NAME = "Beremiz.org"
+BACNET_DEVICE_MODEL_NAME = "Beremiz PLC"
+
+###################################################
+###################################################
+# #
+# S L A V E D E V I C E #
+# #
+###################################################
+###################################################
+
+# NOTE: Objects of class _BacnetSlavePlug are never instantiated directly.
+# The objects are instead instantiated from class FinalCTNClass
+# FinalCTNClass inherits from: - ConfigTreeNode
+# - The tree node plug (in our case _BacnetSlavePlug)
+#class _BacnetSlavePlug:
+class RootClass:
+ XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
+ <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+ <xsd:element name="BACnetServerNode">
+ <xsd:complexType>
+ <xsd:attribute name="Network_Interface" type="xsd:string" use="optional" default="eth0"/>
+ <xsd:attribute name="UDP_Port_Number" use="optional" default="47808">
+ <xsd:simpleType>
+ <xsd:restriction base="xsd:integer">
+ <xsd:minInclusive value="0"/>
+ <xsd:maxInclusive value="65535"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ </xsd:attribute>
+ <xsd:attribute name="BACnet_Communication_Control_Password"
+ type="xsd:string" use="optional" default="Malba Tahan"/>
+ <xsd:attribute name="BACnet_Device_ID" use="optional" default="0">
+ <xsd:simpleType>
+ <xsd:restriction base="xsd:integer">
+ <xsd:minInclusive value="0"/>
+ <xsd:maxInclusive value="4194302"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ </xsd:attribute>
+ <xsd:attribute name="BACnet_Device_Name" type="xsd:string" use="optional" default="Beremiz device 0"/>
+ <xsd:attribute name="BACnet_Device_Location" type="xsd:string" use="optional" default=""/>
+ <xsd:attribute name="BACnet_Device_Description" type="xsd:string" use="optional" default="Beremiz device 0"/>
+ <xsd:attribute name="BACnet_Device_Application_Software_Version" type="xsd:string" use="optional" default="1.0"/>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:schema>
+ """
+ # NOTE: BACnet device (object) IDs are 22 bits long (not counting the 10 bits for the type ID)
+ # so the Device instance ID is limited from 0 to 22^2-1 = 4194303
+ # However, 4194303 is reserved for special use (similar to NULL pointer), so last
+ # valid ID becomes 4194302
+
+
+ # The class/object that will render the graphical interface to edit the
+ # BacnetSlavePlug's configuration parameters. The object of class BacnetSlaveEditorPlug
+ # will be instantiated by the ConfigTreeNode class.
+ # This BacnetSlaveEditorPlug object can be accessed from _BacnetSlavePlug as
+ # 'self._View'
+ # See the following note to understand how this is possible!
+ #
+ # NOTE: Objects of class _BacnetSlavePlug are never instantiated directly.
+ # The objects are instead instantiated from class FinalCTNClass
+ # FinalCTNClass inherits from: - ConfigTreeNode
+ # - The tree node plug (in our case _BacnetSlavePlug)
+ #
+ # This means that objects of class _BacnetSlavePlug may safely access all the members
+ # of classes ConfigTreeNode as well as FinalCTNClass (since they are always instantiated
+ # as a FinalCTNClass)
+ EditorType = BacnetSlaveEditorPlug
+
+ # The following classes follow the model/viewer design pattern
+ #
+ # _BacnetSlavePlug - contains the model (i.e. configuration parameters)
+ # BacnetSlaveEditorPlug - contains the viewer (and editor, so it includes the 'controller' part of the
+ # design pattern which in this case is not separated from the viewer)
+ #
+ # The _BacnetSlavePlug object is 'permanent', i.e. it exists as long as the beremiz project is open
+ # The BacnetSlaveEditorPlug object is 'transient', i.e. it exists only while the editor is visible/open
+ # in the editing panel. It is destoryed whenever
+ # the user closes the corresponding tab in the
+ # editing panel, and a new object is created when
+ # the editor is re-opened.
+ #
+ # _BacnetSlavePlug contains: AV_ObjTable, ...
+ # (these are the objects that actually store the config parameters or 'model'
+ # and are therefore stored to a file)
+ #
+ # _BacnetSlavePlug contains: AV_VarEditor, ...
+ # (these are the objects that implement a grid table to edit/view the
+ # corresponding mode parameters)
+ #
+ # Logic:
+ # - The xx_VarEditor classes inherit from wx.grid.Grid
+ # - The xx_ObjTable classes inherit from wx.grid.PyGridTableBase
+ # To be more precise, the inheritance tree is actually:
+ # xx_VarEditor -> ObjectGrid -> CustomGrid -> wx.grid.Grid
+ # xx_ObjTable -> ObjectTable -> CustomTable -> wx.grid.PyGridTableBase)
+ #
+ # Note that wx.grid.Grid is prepared to work with wx.grid.PyGridTableBase as the container of
+ # data that is displayed and edited in the Grid.
+
+
+ ConfNodeMethods = [
+ {"bitmap" : "ExportSlave",
+ "name" : _("Export slave"),
+ "tooltip" : _("Export BACnet slave to EDE file"),
+ "method" : "_ExportBacnetSlave"},
+ ]
+
+ def __init__(self):
+ # Initialize the dictionary that stores the current configuration for the Analog/Digital/MultiValued Variables
+ # in this BACnet server.
+ self.ObjTablesData = {}
+ self.ObjTablesData[ "AV_Obj"] = [] # Each list will contain an entry for each row in the xxxxVar grid!!
+ self.ObjTablesData[ "AO_Obj"] = [] # Each entry/row will be a dictionary
+ self.ObjTablesData[ "AI_Obj"] = [] # Each dictionary will contain all entries/data
+ self.ObjTablesData[ "BV_Obj"] = [] # for one row in the grid.
+ self.ObjTablesData[ "BO_Obj"] = [] # Same structure as explained above...
+ self.ObjTablesData[ "BI_Obj"] = [] # Same structure as explained above...
+ self.ObjTablesData["MSV_Obj"] = [] # Same structure as explained above...
+ self.ObjTablesData["MSO_Obj"] = [] # Same structure as explained above...
+ self.ObjTablesData["MSI_Obj"] = [] # Same structure as explained above...
+
+ self.ObjTablesData["EDEfile_parm"] = {"next_EDE_file_version":1}
+ # EDE files inlcude extra parameters (ex. file version)
+ # We would like to save the parameters the user configures
+ # so they are available the next time the user opens the project.
+ # Since this plugin is only storing the ObjTablesData[] dict
+ # to file, we add that info to this dictionary too.
+ # Yes, I know this is kind of a hack.
+
+ filepath = self.GetFileName()
+ if(os.path.isfile(filepath)):
+ self.LoadFromFile(filepath)
+
+ self.ObjTables = {}
+ self.ObjTables[ "AV_Obj"] = ObjectTable(self, self.ObjTablesData[ "AV_Obj"], AVObject)
+ self.ObjTables[ "AO_Obj"] = ObjectTable(self, self.ObjTablesData[ "AO_Obj"], AOObject)
+ self.ObjTables[ "AI_Obj"] = ObjectTable(self, self.ObjTablesData[ "AI_Obj"], AIObject)
+ self.ObjTables[ "BV_Obj"] = ObjectTable(self, self.ObjTablesData[ "BV_Obj"], BVObject)
+ self.ObjTables[ "BO_Obj"] = ObjectTable(self, self.ObjTablesData[ "BO_Obj"], BOObject)
+ self.ObjTables[ "BI_Obj"] = ObjectTable(self, self.ObjTablesData[ "BI_Obj"], BIObject)
+ self.ObjTables["MSV_Obj"] = ObjectTable(self, self.ObjTablesData["MSV_Obj"], MSVObject)
+ self.ObjTables["MSO_Obj"] = ObjectTable(self, self.ObjTablesData["MSO_Obj"], MSOObject)
+ self.ObjTables["MSI_Obj"] = ObjectTable(self, self.ObjTablesData["MSI_Obj"], MSIObject)
+ # list containing the data in the table <--^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+
+ ######################################
+ # Functions to be called by CTNClass #
+ ######################################
+ # The following functions would be somewhat equvalent to virtual functions/methods in C++ classes
+ # They will be called by the base class (CTNClass) from which this _BacnetSlavePlug class derives.
+
+ def GetCurrentNodeName(self):
+ return self.CTNName()
+
+ def GetFileName(self):
+ return os.path.join(self.CTNPath(), 'bacnet_slave')
+
+ def OnCTNSave(self, from_project_path=None):
+ return self.SaveToFile(self.GetFileName())
+
+
+ def CTNTestModified(self):
+ # self.ChangesToSave: Check whether any of the parameters, defined in the XSD above, were changed.
+ # This is handled by the ConfigTreeNode class
+ # (Remember that no objects are ever instantiated from _BacnetSlavePlug.
+ # Objects are instead created from FinalCTNClass, which derives from
+ # _BacnetSlavePlug and ConfigTreeNode. This means that we can exceptionally
+ # consider that all objects of type _BacnetSlavePlug will also be a ConfigTreeNode).
+ result = self.ChangesToSave or self.ObjTables[ "AV_Obj"].ChangesToSave \
+ or self.ObjTables[ "AO_Obj"].ChangesToSave \
+ or self.ObjTables[ "AI_Obj"].ChangesToSave \
+ or self.ObjTables[ "BV_Obj"].ChangesToSave \
+ or self.ObjTables[ "BO_Obj"].ChangesToSave \
+ or self.ObjTables[ "BI_Obj"].ChangesToSave \
+ or self.ObjTables["MSV_Obj"].ChangesToSave \
+ or self.ObjTables["MSO_Obj"].ChangesToSave \
+ or self.ObjTables["MSI_Obj"].ChangesToSave
+ return result
+
+ ### Currently not needed. Override _OpenView() in case we need to do some special stuff whenever the editor is opened!
+ ##def _OpenView(self, name=None, onlyopened=False):
+ ##print "_BacnetSlavePlug._OpenView() Called!!!"
+ ##ConfigTreeNode._OpenView(self, name, onlyopened)
+ ###print self._View
+ #####if self._View is not None:
+ #####self._View.SetBusId(self.GetCurrentLocation())
+ ##return self._View
+
+
+ def GetVariableLocationTree(self):
+ current_location = self.GetCurrentLocation()
+ # see comment in CTNGenerate_C regarding identical line of code!
+ locstr = ".".join(map(str,current_location))
+
+ # IDs used by BACnet to identify object types/class.
+ # OBJECT_ANALOG_INPUT = 0,
+ # OBJECT_ANALOG_OUTPUT = 1,
+ # OBJECT_ANALOG_VALUE = 2,
+ # OBJECT_BINARY_INPUT = 3,
+ # OBJECT_BINARY_OUTPUT = 4,
+ # OBJECT_BINARY_VALUE = 5,
+ # OBJECT_MULTI_STATE_INPUT = 13,
+ # OBJECT_MULTI_STATE_OUTPUT = 14,
+ # OBJECT_MULTI_STATE_VALUE = 19,
+ #
+ # Since Binary Value, Analog Value, etc. objects may use the same
+ # object ID (since they have distinct class IDs), we must also distinguish them in some way in
+ # the %MX0.3.4 IEC 61131-3 syntax.
+ #
+ # For this reason we add the BACnet class identifier to the %MX0.5.3 location.
+ # For example, for a BACnet plugin in location '0' of the Beremiz configuration tree,
+ # all Binary Values will be mapped onto: %MX0.5.xxx (xxx is object ID)
+ # all Multi State Values will be mapped onto: %MB0.19.xxx (xxx is object ID)
+ # all Analog Values will be mapped onto: %MD0.2.xxx (xxx is object ID)
+ # etc..
+ #
+ # Value objects will be mapped onto %M
+ # Input objects will be mapped onto %I
+ # Output objects will be mapped onto %Q
+
+ BACnetEntries = []
+ BACnetEntries.append(self.GetSlaveLocationTree(
+ self.ObjTablesData[ "AV_Obj"], 32, 'REAL', 'D', locstr+ '.2', 'Analog Values'))
+ BACnetEntries.append(self.GetSlaveLocationTree(
+ self.ObjTablesData[ "AO_Obj"], 32, 'REAL', 'D', locstr+ '.1', 'Analog Outputs'))
+ BACnetEntries.append(self.GetSlaveLocationTree(
+ self.ObjTablesData[ "AI_Obj"], 32, 'REAL', 'D', locstr+ '.0', 'Analog Inputs'))
+ BACnetEntries.append(self.GetSlaveLocationTree(
+ self.ObjTablesData[ "BV_Obj"], 1, 'BOOL', 'X', locstr+ '.5', 'Binary Values'))
+ BACnetEntries.append(self.GetSlaveLocationTree(
+ self.ObjTablesData[ "BO_Obj"], 1, 'BOOL', 'X', locstr+ '.4', 'Binary Outputs'))
+ BACnetEntries.append(self.GetSlaveLocationTree(
+ self.ObjTablesData[ "BI_Obj"], 1, 'BOOL', 'X', locstr+ '.3', 'Binary Inputs'))
+ BACnetEntries.append(self.GetSlaveLocationTree(
+ self.ObjTablesData["MSV_Obj"], 8, 'BYTE', 'B', locstr+'.19', 'Multi State Values'))
+ BACnetEntries.append(self.GetSlaveLocationTree(
+ self.ObjTablesData["MSO_Obj"], 8, 'BYTE', 'B', locstr+'.14', 'Multi State Outputs'))
+ BACnetEntries.append(self.GetSlaveLocationTree(
+ self.ObjTablesData["MSI_Obj"], 8, 'BYTE', 'B', locstr+'.13', 'Multi State Inputs'))
+
+ return {"name": self.BaseParams.getName(),
+ "type": LOCATION_CONFNODE,
+ "location": locstr + ".x",
+ "children": BACnetEntries}
+
+
+ ############################
+ # Helper functions/methods #
+ ############################
+ # a helper function to GetVariableLocationTree()
+ def GetSlaveLocationTree(self, ObjTablesData, size_in_bits, IECdatatype, location_size, location_str, name):
+ BACnetObjectEntries = []
+ for xx_ObjProp in ObjTablesData:
+ BACnetObjectEntries.append({
+ "name": str(xx_ObjProp["Object Identifier"]) + ': ' + xx_ObjProp["Object Name"],
+ "type": LOCATION_VAR_MEMORY, # LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, or LOCATION_VAR_MEMORY
+ "size": size_in_bits, # 1 or 16
+ "IEC_type": IECdatatype, # 'BOOL', 'WORD', ...
+ "var_name": "var_name", # seems to be ignored??
+ "location": location_size + location_str + "." + str(xx_ObjProp["Object Identifier"]),
+ "description": "description", # seems to be ignored?
+ "children": []})
+
+ BACnetEntries = []
+ return {"name": name,
+ "type": LOCATION_CONFNODE,
+ "location": location_str + ".x",
+ "children": BACnetObjectEntries}
+
+
+ # Returns a dictionary with:
+ # keys: names of BACnet objects
+ # value: number of BACnet objects using this same name
+ # (values larger than 1 indicates an error as BACnet requires unique names)
+ def GetObjectNamesCount(self):
+ # The dictionary is built by first creating a list containing the names of _ALL_
+ # BACnet objects currently configured by the user (using the GUI)
+ ObjectNames = []
+ ObjectNames.extend(self.ObjTables[ "AV_Obj"].GetAllValuesByName("Object Name"))
+ ObjectNames.extend(self.ObjTables[ "AO_Obj"].GetAllValuesByName("Object Name"))
+ ObjectNames.extend(self.ObjTables[ "AI_Obj"].GetAllValuesByName("Object Name"))
+ ObjectNames.extend(self.ObjTables[ "BV_Obj"].GetAllValuesByName("Object Name"))
+ ObjectNames.extend(self.ObjTables[ "BO_Obj"].GetAllValuesByName("Object Name"))
+ ObjectNames.extend(self.ObjTables[ "BI_Obj"].GetAllValuesByName("Object Name"))
+ ObjectNames.extend(self.ObjTables["MSV_Obj"].GetAllValuesByName("Object Name"))
+ ObjectNames.extend(self.ObjTables["MSO_Obj"].GetAllValuesByName("Object Name"))
+ ObjectNames.extend(self.ObjTables["MSI_Obj"].GetAllValuesByName("Object Name"))
+ # This list is then transformed into a collections.Counter class
+ # Which is then transformed into a dictionary using dict()
+ return dict(Counter(ObjectNames))
+
+ # Check whether the current configuration contains BACnet objects configured
+ # with the same identical object name (returns True or False)
+ def HasDuplicateObjectNames(self):
+ ObjectNamesCount = self.GetObjectNamesCount()
+ for ObjName in ObjectNamesCount:
+ if ObjectNamesCount[ObjName] > 1:
+ return True
+ return False
+
+ # Check whether any object ID is used more than once (not valid in BACnet)
+ # (returns True or False)
+ def HasDuplicateObjectIDs(self):
+ res = self.ObjTables[ "AV_Obj"].HasDuplicateObjectIDs()
+ res = res or self.ObjTables[ "AO_Obj"].HasDuplicateObjectIDs()
+ res = res or self.ObjTables[ "AI_Obj"].HasDuplicateObjectIDs()
+ res = res or self.ObjTables[ "BV_Obj"].HasDuplicateObjectIDs()
+ res = res or self.ObjTables[ "BO_Obj"].HasDuplicateObjectIDs()
+ res = res or self.ObjTables[ "BI_Obj"].HasDuplicateObjectIDs()
+ res = res or self.ObjTables["MSV_Obj"].HasDuplicateObjectIDs()
+ res = res or self.ObjTables["MSO_Obj"].HasDuplicateObjectIDs()
+ res = res or self.ObjTables["MSI_Obj"].HasDuplicateObjectIDs()
+ return res
+
+
+ #######################################################
+ # Methods related to files (saving/loading/exporting) #
+ #######################################################
+ def SaveToFile(self, filepath):
+ # Save node data in file
+ # The configuration data declared in the XSD string will be saved by the ConfigTreeNode class,
+ # so we only need to save the data that is stored in ObjTablesData objects
+ # Note that we do not store the ObjTables objects. ObjTables is of a class that
+ # contains more stuff we do not need to store. Actually it is a bad idea to store
+ # this extra stuff (as we would make the files we generate dependent on the actual
+ # version of the wx library we are using!!! Remember that ObjTables evetually
+ # derives/inherits from wx.grid.PyGridTableBase). Another reason not to store the whole
+ # object is because it is not pickable (i.e. pickle.dump() cannot handle it)!!
+ try:
+ fd = open(filepath, "w")
+ pickle.dump(self.ObjTablesData, fd)
+ fd.close()
+ # On successfull save, reset flags to indicate no more changes that need saving
+ self.ObjTables[ "AV_Obj"].ChangesToSave = False
+ self.ObjTables[ "AO_Obj"].ChangesToSave = False
+ self.ObjTables[ "AI_Obj"].ChangesToSave = False
+ self.ObjTables[ "BV_Obj"].ChangesToSave = False
+ self.ObjTables[ "BO_Obj"].ChangesToSave = False
+ self.ObjTables[ "BI_Obj"].ChangesToSave = False
+ self.ObjTables["MSV_Obj"].ChangesToSave = False
+ self.ObjTables["MSO_Obj"].ChangesToSave = False
+ self.ObjTables["MSI_Obj"].ChangesToSave = False
+ return True
+ except:
+ return _("Unable to save to file \"%s\"!")%filepath
+
+ def LoadFromFile(self, filepath):
+ # Load the data that is saved in SaveToFile()
+ try:
+ fd = open(filepath, "r")
+ self.ObjTablesData = pickle.load(fd)
+ fd.close()
+ return True
+ except:
+ return _("Unable to load file \"%s\"!")%filepath
+
+ def _ExportBacnetSlave(self):
+ dialog = wx.FileDialog(self.GetCTRoot().AppFrame,
+ _("Choose a file"),
+ os.path.expanduser("~"),
+ "%s_EDE.csv" % self.CTNName(),
+ _("EDE files (*_EDE.csv)|*_EDE.csv|All files|*.*"),
+ wx.SAVE|wx.OVERWRITE_PROMPT)
+ if dialog.ShowModal() == wx.ID_OK:
+ result = self.GenerateEDEFile(dialog.GetPath())
+ result = False
+ if result:
+ self.GetCTRoot().logger.write_error(_("Error: Export slave failed\n"))
+ dialog.Destroy()
+
+
+ def GenerateEDEFile(self, filename):
+ template_file_dir = os.path.join(os.path.split(__file__)[0],"ede_files")
+
+ #The BACnetServerNode attribute is added dynamically by ConfigTreeNode._AddParamsMembers()
+ # It will be an XML parser object created by GenerateParserFromXSDstring(self.XSD).CreateRoot()
+ BACnet_Device_ID = self.BACnetServerNode.getBACnet_Device_ID()
+
+ # The EDE file contains a header that includes general project data (name, author, ...)
+ # Instead of asking the user for this data, we get it from the configuration
+ # of the Beremiz project itself.
+ # We ask the root Config Tree Node for the data...
+ ProjProp = {}
+ FileProp = {}
+ CTN_Root = self.GetCTRoot() # this should be an object of class ProjectController
+ Project = CTN_Root.Project # this should be an object capable of parsing
+ # PLCopen XML files. The parser is created automatically
+ # (i.e. using GenerateParserFromXSD() from xmlclass module)
+ # using the PLCopen XSD file defining the format of the XML.
+ # See the file plcopen/plcopen.py
+ if Project is not None:
+ # getcontentHeader() and getfileHeader() are functions that are conditionally defined in
+ # plcopn/plcopen.py We cannot rely on their existance
+ if getattr(Project, "getcontentHeader", None) is not None:
+ ProjProp = Project.getcontentHeader()
+ # getcontentHeader() returns a dictionary. Available keys are:
+ # "projectName", "projectVersion", "modificationDateTime",
+ # "organization", "authorName", "language", "pageSize", "scaling"
+ if getattr(Project, "getfileHeader", None) is not None:
+ FileProp = Project.getfileHeader()
+ # getfileHeader() returns a dictionary. Available keys are:
+ # "companyName", "companyURL", "productName", "productVersion",
+ # "productRelease", "creationDateTime", "contentDescription"
+
+ ProjName = ""
+ if "projectName" in ProjProp:
+ ProjName = ProjProp["projectName"]
+ ProjAuthor = ""
+ if "companyName" in FileProp:
+ ProjAuthor += "(" + FileProp["companyName"] + ")"
+ if "authorName" in ProjProp:
+ ProjAuthor = ProjProp["authorName"] + " " + ProjAuthor
+
+ projdata_dict = {}
+ projdata_dict["Project Name"] = ProjName
+ projdata_dict["Project Author"] = ProjAuthor
+ projdata_dict["Current Time"] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
+ projdata_dict["EDE file version"] = self.ObjTablesData["EDEfile_parm"]["next_EDE_file_version"]
+
+ # Next time we generate an EDE file, use another version!
+ self.ObjTablesData["EDEfile_parm"]["next_EDE_file_version"] += 1
+
+ AX_params_format = "%(Object Name)s;" + str(BACnet_Device_ID) + ";%(Object Name)s;%(BACnetObjTypeID)s;%(Object Identifier)s;%(Description)s;0;;;%(Settable)s;N;;;;%(Unit ID)s;"
+
+ BX_params_format = "%(Object Name)s;" + str(BACnet_Device_ID) + ";%(Object Name)s;%(BACnetObjTypeID)s;%(Object Identifier)s;%(Description)s;0;0;1;%(Settable)s;N;;;;;"
+
+ MSX_params_format = "%(Object Name)s;" + str(BACnet_Device_ID) + ";%(Object Name)s;%(BACnetObjTypeID)s;%(Object Identifier)s;%(Description)s;1;1;%(Number of States)s;%(Settable)s;N;;;;;"
+
+ Objects_List = []
+ for ObjType, params_format in [
+ ("AV" , AX_params_format ), ("AO" , AX_params_format ), ("AI" , AX_params_format ),
+ ("BV" , BX_params_format ), ("BO" , BX_params_format ), ("BI" , BX_params_format ),
+ ("MSV", MSX_params_format ), ("MSO", MSX_params_format ), ("MSI", MSX_params_format )
+ ]:
+ self.ObjTables[ObjType + "_Obj"].UpdateAllVirtualProperties()
+ for ObjProp in self.ObjTablesData[ObjType + "_Obj"]:
+ Objects_List.append(params_format % ObjProp)
+
+ # Normalize filename
+ for extension in ["_EDE.csv", "_ObjTypes.csv", "_StateTexts.csv", "_Units.csv"]:
+ if filename.lower().endswith(extension.lower()):
+ filename = filename[:-len(extension)]
+
+ # EDE_header
+ generate_file_name = filename + "_EDE.csv"
+ template_file_name = os.path.join(template_file_dir,"template_EDE.csv")
+ generate_file_content = open(template_file_name).read() % projdata_dict
+ generate_file_handle = open(generate_file_name,'w')
+ generate_file_handle .write(generate_file_content)
+ generate_file_handle .write("\n".join(Objects_List))
+ generate_file_handle .close()
+
+ # templates of remaining files do not need changes. They are simply copied unchanged!
+ for extension in ["_ObjTypes.csv", "_StateTexts.csv", "_Units.csv"]:
+ generate_file_name = filename + extension
+ template_file_name = os.path.join(template_file_dir,"template" + extension)
+ generate_file_content = open(template_file_name).read()
+ generate_file_handle = open(generate_file_name,'w')
+ generate_file_handle .write(generate_file_content)
+ generate_file_handle .close()
+
+
+ #############################
+ # Generate the source files #
+ #############################
+ def CTNGenerate_C(self, buildpath, locations):
+ # Determine the current location in Beremiz's project configuration tree
+ current_location = self.GetCurrentLocation()
+ # The current location of this plugin in Beremiz's configuration tree, separated by underscores
+ # NOTE: Since BACnet plugin currently does not use sub-branches in the tree (in other words, this
+ # _BacnetSlavePlug class was actually renamed as the RootClass), the current_location_dots
+ # will actually be a single number (e.g.: 0 or 3 or 6, corresponding to the location
+ # in which the plugin was inserted in the Beremiz configuration tree on Beremiz's left panel).
+ locstr = "_".join(map(str,current_location))
+
+ # First check whether all the current parameters (inserted by user in the GUI) are valid...
+ if self.HasDuplicateObjectNames():
+ self.GetCTRoot().logger.write_warning(_("Error: BACnet server '%s.x: %s' contains objects with duplicate object names.\n")%(locstr, self.CTNName()))
+ raise Exception, False
+ # TODO: return an error code instead of raising an exception (currently unsupported by Beremiz)
+
+ if self.HasDuplicateObjectIDs():
+ self.GetCTRoot().logger.write_warning(_("Error: BACnet server '%s.x: %s' contains objects with duplicate object identifiers.\n")%(locstr, self.CTNName()))
+ raise Exception, False
+ # TODO: return an error code instead of raising an exception (currently unsupported by Beremiz)
+
+ #-------------------------------------------------------------------------------
+ # Create and populate the loc_dict dictionary with all parameters needed to configure
+ # the generated source code (.c and .h files)
+ #-------------------------------------------------------------------------------
+
+ # 1) Create the dictionary (loc_dict = {})
+ loc_dict = {}
+ loc_dict["locstr"] = locstr
+
+ #The BACnetServerNode attribute is added dynamically by ConfigTreeNode._AddParamsMembers()
+ # It will be an XML parser object created by GenerateParserFromXSDstring(self.XSD).CreateRoot()
+ loc_dict["network_interface"] = self.BACnetServerNode.getNetwork_Interface()
+ loc_dict["port_number"] = self.BACnetServerNode.getUDP_Port_Number()
+ loc_dict["BACnet_Device_ID"] = self.BACnetServerNode.getBACnet_Device_ID()
+ loc_dict["BACnet_Device_Name"] = self.BACnetServerNode.getBACnet_Device_Name()
+ loc_dict["BACnet_Comm_Control_Password"] = self.BACnetServerNode.getBACnet_Communication_Control_Password()
+ loc_dict["BACnet_Device_Location"] = self.BACnetServerNode.getBACnet_Device_Location()
+ loc_dict["BACnet_Device_Description"] = self.BACnetServerNode.getBACnet_Device_Description()
+ loc_dict["BACnet_Device_AppSoft_Version"]= self.BACnetServerNode.getBACnet_Device_Application_Software_Version()
+ loc_dict["BACnet_Vendor_ID"] = BACNET_VENDOR_ID
+ loc_dict["BACnet_Vendor_Name"] = BACNET_VENDOR_NAME
+ loc_dict["BACnet_Model_Name"] = BACNET_DEVICE_MODEL_NAME
+
+ # 2) Add the data specific to each BACnet object type
+ # For each BACnet object type, start off by creating some intermediate helpful lists
+ # a) parameters_list containing the strings that will
+ # be included in the C source code, and which will initialize the struct with the
+ # object (Analog Value, Binary Value, or Multi State Value) parameters
+ # b) locatedvar_list containing the strings that will
+ # declare the memory to store the located variables, as well as the
+ # pointers (required by matiec) that point to that memory.
+
+ # format for delaring IEC 61131-3 variable (and pointer) onto which BACnet object is mapped
+ locvar_format = '%(Ctype)s ___%(loc)s_%(Object Identifier)s; ' + \
+ '%(Ctype)s *__%(loc)s_%(Object Identifier)s = &___%(loc)s_%(Object Identifier)s;'
+
+ # format for initializing a ANALOG_VALUE_DESCR struct in C code
+ # also valid for ANALOG_INPUT and ANALOG_OUTPUT
+ AX_params_format = '{&___%(loc)s_%(Object Identifier)s, ' + \
+ '%(Object Identifier)s, "%(Object Name)s", "%(Description)s", %(Unit ID)d}'
+ # format for initializing a BINARY_VALUE_DESCR struct in C code
+ # also valid for BINARY_INPUT and BINARY_OUTPUT
+ BX_params_format = '{&___%(loc)s_%(Object Identifier)s, ' + \
+ '%(Object Identifier)s, "%(Object Name)s", "%(Description)s"}'
+
+ # format for initializing a MULTISTATE_VALUE_DESCR struct in C code
+ # also valid for MULTISTATE_INPUT and MULTISTATE_OUTPUT
+ MSX_params_format = '{&___%(loc)s_%(Object Identifier)s, ' + \
+ '%(Object Identifier)s, "%(Object Name)s", "%(Description)s", %(Number of States)s}'
+
+ AV_locstr = 'MD' + locstr+'_2' # see the comment in GetVariableLocationTree() to grok the '_2'
+ AO_locstr = 'QD' + locstr+'_1' # see the comment in GetVariableLocationTree() to grok the '_1'
+ AI_locstr = 'ID' + locstr+'_0' # see the comment in GetVariableLocationTree() to grok the '_0'
+ BV_locstr = 'MX' + locstr+'_5' # see the comment in GetVariableLocationTree() to grok the '_5'
+ BO_locstr = 'QX' + locstr+'_4' # see the comment in GetVariableLocationTree() to grok the '_4'
+ BI_locstr = 'IX' + locstr+'_3' # see the comment in GetVariableLocationTree() to grok the '_3'
+ MSV_locstr = 'MB' + locstr+'_19' # see the comment in GetVariableLocationTree() to grok the '_19'
+ MSO_locstr = 'QB' + locstr+'_14' # see the comment in GetVariableLocationTree() to grok the '_14'
+ MSI_locstr = 'IB' + locstr+'_13' # see the comment in GetVariableLocationTree() to grok the '_13'
+
+
+ for ObjType, ObjLocStr, params_format in [
+ ("AV" , AV_locstr, AX_params_format ),
+ ("AO" , AO_locstr, AX_params_format ),
+ ("AI" , AI_locstr, AX_params_format ),
+ ("BV" , BV_locstr, BX_params_format ),
+ ("BO" , BO_locstr, BX_params_format ),
+ ("BI" , BI_locstr, BX_params_format ),
+ ("MSV" , MSV_locstr, MSX_params_format ),
+ ("MSO" , MSO_locstr, MSX_params_format ),
+ ("MSI" , MSI_locstr, MSX_params_format )
+ ]:
+ parameters_list = []
+ locatedvar_list = []
+ self.ObjTables[ObjType + "_Obj"].UpdateAllVirtualProperties()
+ for ObjProp in self.ObjTablesData[ObjType + "_Obj"]:
+ ObjProp["loc" ] = ObjLocStr
+ parameters_list.append(params_format % ObjProp)
+ locatedvar_list.append(locvar_format % ObjProp)
+ loc_dict[ ObjType + "_count"] = len( parameters_list )
+ loc_dict[ ObjType + "_param"] = ",\n".join( parameters_list )
+ loc_dict[ ObjType + "_lvars"] = "\n".join( locatedvar_list )
+
+ #----------------------------------------------------------------------
+ # Create the C source files that implement the BACnet server
+ #----------------------------------------------------------------------
+
+ # Names of the .c files that will be generated, based on a template file with same name
+ # (names without '.c' --> this will be added later)
+ # main server.c file is handled separately
+ Generated_BACnet_c_mainfile = "server"
+ Generated_BACnet_c_files = ["ai", "ao", "av", "bi", "bo", "bv", "msi", "mso", "msv", "device"]
+
+ # Names of the .h files that will be generated, based on a template file with same name
+ # (names without '.h' --> this will be added later)
+ Generated_BACnet_h_files = ["server", "device", "config_bacnet_for_beremiz",
+ "ai", "ao", "av", "bi", "bo", "bv", "msi", "mso", "msv"
+ ]
+
+ # Generate the files with the source code
+ postfix = "_".join(map(str, current_location))
+ template_file_dir = os.path.join(os.path.split(__file__)[0],"runtime")
+
+ def generate_file(file_name, extension):
+ generate_file_name = os.path.join(buildpath, "%s_%s.%s"%(file_name,postfix,extension))
+ template_file_name = os.path.join(template_file_dir,"%s.%s"%(file_name,extension))
+ generate_file_content = open(template_file_name).read() % loc_dict
+ generate_file_handle = open(generate_file_name,'w')
+ generate_file_handle .write(generate_file_content)
+ generate_file_handle .close()
+
+ for file_name in Generated_BACnet_c_files:
+ generate_file(file_name, "c")
+ for file_name in Generated_BACnet_h_files:
+ generate_file(file_name, "h")
+ generate_file(Generated_BACnet_c_mainfile, "c")
+ Generated_BACnet_c_mainfile_name = \
+ os.path.join(buildpath, "%s_%s.%s"%(Generated_BACnet_c_mainfile,postfix,"c"))
+
+ #----------------------------------------------------------------------
+ # Finally, define the compilation and linking commands and flags
+ #----------------------------------------------------------------------
+
+ LDFLAGS = []
+ # when using dynamically linked library...
+ #LDFLAGS.append(' -lbacnet')
+ #LDFLAGS.append(' -L"'+BacnetLibraryPath+'"')
+ #LDFLAGS.append(' "-Wl,-rpath,' + BacnetLibraryPath + '"')
+ # when using static library:
+ LDFLAGS.append(' "'+os.path.join(BacnetLibraryPath, "libbacnet.a")+'"')
+
+ CFLAGS = ' -I"'+BacnetIncludePath+'"'
+ CFLAGS += ' -I"'+BacnetIncludePortPath+'"'
+
+ return [(Generated_BACnet_c_mainfile_name, CFLAGS)], LDFLAGS, True
+
+
+
+###################################################
+###################################################
+# #
+# R O O T C L A S S #
+# #
+###################################################
+###################################################
+#class RootClass:
+ #XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
+ #<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+ #<xsd:element name="BACnetRoot">
+ #</xsd:element>
+ #</xsd:schema>
+ #"""
+ #CTNChildrenTypes = [("BacnetSlave", _BacnetSlavePlug, "Bacnet Slave")
+ ##,("XXX",_XXXXPlug, "XXX")
+ #]
+
+ #def CTNGenerate_C(self, buildpath, locations):
+ #return [], "", True
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bacnet/ede_files/template_EDE.csv Fri Jun 08 13:28:00 2018 +0200
@@ -0,0 +1,8 @@
+#EDE (Engineering-Data-Exchange) - generated by Beremiz BACnet plugin;;;;;;;;;;;;;;;
+PROJECT_NAME;%(Project Name)s;;;;;;;;;;;;;;
+VERSION_OF_REFERENCEFILE;%(EDE file version)d;;;;;;;;;;;;;;
+TIMESTAMP_OF_LAST_CHANGE;%(Current Time)s;;;;;;;;;;;;;;
+AUTHOR_OF_LAST_CHANGE;%(Project Author)s;;;;;;;;;;;;;;
+VERSION_OF_LAYOUT;2.1;;;;;;;;;;;;;;
+#mandatory;mandatory;mandatory;mandatory;mandatory;optional;optional;optional;optional;optional;optional;optional;optional;optional;optional;optional
+#keyname;device obj.-instance;object-name;object-type;object-instance;description;present-value-default;min-present-value;max-present-value;settable;supports COV;hi-limit;low-limit;state-text-reference;unit-code;vendor-specific-address
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bacnet/ede_files/template_ObjTypes.csv Fri Jun 08 13:28:00 2018 +0200
@@ -0,0 +1,27 @@
+#Encoding of BACnet Object Types;;;
+#Code;Object Type;;
+0;Analog Input;;
+1;Analog Output;;
+2;Analog Value;;
+3;Binary Input ;;
+4;Binary Output;;
+5;Binary Value;;
+6;Calendar;;
+7;Command;;
+8;Device;;
+9;Event-Enrollment;;
+10;File;;
+11;Group;;
+12;Loop;;
+13;Multistate Input;;
+14;Multistate Output
+15;Notification Class
+16;Program
+17;Schedule
+18;Averaging
+19;Multistate Value
+20;Trend Log
+21;Life Safety Point
+22;Life Safety Zone
+23;Accumulator
+24;Pulse Converter
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bacnet/ede_files/template_StateTexts.csv Fri Jun 08 13:28:00 2018 +0200
@@ -0,0 +1,2 @@
+#State Text Reference;;;;;;;
+#Reference Number;Text 1;Text 2;Text 3;Text 4;Text 5;...;Text n
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bacnet/ede_files/template_Units.csv Fri Jun 08 13:28:00 2018 +0200
@@ -0,0 +1,264 @@
+#Encoding of BACnet Engineering Units;
+#Code;Unit Text
+166;METERS-PER-SECOND-PER-SECOND
+0;SQUARE-METERS
+116;SQUARE-CENTIMETERS
+1;SQUARE-FEET
+115;SQUARE-INCHES
+105;CURRENCY1
+106;CURRENCY2
+107;CURRENCY3
+108;CURRENCY4
+109;CURRENCY5
+110;CURRENCY6
+111;CURRENCY7
+112;CURRENCY8
+113;CURRENCY9
+114;CURRENCY10
+2;MILLIAMPERES
+3;AMPERES
+167;AMPERES-PER-METER
+168;AMPERES-PER-SQUARE-METER
+169;AMPERE-SQUARE-METERS
+199;DECIBELS
+200;DECIBELS-MILLIVOLT
+201;DECIBELS-VOLT
+170;FARADS
+171;HENRYS
+4;OHMS
+237;OHM-METER-SQUARED-PER-METER
+172;OHM-METERS
+145;MILLIOHMS
+122;KILOHMS
+123;MEGOHMS
+190;MICROSIEMENS
+202;MILLISIEMENS
+173;SIEMENS
+174;SIEMENS-PER-METER
+175;TESLAS
+5;VOLTS
+124;MILLIVOLTS
+6;KILOVOLTS
+7;MEGAVOLTS
+8;VOLT-AMPERES
+9;KILOVOLT-AMPERES
+10;MEGAVOLT-AMPERES
+11;VOLT-AMPERES-REACTIVE
+12;KILOVOLT-AMPERES-REACTIVE
+13;MEGAVOLT-AMPERES-REACTIVE
+176;VOLTS-PER-DEGREE-KELVIN
+177;VOLTS-PER-METER
+14;DEGREES-PHASE
+15;POWER-FACTOR
+178;WEBERS
+238;AMPERE-SECONDS
+239;VOLT-AMPERE-HOURS
+240;KILOVOLT-AMPERE-HOURS
+241;MEGAVOLT-AMPERE-HOURS
+242;VOLT-AMPERE-HOURS-REACTIVE
+243;KILOVOLT-AMPERE-HOURS-REACTIVE
+244;MEGAVOLT-AMPERE-HOURS-REACTIVE
+245;VOLT-SQUARE-HOURS
+246;AMPERE-SQUARE-HOURS
+16;JOULES
+17;KILOJOULES
+125;KILOJOULES-PER-KILOGRAM
+126;MEGAJOULES
+18;WATT-HOURS
+19;KILOWATT-HOURS
+146;MEGAWATT-HOURS
+203;WATT-HOURS-REACTIVE
+204;KILOWATT-HOURS-REACTIVE
+205;MEGAWATT-HOURS-REACTIVE
+20;BTUS
+147;KILO-BTUS
+148;MEGA-BTUS
+21;THERMS
+22;TON-HOURS
+23;JOULES-PER-KILOGRAM-DRY-AIR
+149;KILOJOULES-PER-KILOGRAM-DRY-AIR
+150;MEGAJOULES-PER-KILOGRAM-DRY-AIR
+24;BTUS-PER-POUND-DRY-AIR
+117;BTUS-PER-POUND
+127;JOULES-PER-DEGREE-KELVIN
+151;KILOJOULES-PER-DEGREE-KELVIN
+152;MEGAJOULES-PER-DEGREE-KELVIN
+128;JOULES-PER-KILOGRAM-DEGREE-KELVIN
+153;NEWTON
+25;CYCLES-PER-HOUR
+26;CYCLES-PER-MINUTE
+27;HERTZ
+129;KILOHERTZ
+130;MEGAHERTZ
+131;PER-HOUR
+28;GRAMS-OF-WATER-PER-KILOGRAM-DRY-AIR
+29;PERCENT-RELATIVE-HUMIDITY
+194;MICROMETERS
+30;MILLIMETERS
+118;CENTIMETERS
+193;KILOMETERS
+31;METERS
+32;INCHES
+33;FEET
+179;CANDELAS
+180;CANDELAS-PER-SQUARE-METER
+34;WATTS-PER-SQUARE-FOOT
+35;WATTS-PER-SQUARE-METER
+36;LUMENS
+37;LUXES
+38;FOOT-CANDLES
+196;MILLIGRAMS
+195;GRAMS
+39;KILOGRAMS
+40;POUNDS-MASS
+41;TONS
+154;GRAMS-PER-SECOND
+155;GRAMS-PER-MINUTE
+42;KILOGRAMS-PER-SECOND
+43;KILOGRAMS-PER-MINUTE
+44;KILOGRAMS-PER-HOUR
+119;POUNDS-MASS-PER-SECOND
+45;POUNDS-MASS-PER-MINUTE
+46;POUNDS-MASS-PER-HOUR
+156;TONS-PER-HOUR
+132;MILLIWATTS
+47;WATTS
+48;KILOWATTS
+49;MEGAWATTS
+50;BTUS-PER-HOUR
+157;KILO-BTUS-PER-HOUR
+247;JOULE-PER-HOURS
+51;HORSEPOWER
+52;TONS-REFRIGERATION
+53;PASCALS
+133;HECTOPASCALS
+54;KILOPASCALS
+134;MILLIBARS
+55;BARS
+56;POUNDS-FORCE-PER-SQUARE-INCH
+206;MILLIMETERS-OF-WATER
+57;CENTIMETERS-OF-WATER
+58;INCHES-OF-WATER
+59;MILLIMETERS-OF-MERCURY
+60;CENTIMETERS-OF-MERCURY
+61;INCHES-OF-MERCURY
+62;DEGREES-CELSIUS
+63;DEGREES-KELVIN
+181;DEGREES-KELVIN-PER-HOUR
+182;DEGREES-KELVIN-PER-MINUTE
+64;DEGREES-FAHRENHEIT
+65;DEGREE-DAYS-CELSIUS
+66;DEGREE-DAYS-FAHRENHEIT
+120;DELTA-DEGREES-FAHRENHEIT
+121;DELTA-DEGREES-KELVIN
+67;YEARS
+68;MONTHS
+69;WEEKS
+70;DAYS
+71;HOURS
+72;MINUTES
+73;SECONDS
+158;HUNDREDTHS-SECONDS
+159;MILLISECONDS
+160;NEWTON-METERS
+161;MILLIMETERS-PER-SECOND
+162;MILLIMETERS-PER-MINUTE
+74;METERS-PER-SECOND
+163;METERS-PER-MINUTE
+164;METERS-PER-HOUR
+75;KILOMETERS-PER-HOUR
+76;FEET-PER-SECOND
+77;FEET-PER-MINUTE
+78;MILES-PER-HOUR
+79;CUBIC-FEET
+80;CUBIC-METERS
+81;IMPERIAL-GALLONS
+197;MILLILITERS
+82;LITERS
+83;US-GALLONS
+142;CUBIC-FEET-PER-SECOND
+84;CUBIC-FEET-PER-MINUTE
+254;MILLION-STANDARD-CUBIC-FEET-PER-MINUTE
+191;CUBIC-FEET-PER-HOUR
+248;CUBIC-FEET-PER-DAY
+47808;STANDARD-CUBIC-FEET-PER-DAY
+47809;MILLION-STANDARD-CUBIC-FEET-PER-DAY
+47810;THOUSAND-CUBIC-FEET-PER-DAY
+47811;THOUSAND-STANDARD-CUBIC-FEET-PER-DAY
+47812;POUNDS-MASS-PER-DAY
+85;CUBIC-METERS-PER-SECOND
+165;CUBIC-METERS-PER-MINUTE
+135;CUBIC-METERS-PER-HOUR
+249;CUBIC-METERS-PER-DAY
+86;IMPERIAL-GALLONS-PER-MINUTE
+198;MILLILITERS-PER-SECOND
+87;LITERS-PER-SECOND
+88;LITERS-PER-MINUTE
+136;LITERS-PER-HOUR
+89;US-GALLONS-PER-MINUTE
+192;US-GALLONS-PER-HOUR
+90;DEGREES-ANGULAR
+91;DEGREES-CELSIUS-PER-HOUR
+92;DEGREES-CELSIUS-PER-MINUTE
+93;DEGREES-FAHRENHEIT-PER-HOUR
+94;DEGREES-FAHRENHEIT-PER-MINUTE
+183;JOULE-SECONDS
+186;KILOGRAMS-PER-CUBIC-METER
+137;KILOWATT-HOURS-PER-SQUARE-METER
+138;KILOWATT-HOURS-PER-SQUARE-FOOT
+250;WATT-HOURS-PER-CUBIC-METER
+251;JOULES-PER-CUBIC-METER
+139;MEGAJOULES-PER-SQUARE-METER
+140;MEGAJOULES-PER-SQUARE-FOOT
+252;MOLE-PERCENT
+95;NO-UNITS
+187;NEWTON-SECONDS
+188;NEWTONS-PER-METER
+96;PARTS-PER-MILLION
+97;PARTS-PER-BILLION
+253;PASCAL-SECONDS
+98;PERCENT
+143;PERCENT-OBSCURATION-PER-FOOT
+144;PERCENT-OBSCURATION-PER-METER
+99;PERCENT-PER-SECOND
+100;PER-MINUTE
+101;PER-SECOND
+102;PSI-PER-DEGREE-FAHRENHEIT
+103;RADIANS
+184;RADIANS-PER-SECOND
+104;REVOLUTIONS-PER-MINUTE
+185;SQUARE-METERS-PER-NEWTON
+189;WATTS-PER-METER-PER-DEGREE-KELVIN
+141;WATTS-PER-SQUARE-METER-DEGREE-KELVIN
+207;PER-MILLE
+208;GRAMS-PER-GRAM
+209;KILOGRAMS-PER-KILOGRAM
+210;GRAMS-PER-KILOGRAM
+211;MILLIGRAMS-PER-GRAM
+212;MILLIGRAMS-PER-KILOGRAM
+213;GRAMS-PER-MILLILITER
+214;GRAMS-PER-LITER
+215;MILLIGRAMS-PER-LITER
+216;MICROGRAMS-PER-LITER
+217;GRAMS-PER-CUBIC-METER
+218;MILLIGRAMS-PER-CUBIC-METER
+219;MICROGRAMS-PER-CUBIC-METER
+220;NANOGRAMS-PER-CUBIC-METER
+221;GRAMS-PER-CUBIC-CENTIMETER
+222;BECQUERELS
+223;KILOBECQUERELS
+224;MEGABECQUERELS
+225;GRAY
+226;MILLIGRAY
+227;MICROGRAY
+228;SIEVERTS
+229;MILLISIEVERTS
+230;MICROSIEVERTS
+231;MICROSIEVERTS-PER-HOUR
+47814;MILLIREMS
+47815;MILLIREMS-PER-HOUR
+232;DECIBELS-A
+233;NEPHELOMETRIC-TURBIDITY-UNIT
+234;PH
+235;GRAMS-PER-SQUARE-METER
+236;MINUTES-PER-DEGREE-KELVIN
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bacnet/runtime/ai.c Fri Jun 08 13:28:00 2018 +0200
@@ -0,0 +1,497 @@
+/**************************************************************************
+*
+* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
+* Copyright (C) 2011 Krzysztof Malorny <malornykrzysztof@gmail.com>
+* Copyright (C) 2017 Mario de Sousa <msousa@fe.up.pt>
+*
+* Permission is hereby granted, free of charge, to any person obtaining
+* a copy of this software and associated documentation files (the
+* "Software"), to deal in the Software without restriction, including
+* without limitation the rights to use, copy, modify, merge, publish,
+* distribute, sublicense, and/or sell copies of the Software, and to
+* permit persons to whom the Software is furnished to do so, subject to
+* the following conditions:
+*
+* The above copyright notice and this permission notice shall be included
+* in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*
+*********************************************************************/
+
+/* Analog Input Objects - customize for your use */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h> /* NAN maro */
+
+#include "config_bacnet_for_beremiz_%(locstr)s.h" /* the custom configuration for beremiz plugin */
+#include "bacdef.h"
+#include "bacdcode.h"
+#include "bacenum.h"
+#include "bacapp.h"
+#include "bactext.h"
+#include "device_%(locstr)s.h"
+#include "handlers.h"
+#include "ai_%(locstr)s.h"
+
+
+
+/* initial value for present_value property of each object */
+#define AI_VALUE_INIT (0)
+
+/* The IEC 61131-3 located variables mapped onto the Analog Input objects of BACnet protocol */
+%(AI_lvars)s
+
+
+/* The array where we keep all the state related to the Analog Input Objects */
+#define MAX_ANALOG_INPUTS %(AI_count)s
+static ANALOG_INPUT_DESCR AI_Descr[MAX_ANALOG_INPUTS] = {
+%(AI_param)s
+};
+
+
+
+/* These three arrays are used by the ReadPropertyMultiple handler,
+ * as well as to initialize the XXX_Property_List used by the
+ * Property List (PROP_PROPERTY_LIST) property.
+ */
+static const int Analog_Input_Properties_Required[] = {
+ /* (1) Currently Supported */
+ /* (2) Required by standard ASHRAE 135-2016 */
+ /*(1)(2) */
+ PROP_OBJECT_IDENTIFIER, /* R R ( 75) */
+ PROP_OBJECT_NAME, /* R R ( 77) */
+ PROP_OBJECT_TYPE, /* R R ( 79) */
+ PROP_PRESENT_VALUE, /* W R ( 85) */
+ PROP_STATUS_FLAGS, /* R R (111) */
+ PROP_EVENT_STATE, /* R R ( 36) */
+ PROP_OUT_OF_SERVICE, /* W R ( 81) */
+ PROP_UNITS, /* W R (117) */
+// PROP_PROPERTY_LIST, /* R R (371) */
+ -1
+};
+
+static const int Analog_Input_Properties_Optional[] = {
+ /* (1) Currently Supported */
+ /* (2) Required by standard ASHRAE 135-2016 */
+ /*(1)(2) */
+ PROP_DESCRIPTION, /* R O ( 28) */
+ -1
+};
+
+static const int Analog_Input_Properties_Proprietary[] = {
+ -1
+};
+
+/* This array stores the PROPERTY_LIST which may be read by clients.
+ * End of list is marked by following the last element with the value '-1'
+ *
+ * It is initialized by Analog_Inputs_Init() based off the values
+ * stored in Analog_Input_Properties_Required
+ * Analog_Input_Properties_Optional
+ * Analog_Input_Properties_Proprietary
+ */
+/* TODO: Allocate memory for this array with malloc() at startup */
+static int Analog_Input_Properties_List[64];
+
+
+
+/********************************************************/
+/** Callback functions. **/
+/** Functions required by BACnet devie implementation. **/
+/********************************************************/
+
+
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+void Analog_Input_Property_Lists(
+ const int **pRequired,
+ const int **pOptional,
+ const int **pProprietary)
+{
+ if (pRequired)
+ *pRequired = Analog_Input_Properties_Required;
+ if (pOptional)
+ *pOptional = Analog_Input_Properties_Optional;
+ if (pProprietary)
+ *pProprietary = Analog_Input_Properties_Proprietary;
+
+ return;
+}
+
+
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+void Analog_Input_Init(
+ void)
+{
+ unsigned i, j;
+ static bool initialized = false;
+
+ if (!initialized) {
+ initialized = true;
+
+ /* initialize the Analog_Input_Properties_List array */
+ int len = 0;
+ len += BACnet_Init_Properties_List(Analog_Input_Properties_List + len,
+ Analog_Input_Properties_Required);
+ len += BACnet_Init_Properties_List(Analog_Input_Properties_List + len,
+ Analog_Input_Properties_Optional);
+ len += BACnet_Init_Properties_List(Analog_Input_Properties_List + len,
+ Analog_Input_Properties_Proprietary);
+
+ for (i = 0; i < MAX_ANALOG_INPUTS; i++) {
+ // MJS: the following line in th original demo code was commented out so we do not
+ // overwrite the initial values configured by the user in beremiz IDE
+ // memset(&AI_Descr[i], 0x00, sizeof(ANALOG_INPUT_DESCR));
+ AI_Descr[i].Present_Value = AI_VALUE_INIT;
+ AI_Descr[i].Out_Of_Service = 0;
+ AI_Descr[i].Event_State = 0;
+// AI_Descr[i].Units = UNITS_NO_UNITS;
+ }
+ }
+}
+
+
+
+
+/* validate that the given instance exists */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+bool Analog_Input_Valid_Instance(
+ uint32_t object_instance)
+{
+ return (Analog_Input_Instance_To_Index(object_instance) < MAX_ANALOG_INPUTS);
+}
+
+/* the number of Analog Input Objects */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+unsigned Analog_Input_Count(void) {return MAX_ANALOG_INPUTS;}
+
+
+/* returns the instance (i.e. Object Identifier) that correlates to the correct index */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+uint32_t Analog_Input_Index_To_Instance(unsigned index) {return AI_Descr[index].Object_Identifier;}
+
+
+
+/* returns the index that correlates to the correct instance number (Object Identifier) */
+unsigned Analog_Input_Instance_To_Index(
+ uint32_t object_instance)
+{
+ unsigned index = 0;
+
+ for (index = 0; index < MAX_ANALOG_INPUTS; index++)
+ if (object_instance == AI_Descr[index].Object_Identifier)
+ return index;
+
+ /* error, this object ID is not in our list! */
+ return MAX_ANALOG_INPUTS;
+}
+
+
+
+
+float Analog_Input_Present_Value(
+ uint32_t object_instance)
+{
+ float value = AI_VALUE_INIT;
+ unsigned index = 0;
+ unsigned i = 0;
+
+ index = Analog_Input_Instance_To_Index(object_instance);
+ if (index < MAX_ANALOG_INPUTS)
+ value = AI_Descr[index].Present_Value;
+
+ return value;
+}
+
+
+
+
+/* note: the object name must be unique within this device */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+bool Analog_Input_Object_Name(
+ uint32_t object_instance,
+ BACNET_CHARACTER_STRING * object_name)
+{
+ bool status = false;
+ unsigned index = Analog_Input_Instance_To_Index(object_instance);
+
+ if (index < MAX_ANALOG_INPUTS)
+ status = characterstring_init_ansi(object_name, AI_Descr[index].Object_Name);
+
+ return status;
+}
+
+
+
+bool Analog_Input_Object_Description(
+ uint32_t object_instance,
+ BACNET_CHARACTER_STRING * object_name)
+{
+ bool status = false;
+ unsigned index = Analog_Input_Instance_To_Index(object_instance);
+
+ if (index < MAX_ANALOG_INPUTS)
+ status = characterstring_init_ansi(object_name, AI_Descr[index].Description);
+
+ return status;
+}
+
+
+
+/* return apdu len, or BACNET_STATUS_ERROR on error */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+int Analog_Input_Read_Property(
+ BACNET_READ_PROPERTY_DATA * rpdata)
+{
+ int apdu_len = 0; /* return value */
+ BACNET_BIT_STRING bit_string;
+ BACNET_CHARACTER_STRING char_string;
+ float real_value = (float) 1.414;
+ unsigned object_index = 0;
+ bool state = false;
+ uint8_t *apdu = NULL;
+
+ if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
+ (rpdata->application_data_len == 0)) {
+ return 0;
+ }
+
+ apdu = rpdata->application_data;
+
+ object_index = Analog_Input_Instance_To_Index(rpdata->object_instance);
+ if (object_index >= MAX_ANALOG_INPUTS) {
+ rpdata->error_class = ERROR_CLASS_OBJECT;
+ rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT;
+ return BACNET_STATUS_ERROR;
+ }
+
+ switch (rpdata->object_property) {
+ case PROP_OBJECT_IDENTIFIER:
+ apdu_len =
+ encode_application_object_id(&apdu[0], OBJECT_ANALOG_INPUT,
+ rpdata->object_instance);
+ break;
+
+ case PROP_OBJECT_NAME:
+ Analog_Input_Object_Name(rpdata->object_instance, &char_string);
+ apdu_len =
+ encode_application_character_string(&apdu[0], &char_string);
+ break;
+
+ case PROP_DESCRIPTION:
+ Analog_Input_Object_Description(rpdata->object_instance, &char_string);
+ apdu_len =
+ encode_application_character_string(&apdu[0], &char_string);
+ break;
+
+ case PROP_OBJECT_TYPE:
+ apdu_len =
+ encode_application_enumerated(&apdu[0], OBJECT_ANALOG_INPUT);
+ break;
+
+ case PROP_PRESENT_VALUE:
+ real_value = Analog_Input_Present_Value(rpdata->object_instance);
+ apdu_len = encode_application_real(&apdu[0], real_value);
+ break;
+
+ case PROP_STATUS_FLAGS:
+ bitstring_init(&bit_string);
+ bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false);
+ bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false);
+ bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false);
+ bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE,
+ AI_Descr[object_index].Out_Of_Service);
+
+ apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
+ break;
+
+ case PROP_EVENT_STATE:
+ apdu_len =
+ encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL);
+ break;
+
+ case PROP_OUT_OF_SERVICE:
+ state = AI_Descr[object_index].Out_Of_Service;
+ apdu_len = encode_application_boolean(&apdu[0], state);
+ break;
+
+ case PROP_UNITS:
+ apdu_len =
+ encode_application_enumerated(&apdu[0], AI_Descr[object_index].Units);
+ break;
+
+// case PROP_PROPERTY_LIST:
+// BACnet_encode_array(Analog_Input_Properties_List,
+// property_list_count(Analog_Input_Properties_List),
+// retfalse, encode_application_enumerated);
+// break;
+ default:
+ rpdata->error_class = ERROR_CLASS_PROPERTY;
+ rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
+ apdu_len = BACNET_STATUS_ERROR;
+ break;
+ }
+ /* only array properties can have array options */
+ if ((apdu_len >= 0) &&
+ (rpdata->object_property != PROP_EVENT_TIME_STAMPS) &&
+// (rpdata->object_property != PROP_PROPERTY_LIST) &&
+ (rpdata->array_index != BACNET_ARRAY_ALL)) {
+ rpdata->error_class = ERROR_CLASS_PROPERTY;
+ rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
+ apdu_len = BACNET_STATUS_ERROR;
+ }
+
+ return apdu_len;
+}
+
+
+
+
+/* returns true if successful */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+bool Analog_Input_Write_Property(
+ BACNET_WRITE_PROPERTY_DATA * wp_data)
+{
+ bool status = false; /* return value */
+ unsigned int object_index = 0;
+ int len = 0;
+ BACNET_APPLICATION_DATA_VALUE value;
+ ANALOG_INPUT_DESCR *CurrentAI;
+
+ /* decode the some of the request */
+ len =
+ bacapp_decode_application_data(wp_data->application_data,
+ wp_data->application_data_len, &value);
+ /* FIXME: len < application_data_len: more data? */
+ if (len < 0) {
+ /* error while decoding - a value larger than we can handle */
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
+ return false;
+ }
+ if ((wp_data->object_property != PROP_EVENT_TIME_STAMPS) &&
+ (wp_data->array_index != BACNET_ARRAY_ALL)) {
+ /* only array properties can have array options */
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
+ return false;
+ }
+ object_index = Analog_Input_Instance_To_Index(wp_data->object_instance);
+ if (object_index < MAX_ANALOG_INPUTS)
+ CurrentAI = &AI_Descr[object_index];
+ else
+ return false;
+
+ switch (wp_data->object_property) {
+ case PROP_PRESENT_VALUE:
+ status =
+ WPValidateArgType(&value, BACNET_APPLICATION_TAG_REAL,
+ &wp_data->error_class, &wp_data->error_code);
+ if (!status) {
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
+ status = false; // not really necessary here.
+ } else {
+ if (!AI_Descr[object_index].Out_Of_Service) {
+ /* input objects can only be written to when Out_Of_Service is true! */
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
+ status = false;
+ } else {
+ AI_Descr[object_index].Present_Value = value.type.Real;
+ status = true;
+ }
+ }
+ break;
+
+ case PROP_OUT_OF_SERVICE:
+ {
+ bool Previous_Out_Of_Service = CurrentAI->Out_Of_Service;
+ status =
+ WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN,
+ &wp_data->error_class, &wp_data->error_code);
+ if (status) {
+ CurrentAI->Out_Of_Service = value.type.Boolean;
+ if (Previous_Out_Of_Service && !CurrentAI->Out_Of_Service)
+ /* We have just changed from Out_of_Service -> In Service */
+ /* We need to update the Present_Value to the value
+ * currently in the PLC...
+ */
+ CurrentAI->Present_Value = *(CurrentAI->Located_Var_ptr);
+ }
+ break;
+ }
+
+ case PROP_UNITS:
+ status =
+ WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED,
+ &wp_data->error_class, &wp_data->error_code);
+ if (status) {
+ CurrentAI->Units = value.type.Enumerated;
+ }
+ break;
+
+ case PROP_OBJECT_IDENTIFIER:
+ case PROP_OBJECT_NAME:
+ case PROP_OBJECT_TYPE:
+ case PROP_STATUS_FLAGS:
+ case PROP_EVENT_STATE:
+ case PROP_DESCRIPTION:
+// case PROP_PROPERTY_LIST:
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
+ break;
+ default:
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
+ break;
+ }
+
+ return status;
+}
+
+
+
+
+
+
+
+/********************************************/
+/** Functions required for Beremiz plugin **/
+/********************************************/
+
+void Analog_Input_Copy_Present_Value_to_Located_Var(void) {
+ unsigned i;
+ for (i = 0; i < MAX_ANALOG_INPUTS; i++) {
+ // decouple PLC's Located Variable from Bacnet Object's Present Value if Out_Of_Service is true
+ if (AI_Descr[i].Out_Of_Service)
+ continue;
+
+ // copy the value
+ *(AI_Descr[i].Located_Var_ptr) = Analog_Input_Present_Value(AI_Descr[i].Object_Identifier);
+ }
+}
+
+
+
+void Analog_Input_Copy_Located_Var_to_Present_Value(void) {
+ unsigned i;
+ for (i = 0; i < MAX_ANALOG_INPUTS; i++) {
+ // decouple PLC's Located Variable from Bacnet Object's Present Value if Out_Of_Service is true
+ if (AI_Descr[i].Out_Of_Service)
+ continue;
+
+ // copy the value
+ AI_Descr[i].Present_Value = *(AI_Descr[i].Located_Var_ptr);
+ }
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bacnet/runtime/ai.h Fri Jun 08 13:28:00 2018 +0200
@@ -0,0 +1,102 @@
+/**************************************************************************
+*
+* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
+* Copyright (C) 2011 Krzysztof Malorny <malornykrzysztof@gmail.com>
+* Copyright (C) 2017 Mario de Sousa <msousa@fe.up.pt>
+*
+* Permission is hereby granted, free of charge, to any person obtaining
+* a copy of this software and associated documentation files (the
+* "Software"), to deal in the Software without restriction, including
+* without limitation the rights to use, copy, modify, merge, publish,
+* distribute, sublicense, and/or sell copies of the Software, and to
+* permit persons to whom the Software is furnished to do so, subject to
+* the following conditions:
+*
+* The above copyright notice and this permission notice shall be included
+* in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*
+*********************************************************************/
+#ifndef AI_H
+#define AI_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include "bacdef.h"
+#include "bacerror.h"
+#include "wp.h"
+#include "rp.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+ typedef struct Analog_Input_descr {
+ /* pointer to IEC 61131-3 located variable that is mapped onto this BACnet object */
+ float *Located_Var_ptr;
+ uint32_t Object_Identifier; /* called object 'Instance' in the source code! */
+ char *Object_Name;
+ char *Description;
+ uint16_t Units;
+
+ /* stores the current value */
+ /* one entry per priority value */
+ float Present_Value;
+ unsigned Event_State:3;
+ bool Out_Of_Service;
+ } ANALOG_INPUT_DESCR;
+
+
+ void Analog_Input_Property_Lists(
+ const int **pRequired,
+ const int **pOptional,
+ const int **pProprietary);
+ bool Analog_Input_Valid_Instance(
+ uint32_t object_instance);
+ unsigned Analog_Input_Count(
+ void);
+ uint32_t Analog_Input_Index_To_Instance(
+ unsigned index);
+ unsigned Analog_Input_Instance_To_Index(
+ uint32_t object_instance);
+
+ bool Analog_Input_Object_Name(
+ uint32_t object_instance,
+ BACNET_CHARACTER_STRING * object_name);
+
+ int Analog_Input_Read_Property(
+ BACNET_READ_PROPERTY_DATA * rpdata);
+
+ bool Analog_Input_Write_Property(
+ BACNET_WRITE_PROPERTY_DATA * wp_data);
+
+ float Analog_Input_Present_Value(
+ uint32_t object_instance);
+
+ char *Analog_Input_Description(
+ uint32_t instance);
+
+ uint16_t Analog_Input_Units(
+ uint32_t instance);
+
+ bool Analog_Input_Out_Of_Service(
+ uint32_t instance);
+ void Analog_Input_Out_Of_Service_Set(
+ uint32_t instance,
+ bool oos_flag);
+
+ void Analog_Input_Init(
+ void);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bacnet/runtime/ao.c Fri Jun 08 13:28:00 2018 +0200
@@ -0,0 +1,620 @@
+/**************************************************************************
+*
+* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
+* Copyright (C) 2011 Krzysztof Malorny <malornykrzysztof@gmail.com>
+* Copyright (C) 2017 Mario de Sousa <msousa@fe.up.pt>
+*
+* Permission is hereby granted, free of charge, to any person obtaining
+* a copy of this software and associated documentation files (the
+* "Software"), to deal in the Software without restriction, including
+* without limitation the rights to use, copy, modify, merge, publish,
+* distribute, sublicense, and/or sell copies of the Software, and to
+* permit persons to whom the Software is furnished to do so, subject to
+* the following conditions:
+*
+* The above copyright notice and this permission notice shall be included
+* in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*
+*********************************************************************/
+
+/* Analog Output Objects - customize for your use */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h> /* NAN maro */
+
+#include "config_bacnet_for_beremiz_%(locstr)s.h" /* the custom configuration for beremiz plugin */
+#include "bacdef.h"
+#include "bacdcode.h"
+#include "bacenum.h"
+#include "bacapp.h"
+#include "bactext.h"
+#include "device_%(locstr)s.h"
+#include "handlers.h"
+#include "ao_%(locstr)s.h"
+
+/* we choose to have a NULL level in our system represented by */
+/* a particular value. When the priorities are not in use, they */
+/* will be relinquished (i.e. set to the NULL level). */
+/* Since the values are floats, we use NAN (Not A Number) as our NULL value. */
+/* WARNING: Never use comparisons like 'if (value == AO_LEVEL_NULL)'
+ * as it will always return false, even if both values are NAN.
+ * Use instead the negated version 'if (value != AO_LEVEL_NULL)'
+ * and add an 'else' to the 'if' condition if necessary.
+ * However, some compilers may screw this up if they do not
+ * implement IEEE 754 properly. It is probably best to stick with
+ * the isnan() macro if available.
+ */
+#define AO_VALUE_NULL NAN
+#define AO_VALUE_IS_NULL(x) (isnan(x))
+/* When all the priorities are level null, the present value returns */
+/* the Relinquish Default value */
+#define AO_VALUE_RELINQUISH_DEFAULT (0.0)
+
+
+/* The IEC 61131-3 located variables mapped onto the Analog Output objects of BACnet protocol */
+%(AO_lvars)s
+
+
+/* The array where we keep all the state related to the Analog Output Objects */
+#define MAX_ANALOG_OUTPUTS %(AO_count)s
+static ANALOG_OUTPUT_DESCR AO_Descr[MAX_ANALOG_OUTPUTS] = {
+%(AO_param)s
+};
+
+
+/* These three arrays are used by the ReadPropertyMultiple handler,
+ * as well as to initialize the XXX_Property_List used by the
+ * Property List (PROP_PROPERTY_LIST) property.
+ */
+static const int Analog_Output_Properties_Required[] = {
+ /* (1) Currently Supported */
+ /* (2) Required by standard ASHRAE 135-2016 */
+ /*(1)(2) */
+ PROP_OBJECT_IDENTIFIER, /* R R ( 75) */
+ PROP_OBJECT_NAME, /* R R ( 77) */
+ PROP_OBJECT_TYPE, /* R R ( 79) */
+ PROP_PRESENT_VALUE, /* W W ( 85) */
+ PROP_STATUS_FLAGS, /* R R (111) */
+ PROP_EVENT_STATE, /* R R ( 36) */
+ PROP_OUT_OF_SERVICE, /* W R ( 81) */
+ PROP_UNITS, /* W R (117) */
+ PROP_PRIORITY_ARRAY, /* R R ( 87) */
+ PROP_RELINQUISH_DEFAULT, /* R R (104) */
+// PROP_PROPERTY_LIST, /* R R (371) */
+// PROP_CURRENT_COMMAND_PRIORITY,/* R R (431) */
+ -1
+};
+
+static const int Analog_Output_Properties_Optional[] = {
+ /* (1) Currently Supported */
+ /* (2) Required by standard ASHRAE 135-2016 */
+ /*(1)(2) */
+ PROP_DESCRIPTION, /* R O ( 28) */
+ -1
+};
+
+static const int Analog_Output_Properties_Proprietary[] = {
+ -1
+};
+
+/* This array stores the PROPERTY_LIST which may be read by clients.
+ * End of list is marked by following the last element with the value '-1'
+ *
+ * It is initialized by Analog_Outputs_Init() based off the values
+ * stored in Analog_Output_Properties_Required
+ * Analog_Output_Properties_Optional
+ * Analog_Output_Properties_Proprietary
+ */
+/* TODO: Allocate memory for this array with malloc() at startup */
+static int Analog_Output_Properties_List[64];
+
+
+
+/********************************************************/
+/** Callback functions. **/
+/** Functions required by BACnet devie implementation. **/
+/********************************************************/
+
+
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+void Analog_Output_Property_Lists(
+ const int **pRequired,
+ const int **pOptional,
+ const int **pProprietary)
+{
+ if (pRequired)
+ *pRequired = Analog_Output_Properties_Required;
+ if (pOptional)
+ *pOptional = Analog_Output_Properties_Optional;
+ if (pProprietary)
+ *pProprietary = Analog_Output_Properties_Proprietary;
+
+ return;
+}
+
+
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+void Analog_Output_Init(
+ void)
+{
+ unsigned i, j;
+ static bool initialized = false;
+
+ if (!initialized) {
+ initialized = true;
+
+ /* initialize the Analog_Output_Properties_List array */
+ int len = 0;
+ len += BACnet_Init_Properties_List(Analog_Output_Properties_List + len,
+ Analog_Output_Properties_Required);
+ len += BACnet_Init_Properties_List(Analog_Output_Properties_List + len,
+ Analog_Output_Properties_Optional);
+ len += BACnet_Init_Properties_List(Analog_Output_Properties_List + len,
+ Analog_Output_Properties_Proprietary);
+
+ for (i = 0; i < MAX_ANALOG_OUTPUTS; i++) {
+ // MJS: the following line in th original demo code was commented out so we do not
+ // overwrite the initial values configured by the user in beremiz IDE
+ // memset(&AO_Descr[i], 0x00, sizeof(ANALOG_OUTPUT_DESCR));
+ for (j = 0; j < BACNET_MAX_PRIORITY; j++) {
+ AO_Descr[i].Present_Value[j] = AO_VALUE_NULL;
+ }
+ AO_Descr[i].Out_Of_Service = 0;
+ AO_Descr[i].Event_State = 0;
+// AO_Descr[i].Units = UNITS_NO_UNITS;
+ }
+ }
+}
+
+
+
+
+/* validate that the given instance exists */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+bool Analog_Output_Valid_Instance(
+ uint32_t object_instance)
+{
+ return (Analog_Output_Instance_To_Index(object_instance) < MAX_ANALOG_OUTPUTS);
+}
+
+/* the number of Analog Output Objects */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+unsigned Analog_Output_Count(void) {return MAX_ANALOG_OUTPUTS;}
+
+
+/* returns the instance (i.e. Object Identifier) that correlates to the correct index */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+uint32_t Analog_Output_Index_To_Instance(unsigned index) {return AO_Descr[index].Object_Identifier;}
+
+
+
+/* returns the index that correlates to the correct instance number (Object Identifier) */
+unsigned Analog_Output_Instance_To_Index(
+ uint32_t object_instance)
+{
+ unsigned index = 0;
+
+ for (index = 0; index < MAX_ANALOG_OUTPUTS; index++)
+ if (object_instance == AO_Descr[index].Object_Identifier)
+ return index;
+
+ /* error, this object ID is not in our list! */
+ return MAX_ANALOG_OUTPUTS;
+}
+
+
+
+
+float Analog_Output_Present_Value(
+ uint32_t object_instance)
+{
+ float value = AO_VALUE_RELINQUISH_DEFAULT;
+ unsigned index = 0;
+ unsigned i = 0;
+
+ index = Analog_Output_Instance_To_Index(object_instance);
+ if (index < MAX_ANALOG_OUTPUTS) {
+ for (i = 0; i < BACNET_MAX_PRIORITY; i++) {
+ if (!AO_VALUE_IS_NULL(AO_Descr[index].Present_Value[i])) {
+ value = AO_Descr[index].Present_Value[i];
+ break;
+ }
+ }
+ }
+
+ return value;
+}
+
+
+
+/* returns command priority (1..16), or 0 if all priority values are at NULL */
+int Analog_Output_Current_Command_Priority(
+ uint32_t object_instance)
+{
+ unsigned index = 0;
+ unsigned i = 0;
+
+ index = Analog_Output_Instance_To_Index(object_instance);
+ if (index < MAX_ANALOG_OUTPUTS) {
+ for (i = 0; i < BACNET_MAX_PRIORITY; i++) {
+ if (!AO_VALUE_IS_NULL(AO_Descr[index].Present_Value[i])) {
+ return i+1; // +1 since priority is 1..16, and not 0..15
+ }
+ }
+ }
+ // command values in all priorities are set to NULL
+ return 0;
+}
+
+
+
+/* For a given object instance-number, sets the present-value at a given
+ * priority 1..16 (except 6, see ASHRAE 135-2016, section 19.2.2)
+ */
+bool Analog_Output_Present_Value_Set(
+ uint32_t object_instance,
+ float value,
+ uint8_t priority)
+{
+ unsigned index = 0;
+
+ index = Analog_Output_Instance_To_Index(object_instance);
+ if (index >= MAX_ANALOG_OUTPUTS)
+ return false;
+ if ((priority == 0) || (priority > BACNET_MAX_PRIORITY) ||
+ (priority == 6 /* reserved, ASHRAE 135-2016, section 19.2.2 */))
+ return false;
+
+ priority--;
+ AO_Descr[index].Present_Value[priority] = value;
+ return true;
+}
+
+
+
+bool Analog_Output_Present_Value_Relinquish(
+ uint32_t object_instance,
+ unsigned priority)
+{
+ unsigned index = 0;
+
+ index = Analog_Output_Instance_To_Index(object_instance);
+ if (index >= MAX_ANALOG_OUTPUTS)
+ return false;
+
+ if ((priority == 0) || (priority > BACNET_MAX_PRIORITY) ||
+ (priority == 6 /* reserved, ASHRAE 135-2016, section 19.2.2 */))
+ return false;
+
+ priority--;
+ AO_Descr[index].Present_Value[priority] = AO_VALUE_NULL;
+ return true;
+}
+
+
+
+/* note: the object name must be unique within this device */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+bool Analog_Output_Object_Name(
+ uint32_t object_instance,
+ BACNET_CHARACTER_STRING * object_name)
+{
+ bool status = false;
+ unsigned index = Analog_Output_Instance_To_Index(object_instance);
+
+ if (index < MAX_ANALOG_OUTPUTS)
+ status = characterstring_init_ansi(object_name, AO_Descr[index].Object_Name);
+
+ return status;
+}
+
+
+
+bool Analog_Output_Object_Description(
+ uint32_t object_instance,
+ BACNET_CHARACTER_STRING * object_name)
+{
+ bool status = false;
+ unsigned index = Analog_Output_Instance_To_Index(object_instance);
+
+ if (index < MAX_ANALOG_OUTPUTS)
+ status = characterstring_init_ansi(object_name, AO_Descr[index].Description);
+
+ return status;
+}
+
+
+
+/* return apdu len, or BACNET_STATUS_ERROR on error */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+int Analog_Output_Read_Property(
+ BACNET_READ_PROPERTY_DATA * rpdata)
+{
+ int apdu_len = 0; /* return value */
+ BACNET_BIT_STRING bit_string;
+ BACNET_CHARACTER_STRING char_string;
+ float real_value = (float) 1.414;
+ unsigned object_index = 0;
+ bool state = false;
+ uint8_t *apdu = NULL;
+
+ if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
+ (rpdata->application_data_len == 0)) {
+ return 0;
+ }
+
+ apdu = rpdata->application_data;
+
+ object_index = Analog_Output_Instance_To_Index(rpdata->object_instance);
+ if (object_index >= MAX_ANALOG_OUTPUTS) {
+ rpdata->error_class = ERROR_CLASS_OBJECT;
+ rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT;
+ return BACNET_STATUS_ERROR;
+ }
+
+ switch (rpdata->object_property) {
+ case PROP_OBJECT_IDENTIFIER:
+ apdu_len =
+ encode_application_object_id(&apdu[0], OBJECT_ANALOG_OUTPUT,
+ rpdata->object_instance);
+ break;
+
+ case PROP_OBJECT_NAME:
+ Analog_Output_Object_Name(rpdata->object_instance, &char_string);
+ apdu_len =
+ encode_application_character_string(&apdu[0], &char_string);
+ break;
+
+ case PROP_DESCRIPTION:
+ Analog_Output_Object_Description(rpdata->object_instance, &char_string);
+ apdu_len =
+ encode_application_character_string(&apdu[0], &char_string);
+ break;
+
+ case PROP_OBJECT_TYPE:
+ apdu_len =
+ encode_application_enumerated(&apdu[0], OBJECT_ANALOG_OUTPUT);
+ break;
+
+ case PROP_PRESENT_VALUE:
+ real_value = Analog_Output_Present_Value(rpdata->object_instance);
+ apdu_len = encode_application_real(&apdu[0], real_value);
+ break;
+
+ case PROP_STATUS_FLAGS:
+ bitstring_init(&bit_string);
+ bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false);
+ bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false);
+ bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false);
+ bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE,
+ AO_Descr[object_index].Out_Of_Service);
+
+ apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
+ break;
+
+ case PROP_EVENT_STATE:
+ apdu_len =
+ encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL);
+ break;
+
+ case PROP_OUT_OF_SERVICE:
+ state = AO_Descr[object_index].Out_Of_Service;
+ apdu_len = encode_application_boolean(&apdu[0], state);
+ break;
+
+ case PROP_PRIORITY_ARRAY:
+ BACnet_encode_array(AO_Descr[object_index].Present_Value,
+ BACNET_MAX_PRIORITY,
+ AO_VALUE_IS_NULL,
+ encode_application_real)
+ break;
+
+// case PROP_CURRENT_COMMAND_PRIORITY: {
+// unsigned i = Analog_Output_Current_Command_Priority(rpdata->object_instance);
+// if (i == 0) apdu_len = encode_application_null (&apdu[0]);
+// else apdu_len = encode_application_unsigned(&apdu[0], i);
+// break;
+// }
+
+ case PROP_RELINQUISH_DEFAULT:
+ real_value = AO_VALUE_RELINQUISH_DEFAULT;
+ apdu_len = encode_application_real(&apdu[0], real_value);
+ break;
+
+ case PROP_UNITS:
+ apdu_len =
+ encode_application_enumerated(&apdu[0], AO_Descr[object_index].Units);
+ break;
+
+// case PROP_PROPERTY_LIST:
+// BACnet_encode_array(Analog_Output_Properties_List,
+// property_list_count(Analog_Output_Properties_List),
+// retfalse, encode_application_enumerated);
+// break;
+ default:
+ rpdata->error_class = ERROR_CLASS_PROPERTY;
+ rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
+ apdu_len = BACNET_STATUS_ERROR;
+ break;
+ }
+ /* only array properties can have array options */
+ if ((apdu_len >= 0) && (rpdata->object_property != PROP_PRIORITY_ARRAY) &&
+ (rpdata->object_property != PROP_EVENT_TIME_STAMPS) &&
+// (rpdata->object_property != PROP_PROPERTY_LIST) &&
+ (rpdata->array_index != BACNET_ARRAY_ALL)) {
+ rpdata->error_class = ERROR_CLASS_PROPERTY;
+ rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
+ apdu_len = BACNET_STATUS_ERROR;
+ }
+
+ return apdu_len;
+}
+
+
+
+
+/* returns true if successful */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+bool Analog_Output_Write_Property(
+ BACNET_WRITE_PROPERTY_DATA * wp_data)
+{
+ bool status = false; /* return value */
+ unsigned int object_index = 0;
+ int len = 0;
+ BACNET_APPLICATION_DATA_VALUE value;
+ ANALOG_OUTPUT_DESCR *CurrentAO;
+
+ /* decode the some of the request */
+ len =
+ bacapp_decode_application_data(wp_data->application_data,
+ wp_data->application_data_len, &value);
+ /* FIXME: len < application_data_len: more data? */
+ if (len < 0) {
+ /* error while decoding - a value larger than we can handle */
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
+ return false;
+ }
+ if ((wp_data->object_property != PROP_PRIORITY_ARRAY) &&
+ (wp_data->object_property != PROP_EVENT_TIME_STAMPS) &&
+ (wp_data->array_index != BACNET_ARRAY_ALL)) {
+ /* only array properties can have array options */
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
+ return false;
+ }
+ object_index = Analog_Output_Instance_To_Index(wp_data->object_instance);
+ if (object_index < MAX_ANALOG_OUTPUTS)
+ CurrentAO = &AO_Descr[object_index];
+ else
+ return false;
+
+ switch (wp_data->object_property) {
+ case PROP_PRESENT_VALUE:
+ if (value.tag == BACNET_APPLICATION_TAG_REAL) {
+ if (Analog_Output_Present_Value_Set(wp_data->object_instance,
+ value.type.Real, wp_data->priority)) {
+ status = true;
+ } else if (wp_data->priority == 6) {
+ /* Command priority 6 is reserved for use by Minimum On/Off
+ algorithm and may not be used for other purposes in any
+ object. */
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
+ } else {
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
+ }
+ } else {
+ status =
+ WPValidateArgType(&value, BACNET_APPLICATION_TAG_NULL,
+ &wp_data->error_class, &wp_data->error_code);
+ if (status) {
+ status =
+ Analog_Output_Present_Value_Relinquish
+ (wp_data->object_instance, wp_data->priority);
+ }
+ if (!status) {
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
+ }
+ }
+ break;
+
+ case PROP_OUT_OF_SERVICE:
+ {
+ bool Previous_Out_Of_Service = CurrentAO->Out_Of_Service;
+ status =
+ WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN,
+ &wp_data->error_class, &wp_data->error_code);
+ if (status) {
+ CurrentAO->Out_Of_Service = value.type.Boolean;
+ if (Previous_Out_Of_Service && !CurrentAO->Out_Of_Service)
+ /* We have just changed from Out_of_Service -> In Service */
+ /* We need to update the Present_Value to the value
+ * currently in the PLC...
+ */
+ CurrentAO->Present_Value[BACNET_MAX_PRIORITY-1] =
+ *(CurrentAO->Located_Var_ptr);
+ }
+ break;
+ }
+
+ case PROP_UNITS:
+ status =
+ WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED,
+ &wp_data->error_class, &wp_data->error_code);
+ if (status) {
+ CurrentAO->Units = value.type.Enumerated;
+ }
+ break;
+
+ case PROP_OBJECT_IDENTIFIER:
+ case PROP_OBJECT_NAME:
+ case PROP_OBJECT_TYPE:
+ case PROP_STATUS_FLAGS:
+ case PROP_EVENT_STATE:
+ case PROP_DESCRIPTION:
+ case PROP_RELINQUISH_DEFAULT:
+ case PROP_PRIORITY_ARRAY:
+// case PROP_PROPERTY_LIST:
+// case PROP_CURRENT_COMMAND_PRIORITY:
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
+ break;
+ default:
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
+ break;
+ }
+
+ return status;
+}
+
+
+
+
+
+
+
+/********************************************/
+/** Functions required for Beremiz plugin **/
+/********************************************/
+
+void Analog_Output_Copy_Present_Value_to_Located_Var(void) {
+ unsigned i;
+ for (i = 0; i < MAX_ANALOG_OUTPUTS; i++) {
+ // decouple PLC's Located Variable from Bacnet Object's Present Value if Out_Of_Service is true
+ if (AO_Descr[i].Out_Of_Service)
+ continue;
+
+ // copy the value
+ *(AO_Descr[i].Located_Var_ptr) = Analog_Output_Present_Value(AO_Descr[i].Object_Identifier);
+ }
+}
+
+
+
+void Analog_Output_Copy_Located_Var_to_Present_Value(void) {
+ unsigned i;
+ for (i = 0; i < MAX_ANALOG_OUTPUTS; i++) {
+ // decouple PLC's Located Variable from Bacnet Object's Present Value if Out_Of_Service is true
+ if (AO_Descr[i].Out_Of_Service)
+ continue;
+
+ // copy the value
+ AO_Descr[i].Present_Value[BACNET_MAX_PRIORITY-1] = *(AO_Descr[i].Located_Var_ptr);
+ }
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bacnet/runtime/ao.h Fri Jun 08 13:28:00 2018 +0200
@@ -0,0 +1,106 @@
+/**************************************************************************
+*
+* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
+* Copyright (C) 2011 Krzysztof Malorny <malornykrzysztof@gmail.com>
+* Copyright (C) 2017 Mario de Sousa <msousa@fe.up.pt>
+*
+* Permission is hereby granted, free of charge, to any person obtaining
+* a copy of this software and associated documentation files (the
+* "Software"), to deal in the Software without restriction, including
+* without limitation the rights to use, copy, modify, merge, publish,
+* distribute, sublicense, and/or sell copies of the Software, and to
+* permit persons to whom the Software is furnished to do so, subject to
+* the following conditions:
+*
+* The above copyright notice and this permission notice shall be included
+* in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*
+*********************************************************************/
+#ifndef AO_H
+#define AO_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include "bacdef.h"
+#include "bacerror.h"
+#include "wp.h"
+#include "rp.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+ typedef struct Analog_Output_descr {
+ /* pointer to IEC 61131-3 located variable that is mapped onto this BACnet object */
+ float *Located_Var_ptr;
+ uint32_t Object_Identifier; /* called object 'Instance' in the source code! */
+ char *Object_Name;
+ char *Description;
+ uint16_t Units;
+
+ /* stores the current value */
+ /* one entry per priority value */
+ float Present_Value[BACNET_MAX_PRIORITY];
+ unsigned Event_State:3;
+ bool Out_Of_Service;
+ } ANALOG_OUTPUT_DESCR;
+
+
+ void Analog_Output_Property_Lists(
+ const int **pRequired,
+ const int **pOptional,
+ const int **pProprietary);
+ bool Analog_Output_Valid_Instance(
+ uint32_t object_instance);
+ unsigned Analog_Output_Count(
+ void);
+ uint32_t Analog_Output_Index_To_Instance(
+ unsigned index);
+ unsigned Analog_Output_Instance_To_Index(
+ uint32_t object_instance);
+
+ bool Analog_Output_Object_Name(
+ uint32_t object_instance,
+ BACNET_CHARACTER_STRING * object_name);
+
+ int Analog_Output_Read_Property(
+ BACNET_READ_PROPERTY_DATA * rpdata);
+
+ bool Analog_Output_Write_Property(
+ BACNET_WRITE_PROPERTY_DATA * wp_data);
+
+ bool Analog_Output_Present_Value_Set(
+ uint32_t object_instance,
+ float value,
+ uint8_t priority);
+ float Analog_Output_Present_Value(
+ uint32_t object_instance);
+
+ char *Analog_Output_Description(
+ uint32_t instance);
+
+ uint16_t Analog_Output_Units(
+ uint32_t instance);
+
+ bool Analog_Output_Out_Of_Service(
+ uint32_t instance);
+ void Analog_Output_Out_Of_Service_Set(
+ uint32_t instance,
+ bool oos_flag);
+
+ void Analog_Output_Init(
+ void);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bacnet/runtime/av.c Fri Jun 08 13:28:00 2018 +0200
@@ -0,0 +1,622 @@
+/**************************************************************************
+*
+* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
+* Copyright (C) 2011 Krzysztof Malorny <malornykrzysztof@gmail.com>
+* Copyright (C) 2017 Mario de Sousa <msousa@fe.up.pt>
+*
+* Permission is hereby granted, free of charge, to any person obtaining
+* a copy of this software and associated documentation files (the
+* "Software"), to deal in the Software without restriction, including
+* without limitation the rights to use, copy, modify, merge, publish,
+* distribute, sublicense, and/or sell copies of the Software, and to
+* permit persons to whom the Software is furnished to do so, subject to
+* the following conditions:
+*
+* The above copyright notice and this permission notice shall be included
+* in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*
+*********************************************************************/
+
+/* Analog Value Objects - customize for your use */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h> /* NAN maro */
+
+#include "config_bacnet_for_beremiz_%(locstr)s.h" /* the custom configuration for beremiz plugin */
+#include "bacdef.h"
+#include "bacdcode.h"
+#include "bacenum.h"
+#include "bacapp.h"
+#include "bactext.h"
+#include "device_%(locstr)s.h"
+#include "handlers.h"
+#include "av_%(locstr)s.h"
+
+/* we choose to have a NULL level in our system represented by */
+/* a particular value. When the priorities are not in use, they */
+/* will be relinquished (i.e. set to the NULL level). */
+/* Since the values are floats, we use NAN (Not A Number) as our NULL value. */
+/* WARNING: Never use comparisons like 'if (value == AO_LEVEL_NULL)'
+ * as it will always return false, even if both values are NAN.
+ * Use instead the negated version 'if (value != AO_LEVEL_NULL)'
+ * and add an 'else' to the 'if' condition if necessary.
+ * However, some compilers may screw this up if they do not
+ * implement IEEE 754 properly. It is probably best to stick with
+ * the isnan() macro if available.
+ */
+#define AV_VALUE_NULL NAN
+#define AV_VALUE_IS_NULL(x) (isnan(x))
+/* When all the priorities are level null, the present value returns */
+/* the Relinquish Default value */
+#define AV_VALUE_RELINQUISH_DEFAULT (0.0)
+
+
+/* The IEC 61131-3 located variables mapped onto the Analog Value objects of BACnet protocol */
+%(AV_lvars)s
+
+
+/* The array where we keep all the state related to the Analog Value Objects */
+#define MAX_ANALOG_VALUES %(AV_count)s
+static ANALOG_VALUE_DESCR AV_Descr[MAX_ANALOG_VALUES] = {
+%(AV_param)s
+};
+
+
+/* These three arrays are used by the ReadPropertyMultiple handler,
+ * as well as to initialize the XXX_Property_List used by the
+ * Property List (PROP_PROPERTY_LIST) property.
+ */
+static const int Analog_Value_Properties_Required[] = {
+ /* (1) Currently Supported */
+ /* (2) Required by standard ASHRAE 135-2016 */
+ /*(1)(2) */
+ PROP_OBJECT_IDENTIFIER, /* R R ( 75) */
+ PROP_OBJECT_NAME, /* R R ( 77) */
+ PROP_OBJECT_TYPE, /* R R ( 79) */
+ PROP_PRESENT_VALUE, /* W R ( 85) */
+ PROP_STATUS_FLAGS, /* R R (111) */
+ PROP_EVENT_STATE, /* R R ( 36) */
+ PROP_OUT_OF_SERVICE, /* W R ( 81) */
+ PROP_UNITS, /* W R (117) */
+// PROP_PROPERTY_LIST, /* R R (371) */
+ -1
+};
+
+static const int Analog_Value_Properties_Optional[] = {
+ /* (1) Currently Supported */
+ /* (2) Required by standard ASHRAE 135-2016 */
+ /*(1)(2) */
+ PROP_DESCRIPTION, /* R O ( 28) */
+ /* required if Present_Value is writable (which is true in our case!) */
+ PROP_PRIORITY_ARRAY, /* R O ( 87) */
+ PROP_RELINQUISH_DEFAULT, /* R O (104) */
+// PROP_CURRENT_COMMAND_PRIORITY,/* R O (431) */
+ -1
+};
+
+static const int Analog_Value_Properties_Proprietary[] = {
+ -1
+};
+
+/* This array stores the PROPERTY_LIST which may be read by clients.
+ * End of list is marked by following the last element with the value '-1'
+ *
+ * It is initialized by Analog_Values_Init() based off the values
+ * stored in Analog_Value_Properties_Required
+ * Analog_Value_Properties_Optional
+ * Analog_Value_Properties_Proprietary
+ */
+/* TODO: Allocate memory for this array with malloc() at startup */
+static int Analog_Value_Properties_List[64];
+
+
+
+/********************************************************/
+/** Callback functions. **/
+/** Functions required by BACnet devie implementation. **/
+/********************************************************/
+
+
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+void Analog_Value_Property_Lists(
+ const int **pRequired,
+ const int **pOptional,
+ const int **pProprietary)
+{
+ if (pRequired)
+ *pRequired = Analog_Value_Properties_Required;
+ if (pOptional)
+ *pOptional = Analog_Value_Properties_Optional;
+ if (pProprietary)
+ *pProprietary = Analog_Value_Properties_Proprietary;
+
+ return;
+}
+
+
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+void Analog_Value_Init(
+ void)
+{
+ unsigned i, j;
+ static bool initialized = false;
+
+ if (!initialized) {
+ initialized = true;
+
+ /* initialize the Analog_Value_Properties_List array */
+ int len = 0;
+ len += BACnet_Init_Properties_List(Analog_Value_Properties_List + len,
+ Analog_Value_Properties_Required);
+ len += BACnet_Init_Properties_List(Analog_Value_Properties_List + len,
+ Analog_Value_Properties_Optional);
+ len += BACnet_Init_Properties_List(Analog_Value_Properties_List + len,
+ Analog_Value_Properties_Proprietary);
+
+ for (i = 0; i < MAX_ANALOG_VALUES; i++) {
+ // MJS: the following line in th original demo code was commented out so we do not
+ // overwrite the initial values configured by the user in beremiz IDE
+ // memset(&AV_Descr[i], 0x00, sizeof(ANALOG_VALUE_DESCR));
+ for (j = 0; j < BACNET_MAX_PRIORITY; j++) {
+ AV_Descr[i].Present_Value[j] = AV_VALUE_NULL;
+ }
+ AV_Descr[i].Out_Of_Service = 0;
+ AV_Descr[i].Event_State = 0;
+// AV_Descr[i].Units = UNITS_NO_UNITS;
+ }
+ }
+}
+
+
+
+
+/* validate that the given instance exists */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+bool Analog_Value_Valid_Instance(
+ uint32_t object_instance)
+{
+ return (Analog_Value_Instance_To_Index(object_instance) < MAX_ANALOG_VALUES);
+}
+
+/* the number of Analog Value Objects */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+unsigned Analog_Value_Count(void) {return MAX_ANALOG_VALUES;}
+
+
+/* returns the instance (i.e. Object Identifier) that correlates to the correct index */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+uint32_t Analog_Value_Index_To_Instance(unsigned index) {return AV_Descr[index].Object_Identifier;}
+
+
+
+/* returns the index that correlates to the correct instance number (Object Identifier) */
+unsigned Analog_Value_Instance_To_Index(
+ uint32_t object_instance)
+{
+ unsigned index = 0;
+
+ for (index = 0; index < MAX_ANALOG_VALUES; index++)
+ if (object_instance == AV_Descr[index].Object_Identifier)
+ return index;
+
+ /* error, this object ID is not in our list! */
+ return MAX_ANALOG_VALUES;
+}
+
+
+
+
+float Analog_Value_Present_Value(
+ uint32_t object_instance)
+{
+ float value = AV_VALUE_RELINQUISH_DEFAULT;
+ unsigned index = 0;
+ unsigned i = 0;
+
+ index = Analog_Value_Instance_To_Index(object_instance);
+ if (index < MAX_ANALOG_VALUES) {
+ for (i = 0; i < BACNET_MAX_PRIORITY; i++) {
+ if (!AV_VALUE_IS_NULL(AV_Descr[index].Present_Value[i])) {
+ value = AV_Descr[index].Present_Value[i];
+ break;
+ }
+ }
+ }
+
+ return value;
+}
+
+
+
+/* returns command priority (1..16), or 0 if all priority values are at NULL */
+int Analog_Value_Current_Command_Priority(
+ uint32_t object_instance)
+{
+ unsigned index = 0;
+ unsigned i = 0;
+
+ index = Analog_Value_Instance_To_Index(object_instance);
+ if (index < MAX_ANALOG_VALUES) {
+ for (i = 0; i < BACNET_MAX_PRIORITY; i++) {
+ if (!AV_VALUE_IS_NULL(AV_Descr[index].Present_Value[i])) {
+ return i+1; // +1 since priority is 1..16, and not 0..15
+ }
+ }
+ }
+ // command values in all priorities are set to NULL
+ return 0;
+}
+
+
+
+/* For a given object instance-number, sets the present-value at a given
+ * priority 1..16 (except 6, see ASHRAE 135-2016, section 19.2.2)
+ */
+bool Analog_Value_Present_Value_Set(
+ uint32_t object_instance,
+ float value,
+ uint8_t priority)
+{
+ unsigned index = 0;
+
+ index = Analog_Value_Instance_To_Index(object_instance);
+ if (index >= MAX_ANALOG_VALUES)
+ return false;
+ if ((priority == 0) || (priority > BACNET_MAX_PRIORITY) ||
+ (priority == 6 /* reserved, ASHRAE 135-2016, section 19.2.2 */))
+ return false;
+
+ priority--;
+ AV_Descr[index].Present_Value[priority] = value;
+ return true;
+}
+
+
+
+bool Analog_Value_Present_Value_Relinquish(
+ uint32_t object_instance,
+ unsigned priority)
+{
+ unsigned index = 0;
+
+ index = Analog_Value_Instance_To_Index(object_instance);
+ if (index >= MAX_ANALOG_VALUES)
+ return false;
+
+ if ((priority == 0) || (priority > BACNET_MAX_PRIORITY) ||
+ (priority == 6 /* reserved, ASHRAE 135-2016, section 19.2.2 */))
+ return false;
+
+ priority--;
+ AV_Descr[index].Present_Value[priority] = AV_VALUE_NULL;
+ return true;
+}
+
+
+
+/* note: the object name must be unique within this device */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+bool Analog_Value_Object_Name(
+ uint32_t object_instance,
+ BACNET_CHARACTER_STRING * object_name)
+{
+ bool status = false;
+ unsigned index = Analog_Value_Instance_To_Index(object_instance);
+
+ if (index < MAX_ANALOG_VALUES)
+ status = characterstring_init_ansi(object_name, AV_Descr[index].Object_Name);
+
+ return status;
+}
+
+
+
+bool Analog_Value_Object_Description(
+ uint32_t object_instance,
+ BACNET_CHARACTER_STRING * object_name)
+{
+ bool status = false;
+ unsigned index = Analog_Value_Instance_To_Index(object_instance);
+
+ if (index < MAX_ANALOG_VALUES)
+ status = characterstring_init_ansi(object_name, AV_Descr[index].Description);
+
+ return status;
+}
+
+
+
+/* return apdu len, or BACNET_STATUS_ERROR on error */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+int Analog_Value_Read_Property(
+ BACNET_READ_PROPERTY_DATA * rpdata)
+{
+ int apdu_len = 0; /* return value */
+ BACNET_BIT_STRING bit_string;
+ BACNET_CHARACTER_STRING char_string;
+ float real_value = (float) 1.414;
+ unsigned object_index = 0;
+ bool state = false;
+ uint8_t *apdu = NULL;
+ ANALOG_VALUE_DESCR *CurrentAV;
+
+ if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
+ (rpdata->application_data_len == 0)) {
+ return 0;
+ }
+
+ apdu = rpdata->application_data;
+
+ object_index = Analog_Value_Instance_To_Index(rpdata->object_instance);
+ if (object_index >= MAX_ANALOG_VALUES) {
+ rpdata->error_class = ERROR_CLASS_OBJECT;
+ rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT;
+ return BACNET_STATUS_ERROR;
+ }
+
+ switch (rpdata->object_property) {
+ case PROP_OBJECT_IDENTIFIER:
+ apdu_len =
+ encode_application_object_id(&apdu[0], OBJECT_ANALOG_VALUE,
+ rpdata->object_instance);
+ break;
+
+ case PROP_OBJECT_NAME:
+ Analog_Value_Object_Name(rpdata->object_instance, &char_string);
+ apdu_len =
+ encode_application_character_string(&apdu[0], &char_string);
+ break;
+
+ case PROP_DESCRIPTION:
+ Analog_Value_Object_Description(rpdata->object_instance, &char_string);
+ apdu_len =
+ encode_application_character_string(&apdu[0], &char_string);
+ break;
+
+ case PROP_OBJECT_TYPE:
+ apdu_len =
+ encode_application_enumerated(&apdu[0], OBJECT_ANALOG_VALUE);
+ break;
+
+ case PROP_PRESENT_VALUE:
+ real_value = Analog_Value_Present_Value(rpdata->object_instance);
+ apdu_len = encode_application_real(&apdu[0], real_value);
+ break;
+
+ case PROP_STATUS_FLAGS:
+ bitstring_init(&bit_string);
+ bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false);
+ bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false);
+ bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false);
+ bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE,
+ AV_Descr[object_index].Out_Of_Service);
+
+ apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
+ break;
+
+ case PROP_EVENT_STATE:
+ apdu_len =
+ encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL);
+ break;
+
+ case PROP_OUT_OF_SERVICE:
+ state = AV_Descr[object_index].Out_Of_Service;
+ apdu_len = encode_application_boolean(&apdu[0], state);
+ break;
+
+ case PROP_PRIORITY_ARRAY:
+ BACnet_encode_array(AV_Descr[object_index].Present_Value,
+ BACNET_MAX_PRIORITY,
+ AV_VALUE_IS_NULL,
+ encode_application_real)
+ break;
+
+// case PROP_CURRENT_COMMAND_PRIORITY: {
+// unsigned i = Analog_Value_Current_Command_Priority(rpdata->object_instance);
+// if (i == 0) apdu_len = encode_application_null (&apdu[0]);
+// else apdu_len = encode_application_unsigned(&apdu[0], i);
+// break;
+// }
+
+ case PROP_RELINQUISH_DEFAULT:
+ real_value = AV_VALUE_RELINQUISH_DEFAULT;
+ apdu_len = encode_application_real(&apdu[0], real_value);
+ break;
+
+ case PROP_UNITS:
+ apdu_len =
+ encode_application_enumerated(&apdu[0], AV_Descr[object_index].Units);
+ break;
+
+// case PROP_PROPERTY_LIST:
+// BACnet_encode_array(Analog_Value_Properties_List,
+// property_list_count(Analog_Value_Properties_List),
+// retfalse, encode_application_enumerated);
+// break;
+ default:
+ rpdata->error_class = ERROR_CLASS_PROPERTY;
+ rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
+ apdu_len = BACNET_STATUS_ERROR;
+ break;
+ }
+ /* only array properties can have array options */
+ if ((apdu_len >= 0) && (rpdata->object_property != PROP_PRIORITY_ARRAY) &&
+ (rpdata->object_property != PROP_EVENT_TIME_STAMPS) &&
+// (rpdata->object_property != PROP_PROPERTY_LIST) &&
+ (rpdata->array_index != BACNET_ARRAY_ALL)) {
+ rpdata->error_class = ERROR_CLASS_PROPERTY;
+ rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
+ apdu_len = BACNET_STATUS_ERROR;
+ }
+
+ return apdu_len;
+}
+
+
+
+
+/* returns true if successful */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+bool Analog_Value_Write_Property(
+ BACNET_WRITE_PROPERTY_DATA * wp_data)
+{
+ bool status = false; /* return value */
+ unsigned int object_index = 0;
+ int len = 0;
+ BACNET_APPLICATION_DATA_VALUE value;
+ ANALOG_VALUE_DESCR *CurrentAV;
+
+ /* decode the some of the request */
+ len =
+ bacapp_decode_application_data(wp_data->application_data,
+ wp_data->application_data_len, &value);
+ /* FIXME: len < application_data_len: more data? */
+ if (len < 0) {
+ /* error while decoding - a value larger than we can handle */
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
+ return false;
+ }
+ if ((wp_data->object_property != PROP_PRIORITY_ARRAY) &&
+ (wp_data->object_property != PROP_EVENT_TIME_STAMPS) &&
+ (wp_data->array_index != BACNET_ARRAY_ALL)) {
+ /* only array properties can have array options */
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
+ return false;
+ }
+ object_index = Analog_Value_Instance_To_Index(wp_data->object_instance);
+ if (object_index < MAX_ANALOG_VALUES)
+ CurrentAV = &AV_Descr[object_index];
+ else
+ return false;
+
+ switch (wp_data->object_property) {
+ case PROP_PRESENT_VALUE:
+ if (value.tag == BACNET_APPLICATION_TAG_REAL) {
+ if (Analog_Value_Present_Value_Set(wp_data->object_instance,
+ value.type.Real, wp_data->priority)) {
+ status = true;
+ } else if (wp_data->priority == 6) {
+ /* Command priority 6 is reserved for use by Minimum On/Off
+ algorithm and may not be used for other purposes in any
+ object. */
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
+ } else {
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
+ }
+ } else {
+ status =
+ WPValidateArgType(&value, BACNET_APPLICATION_TAG_NULL,
+ &wp_data->error_class, &wp_data->error_code);
+ if (status) {
+ status =
+ Analog_Value_Present_Value_Relinquish
+ (wp_data->object_instance, wp_data->priority);
+ }
+ if (!status) {
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
+ }
+ }
+ break;
+
+ case PROP_OUT_OF_SERVICE:
+ {
+ bool Previous_Out_Of_Service = CurrentAV->Out_Of_Service;
+ status =
+ WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN,
+ &wp_data->error_class, &wp_data->error_code);
+ if (status) {
+ CurrentAV->Out_Of_Service = value.type.Boolean;
+ if (Previous_Out_Of_Service && !CurrentAV->Out_Of_Service)
+ /* We have just changed from Out_of_Service -> In Service */
+ /* We need to update the Present_Value to the value
+ * currently in the PLC...
+ */
+ CurrentAV->Present_Value[BACNET_MAX_PRIORITY-1] =
+ *(CurrentAV->Located_Var_ptr);
+ }
+ break;
+ }
+
+ case PROP_UNITS:
+ status =
+ WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED,
+ &wp_data->error_class, &wp_data->error_code);
+ if (status) {
+ CurrentAV->Units = value.type.Enumerated;
+ }
+ break;
+
+ case PROP_OBJECT_IDENTIFIER:
+ case PROP_OBJECT_NAME:
+ case PROP_OBJECT_TYPE:
+ case PROP_STATUS_FLAGS:
+ case PROP_EVENT_STATE:
+ case PROP_DESCRIPTION:
+ case PROP_RELINQUISH_DEFAULT:
+ case PROP_PRIORITY_ARRAY:
+// case PROP_PROPERTY_LIST:
+// case PROP_CURRENT_COMMAND_PRIORITY:
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
+ break;
+ default:
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
+ break;
+ }
+
+ return status;
+}
+
+
+
+
+
+
+
+/********************************************/
+/** Functions required for Beremiz plugin **/
+/********************************************/
+
+void Analog_Value_Copy_Present_Value_to_Located_Var(void) {
+ unsigned i;
+ for (i = 0; i < MAX_ANALOG_VALUES; i++) {
+ // decouple PLC's Located Variable from Bacnet Object's Present Value if Out_Of_Service is true
+ if (AV_Descr[i].Out_Of_Service)
+ continue;
+
+ // copy the value
+ *(AV_Descr[i].Located_Var_ptr) = Analog_Value_Present_Value(AV_Descr[i].Object_Identifier);
+ }
+}
+
+
+
+void Analog_Value_Copy_Located_Var_to_Present_Value(void) {
+ unsigned i;
+ for (i = 0; i < MAX_ANALOG_VALUES; i++) {
+ // decouple PLC's Located Variable from Bacnet Object's Present Value if Out_Of_Service is true
+ if (AV_Descr[i].Out_Of_Service)
+ continue;
+
+ // copy the value
+ AV_Descr[i].Present_Value[BACNET_MAX_PRIORITY-1] = *(AV_Descr[i].Located_Var_ptr);
+ }
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bacnet/runtime/av.h Fri Jun 08 13:28:00 2018 +0200
@@ -0,0 +1,106 @@
+/**************************************************************************
+*
+* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
+* Copyright (C) 2011 Krzysztof Malorny <malornykrzysztof@gmail.com>
+* Copyright (C) 2017 Mario de Sousa <msousa@fe.up.pt>
+*
+* Permission is hereby granted, free of charge, to any person obtaining
+* a copy of this software and associated documentation files (the
+* "Software"), to deal in the Software without restriction, including
+* without limitation the rights to use, copy, modify, merge, publish,
+* distribute, sublicense, and/or sell copies of the Software, and to
+* permit persons to whom the Software is furnished to do so, subject to
+* the following conditions:
+*
+* The above copyright notice and this permission notice shall be included
+* in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*
+*********************************************************************/
+#ifndef AV_H
+#define AV_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include "bacdef.h"
+#include "bacerror.h"
+#include "wp.h"
+#include "rp.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+ typedef struct analog_value_descr {
+ /* pointer to IEC 61131-3 located variable that is mapped onto this BACnet object */
+ float *Located_Var_ptr;
+ uint32_t Object_Identifier; /* called object 'Instance' in the source code! */
+ char *Object_Name;
+ char *Description;
+ uint16_t Units;
+
+ /* stores the current value */
+ /* one entry per priority value */
+ float Present_Value[BACNET_MAX_PRIORITY];
+ unsigned Event_State:3;
+ bool Out_Of_Service;
+ } ANALOG_VALUE_DESCR;
+
+
+ void Analog_Value_Property_Lists(
+ const int **pRequired,
+ const int **pOptional,
+ const int **pProprietary);
+ bool Analog_Value_Valid_Instance(
+ uint32_t object_instance);
+ unsigned Analog_Value_Count(
+ void);
+ uint32_t Analog_Value_Index_To_Instance(
+ unsigned index);
+ unsigned Analog_Value_Instance_To_Index(
+ uint32_t object_instance);
+
+ bool Analog_Value_Object_Name(
+ uint32_t object_instance,
+ BACNET_CHARACTER_STRING * object_name);
+
+ int Analog_Value_Read_Property(
+ BACNET_READ_PROPERTY_DATA * rpdata);
+
+ bool Analog_Value_Write_Property(
+ BACNET_WRITE_PROPERTY_DATA * wp_data);
+
+ bool Analog_Value_Present_Value_Set(
+ uint32_t object_instance,
+ float value,
+ uint8_t priority);
+ float Analog_Value_Present_Value(
+ uint32_t object_instance);
+
+ char *Analog_Value_Description(
+ uint32_t instance);
+
+ uint16_t Analog_Value_Units(
+ uint32_t instance);
+
+ bool Analog_Value_Out_Of_Service(
+ uint32_t instance);
+ void Analog_Value_Out_Of_Service_Set(
+ uint32_t instance,
+ bool oos_flag);
+
+ void Analog_Value_Init(
+ void);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bacnet/runtime/bi.c Fri Jun 08 13:28:00 2018 +0200
@@ -0,0 +1,492 @@
+/**************************************************************************
+*
+* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
+* Copyright (C) 2017 Mario de Sousa <msousa@fe.up.pt>
+*
+* Permission is hereby granted, free of charge, to any person obtaining
+* a copy of this software and associated documentation files (the
+* "Software"), to deal in the Software without restriction, including
+* without limitation the rights to use, copy, modify, merge, publish,
+* distribute, sublicense, and/or sell copies of the Software, and to
+* permit persons to whom the Software is furnished to do so, subject to
+* the following conditions:
+*
+* The above copyright notice and this permission notice shall be included
+* in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*
+*********************************************************************/
+
+/* Binary Input Objects - customize for your use */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "config_bacnet_for_beremiz_%(locstr)s.h" /* the custom configuration for beremiz plugin */
+#include "bacdef.h"
+#include "bacdcode.h"
+#include "bacenum.h"
+#include "bacapp.h"
+#include "wp.h"
+#include "rp.h"
+#include "bi_%(locstr)s.h"
+#include "handlers.h"
+
+
+
+/* initial value for present_value property of each object */
+#define BI_VALUE_INIT (0)
+
+
+/* The IEC 61131-3 located variables mapped onto the Binary Input objects of BACnet protocol */
+%(BI_lvars)s
+
+
+/* The array where we keep all the state related to the Binary Input Objects */
+#define MAX_BINARY_INPUTS %(BI_count)s
+static BINARY_INPUT_DESCR BI_Descr[MAX_BINARY_INPUTS] = {
+%(BI_param)s
+};
+
+
+
+
+/* These three arrays are used by the ReadPropertyMultiple handler,
+ * as well as to initialize the XXX_Property_List used by the
+ * Property List (PROP_PROPERTY_LIST) property.
+ */
+static const int Binary_Input_Properties_Required[] = {
+ /* (1) Currently Supported */
+ /* (2) Required by standard ASHRAE 135-2016 */
+ /*(1)(2) */
+ PROP_OBJECT_IDENTIFIER, /* R R ( 75) */
+ PROP_OBJECT_NAME, /* R R ( 77) */
+ PROP_OBJECT_TYPE, /* R R ( 79) */
+ PROP_PRESENT_VALUE, /* W R ( 85) */
+ PROP_STATUS_FLAGS, /* R R (111) */
+ PROP_EVENT_STATE, /* R R ( 36) */
+ PROP_OUT_OF_SERVICE, /* W R ( 81) */
+ PROP_POLARITY, /* R R ( 84) */
+// PROP_PROPERTY_LIST, /* R R (371) */
+ -1
+};
+
+static const int Binary_Input_Properties_Optional[] = {
+ /* (1) Currently Supported */
+ /* (2) Required by standard ASHRAE 135-2016 */
+ /*(1)(2) */
+ PROP_DESCRIPTION, /* R O ( 28) */
+ -1
+};
+
+static const int Binary_Input_Properties_Proprietary[] = {
+ -1
+};
+
+
+/* This array stores the PROPERTY_LIST which may be read by clients.
+ * End of list is marked by following the last element with the value '-1'
+ *
+ * It is initialized by Binary_Input_Init() based off the values
+ * stored in Binary_Input_Properties_Required
+ * Binary_Input_Properties_Optional
+ * Binary_Input_Properties_Proprietary
+ */
+/* TODO: Allocate memory for this array with malloc() at startup */
+static int Binary_Input_Properties_List[64];
+
+
+
+
+
+/********************************************************/
+/** Callback functions. **/
+/** Functions required by BACnet devie implementation. **/
+/********************************************************/
+
+
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+void Binary_Input_Property_Lists(
+ const int **pRequired,
+ const int **pOptional,
+ const int **pProprietary)
+{
+ if (pRequired)
+ *pRequired = Binary_Input_Properties_Required;
+ if (pOptional)
+ *pOptional = Binary_Input_Properties_Optional;
+ if (pProprietary)
+ *pProprietary = Binary_Input_Properties_Proprietary;
+
+ return;
+}
+
+
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+void Binary_Input_Init(
+ void)
+{
+ unsigned i, j;
+ static bool initialized = false;
+
+ // fprintf(stderr, "BACnet plugin: Binary_Input_Init() called!\n");
+
+ if (!initialized) {
+ initialized = true;
+
+ /* initialize the Binary_Input_Properties_List array */
+ int len = 0;
+ len += BACnet_Init_Properties_List(Binary_Input_Properties_List + len,
+ Binary_Input_Properties_Required);
+ len += BACnet_Init_Properties_List(Binary_Input_Properties_List + len,
+ Binary_Input_Properties_Optional);
+ len += BACnet_Init_Properties_List(Binary_Input_Properties_List + len,
+ Binary_Input_Properties_Proprietary);
+
+ /* initialize all the binary values priority arrays to NULL */
+ for (i = 0; i < MAX_BINARY_INPUTS; i++) {
+ BI_Descr[i].Present_Value = BI_VALUE_INIT;
+ BI_Descr[i].Polarity = POLARITY_NORMAL;
+ }
+ }
+
+ return;
+}
+
+
+
+/* validate that the given instance (Object ID) exists */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+bool Binary_Input_Valid_Instance(
+ uint32_t object_instance)
+{
+ // fprintf(stderr, "BACnet plugin: Binary_Input_Valid_Instance(obj_ID=%%u) called!\n", object _instance);
+ return (Binary_Input_Instance_To_Index(object_instance) < MAX_BINARY_INPUTS);
+}
+
+
+/* the number of Binary Input Objects */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+unsigned Binary_Input_Count(void) {return MAX_BINARY_INPUTS;}
+
+
+/* returns the instance (i.e. Object Identifier) that correlates to the correct index */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+uint32_t Binary_Input_Index_To_Instance(unsigned index) {return BI_Descr[index].Object_Identifier;}
+
+
+/* returns the index that correlates to the correct instance number (Object Identifier) */
+unsigned Binary_Input_Instance_To_Index(
+ uint32_t object_instance)
+{
+ unsigned index = 0;
+
+ for (index = 0; index < MAX_BINARY_INPUTS; index++)
+ if (object_instance == BI_Descr[index].Object_Identifier)
+ return index;
+
+ /* error, this object ID is not in our list! */
+ return MAX_BINARY_INPUTS;
+}
+
+
+
+BACNET_BINARY_PV Binary_Input_Present_Value(
+ uint32_t object_instance)
+{
+ BACNET_BINARY_PV value = BI_VALUE_INIT;
+ unsigned index = 0;
+ unsigned i = 0;
+
+ // fprintf(stderr, "BACnet plugin: Binary_Input_Present_Value(obj_ID=%%u) called!\n", object_instance);
+
+ index = Binary_Input_Instance_To_Index(object_instance);
+ if (index < MAX_BINARY_INPUTS)
+ value = BI_Descr[index].Present_Value;
+
+ return value;
+}
+
+
+
+/* note: the object name must be unique within this device */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+bool Binary_Input_Object_Name(
+ uint32_t object_instance,
+ BACNET_CHARACTER_STRING * object_name)
+{
+ bool status = false;
+ unsigned index = Binary_Input_Instance_To_Index(object_instance);
+
+ if (index < MAX_BINARY_INPUTS)
+ status = characterstring_init_ansi(object_name, BI_Descr[index].Object_Name);
+
+ return status;
+}
+
+
+
+bool Binary_Input_Object_Description(
+ uint32_t object_instance,
+ BACNET_CHARACTER_STRING * object_name)
+{
+ bool status = false;
+ unsigned index = Binary_Input_Instance_To_Index(object_instance);
+
+ if (index < MAX_BINARY_INPUTS)
+ status = characterstring_init_ansi(object_name, BI_Descr[index].Description);
+
+ return status;
+}
+
+
+
+/* return apdu len, or BACNET_STATUS_ERROR on error */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+int Binary_Input_Read_Property(
+ BACNET_READ_PROPERTY_DATA * rpdata)
+{
+ int len = 0;
+ int apdu_len = 0; /* return value */
+ BACNET_BIT_STRING bit_string;
+ BACNET_CHARACTER_STRING char_string;
+ BACNET_BINARY_PV present_value = BINARY_INACTIVE;
+ unsigned object_index = 0;
+ unsigned i = 0;
+ bool state = false;
+ uint8_t *apdu = NULL;
+
+ // fprintf(stderr, "BACnet plugin: Binary_Input_Read_Property() called!\n");
+
+ if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
+ (rpdata->application_data_len == 0)) {
+ return 0;
+ }
+
+ object_index = Binary_Input_Instance_To_Index(rpdata->object_instance);
+ if (object_index >= MAX_BINARY_INPUTS) {
+ rpdata->error_class = ERROR_CLASS_OBJECT;
+ rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT;
+ return BACNET_STATUS_ERROR;
+ }
+
+ apdu = rpdata->application_data;
+ switch (rpdata->object_property) {
+ case PROP_OBJECT_IDENTIFIER:
+ apdu_len =
+ encode_application_object_id(&apdu[0], OBJECT_BINARY_INPUT,
+ rpdata->object_instance);
+ break;
+ case PROP_OBJECT_NAME:
+ Binary_Input_Object_Name(rpdata->object_instance, &char_string);
+ apdu_len =
+ encode_application_character_string(&apdu[0], &char_string);
+ break;
+ case PROP_DESCRIPTION:
+ Binary_Input_Object_Description(rpdata->object_instance, &char_string);
+ apdu_len =
+ encode_application_character_string(&apdu[0], &char_string);
+ break;
+ case PROP_OBJECT_TYPE:
+ apdu_len =
+ encode_application_enumerated(&apdu[0], OBJECT_BINARY_INPUT);
+ break;
+ case PROP_PRESENT_VALUE:
+ present_value =
+ Binary_Input_Present_Value(rpdata->object_instance);
+ apdu_len = encode_application_enumerated(&apdu[0], present_value);
+ break;
+ case PROP_STATUS_FLAGS:
+ /* note: see the details in the standard on how to use these */
+ bitstring_init(&bit_string);
+ bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false);
+ bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false);
+ bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false);
+ state = BI_Descr[object_index].Out_Of_Service;
+ bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, state);
+ apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
+ break;
+ case PROP_EVENT_STATE:
+ /* note: see the details in the standard on how to use this */
+ apdu_len =
+ encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL);
+ break;
+ case PROP_OUT_OF_SERVICE:
+ state = BI_Descr[object_index].Out_Of_Service;
+ apdu_len = encode_application_boolean(&apdu[0], state);
+ break;
+ case PROP_POLARITY:
+ apdu_len = encode_application_enumerated(&apdu[0],
+ BI_Descr[object_index].Polarity);
+ break;
+// case PROP_PROPERTY_LIST:
+// BACnet_encode_array(Binary_Input_Properties_List,
+// property_list_count(Binary_Input_Properties_List),
+// retfalse, encode_application_enumerated);
+// break;
+ default:
+ rpdata->error_class = ERROR_CLASS_PROPERTY;
+ rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
+ apdu_len = BACNET_STATUS_ERROR;
+ break;
+ }
+ /* only array properties can have array options */
+ if ((apdu_len >= 0) &&
+// (rpdata->object_property != PROP_PROPERTY_LIST) &&
+ (rpdata->array_index != BACNET_ARRAY_ALL)) {
+ rpdata->error_class = ERROR_CLASS_PROPERTY;
+ rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
+ apdu_len = BACNET_STATUS_ERROR;
+ }
+
+ return apdu_len;
+}
+
+
+
+
+/* returns true if successful */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+bool Binary_Input_Write_Property(
+ BACNET_WRITE_PROPERTY_DATA * wp_data)
+{
+ bool status = false; /* return value */
+ unsigned int object_index = 0;
+ unsigned int priority = 0;
+ BACNET_BINARY_PV level = BINARY_NULL;
+ int len = 0;
+ BACNET_APPLICATION_DATA_VALUE value;
+
+ /* decode the some of the request */
+ len =
+ bacapp_decode_application_data(wp_data->application_data,
+ wp_data->application_data_len, &value);
+ /* FIXME: len < application_data_len: more data? */
+ if (len < 0) {
+ /* error while decoding - a value larger than we can handle */
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
+ return false;
+ }
+ /* only array properties can have array options */
+ if (wp_data->array_index != BACNET_ARRAY_ALL) {
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
+ return false;
+ }
+ /* No need to check whether object_index is within bounds.
+ * Has already been checked before Binary_Input_Write_Property() is called
+ */
+ object_index = Binary_Input_Instance_To_Index(wp_data->object_instance);
+
+ switch (wp_data->object_property) {
+ case PROP_PRESENT_VALUE:
+ status =
+ WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED,
+ &wp_data->error_class, &wp_data->error_code);
+ if (!status) {
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
+ } else {
+ if (!BI_Descr[object_index].Out_Of_Service) {
+ /* input objects can only be written to when Out_Of_Service is true! */
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
+ status = false; // not really necessary here.
+ } else {
+ if (!(value.type.Enumerated <= MAX_BINARY_PV)) {
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
+ status = false;
+ } else {
+ level = (BACNET_BINARY_PV) value.type.Enumerated;
+ BI_Descr[object_index].Present_Value = level;
+ status = true;
+ }
+ }
+ }
+ break;
+ case PROP_OUT_OF_SERVICE:
+ {
+ bool Previous_Out_Of_Service = BI_Descr[object_index].Out_Of_Service;
+ status =
+ WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN,
+ &wp_data->error_class, &wp_data->error_code);
+ if (status) {
+ BI_Descr[object_index].Out_Of_Service = value.type.Boolean;
+ if (Previous_Out_Of_Service && !BI_Descr[object_index].Out_Of_Service)
+ /* We have just changed from Out_of_Service -> In Service */
+ /* We need to update the Present_Value to the value
+ * currently in the PLC...
+ */
+ BI_Descr[object_index].Present_Value =
+ *(BI_Descr[object_index].Located_Var_ptr);
+ }
+ break;
+ }
+ case PROP_OBJECT_IDENTIFIER:
+ case PROP_OBJECT_NAME:
+ case PROP_DESCRIPTION:
+ case PROP_OBJECT_TYPE:
+ case PROP_STATUS_FLAGS:
+ case PROP_EVENT_STATE:
+ case PROP_POLARITY:
+// case PROP_PROPERTY_LIST:
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
+ break;
+ default:
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
+ break;
+ }
+
+ return status;
+}
+
+
+
+
+
+/********************************************/
+/** Functions required for Beremiz plugin **/
+/********************************************/
+
+
+
+void Binary_Input_Copy_Present_Value_to_Located_Var(void) {
+ unsigned i;
+ for (i = 0; i < MAX_BINARY_INPUTS; i++) {
+ // decouple PLC's Located Variable from Bacnet Object's Present Value if Out_Of_Service is true
+ if (BI_Descr[i].Out_Of_Service)
+ continue;
+
+ // copy the value
+ *(BI_Descr[i].Located_Var_ptr) = Binary_Input_Present_Value(BI_Descr[i].Object_Identifier);
+ }
+}
+
+void Binary_Input_Copy_Located_Var_to_Present_Value(void) {
+ unsigned i;
+ for (i = 0; i < MAX_BINARY_INPUTS; i++) {
+ // decouple PLC's Located Variable from Bacnet Object's Present Value if Out_Of_Service is true
+ if (BI_Descr[i].Out_Of_Service)
+ continue;
+
+ // copy the value (0 is false, all other values are true)
+ if (*(BI_Descr[i].Located_Var_ptr))
+ BI_Descr[i].Present_Value = BINARY_ACTIVE;
+ else
+ BI_Descr[i].Present_Value = BINARY_INACTIVE;
+ }
+}
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bacnet/runtime/bi.h Fri Jun 08 13:28:00 2018 +0200
@@ -0,0 +1,104 @@
+/**************************************************************************
+*
+* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
+* Copyright (C) 2017 Mario de Sousa <msousa@fe.up.pt>
+*
+* Permission is hereby granted, free of charge, to any person obtaining
+* a copy of this software and associated documentation files (the
+* "Software"), to deal in the Software without restriction, including
+* without limitation the rights to use, copy, modify, merge, publish,
+* distribute, sublicense, and/or sell copies of the Software, and to
+* permit persons to whom the Software is furnished to do so, subject to
+* the following conditions:
+*
+* The above copyright notice and this permission notice shall be included
+* in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*
+*********************************************************************/
+#ifndef BI_H
+#define BI_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include "bacdef.h"
+#include "bacerror.h"
+#include "rp.h"
+#include "wp.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+ typedef struct Binary_Input_descr {
+ /* pointer to IEC 61131-3 located variable that is mapped onto this BACnet object */
+ uint8_t *Located_Var_ptr;
+ uint32_t Object_Identifier; /* called object 'Instance' in the source code! */
+ char *Object_Name;
+ char *Description;
+ /* stores the current value */
+ BACNET_BINARY_PV Present_Value;
+ /* Writable out-of-service allows others to play with our Present Value */
+ /* without changing the physical output */
+ bool Out_Of_Service;
+ BACNET_POLARITY Polarity;
+ } BINARY_INPUT_DESCR;
+
+
+ void Binary_Input_Property_Lists(
+ const int **pRequired,
+ const int **pOptional,
+ const int **pProprietary);
+ bool Binary_Input_Valid_Instance(
+ uint32_t object_instance);
+ unsigned Binary_Input_Count(
+ void);
+ uint32_t Binary_Input_Index_To_Instance(
+ unsigned index);
+ unsigned Binary_Input_Instance_To_Index(
+ uint32_t object_instance);
+
+ bool Binary_Input_Object_Name(
+ uint32_t object_instance,
+ BACNET_CHARACTER_STRING * object_name);
+
+ char *Binary_Input_Description(
+ uint32_t instance);
+
+ BACNET_BINARY_PV Binary_Input_Present_Value(
+ uint32_t instance);
+ bool Binary_Input_Present_Value_Set(
+ uint32_t instance,
+ BACNET_BINARY_PV value);
+
+ bool Binary_Input_Out_Of_Service(
+ uint32_t instance);
+ void Binary_Input_Out_Of_Service_Set(
+ uint32_t instance,
+ bool value);
+
+ char *Binary_Input_Description(
+ uint32_t instance);
+
+ int Binary_Input_Read_Property(
+ BACNET_READ_PROPERTY_DATA * rpdata);
+
+ bool Binary_Input_Write_Property(
+ BACNET_WRITE_PROPERTY_DATA * wp_data);
+
+ void Binary_Input_Init(
+ void);
+
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bacnet/runtime/bo.c Fri Jun 08 13:28:00 2018 +0200
@@ -0,0 +1,567 @@
+/**************************************************************************
+*
+* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
+* Copyright (C) 2017 Mario de Sousa <msousa@fe.up.pt>
+*
+* Permission is hereby granted, free of charge, to any person obtaining
+* a copy of this software and associated documentation files (the
+* "Software"), to deal in the Software without restriction, including
+* without limitation the rights to use, copy, modify, merge, publish,
+* distribute, sublicense, and/or sell copies of the Software, and to
+* permit persons to whom the Software is furnished to do so, subject to
+* the following conditions:
+*
+* The above copyright notice and this permission notice shall be included
+* in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*
+*********************************************************************/
+
+/* Binary Output Objects - customize for your use */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "config_bacnet_for_beremiz_%(locstr)s.h" /* the custom configuration for beremiz plugin */
+#include "bacdef.h"
+#include "bacdcode.h"
+#include "bacenum.h"
+#include "bacapp.h"
+#include "wp.h"
+#include "rp.h"
+#include "bo_%(locstr)s.h"
+#include "handlers.h"
+
+/* we choose to have a NULL level in our system represented by */
+/* a particular value. When the priorities are not in use, they */
+/* will be relinquished (i.e. set to the NULL level). */
+// BINARY_NULL
+/* test whether value is NULL */
+#define BINARY_OUTPUT_IS_NULL(x) ((x) == BINARY_NULL)
+
+/* When all the priorities are level null, the present value returns
+ * the Relinquish Default value
+ */
+#define BO_VALUE_RELINQUISH_DEFAULT BINARY_INACTIVE
+
+/* The IEC 61131-3 located variables mapped onto the Binary Output objects of BACnet protocol */
+%(BO_lvars)s
+
+
+/* The array where we keep all the state related to the Binary Output Objects */
+#define MAX_BINARY_OUTPUTS %(BO_count)s
+static BINARY_OUTPUT_DESCR BO_Descr[MAX_BINARY_OUTPUTS] = {
+%(BO_param)s
+};
+
+
+
+
+/* These three arrays are used by the ReadPropertyMultiple handler,
+ * as well as to initialize the XXX_Property_List used by the
+ * Property List (PROP_PROPERTY_LIST) property.
+ */
+static const int Binary_Output_Properties_Required[] = {
+ /* (1) Currently Supported */
+ /* (2) Required by standard ASHRAE 135-2016 */
+ /*(1)(2) */
+ PROP_OBJECT_IDENTIFIER, /* R R ( 75) */
+ PROP_OBJECT_NAME, /* R R ( 77) */
+ PROP_OBJECT_TYPE, /* R R ( 79) */
+ PROP_PRESENT_VALUE, /* W W ( 85) */
+ PROP_STATUS_FLAGS, /* R R (111) */
+ PROP_EVENT_STATE, /* R R ( 36) */
+ PROP_OUT_OF_SERVICE, /* W R ( 81) */
+ PROP_POLARITY, /* R R ( 84) */
+ PROP_PRIORITY_ARRAY, /* R R ( 87) */
+ PROP_RELINQUISH_DEFAULT, /* R R (104) */
+// PROP_PROPERTY_LIST, /* R R (371) */
+// PROP_CURRENT_COMMAND_PRIORITY,/* R R (431) */
+ -1
+};
+
+static const int Binary_Output_Properties_Optional[] = {
+ /* (1) Currently Supported */
+ /* (2) Required by standard ASHRAE 135-2016 */
+ /*(1)(2) */
+ PROP_DESCRIPTION, /* R O ( 28) */
+ -1
+};
+
+static const int Binary_Output_Properties_Proprietary[] = {
+ -1
+};
+
+
+/* This array stores the PROPERTY_LIST which may be read by clients.
+ * End of list is marked by following the last element with the value '-1'
+ *
+ * It is initialized by Binary_Output_Init() based off the values
+ * stored in Binary_Output_Properties_Required
+ * Binary_Output_Properties_Optional
+ * Binary_Output_Properties_Proprietary
+ */
+/* TODO: Allocate memory for this array with malloc() at startup */
+static int Binary_Output_Properties_List[64];
+
+
+
+
+
+/********************************************************/
+/** Callback functions. **/
+/** Functions required by BACnet devie implementation. **/
+/********************************************************/
+
+
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+void Binary_Output_Property_Lists(
+ const int **pRequired,
+ const int **pOptional,
+ const int **pProprietary)
+{
+ if (pRequired)
+ *pRequired = Binary_Output_Properties_Required;
+ if (pOptional)
+ *pOptional = Binary_Output_Properties_Optional;
+ if (pProprietary)
+ *pProprietary = Binary_Output_Properties_Proprietary;
+
+ return;
+}
+
+
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+void Binary_Output_Init(
+ void)
+{
+ unsigned i, j;
+ static bool initialized = false;
+
+ // fprintf(stderr, "BACnet plugin: Binary_Output_Init() called!\n");
+
+ if (!initialized) {
+ initialized = true;
+
+ /* initialize the Binary_Output_Properties_List array */
+ int len = 0;
+ len += BACnet_Init_Properties_List(Binary_Output_Properties_List + len,
+ Binary_Output_Properties_Required);
+ len += BACnet_Init_Properties_List(Binary_Output_Properties_List + len,
+ Binary_Output_Properties_Optional);
+ len += BACnet_Init_Properties_List(Binary_Output_Properties_List + len,
+ Binary_Output_Properties_Proprietary);
+
+ /* initialize all the binary values priority arrays to NULL */
+ for (i = 0; i < MAX_BINARY_OUTPUTS; i++) {
+ for (j = 0; j < BACNET_MAX_PRIORITY; j++) {
+ BO_Descr[i].Present_Value[j] = BINARY_NULL;
+ }
+ }
+ }
+
+ return;
+}
+
+
+
+/* validate that the given instance (Object ID) exists */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+bool Binary_Output_Valid_Instance(
+ uint32_t object_instance)
+{
+ // fprintf(stderr, "BACnet plugin: Binary_Output_Valid_Instance(obj_ID=%%u) called!\n", object _instance);
+ return (Binary_Output_Instance_To_Index(object_instance) < MAX_BINARY_OUTPUTS);
+}
+
+
+/* the number of Binary Output Objects */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+unsigned Binary_Output_Count(void) {return MAX_BINARY_OUTPUTS;}
+
+
+/* returns the instance (i.e. Object Identifier) that correlates to the correct index */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+uint32_t Binary_Output_Index_To_Instance(unsigned index) {return BO_Descr[index].Object_Identifier;}
+
+
+/* returns the index that correlates to the correct instance number (Object Identifier) */
+unsigned Binary_Output_Instance_To_Index(
+ uint32_t object_instance)
+{
+ unsigned index = 0;
+
+ for (index = 0; index < MAX_BINARY_OUTPUTS; index++)
+ if (object_instance == BO_Descr[index].Object_Identifier)
+ return index;
+
+ /* error, this object ID is not in our list! */
+ return MAX_BINARY_OUTPUTS;
+}
+
+
+
+BACNET_BINARY_PV Binary_Output_Present_Value(
+ uint32_t object_instance)
+{
+ BACNET_BINARY_PV value = BO_VALUE_RELINQUISH_DEFAULT;
+ unsigned index = 0;
+ unsigned i = 0;
+
+ // fprintf(stderr, "BACnet plugin: Binary_Output_Present_Value(obj_ID=%%u) called!\n", object_instance);
+
+ index = Binary_Output_Instance_To_Index(object_instance);
+ if (index < MAX_BINARY_OUTPUTS) {
+ for (i = 0; i < BACNET_MAX_PRIORITY; i++) {
+ if (!BINARY_OUTPUT_IS_NULL(BO_Descr[index].Present_Value[i])) {
+ value = BO_Descr[index].Present_Value[i];
+ break;
+ }
+ }
+ }
+
+ return value;
+}
+
+
+
+/* returns command priority (1..16), or 0 if all priority values are at NULL */
+int Binary_Output_Current_Command_Priority(
+ uint32_t object_instance)
+{
+ unsigned index = 0;
+ unsigned i = 0;
+
+ index = Binary_Output_Instance_To_Index(object_instance);
+ if (index < MAX_BINARY_OUTPUTS) {
+ for (i = 0; i < BACNET_MAX_PRIORITY; i++) {
+ if (!BINARY_OUTPUT_IS_NULL(BO_Descr[index].Present_Value[i])) {
+ return i+1; // +1 since priority is 1..16, and not 0..15
+ }
+ }
+ }
+ // command values in all priorities are set to NULL
+ return 0;
+}
+
+
+
+/* note: the object name must be unique within this device */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+bool Binary_Output_Object_Name(
+ uint32_t object_instance,
+ BACNET_CHARACTER_STRING * object_name)
+{
+ bool status = false;
+ unsigned index = Binary_Output_Instance_To_Index(object_instance);
+
+ if (index < MAX_BINARY_OUTPUTS)
+ status = characterstring_init_ansi(object_name, BO_Descr[index].Object_Name);
+
+ return status;
+}
+
+
+
+bool Binary_Output_Object_Description(
+ uint32_t object_instance,
+ BACNET_CHARACTER_STRING * object_name)
+{
+ bool status = false;
+ unsigned index = Binary_Output_Instance_To_Index(object_instance);
+
+ if (index < MAX_BINARY_OUTPUTS)
+ status = characterstring_init_ansi(object_name, BO_Descr[index].Description);
+
+ return status;
+}
+
+
+
+/* return apdu len, or BACNET_STATUS_ERROR on error */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+int Binary_Output_Read_Property(
+ BACNET_READ_PROPERTY_DATA * rpdata)
+{
+ int len = 0;
+ int apdu_len = 0; /* return value */
+ BACNET_BIT_STRING bit_string;
+ BACNET_CHARACTER_STRING char_string;
+ BACNET_BINARY_PV present_value = BINARY_INACTIVE;
+ unsigned object_index = 0;
+ unsigned i = 0;
+ bool state = false;
+ uint8_t *apdu = NULL;
+
+ // fprintf(stderr, "BACnet plugin: Binary_Output_Read_Property() called!\n");
+
+ if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
+ (rpdata->application_data_len == 0)) {
+ return 0;
+ }
+
+ object_index = Binary_Output_Instance_To_Index(rpdata->object_instance);
+ if (object_index >= MAX_BINARY_OUTPUTS) {
+ rpdata->error_class = ERROR_CLASS_OBJECT;
+ rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT;
+ return BACNET_STATUS_ERROR;
+ }
+
+ apdu = rpdata->application_data;
+ switch (rpdata->object_property) {
+ case PROP_OBJECT_IDENTIFIER:
+ apdu_len =
+ encode_application_object_id(&apdu[0], OBJECT_BINARY_OUTPUT,
+ rpdata->object_instance);
+ break;
+ case PROP_OBJECT_NAME:
+ Binary_Output_Object_Name(rpdata->object_instance, &char_string);
+ apdu_len =
+ encode_application_character_string(&apdu[0], &char_string);
+ break;
+ case PROP_DESCRIPTION:
+ Binary_Output_Object_Description(rpdata->object_instance, &char_string);
+ apdu_len =
+ encode_application_character_string(&apdu[0], &char_string);
+ break;
+ case PROP_OBJECT_TYPE:
+ apdu_len =
+ encode_application_enumerated(&apdu[0], OBJECT_BINARY_OUTPUT);
+ break;
+ case PROP_PRESENT_VALUE:
+ present_value =
+ Binary_Output_Present_Value(rpdata->object_instance);
+ apdu_len = encode_application_enumerated(&apdu[0], present_value);
+ break;
+ case PROP_STATUS_FLAGS:
+ /* note: see the details in the standard on how to use these */
+ bitstring_init(&bit_string);
+ bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false);
+ bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false);
+ bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false);
+ state = BO_Descr[object_index].Out_Of_Service;
+ bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, state);
+ apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
+ break;
+ case PROP_EVENT_STATE:
+ /* note: see the details in the standard on how to use this */
+ apdu_len =
+ encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL);
+ break;
+ case PROP_OUT_OF_SERVICE:
+ state = BO_Descr[object_index].Out_Of_Service;
+ apdu_len = encode_application_boolean(&apdu[0], state);
+ break;
+ case PROP_PRIORITY_ARRAY:
+ BACnet_encode_array(BO_Descr[object_index].Present_Value,
+ BACNET_MAX_PRIORITY,
+ BINARY_OUTPUT_IS_NULL,
+ encode_application_enumerated)
+ break;
+// case PROP_CURRENT_COMMAND_PRIORITY: {
+// unsigned i = Binary_Output_Current_Command_Priority(rpdata->object_instance);
+// if (i == 0) apdu_len = encode_application_null (&apdu[0]);
+// else apdu_len = encode_application_unsigned(&apdu[0], i);
+// break;
+// }
+
+ case PROP_RELINQUISH_DEFAULT:
+ present_value = BO_VALUE_RELINQUISH_DEFAULT;
+ apdu_len = encode_application_enumerated(&apdu[0], present_value);
+ break;
+ case PROP_POLARITY:
+ apdu_len = encode_application_enumerated(&apdu[0],
+ BO_Descr[object_index].Polarity);
+ break;
+// case PROP_PROPERTY_LIST:
+// BACnet_encode_array(Binary_Output_Properties_List,
+// property_list_count(Binary_Output_Properties_List),
+// retfalse, encode_application_enumerated);
+// break;
+ default:
+ rpdata->error_class = ERROR_CLASS_PROPERTY;
+ rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
+ apdu_len = BACNET_STATUS_ERROR;
+ break;
+ }
+ /* only array properties can have array options */
+ if ((apdu_len >= 0) && (rpdata->object_property != PROP_PRIORITY_ARRAY) &&
+// (rpdata->object_property != PROP_PROPERTY_LIST) &&
+ (rpdata->array_index != BACNET_ARRAY_ALL)) {
+ rpdata->error_class = ERROR_CLASS_PROPERTY;
+ rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
+ apdu_len = BACNET_STATUS_ERROR;
+ }
+
+ return apdu_len;
+}
+
+
+
+
+/* returns true if successful */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+bool Binary_Output_Write_Property(
+ BACNET_WRITE_PROPERTY_DATA * wp_data)
+{
+ bool status = false; /* return value */
+ unsigned int object_index = 0;
+ unsigned int priority = 0;
+ BACNET_BINARY_PV level = BINARY_NULL;
+ int len = 0;
+ BACNET_APPLICATION_DATA_VALUE value;
+
+ /* decode the some of the request */
+ len =
+ bacapp_decode_application_data(wp_data->application_data,
+ wp_data->application_data_len, &value);
+ /* FIXME: len < application_data_len: more data? */
+ if (len < 0) {
+ /* error while decoding - a value larger than we can handle */
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
+ return false;
+ }
+ /* only array properties can have array options */
+ if ((wp_data->object_property != PROP_PRIORITY_ARRAY) &&
+ (wp_data->array_index != BACNET_ARRAY_ALL)) {
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
+ return false;
+ }
+ /* No need to check whether object_index is within bounds.
+ * Has already been checked before Binary_Output_Write_Property() is called
+ */
+ object_index = Binary_Output_Instance_To_Index(wp_data->object_instance);
+
+ switch (wp_data->object_property) {
+ case PROP_PRESENT_VALUE:
+ if (value.tag == BACNET_APPLICATION_TAG_ENUMERATED) {
+ priority = wp_data->priority;
+ if (priority && (priority <= BACNET_MAX_PRIORITY) &&
+ (priority != 6 /* reserved */ ) &&
+ (value.type.Enumerated <= MAX_BINARY_PV)) {
+ level = (BACNET_BINARY_PV) value.type.Enumerated;
+ priority--;
+ BO_Descr[object_index].Present_Value[priority] = level;
+ status = true;
+ } else if (priority == 6) {
+ /* Command priority 6 is reserved for use by Minimum On/Off
+ algorithm and may not be used for other purposes in any
+ object. */
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
+ } else {
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
+ }
+ } else {
+ status =
+ WPValidateArgType(&value, BACNET_APPLICATION_TAG_NULL,
+ &wp_data->error_class, &wp_data->error_code);
+ if (status) {
+ level = BINARY_NULL;
+ priority = wp_data->priority;
+ if (priority && (priority <= BACNET_MAX_PRIORITY)) {
+ priority--;
+ BO_Descr[object_index].Present_Value[priority] = level;
+ } else {
+ status = false;
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
+ }
+ }
+ }
+ break;
+ case PROP_OUT_OF_SERVICE:
+ {
+ bool Previous_Out_Of_Service = BO_Descr[object_index].Out_Of_Service;
+ status =
+ WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN,
+ &wp_data->error_class, &wp_data->error_code);
+ if (status) {
+ BO_Descr[object_index].Out_Of_Service = value.type.Boolean;
+ if (Previous_Out_Of_Service && !BO_Descr[object_index].Out_Of_Service)
+ /* We have just changed from Out_of_Service -> In Service */
+ /* We need to update the Present_Value to the value
+ * currently in the PLC...
+ */
+ BO_Descr[object_index].Present_Value[BACNET_MAX_PRIORITY-1] =
+ *(BO_Descr[object_index].Located_Var_ptr);
+ }
+ break;
+ }
+ case PROP_OBJECT_IDENTIFIER:
+ case PROP_OBJECT_NAME:
+ case PROP_DESCRIPTION:
+ case PROP_OBJECT_TYPE:
+ case PROP_STATUS_FLAGS:
+ case PROP_EVENT_STATE:
+ case PROP_PRIORITY_ARRAY:
+// case PROP_CURRENT_COMMAND_PRIORITY:
+ case PROP_RELINQUISH_DEFAULT:
+ case PROP_POLARITY:
+// case PROP_PROPERTY_LIST:
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
+ break;
+ default:
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
+ break;
+ }
+
+ return status;
+}
+
+
+
+
+
+/********************************************/
+/** Functions required for Beremiz plugin **/
+/********************************************/
+
+
+
+void Binary_Output_Copy_Present_Value_to_Located_Var(void) {
+ unsigned i;
+ for (i = 0; i < MAX_BINARY_OUTPUTS; i++) {
+ // decouple PLC's Located Variable from Bacnet Object's Present Value if Out_Of_Service is true
+ if (BO_Descr[i].Out_Of_Service)
+ continue;
+
+ // copy the value
+ *(BO_Descr[i].Located_Var_ptr) = Binary_Output_Present_Value(BO_Descr[i].Object_Identifier);
+ }
+}
+
+void Binary_Output_Copy_Located_Var_to_Present_Value(void) {
+ unsigned i;
+ for (i = 0; i < MAX_BINARY_OUTPUTS; i++) {
+ // decouple PLC's Located Variable from Bacnet Object's Present Value if Out_Of_Service is true
+ if (BO_Descr[i].Out_Of_Service)
+ continue;
+
+ // copy the value
+ BO_Descr[i].Present_Value[BACNET_MAX_PRIORITY-1] = *(BO_Descr[i].Located_Var_ptr);
+
+ // If the Present_Value was set to an invalid value (i.e. > 1, and < BINARY_NULL)
+ // then we set it to BINARY_ACTIVE
+ // (i.e. we assume 0 is FALSE, all other non NULL values are TRUE)
+ if ((BO_Descr[i].Present_Value[BACNET_MAX_PRIORITY-1] != BINARY_INACTIVE) &&
+ (BO_Descr[i].Present_Value[BACNET_MAX_PRIORITY-1] != BINARY_ACTIVE ) &&
+ (BO_Descr[i].Present_Value[BACNET_MAX_PRIORITY-1] != BINARY_NULL ))
+ BO_Descr[i].Present_Value[BACNET_MAX_PRIORITY-1] = BINARY_ACTIVE;
+ }
+}
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bacnet/runtime/bo.h Fri Jun 08 13:28:00 2018 +0200
@@ -0,0 +1,105 @@
+/**************************************************************************
+*
+* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
+* Copyright (C) 2017 Mario de Sousa <msousa@fe.up.pt>
+*
+* Permission is hereby granted, free of charge, to any person obtaining
+* a copy of this software and associated documentation files (the
+* "Software"), to deal in the Software without restriction, including
+* without limitation the rights to use, copy, modify, merge, publish,
+* distribute, sublicense, and/or sell copies of the Software, and to
+* permit persons to whom the Software is furnished to do so, subject to
+* the following conditions:
+*
+* The above copyright notice and this permission notice shall be included
+* in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*
+*********************************************************************/
+#ifndef BO_H
+#define BO_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include "bacdef.h"
+#include "bacerror.h"
+#include "rp.h"
+#include "wp.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+ typedef struct Binary_Output_descr {
+ /* pointer to IEC 61131-3 located variable that is mapped onto this BACnet object */
+ uint8_t *Located_Var_ptr;
+ uint32_t Object_Identifier; /* called object 'Instance' in the source code! */
+ char *Object_Name;
+ char *Description;
+ /* stores the current value */
+ /* one entry per priority value */
+ BACNET_BINARY_PV Present_Value[BACNET_MAX_PRIORITY];
+ /* Writable out-of-service allows others to play with our Present Value */
+ /* without changing the physical output */
+ bool Out_Of_Service;
+ BACNET_POLARITY Polarity;
+ } BINARY_OUTPUT_DESCR;
+
+
+ void Binary_Output_Property_Lists(
+ const int **pRequired,
+ const int **pOptional,
+ const int **pProprietary);
+ bool Binary_Output_Valid_Instance(
+ uint32_t object_instance);
+ unsigned Binary_Output_Count(
+ void);
+ uint32_t Binary_Output_Index_To_Instance(
+ unsigned index);
+ unsigned Binary_Output_Instance_To_Index(
+ uint32_t object_instance);
+
+ bool Binary_Output_Object_Name(
+ uint32_t object_instance,
+ BACNET_CHARACTER_STRING * object_name);
+
+ char *Binary_Output_Description(
+ uint32_t instance);
+
+ BACNET_BINARY_PV Binary_Output_Present_Value(
+ uint32_t instance);
+ bool Binary_Output_Present_Value_Set(
+ uint32_t instance,
+ BACNET_BINARY_PV value);
+
+ bool Binary_Output_Out_Of_Service(
+ uint32_t instance);
+ void Binary_Output_Out_Of_Service_Set(
+ uint32_t instance,
+ bool value);
+
+ char *Binary_Output_Description(
+ uint32_t instance);
+
+ int Binary_Output_Read_Property(
+ BACNET_READ_PROPERTY_DATA * rpdata);
+
+ bool Binary_Output_Write_Property(
+ BACNET_WRITE_PROPERTY_DATA * wp_data);
+
+ void Binary_Output_Init(
+ void);
+
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bacnet/runtime/bv.c Fri Jun 08 13:28:00 2018 +0200
@@ -0,0 +1,562 @@
+/**************************************************************************
+*
+* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
+* Copyright (C) 2017 Mario de Sousa <msousa@fe.up.pt>
+*
+* Permission is hereby granted, free of charge, to any person obtaining
+* a copy of this software and associated documentation files (the
+* "Software"), to deal in the Software without restriction, including
+* without limitation the rights to use, copy, modify, merge, publish,
+* distribute, sublicense, and/or sell copies of the Software, and to
+* permit persons to whom the Software is furnished to do so, subject to
+* the following conditions:
+*
+* The above copyright notice and this permission notice shall be included
+* in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*
+*********************************************************************/
+
+/* Binary Value Objects - customize for your use */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "config_bacnet_for_beremiz_%(locstr)s.h" /* the custom configuration for beremiz plugin */
+#include "bacdef.h"
+#include "bacdcode.h"
+#include "bacenum.h"
+#include "bacapp.h"
+#include "wp.h"
+#include "rp.h"
+#include "bv_%(locstr)s.h"
+#include "handlers.h"
+
+/* we choose to have a NULL level in our system represented by */
+/* a particular value. When the priorities are not in use, they */
+/* will be relinquished (i.e. set to the NULL level). */
+// BINARY_NULL
+/* test whether value is NULL */
+#define BINARY_VALUE_IS_NULL(x) ((x) == BINARY_NULL)
+
+/* When all the priorities are level null, the present value returns
+ * the Relinquish Default value
+ */
+#define BV_VALUE_RELINQUISH_DEFAULT BINARY_INACTIVE
+
+/* The IEC 61131-3 located variables mapped onto the Binary Value objects of BACnet protocol */
+%(BV_lvars)s
+
+
+/* The array where we keep all the state related to the Binary Value Objects */
+#define MAX_BINARY_VALUES %(BV_count)s
+static BINARY_VALUE_DESCR BV_Descr[MAX_BINARY_VALUES] = {
+%(BV_param)s
+};
+
+
+
+
+/* These three arrays are used by the ReadPropertyMultiple handler,
+ * as well as to initialize the XXX_Property_List used by the
+ * Property List (PROP_PROPERTY_LIST) property.
+ */
+static const int Binary_Value_Properties_Required[] = {
+ /* (1) Currently Supported */
+ /* (2) Required by standard ASHRAE 135-2016 */
+ /*(1)(2) */
+ PROP_OBJECT_IDENTIFIER, /* R R ( 75) */
+ PROP_OBJECT_NAME, /* R R ( 77) */
+ PROP_OBJECT_TYPE, /* R R ( 79) */
+ PROP_PRESENT_VALUE, /* W R ( 85) */
+ PROP_STATUS_FLAGS, /* R R (111) */
+ PROP_EVENT_STATE, /* R R ( 36) */
+ PROP_OUT_OF_SERVICE, /* W R ( 81) */
+// PROP_PROPERTY_LIST, /* R R (371) */
+ -1
+};
+
+static const int Binary_Value_Properties_Optional[] = {
+ /* (1) Currently Supported */
+ /* (2) Required by standard ASHRAE 135-2016 */
+ /*(1)(2) */
+ PROP_DESCRIPTION, /* R O ( 28) */
+ /* required if Present_Value is writable (which is true in our case!) */
+ PROP_PRIORITY_ARRAY, /* R O ( 87) */
+ PROP_RELINQUISH_DEFAULT, /* R O (104) */
+// PROP_CURRENT_COMMAND_PRIORITY,/* R O (431) */
+ -1
+};
+
+static const int Binary_Value_Properties_Proprietary[] = {
+ -1
+};
+
+
+/* This array stores the PROPERTY_LIST which may be read by clients.
+ * End of list is marked by following the last element with the value '-1'
+ *
+ * It is initialized by Binary_Value_Init() based off the values
+ * stored in Binary_Value_Properties_Required
+ * Binary_Value_Properties_Optional
+ * Binary_Value_Properties_Proprietary
+ */
+/* TODO: Allocate memory for this array with malloc() at startup */
+static int Binary_Value_Properties_List[64];
+
+
+
+
+
+/********************************************************/
+/** Callback functions. **/
+/** Functions required by BACnet devie implementation. **/
+/********************************************************/
+
+
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+void Binary_Value_Property_Lists(
+ const int **pRequired,
+ const int **pOptional,
+ const int **pProprietary)
+{
+ if (pRequired)
+ *pRequired = Binary_Value_Properties_Required;
+ if (pOptional)
+ *pOptional = Binary_Value_Properties_Optional;
+ if (pProprietary)
+ *pProprietary = Binary_Value_Properties_Proprietary;
+
+ return;
+}
+
+
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+void Binary_Value_Init(
+ void)
+{
+ unsigned i, j;
+ static bool initialized = false;
+
+ // fprintf(stderr, "BACnet plugin: Binary_Value_Init() called!\n");
+
+ if (!initialized) {
+ initialized = true;
+
+ /* initialize the Binary_Value_Properties_List array */
+ int len = 0;
+ len += BACnet_Init_Properties_List(Binary_Value_Properties_List + len,
+ Binary_Value_Properties_Required);
+ len += BACnet_Init_Properties_List(Binary_Value_Properties_List + len,
+ Binary_Value_Properties_Optional);
+ len += BACnet_Init_Properties_List(Binary_Value_Properties_List + len,
+ Binary_Value_Properties_Proprietary);
+
+ /* initialize all the binary values priority arrays to NULL */
+ for (i = 0; i < MAX_BINARY_VALUES; i++) {
+ for (j = 0; j < BACNET_MAX_PRIORITY; j++) {
+ BV_Descr[i].Present_Value[j] = BINARY_NULL;
+ }
+ }
+ }
+
+ return;
+}
+
+
+
+/* validate that the given instance (Object ID) exists */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+bool Binary_Value_Valid_Instance(
+ uint32_t object_instance)
+{
+ // fprintf(stderr, "BACnet plugin: Binary_Value_Valid_Instance(obj_ID=%%u) called!\n", object _instance);
+ return (Binary_Value_Instance_To_Index(object_instance) < MAX_BINARY_VALUES);
+}
+
+
+/* the number of Binary Value Objects */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+unsigned Binary_Value_Count(void) {return MAX_BINARY_VALUES;}
+
+
+/* returns the instance (i.e. Object Identifier) that correlates to the correct index */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+uint32_t Binary_Value_Index_To_Instance(unsigned index) {return BV_Descr[index].Object_Identifier;}
+
+
+/* returns the index that correlates to the correct instance number (Object Identifier) */
+unsigned Binary_Value_Instance_To_Index(
+ uint32_t object_instance)
+{
+ unsigned index = 0;
+
+ for (index = 0; index < MAX_BINARY_VALUES; index++)
+ if (object_instance == BV_Descr[index].Object_Identifier)
+ return index;
+
+ /* error, this object ID is not in our list! */
+ return MAX_BINARY_VALUES;
+}
+
+
+
+BACNET_BINARY_PV Binary_Value_Present_Value(
+ uint32_t object_instance)
+{
+ BACNET_BINARY_PV value = BV_VALUE_RELINQUISH_DEFAULT;
+ unsigned index = 0;
+ unsigned i = 0;
+
+ // fprintf(stderr, "BACnet plugin: Binary_Value_Present_Value(obj_ID=%%u) called!\n", object_instance);
+
+ index = Binary_Value_Instance_To_Index(object_instance);
+ if (index < MAX_BINARY_VALUES) {
+ for (i = 0; i < BACNET_MAX_PRIORITY; i++) {
+ if (!BINARY_VALUE_IS_NULL(BV_Descr[index].Present_Value[i])) {
+ value = BV_Descr[index].Present_Value[i];
+ break;
+ }
+ }
+ }
+
+ return value;
+}
+
+
+
+/* returns command priority (1..16), or 0 if all priority values are at NULL */
+int Binary_Value_Current_Command_Priority(
+ uint32_t object_instance)
+{
+ unsigned index = 0;
+ unsigned i = 0;
+
+ index = Binary_Value_Instance_To_Index(object_instance);
+ if (index < MAX_BINARY_VALUES) {
+ for (i = 0; i < BACNET_MAX_PRIORITY; i++) {
+ if (!BINARY_VALUE_IS_NULL(BV_Descr[index].Present_Value[i])) {
+ return i+1; // +1 since priority is 1..16, and not 0..15
+ }
+ }
+ }
+ // command values in all priorities are set to NULL
+ return 0;
+}
+
+
+
+/* note: the object name must be unique within this device */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+bool Binary_Value_Object_Name(
+ uint32_t object_instance,
+ BACNET_CHARACTER_STRING * object_name)
+{
+ bool status = false;
+ unsigned index = Binary_Value_Instance_To_Index(object_instance);
+
+ if (index < MAX_BINARY_VALUES)
+ status = characterstring_init_ansi(object_name, BV_Descr[index].Object_Name);
+
+ return status;
+}
+
+
+
+bool Binary_Value_Object_Description(
+ uint32_t object_instance,
+ BACNET_CHARACTER_STRING * object_name)
+{
+ bool status = false;
+ unsigned index = Binary_Value_Instance_To_Index(object_instance);
+
+ if (index < MAX_BINARY_VALUES)
+ status = characterstring_init_ansi(object_name, BV_Descr[index].Description);
+
+ return status;
+}
+
+
+
+/* return apdu len, or BACNET_STATUS_ERROR on error */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+int Binary_Value_Read_Property(
+ BACNET_READ_PROPERTY_DATA * rpdata)
+{
+ int len = 0;
+ int apdu_len = 0; /* return value */
+ BACNET_BIT_STRING bit_string;
+ BACNET_CHARACTER_STRING char_string;
+ BACNET_BINARY_PV present_value = BINARY_INACTIVE;
+ unsigned object_index = 0;
+ unsigned i = 0;
+ bool state = false;
+ uint8_t *apdu = NULL;
+
+ // fprintf(stderr, "BACnet plugin: Binary_Value_Read_Property() called!\n");
+
+ if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
+ (rpdata->application_data_len == 0)) {
+ return 0;
+ }
+
+ object_index = Binary_Value_Instance_To_Index(rpdata->object_instance);
+ if (object_index >= MAX_BINARY_VALUES) {
+ rpdata->error_class = ERROR_CLASS_OBJECT;
+ rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT;
+ return BACNET_STATUS_ERROR;
+ }
+
+ apdu = rpdata->application_data;
+ switch (rpdata->object_property) {
+ case PROP_OBJECT_IDENTIFIER:
+ apdu_len =
+ encode_application_object_id(&apdu[0], OBJECT_BINARY_VALUE,
+ rpdata->object_instance);
+ break;
+ case PROP_OBJECT_NAME:
+ Binary_Value_Object_Name(rpdata->object_instance, &char_string);
+ apdu_len =
+ encode_application_character_string(&apdu[0], &char_string);
+ break;
+ case PROP_DESCRIPTION:
+ Binary_Value_Object_Description(rpdata->object_instance, &char_string);
+ apdu_len =
+ encode_application_character_string(&apdu[0], &char_string);
+ break;
+ case PROP_OBJECT_TYPE:
+ apdu_len =
+ encode_application_enumerated(&apdu[0], OBJECT_BINARY_VALUE);
+ break;
+ case PROP_PRESENT_VALUE:
+ present_value =
+ Binary_Value_Present_Value(rpdata->object_instance);
+ apdu_len = encode_application_enumerated(&apdu[0], present_value);
+ break;
+ case PROP_STATUS_FLAGS:
+ /* note: see the details in the standard on how to use these */
+ bitstring_init(&bit_string);
+ bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false);
+ bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false);
+ bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false);
+ state = BV_Descr[object_index].Out_Of_Service;
+ bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, state);
+ apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
+ break;
+ case PROP_EVENT_STATE:
+ /* note: see the details in the standard on how to use this */
+ apdu_len =
+ encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL);
+ break;
+ case PROP_OUT_OF_SERVICE:
+ state = BV_Descr[object_index].Out_Of_Service;
+ apdu_len = encode_application_boolean(&apdu[0], state);
+ break;
+ case PROP_PRIORITY_ARRAY:
+ BACnet_encode_array(BV_Descr[object_index].Present_Value,
+ BACNET_MAX_PRIORITY,
+ BINARY_VALUE_IS_NULL,
+ encode_application_enumerated)
+ break;
+// case PROP_CURRENT_COMMAND_PRIORITY: {
+// unsigned i = Binary_Value_Current_Command_Priority(rpdata->object_instance);
+// if (i == 0) apdu_len = encode_application_null (&apdu[0]);
+// else apdu_len = encode_application_unsigned(&apdu[0], i);
+// break;
+// }
+
+ case PROP_RELINQUISH_DEFAULT:
+ present_value = BV_VALUE_RELINQUISH_DEFAULT;
+ apdu_len = encode_application_enumerated(&apdu[0], present_value);
+ break;
+// case PROP_PROPERTY_LIST:
+// BACnet_encode_array(Binary_Value_Properties_List,
+// property_list_count(Binary_Value_Properties_List),
+// retfalse, encode_application_enumerated);
+// break;
+ default:
+ rpdata->error_class = ERROR_CLASS_PROPERTY;
+ rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
+ apdu_len = BACNET_STATUS_ERROR;
+ break;
+ }
+ /* only array properties can have array options */
+ if ((apdu_len >= 0) && (rpdata->object_property != PROP_PRIORITY_ARRAY) &&
+// (rpdata->object_property != PROP_PROPERTY_LIST) &&
+ (rpdata->array_index != BACNET_ARRAY_ALL)) {
+ rpdata->error_class = ERROR_CLASS_PROPERTY;
+ rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
+ apdu_len = BACNET_STATUS_ERROR;
+ }
+
+ return apdu_len;
+}
+
+
+
+
+/* returns true if successful */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+bool Binary_Value_Write_Property(
+ BACNET_WRITE_PROPERTY_DATA * wp_data)
+{
+ bool status = false; /* return value */
+ unsigned int object_index = 0;
+ unsigned int priority = 0;
+ BACNET_BINARY_PV level = BINARY_NULL;
+ int len = 0;
+ BACNET_APPLICATION_DATA_VALUE value;
+
+ /* decode the some of the request */
+ len =
+ bacapp_decode_application_data(wp_data->application_data,
+ wp_data->application_data_len, &value);
+ /* FIXME: len < application_data_len: more data? */
+ if (len < 0) {
+ /* error while decoding - a value larger than we can handle */
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
+ return false;
+ }
+ /* only array properties can have array options */
+ if ((wp_data->object_property != PROP_PRIORITY_ARRAY) &&
+ (wp_data->array_index != BACNET_ARRAY_ALL)) {
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
+ return false;
+ }
+ /* No need to check whether object_index is within bounds.
+ * Has already been checked before Binary_Value_Write_Property() is called
+ */
+ object_index = Binary_Value_Instance_To_Index(wp_data->object_instance);
+
+ switch (wp_data->object_property) {
+ case PROP_PRESENT_VALUE:
+ if (value.tag == BACNET_APPLICATION_TAG_ENUMERATED) {
+ priority = wp_data->priority;
+ if (priority && (priority <= BACNET_MAX_PRIORITY) &&
+ (priority != 6 /* reserved */ ) &&
+ (value.type.Enumerated <= MAX_BINARY_PV)) {
+ level = (BACNET_BINARY_PV) value.type.Enumerated;
+ priority--;
+ BV_Descr[object_index].Present_Value[priority] = level;
+ status = true;
+ } else if (priority == 6) {
+ /* Command priority 6 is reserved for use by Minimum On/Off
+ algorithm and may not be used for other purposes in any
+ object. */
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
+ } else {
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
+ }
+ } else {
+ status =
+ WPValidateArgType(&value, BACNET_APPLICATION_TAG_NULL,
+ &wp_data->error_class, &wp_data->error_code);
+ if (status) {
+ level = BINARY_NULL;
+ priority = wp_data->priority;
+ if (priority && (priority <= BACNET_MAX_PRIORITY)) {
+ priority--;
+ BV_Descr[object_index].Present_Value[priority] = level;
+ } else {
+ status = false;
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
+ }
+ }
+ }
+ break;
+ case PROP_OUT_OF_SERVICE:
+ {
+ bool Previous_Out_Of_Service = BV_Descr[object_index].Out_Of_Service;
+ status =
+ WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN,
+ &wp_data->error_class, &wp_data->error_code);
+ if (status) {
+ BV_Descr[object_index].Out_Of_Service = value.type.Boolean;
+ if (Previous_Out_Of_Service && !BV_Descr[object_index].Out_Of_Service)
+ /* We have just changed from Out_of_Service -> In Service */
+ /* We need to update the Present_Value to the value
+ * currently in the PLC...
+ */
+ BV_Descr[object_index].Present_Value[BACNET_MAX_PRIORITY-1] =
+ *(BV_Descr[object_index].Located_Var_ptr);
+ }
+ break;
+ }
+ case PROP_OBJECT_IDENTIFIER:
+ case PROP_OBJECT_NAME:
+ case PROP_DESCRIPTION:
+ case PROP_OBJECT_TYPE:
+ case PROP_STATUS_FLAGS:
+ case PROP_EVENT_STATE:
+ case PROP_PRIORITY_ARRAY:
+// case PROP_CURRENT_COMMAND_PRIORITY:
+ case PROP_RELINQUISH_DEFAULT:
+// case PROP_PROPERTY_LIST:
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
+ break;
+ default:
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
+ break;
+ }
+
+ return status;
+}
+
+
+
+
+
+/********************************************/
+/** Functions required for Beremiz plugin **/
+/********************************************/
+
+
+
+void Binary_Value_Copy_Present_Value_to_Located_Var(void) {
+ unsigned i;
+ for (i = 0; i < MAX_BINARY_VALUES; i++) {
+ // decouple PLC's Located Variable from Bacnet Object's Present Value if Out_Of_Service is true
+ if (BV_Descr[i].Out_Of_Service)
+ continue;
+
+ // copy the value
+ *(BV_Descr[i].Located_Var_ptr) = Binary_Value_Present_Value(BV_Descr[i].Object_Identifier);
+ }
+}
+
+void Binary_Value_Copy_Located_Var_to_Present_Value(void) {
+ unsigned i;
+ for (i = 0; i < MAX_BINARY_VALUES; i++) {
+ // decouple PLC's Located Variable from Bacnet Object's Present Value if Out_Of_Service is true
+ if (BV_Descr[i].Out_Of_Service)
+ continue;
+
+ // copy the value
+ BV_Descr[i].Present_Value[BACNET_MAX_PRIORITY-1] = *(BV_Descr[i].Located_Var_ptr);
+
+ // If the Present_Value was set to an invalid value (i.e. > 1, and < BINARY_NULL)
+ // then we set it to BINARY_ACTIVE
+ // (i.e. we assume 0 is FALSE, all other non NULL values are TRUE)
+ if ((BV_Descr[i].Present_Value[BACNET_MAX_PRIORITY-1] != BINARY_INACTIVE) &&
+ (BV_Descr[i].Present_Value[BACNET_MAX_PRIORITY-1] != BINARY_ACTIVE ) &&
+ (BV_Descr[i].Present_Value[BACNET_MAX_PRIORITY-1] != BINARY_NULL ))
+ BV_Descr[i].Present_Value[BACNET_MAX_PRIORITY-1] = BINARY_ACTIVE;
+ }
+}
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bacnet/runtime/bv.h Fri Jun 08 13:28:00 2018 +0200
@@ -0,0 +1,104 @@
+/**************************************************************************
+*
+* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
+* Copyright (C) 2017 Mario de Sousa <msousa@fe.up.pt>
+*
+* Permission is hereby granted, free of charge, to any person obtaining
+* a copy of this software and associated documentation files (the
+* "Software"), to deal in the Software without restriction, including
+* without limitation the rights to use, copy, modify, merge, publish,
+* distribute, sublicense, and/or sell copies of the Software, and to
+* permit persons to whom the Software is furnished to do so, subject to
+* the following conditions:
+*
+* The above copyright notice and this permission notice shall be included
+* in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*
+*********************************************************************/
+#ifndef BV_H
+#define BV_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include "bacdef.h"
+#include "bacerror.h"
+#include "rp.h"
+#include "wp.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+ typedef struct binary_value_descr {
+ /* pointer to IEC 61131-3 located variable that is mapped onto this BACnet object */
+ uint8_t *Located_Var_ptr;
+ uint32_t Object_Identifier; /* called object 'Instance' in the source code! */
+ char *Object_Name;
+ char *Description;
+ /* stores the current value */
+ /* one entry per priority value */
+ BACNET_BINARY_PV Present_Value[BACNET_MAX_PRIORITY];
+ /* Writable out-of-service allows others to play with our Present Value */
+ /* without changing the physical output */
+ bool Out_Of_Service;
+ } BINARY_VALUE_DESCR;
+
+
+ void Binary_Value_Property_Lists(
+ const int **pRequired,
+ const int **pOptional,
+ const int **pProprietary);
+ bool Binary_Value_Valid_Instance(
+ uint32_t object_instance);
+ unsigned Binary_Value_Count(
+ void);
+ uint32_t Binary_Value_Index_To_Instance(
+ unsigned index);
+ unsigned Binary_Value_Instance_To_Index(
+ uint32_t object_instance);
+
+ bool Binary_Value_Object_Name(
+ uint32_t object_instance,
+ BACNET_CHARACTER_STRING * object_name);
+
+ char *Binary_Value_Description(
+ uint32_t instance);
+
+ BACNET_BINARY_PV Binary_Value_Present_Value(
+ uint32_t instance);
+ bool Binary_Value_Present_Value_Set(
+ uint32_t instance,
+ BACNET_BINARY_PV value);
+
+ bool Binary_Value_Out_Of_Service(
+ uint32_t instance);
+ void Binary_Value_Out_Of_Service_Set(
+ uint32_t instance,
+ bool value);
+
+ char *Binary_Value_Description(
+ uint32_t instance);
+
+ int Binary_Value_Read_Property(
+ BACNET_READ_PROPERTY_DATA * rpdata);
+
+ bool Binary_Value_Write_Property(
+ BACNET_WRITE_PROPERTY_DATA * wp_data);
+
+ void Binary_Value_Init(
+ void);
+
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bacnet/runtime/config_bacnet_for_beremiz.h Fri Jun 08 13:28:00 2018 +0200
@@ -0,0 +1,284 @@
+/**************************************************************************
+*
+* Copyright (C) 2004 Steve Karg <skarg@users.sourceforge.net>
+* Copyright (C) 2017 Mario de Sousa <msousa@fe.up.pt>
+*
+* Permission is hereby granted, free of charge, to any person obtaining
+* a copy of this software and associated documentation files (the
+* "Software"), to deal in the Software without restriction, including
+* without limitation the rights to use, copy, modify, merge, publish,
+* distribute, sublicense, and/or sell copies of the Software, and to
+* permit persons to whom the Software is furnished to do so, subject to
+* the following conditions:
+*
+* The above copyright notice and this permission notice shall be included
+* in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*
+*********************************************************************/
+
+
+#ifndef CONFIG_BACNET_FOR_BEREMIZ_H
+#define CONFIG_BACNET_FOR_BEREMIZ_H
+
+#ifdef CONFIG_H
+#error "config.h already processed! (config_bacnet_for_beremiz.h should be included before config.h)"
+#endif
+
+/* Compilaton options for BACnet library, configured for the BACnet sserver
+ * running on Beremiz.
+ */
+
+/* declare a single physical layer using your compiler define.
+ * see datalink.h for possible defines.
+ */
+/* Use BACnet/IP */
+#define BACDL_BIP
+
+/* optional configuration for BACnet/IP datalink layers */
+/* other BIP defines (define as 1 to enable):
+ USE_INADDR - uses INADDR_BROADCAST for broadcast and binds using INADDR_ANY
+ USE_CLASSADDR = uses IN_CLASSx_HOST where x=A,B,C or D for broadcast
+*/
+#define BBMD_ENABLED 1
+
+/* name of file in which BDT table will be stored */
+#define BBMD_BACKUP_FILE beremiz_BACnet_BDT_table
+
+/* Enable the Gateway (Routing) functionality here, if desired. */
+#define MAX_NUM_DEVICES 1 /* Just the one normal BACnet Device Object */
+
+
+/* Define your processor architecture as
+ Big Endian (PowerPC,68K,Sparc) or Little Endian (Intel,AVR)
+ ARM and MIPS can be either - what is your setup? */
+
+/* WARNING: The following files are being included:
+ * <stdib.h> --> <endian.h> --> <bits/endian.h>
+ *
+ * endian.h defines the following constants as:
+ * #define __LITTLE_ENDIAN and LITTLE_ENDIAN as 1234
+ * #define __BIG_ENDIAN and BIG_ENDIAN as 4321
+ * #define __PDP_ENDIAN and PDP_ENDIAN as 3412
+ *
+ * bits/endian.h defines the constant BYTE_ORDER as:
+ * #define __BYTE_ORDER as __LITTLE_ENDIAN
+ *
+ * endian.h then sets the following constants
+ * (if __USE_BSD is set, which seems to be true):
+ * # define LITTLE_ENDIAN __LITTLE_ENDIAN
+ * # define BIG_ENDIAN __BIG_ENDIAN
+ * # define PDP_ENDIAN __PDP_ENDIAN
+ * # define BYTE_ORDER __BYTE_ORDER
+ *
+ * CONCLUSION:
+ * The bacnet library uses the BIG_ENDIAN constant (set to 0, or anything <>0)
+ * to indicate whether we are compiling on a little or big endian platform.
+ * However, <stdlib.h> is defining this same constant as '4321' !!!
+ * The decision to use BIG_ENDIAN as the constant is a unfortunate
+ * on the part of the bacnet coders, but we live with it for now...
+ * We simply start off by undefining the BIG_ENDIAN constant, and carry
+ * on from there!
+ */
+#undef BIG_ENDIAN
+
+#ifndef BIG_ENDIAN
+#if defined(__GNUC__)
+ /* We have GCC, which should define __LITTLE_ENDIAN__ or __BIG_ENDIAN__ */
+# if defined(__LITTLE_ENDIAN__)
+/*# warning "Using gcc to determine platform endianness."*/
+# define BIG_ENDIAN 0
+# elif defined(__BIG_ENDIAN__)
+/*# warning "Using gcc to determine platform endianness."*/
+# define BIG_ENDIAN 1
+# endif
+#endif /* __GNUC__ */
+#endif /* BIG_ENDIAN */
+
+
+/* If we still don't know byte order, try to get it from <endian.h> */
+#ifndef BIG_ENDIAN
+#include <endian.h>
+# ifdef BYTE_ORDER
+# if BYTE_ORDER == LITTLE_ENDIAN
+/*# warning "Using <endian.h> to determine platform endianness."*/
+# undef BIG_ENDIAN
+# define BIG_ENDIAN 0
+# elif BYTE_ORDER == BIG_ENDIAN
+/*# warning "Using <endian.h> to determine platform endianness."*/
+# undef BIG_ENDIAN
+# define BIG_ENDIAN 1
+# else
+# undef BIG_ENDIAN
+# endif
+# endif /* BYTE_ORDER */
+#endif /* BIG_ENDIAN */
+
+
+#ifndef BIG_ENDIAN
+#error "Unable to determine platform's byte order. Aborting compilation."
+#elif BIG_ENDIAN
+/*#warning "Compiling for BIG endian platform."*/
+#else
+/*#warning "Compiling for LITTLE endian platform."*/
+#endif
+
+
+
+/* Define your Vendor Identifier assigned by ASHRAE */
+#define BACNET_VENDOR_ID %(BACnet_Vendor_ID)s
+#define BACNET_VENDOR_NAME "%(BACnet_Vendor_Name)s"
+#define BACNET_DEVICE_MODEL_NAME "%(BACnet_Model_Name)s"
+#define BACNET_FIRMWARE_REVISION "Beremiz BACnet Extension, BACnet Stack:" BACNET_VERSION_TEXT
+#define BACNET_DEVICE_LOCATION "%(BACnet_Device_Location)s"
+#define BACNET_DEVICE_DESCRIPTION "%(BACnet_Device_Description)s"
+#define BACNET_DEVICE_APPSOFT_VER "%(BACnet_Device_AppSoft_Version)s"
+
+
+/* Max number of bytes in an APDU. */
+/* Typical sizes are 50, 128, 206, 480, 1024, and 1476 octets */
+/* This is used in constructing messages and to tell others our limits */
+/* 50 is the minimum; adjust to your memory and physical layer constraints */
+/* Lon=206, MS/TP=480, ARCNET=480, Ethernet=1476, BACnet/IP=1476 */
+#define MAX_APDU 1476
+/* #define MAX_APDU 128 enable this IP for testing readrange so you get the More Follows flag set */
+
+
+/* for confirmed messages, this is the number of transactions */
+/* that we hold in a queue waiting for timeout. */
+/* Configure to zero if you don't want any confirmed messages */
+/* Configure from 1..255 for number of outstanding confirmed */
+/* requests available. */
+#define MAX_TSM_TRANSACTIONS 255
+
+/* The address cache is used for binding to BACnet devices */
+/* The number of entries corresponds to the number of */
+/* devices that might respond to an I-Am on the network. */
+/* If your device is a simple server and does not need to bind, */
+/* then you don't need to use this. */
+#define MAX_ADDRESS_CACHE 255
+
+/* some modules have debugging enabled using PRINT_ENABLED */
+#define PRINT_ENABLED 0
+
+
+/* BACAPP decodes WriteProperty service requests
+ Choose the datatypes that your application supports */
+#if !(defined(BACAPP_ALL) || \
+ defined(BACAPP_NULL) || \
+ defined(BACAPP_BOOLEAN) || \
+ defined(BACAPP_UNSIGNED) || \
+ defined(BACAPP_SIGNED) || \
+ defined(BACAPP_REAL) || \
+ defined(BACAPP_DOUBLE) || \
+ defined(BACAPP_OCTET_STRING) || \
+ defined(BACAPP_CHARACTER_STRING) || \
+ defined(BACAPP_BIT_STRING) || \
+ defined(BACAPP_ENUMERATED) || \
+ defined(BACAPP_DATE) || \
+ defined(BACAPP_TIME) || \
+ defined(BACAPP_OBJECT_ID) || \
+ defined(BACAPP_DEVICE_OBJECT_PROP_REF))
+#define BACAPP_ALL
+#endif
+
+#if defined (BACAPP_ALL)
+#define BACAPP_NULL
+#define BACAPP_BOOLEAN
+#define BACAPP_UNSIGNED
+#define BACAPP_SIGNED
+#define BACAPP_REAL
+#define BACAPP_DOUBLE
+#define BACAPP_OCTET_STRING
+#define BACAPP_CHARACTER_STRING
+#define BACAPP_BIT_STRING
+#define BACAPP_ENUMERATED
+#define BACAPP_DATE
+#define BACAPP_TIME
+#define BACAPP_OBJECT_ID
+#define BACAPP_DEVICE_OBJECT_PROP_REF
+#endif
+
+/*
+** Set the maximum vector type sizes
+*/
+#define MAX_BITSTRING_BYTES (15)
+#define MAX_CHARACTER_STRING_BYTES (MAX_APDU-6)
+#define MAX_OCTET_STRING_BYTES (MAX_APDU-6)
+
+/*
+** Control the selection of services etc to enable code size reduction for those
+** compiler suites which do not handle removing of unused functions in modules
+** so well.
+**
+** We will start with the A type services code first as these are least likely
+** to be required in embedded systems using the stack.
+*/
+
+/*
+** Define the services that may be required.
+**
+**/
+
+/* For the moment enable them all to avoid breaking things */
+#define BACNET_SVC_I_HAVE_A 1 /* Do we send I_Have requests? */
+#define BACNET_SVC_WP_A 1 /* Do we send WriteProperty requests? */
+#define BACNET_SVC_RP_A 1 /* Do we send ReadProperty requests? */
+#define BACNET_SVC_RPM_A 1 /* Do we send ReadPropertyMultiple requests? */
+#define BACNET_SVC_DCC_A 1 /* Do we send DeviceCommunicationControl requests? */
+#define BACNET_SVC_RD_A 1 /* Do we send ReinitialiseDevice requests? */
+#define BACNET_SVC_TS_A 1
+#define BACNET_SVC_SERVER 0 /* Are we a pure server type device? */
+#define BACNET_USE_OCTETSTRING 1 /* Do we need any octet strings? */
+#define BACNET_USE_DOUBLE 1 /* Do we need any doubles? */
+#define BACNET_USE_SIGNED 1 /* Do we need any signed integers */
+
+/* Do them one by one */
+#ifndef BACNET_SVC_I_HAVE_A /* Do we send I_Have requests? */
+#define BACNET_SVC_I_HAVE_A 0
+#endif
+
+#ifndef BACNET_SVC_WP_A /* Do we send WriteProperty requests? */
+#define BACNET_SVC_WP_A 0
+#endif
+
+#ifndef BACNET_SVC_RP_A /* Do we send ReadProperty requests? */
+#define BACNET_SVC_RP_A 0
+#endif
+
+#ifndef BACNET_SVC_RPM_A /* Do we send ReadPropertyMultiple requests? */
+#define BACNET_SVC_RPM_A 0
+#endif
+
+#ifndef BACNET_SVC_DCC_A /* Do we send DeviceCommunicationControl requests? */
+#define BACNET_SVC_DCC_A 0
+#endif
+
+#ifndef BACNET_SVC_RD_A /* Do we send ReinitialiseDevice requests? */
+#define BACNET_SVC_RD_A 0
+#endif
+
+#ifndef BACNET_SVC_SERVER /* Are we a pure server type device? */
+#define BACNET_SVC_SERVER 1
+#endif
+
+#ifndef BACNET_USE_OCTETSTRING /* Do we need any octet strings? */
+#define BACNET_USE_OCTETSTRING 0
+#endif
+
+#ifndef BACNET_USE_DOUBLE /* Do we need any doubles? */
+#define BACNET_USE_DOUBLE 0
+#endif
+
+#ifndef BACNET_USE_SIGNED /* Do we need any signed integers */
+#define BACNET_USE_SIGNED 0
+#endif
+
+#endif /* CONFIG_BACNET_FOR_BEREMIZ_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bacnet/runtime/device.c Fri Jun 08 13:28:00 2018 +0200
@@ -0,0 +1,1685 @@
+/**************************************************************************
+*
+* Copyright (C) 2005,2006,2009 Steve Karg <skarg@users.sourceforge.net>
+* Copyright (C) 2017 Mario de Sousa <msousa@fe.up.pt>
+*
+* Permission is hereby granted, free of charge, to any person obtaining
+* a copy of this software and associated documentation files (the
+* "Software"), to deal in the Software without restriction, including
+* without limitation the rights to use, copy, modify, merge, publish,
+* distribute, sublicense, and/or sell copies of the Software, and to
+* permit persons to whom the Software is furnished to do so, subject to
+* the following conditions:
+*
+* The above copyright notice and this permission notice shall be included
+* in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*
+*********************************************************************/
+
+/** Base "class" for handling all BACnet objects belonging
+ * to a BACnet device, as well as Device-specific properties. */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h> /* for memmove */
+#include <time.h> /* for timezone, localtime */
+
+#include "config_bacnet_for_beremiz_%(locstr)s.h" /* the custom configuration for beremiz plugin */
+#include "bacdef.h"
+#include "bacdcode.h"
+#include "bacenum.h"
+#include "bacapp.h"
+#include "apdu.h"
+#include "wp.h" /* WriteProperty handling */
+#include "rp.h" /* ReadProperty handling */
+#include "dcc.h" /* DeviceCommunicationControl handling */
+#include "version.h"
+#include "device_%(locstr)s.h" /* me */
+#include "handlers.h"
+#include "datalink.h"
+#include "address.h"
+/* os specfic includes */
+#include "timer.h"
+/* include the device object */
+#include "device_%(locstr)s.h"
+#include "ai_%(locstr)s.h"
+#include "ao_%(locstr)s.h"
+#include "av_%(locstr)s.h"
+#include "bi_%(locstr)s.h"
+#include "bo_%(locstr)s.h"
+#include "bv_%(locstr)s.h"
+#include "msi_%(locstr)s.h"
+#include "mso_%(locstr)s.h"
+#include "msv_%(locstr)s.h"
+
+
+#if defined(__BORLANDC__) || defined(_WIN32)
+/* Not included in time.h as specified by The Open Group */
+/* Difference from UTC and local standard time */
+long int timezone;
+#endif
+
+/* local forward (semi-private) and external prototypes */
+int Device_Read_Property_Local(
+ BACNET_READ_PROPERTY_DATA * rpdata);
+bool Device_Write_Property_Local(
+ BACNET_WRITE_PROPERTY_DATA * wp_data);
+extern int Routed_Device_Read_Property_Local(
+ BACNET_READ_PROPERTY_DATA * rpdata);
+extern bool Routed_Device_Write_Property_Local(
+ BACNET_WRITE_PROPERTY_DATA * wp_data);
+
+/* may be overridden by outside table */
+static object_functions_t *Object_Table;
+
+static object_functions_t My_Object_Table[] = {
+ {OBJECT_DEVICE,
+ NULL /* Init - don't init Device or it will recurse! */ ,
+ Device_Count,
+ Device_Index_To_Instance,
+ Device_Valid_Object_Instance_Number,
+ Device_Object_Name,
+ Device_Read_Property_Local,
+ Device_Write_Property_Local,
+ Device_Property_Lists,
+ DeviceGetRRInfo,
+ NULL /* Iterator */ ,
+ NULL /* Value_Lists */ ,
+ NULL /* COV */ ,
+ NULL /* COV Clear */ ,
+ NULL /* Intrinsic Reporting */ },
+ {OBJECT_ANALOG_INPUT,
+ Analog_Input_Init,
+ Analog_Input_Count,
+ Analog_Input_Index_To_Instance,
+ Analog_Input_Valid_Instance,
+ Analog_Input_Object_Name,
+ Analog_Input_Read_Property,
+ Analog_Input_Write_Property,
+ Analog_Input_Property_Lists,
+ NULL /* ReadRangeInfo */ ,
+ NULL /* Iterator */ ,
+ NULL /* Value_Lists */ ,
+ NULL /* COV */ ,
+ NULL /* COV Clear */ ,
+ NULL /* Intrinsic Reporting */ },
+ {OBJECT_ANALOG_OUTPUT,
+ Analog_Output_Init,
+ Analog_Output_Count,
+ Analog_Output_Index_To_Instance,
+ Analog_Output_Valid_Instance,
+ Analog_Output_Object_Name,
+ Analog_Output_Read_Property,
+ Analog_Output_Write_Property,
+ Analog_Output_Property_Lists,
+ NULL /* ReadRangeInfo */ ,
+ NULL /* Iterator */ ,
+ NULL /* Value_Lists */ ,
+ NULL /* COV */ ,
+ NULL /* COV Clear */ ,
+ NULL /* Intrinsic Reporting */ },
+ {OBJECT_ANALOG_VALUE,
+ Analog_Value_Init,
+ Analog_Value_Count,
+ Analog_Value_Index_To_Instance,
+ Analog_Value_Valid_Instance,
+ Analog_Value_Object_Name,
+ Analog_Value_Read_Property,
+ Analog_Value_Write_Property,
+ Analog_Value_Property_Lists,
+ NULL /* ReadRangeInfo */ ,
+ NULL /* Iterator */ ,
+ NULL /* Value_Lists */ ,
+ NULL /* COV */ ,
+ NULL /* COV Clear */ ,
+ NULL /* Intrinsic Reporting */ },
+ {OBJECT_BINARY_INPUT,
+ Binary_Input_Init,
+ Binary_Input_Count,
+ Binary_Input_Index_To_Instance,
+ Binary_Input_Valid_Instance,
+ Binary_Input_Object_Name,
+ Binary_Input_Read_Property,
+ Binary_Input_Write_Property,
+ Binary_Input_Property_Lists,
+ NULL /* ReadRangeInfo */ ,
+ NULL /* Iterator */ ,
+ NULL /* Value_Lists */ ,
+ NULL /* COV */ ,
+ NULL /* COV Clear */ ,
+ NULL /* Intrinsic Reporting */ },
+ {OBJECT_BINARY_OUTPUT,
+ Binary_Output_Init,
+ Binary_Output_Count,
+ Binary_Output_Index_To_Instance,
+ Binary_Output_Valid_Instance,
+ Binary_Output_Object_Name,
+ Binary_Output_Read_Property,
+ Binary_Output_Write_Property,
+ Binary_Output_Property_Lists,
+ NULL /* ReadRangeInfo */ ,
+ NULL /* Iterator */ ,
+ NULL /* Value_Lists */ ,
+ NULL /* COV */ ,
+ NULL /* COV Clear */ ,
+ NULL /* Intrinsic Reporting */ },
+ {OBJECT_BINARY_VALUE,
+ Binary_Value_Init,
+ Binary_Value_Count,
+ Binary_Value_Index_To_Instance,
+ Binary_Value_Valid_Instance,
+ Binary_Value_Object_Name,
+ Binary_Value_Read_Property,
+ Binary_Value_Write_Property,
+ Binary_Value_Property_Lists,
+ NULL /* ReadRangeInfo */ ,
+ NULL /* Iterator */ ,
+ NULL /* Value_Lists */ ,
+ NULL /* COV */ ,
+ NULL /* COV Clear */ ,
+ NULL /* Intrinsic Reporting */ },
+ {OBJECT_MULTI_STATE_INPUT,
+ Multistate_Input_Init,
+ Multistate_Input_Count,
+ Multistate_Input_Index_To_Instance,
+ Multistate_Input_Valid_Instance,
+ Multistate_Input_Object_Name,
+ Multistate_Input_Read_Property,
+ Multistate_Input_Write_Property,
+ Multistate_Input_Property_Lists,
+ NULL /* ReadRangeInfo */ ,
+ NULL /* Iterator */ ,
+ NULL /* Value_Lists */ ,
+ NULL /* COV */ ,
+ NULL /* COV Clear */ ,
+ NULL /* Intrinsic Reporting */ },
+ {OBJECT_MULTI_STATE_OUTPUT,
+ Multistate_Output_Init,
+ Multistate_Output_Count,
+ Multistate_Output_Index_To_Instance,
+ Multistate_Output_Valid_Instance,
+ Multistate_Output_Object_Name,
+ Multistate_Output_Read_Property,
+ Multistate_Output_Write_Property,
+ Multistate_Output_Property_Lists,
+ NULL /* ReadRangeInfo */ ,
+ NULL /* Iterator */ ,
+ NULL /* Value_Lists */ ,
+ NULL /* COV */ ,
+ NULL /* COV Clear */ ,
+ NULL /* Intrinsic Reporting */ },
+ {OBJECT_MULTI_STATE_VALUE,
+ Multistate_Value_Init,
+ Multistate_Value_Count,
+ Multistate_Value_Index_To_Instance,
+ Multistate_Value_Valid_Instance,
+ Multistate_Value_Object_Name,
+ Multistate_Value_Read_Property,
+ Multistate_Value_Write_Property,
+ Multistate_Value_Property_Lists,
+ NULL /* ReadRangeInfo */ ,
+ NULL /* Iterator */ ,
+ NULL /* Value_Lists */ ,
+ NULL /* COV */ ,
+ NULL /* COV Clear */ ,
+ NULL /* Intrinsic Reporting */ },
+ {MAX_BACNET_OBJECT_TYPE,
+ NULL /* Init */ ,
+ NULL /* Count */ ,
+ NULL /* Index_To_Instance */ ,
+ NULL /* Valid_Instance */ ,
+ NULL /* Object_Name */ ,
+ NULL /* Read_Property */ ,
+ NULL /* Write_Property */ ,
+ NULL /* Property_Lists */ ,
+ NULL /* ReadRangeInfo */ ,
+ NULL /* Iterator */ ,
+ NULL /* Value_Lists */ ,
+ NULL /* COV */ ,
+ NULL /* COV Clear */ ,
+ NULL /* Intrinsic Reporting */ }
+};
+
+/** Glue function to let the Device object, when called by a handler,
+ * lookup which Object type needs to be invoked.
+ * param: Object_Type [in] The type of BACnet Object the handler wants to access.
+ * return: Pointer to the group of object helper functions that implement this
+ * type of Object.
+ */
+static struct object_functions *Device_Objects_Find_Functions(
+ BACNET_OBJECT_TYPE Object_Type)
+{
+ struct object_functions *pObject = NULL;
+
+ pObject = Object_Table;
+ while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) {
+ /* handle each object type */
+ if (pObject->Object_Type == Object_Type) {
+ return (pObject);
+ }
+ pObject++;
+ }
+
+ return (NULL);
+}
+
+/** Try to find a rr_info_function helper function for the requested object type.
+ *
+ * param: object_type [in] The type of BACnet Object the handler wants to access.
+ * return: Pointer to the object helper function that implements the
+ * ReadRangeInfo function, Object_RR_Info, for this type of Object on
+ * success, else a NULL pointer if the type of Object isn't supported
+ * or doesn't have a ReadRangeInfo function.
+ */
+rr_info_function Device_Objects_RR_Info(
+ BACNET_OBJECT_TYPE object_type)
+{
+ struct object_functions *pObject = NULL;
+
+ pObject = Device_Objects_Find_Functions(object_type);
+ return (pObject != NULL ? pObject->Object_RR_Info : NULL);
+}
+
+/** For a given object type, returns the special property list.
+ * This function is used for ReadPropertyMultiple calls which want
+ * just Required, just Optional, or All properties.
+ *
+ * param: object_type [in] The desired BACNET_OBJECT_TYPE whose properties
+ * are to be listed.
+ * param: pPropertyList [out] Reference to the structure which will, on return,
+ * list, separately, the Required, Optional, and Proprietary object
+ * properties with their counts.
+ */
+void Device_Objects_Property_List(
+ BACNET_OBJECT_TYPE object_type,
+ struct special_property_list_t *pPropertyList)
+{
+ struct object_functions *pObject = NULL;
+
+ pPropertyList->Required.pList = NULL;
+ pPropertyList->Optional.pList = NULL;
+ pPropertyList->Proprietary.pList = NULL;
+
+ /* If we can find an entry for the required object type
+ * and there is an Object_List_RPM fn ptr then call it
+ * to populate the pointers to the individual list counters.
+ */
+
+ pObject = Device_Objects_Find_Functions(object_type);
+ if ((pObject != NULL) && (pObject->Object_RPM_List != NULL)) {
+ pObject->Object_RPM_List(&pPropertyList->Required.pList,
+ &pPropertyList->Optional.pList, &pPropertyList->Proprietary.pList);
+ }
+
+ /* Fetch the counts if available otherwise zero them */
+ pPropertyList->Required.count =
+ pPropertyList->Required.pList ==
+ NULL ? 0 : property_list_count(pPropertyList->Required.pList);
+
+ pPropertyList->Optional.count =
+ pPropertyList->Optional.pList ==
+ NULL ? 0 : property_list_count(pPropertyList->Optional.pList);
+
+ pPropertyList->Proprietary.count =
+ pPropertyList->Proprietary.pList ==
+ NULL ? 0 : property_list_count(pPropertyList->Proprietary.pList);
+
+ return;
+}
+
+/** Commands a Device re-initialization, to a given state.
+ * The request's password must match for the operation to succeed.
+ * This implementation provides a framework, but doesn't
+ * actually *DO* anything.
+ * @note You could use a mix of states and passwords to multiple outcomes.
+ * @note You probably want to restart *after* the simple ack has been sent
+ * from the return handler, so just set a local flag here.
+ *
+ * param: rd_data [in,out] The information from the RD request.
+ * On failure, the error class and code will be set.
+ * return: True if succeeds (password is correct), else False.
+ */
+bool Device_Reinitialize(
+ BACNET_REINITIALIZE_DEVICE_DATA * rd_data)
+{
+ bool status = false;
+
+ if (characterstring_ansi_same(&rd_data->password, "Jesus")) {
+ switch (rd_data->state) {
+ case BACNET_REINIT_COLDSTART:
+ case BACNET_REINIT_WARMSTART:
+ dcc_set_status_duration(COMMUNICATION_ENABLE, 0);
+ break;
+ case BACNET_REINIT_STARTBACKUP:
+ break;
+ case BACNET_REINIT_ENDBACKUP:
+ break;
+ case BACNET_REINIT_STARTRESTORE:
+ break;
+ case BACNET_REINIT_ENDRESTORE:
+ break;
+ case BACNET_REINIT_ABORTRESTORE:
+ break;
+ default:
+ break;
+ }
+ /* Note: you could use a mix of state
+ and password to multiple things */
+ /* note: you probably want to restart *after* the
+ simple ack has been sent from the return handler
+ so just set a flag from here */
+ status = true;
+ } else {
+ rd_data->error_class = ERROR_CLASS_SECURITY;
+ rd_data->error_code = ERROR_CODE_PASSWORD_FAILURE;
+ }
+
+ return status;
+}
+
+/* These three arrays are used by the ReadPropertyMultiple handler,
+ * as well as to initialize the XXX_Property_List used by the
+ * Property List (PROP_PROPERTY_LIST) property.
+ */
+static const int Device_Properties_Required[] = {
+ /* (1) Currently Supported */
+ /* (2) Required by standard ASHRAE 135-2016 */
+ /*(1)(2) */
+ PROP_OBJECT_IDENTIFIER, /* W R ( 75) */
+ PROP_OBJECT_NAME, /* W R ( 77) */
+ PROP_OBJECT_TYPE, /* R R ( 79) */
+ PROP_SYSTEM_STATUS, /* R R (112) */
+ PROP_VENDOR_NAME, /* R R (121) */
+ PROP_VENDOR_IDENTIFIER, /* W R (120) */
+ PROP_MODEL_NAME, /* W R ( 70) */
+ PROP_FIRMWARE_REVISION, /* R R ( 44) */
+ PROP_APPLICATION_SOFTWARE_VERSION, /* R R ( 12) */
+ PROP_PROTOCOL_VERSION, /* R R ( 98) */
+ PROP_PROTOCOL_REVISION, /* R R (139) */
+ PROP_PROTOCOL_SERVICES_SUPPORTED, /* R R ( 97) */
+ PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED, /* R R ( 96) */
+ PROP_OBJECT_LIST, /* R R ( 76) */
+ PROP_MAX_APDU_LENGTH_ACCEPTED, /* R R ( 62) */
+ PROP_SEGMENTATION_SUPPORTED, /* R R (107) */
+ PROP_APDU_TIMEOUT, /* W R ( 11) */
+ PROP_NUMBER_OF_APDU_RETRIES, /* W R ( 73) */
+ PROP_DEVICE_ADDRESS_BINDING, /* R R ( 30) */
+ PROP_DATABASE_REVISION, /* R R (155) */
+// PROP_PROPERTY_LIST, /* R R (371) */
+ -1
+};
+
+static const int Device_Properties_Optional[] = {
+ PROP_DESCRIPTION, /* W O ( 28) */
+ PROP_LOCAL_TIME, /* R O ( 57) */
+ PROP_UTC_OFFSET, /* R O (119) */
+ PROP_LOCAL_DATE, /* R O ( 56) */
+ PROP_DAYLIGHT_SAVINGS_STATUS, /* R O ( 24) */
+ PROP_LOCATION, /* W O ( 58) */
+ -1
+};
+
+static const int Device_Properties_Proprietary[] = {
+ -1
+};
+
+/* This array stores the PROPERTY_LIST which may be read by clients.
+ * End of list is marked by following the last element with the value '-1'
+ *
+ * It is initialized by Binary_Value_Init() based off the values
+ * stored in Binary_Value_Properties_Required
+ * Binary_Value_Properties_Optional
+ * Binary_Value_Properties_Proprietary
+ */
+/* TODO: Allocate memory for this array with malloc() at startup */
+static int Device_Properties_List[64];
+
+
+void Device_Property_Lists(
+ const int **pRequired,
+ const int **pOptional,
+ const int **pProprietary)
+{
+ if (pRequired)
+ *pRequired = Device_Properties_Required;
+ if (pOptional)
+ *pOptional = Device_Properties_Optional;
+ if (pProprietary)
+ *pProprietary = Device_Properties_Proprietary;
+
+ return;
+}
+
+
+static BACNET_DEVICE_STATUS System_Status = STATUS_OPERATIONAL;
+
+static BACNET_CHARACTER_STRING My_Object_Name;
+static uint32_t Object_Instance_Number = 260001;
+static uint16_t Vendor_Identifier = BACNET_VENDOR_ID;
+static char *Vendor_Name = BACNET_VENDOR_NAME;
+static char *Firmware_Revision = BACNET_FIRMWARE_REVISION;
+static char Model_Name [MAX_DEV_MOD_LEN + 1] = BACNET_DEVICE_MODEL_NAME;
+static char Application_Software_Version[MAX_DEV_VER_LEN + 1] = BACNET_DEVICE_APPSOFT_VER;
+static char Location [MAX_DEV_LOC_LEN + 1] = BACNET_DEVICE_LOCATION;
+static char Description [MAX_DEV_DESC_LEN + 1] = BACNET_DEVICE_DESCRIPTION;
+/* static uint8_t Protocol_Version = 1; - constant, not settable */
+/* static uint8_t Protocol_Revision = 4; - constant, not settable */
+/* Protocol_Services_Supported - dynamically generated */
+/* Protocol_Object_Types_Supported - in RP encoding */
+/* Object_List - dynamically generated */
+/* static BACNET_SEGMENTATION Segmentation_Supported = SEGMENTATION_NONE; */
+/* static uint8_t Max_Segments_Accepted = 0; */
+/* VT_Classes_Supported */
+/* Active_VT_Sessions */
+static BACNET_TIME Local_Time; /* rely on OS, if there is one */
+static BACNET_DATE Local_Date; /* rely on OS, if there is one */
+/* NOTE: BACnet UTC Offset is inverse of common practice.
+ If your UTC offset is -5hours of GMT,
+ then BACnet UTC offset is +5hours.
+ BACnet UTC offset is expressed in minutes. */
+static int32_t UTC_Offset = 5 * 60;
+static bool Daylight_Savings_Status = false; /* rely on OS */
+/* List_Of_Session_Keys */
+/* Time_Synchronization_Recipients */
+/* Max_Master - rely on MS/TP subsystem, if there is one */
+/* Max_Info_Frames - rely on MS/TP subsystem, if there is one */
+/* Device_Address_Binding - required, but relies on binding cache */
+static uint32_t Database_Revision = 0;
+/* Configuration_Files */
+/* Last_Restore_Time */
+/* Backup_Failure_Timeout */
+/* Active_COV_Subscriptions */
+/* Slave_Proxy_Enable */
+/* Manual_Slave_Address_Binding */
+/* Auto_Slave_Discovery */
+/* Slave_Address_Binding */
+/* Profile_Name */
+
+unsigned Device_Count(
+ void)
+{
+ return 1;
+}
+
+uint32_t Device_Index_To_Instance(
+ unsigned index)
+{
+ index = index;
+ return Object_Instance_Number;
+}
+
+/* methods to manipulate the data */
+
+/** Return the Object Instance number for our (single) Device Object.
+ * This is a key function, widely invoked by the handler code, since
+ * it provides "our" (ie, local) address.
+ * return: The Instance number used in the BACNET_OBJECT_ID for the Device.
+ */
+uint32_t Device_Object_Instance_Number(
+ void)
+{
+ return Object_Instance_Number;
+}
+
+bool Device_Set_Object_Instance_Number(
+ uint32_t object_id)
+{
+ bool status = true; /* return value */
+
+ if (object_id <= BACNET_MAX_INSTANCE) {
+ /* Make the change and update the database revision */
+ Object_Instance_Number = object_id;
+ Device_Inc_Database_Revision();
+ } else
+ status = false;
+
+ return status;
+}
+
+bool Device_Valid_Object_Instance_Number(
+ uint32_t object_id)
+{
+ return (Object_Instance_Number == object_id);
+}
+
+bool Device_Object_Name(
+ uint32_t object_instance,
+ BACNET_CHARACTER_STRING * object_name)
+{
+ bool status = false;
+
+ if (object_instance == Object_Instance_Number) {
+ status = characterstring_copy(object_name, &My_Object_Name);
+ }
+
+ return status;
+}
+
+bool Device_Set_Object_Name(
+ BACNET_CHARACTER_STRING * object_name)
+{
+ bool status = false; /*return value */
+
+ if (!characterstring_same(&My_Object_Name, object_name)) {
+ /* Make the change and update the database revision */
+ status = characterstring_copy(&My_Object_Name, object_name);
+ Device_Inc_Database_Revision();
+ }
+
+ return status;
+}
+
+BACNET_DEVICE_STATUS Device_System_Status(
+ void)
+{
+ return System_Status;
+}
+
+int Device_Set_System_Status(
+ BACNET_DEVICE_STATUS status,
+ bool local)
+{
+ int result = 0; /*return value - 0 = ok, -1 = bad value, -2 = not allowed */
+
+ /* We limit the options available depending on whether the source is
+ * internal or external. */
+ if (local) {
+ switch (status) {
+ case STATUS_OPERATIONAL:
+ case STATUS_OPERATIONAL_READ_ONLY:
+ case STATUS_DOWNLOAD_REQUIRED:
+ case STATUS_DOWNLOAD_IN_PROGRESS:
+ case STATUS_NON_OPERATIONAL:
+ System_Status = status;
+ break;
+
+ /* Don't support backup at present so don't allow setting */
+ case STATUS_BACKUP_IN_PROGRESS:
+ result = -2;
+ break;
+
+ default:
+ result = -1;
+ break;
+ }
+ } else {
+ switch (status) {
+ /* Allow these for the moment as a way to easily alter
+ * overall device operation. The lack of password protection
+ * or other authentication makes allowing writes to this
+ * property a risky facility to provide.
+ */
+ case STATUS_OPERATIONAL:
+ case STATUS_OPERATIONAL_READ_ONLY:
+ case STATUS_NON_OPERATIONAL:
+ System_Status = status;
+ break;
+
+ /* Don't allow outsider set this - it should probably
+ * be set if the device config is incomplete or
+ * corrupted or perhaps after some sort of operator
+ * wipe operation.
+ */
+ case STATUS_DOWNLOAD_REQUIRED:
+ /* Don't allow outsider set this - it should be set
+ * internally at the start of a multi packet download
+ * perhaps indirectly via PT or WF to a config file.
+ */
+ case STATUS_DOWNLOAD_IN_PROGRESS:
+ /* Don't support backup at present so don't allow setting */
+ case STATUS_BACKUP_IN_PROGRESS:
+ result = -2;
+ break;
+
+ default:
+ result = -1;
+ break;
+ }
+ }
+
+ return (result);
+}
+
+const char *Device_Vendor_Name(
+ void)
+{
+ return Vendor_Name;
+}
+
+/** Returns the Vendor ID for this Device.
+ * See the assignments at http://www.bacnet.org/VendorID/BACnet%%20Vendor%%20IDs.htm
+ * return: The Vendor ID of this Device.
+ */
+uint16_t Device_Vendor_Identifier(
+ void)
+{
+ return Vendor_Identifier;
+}
+
+void Device_Set_Vendor_Identifier(
+ uint16_t vendor_id)
+{
+ Vendor_Identifier = vendor_id;
+}
+
+const char *Device_Model_Name(
+ void)
+{
+ return Model_Name;
+}
+
+bool Device_Set_Model_Name(
+ const char *name,
+ size_t length)
+{
+ bool status = false; /*return value */
+
+ if (length < sizeof(Model_Name)) {
+ memmove(Model_Name, name, length);
+ Model_Name[length] = 0;
+ status = true;
+ }
+
+ return status;
+}
+
+const char *Device_Firmware_Revision(
+ void)
+{
+ return Firmware_Revision;
+}
+
+const char *Device_Application_Software_Version(
+ void)
+{
+ return Application_Software_Version;
+}
+
+bool Device_Set_Application_Software_Version(
+ const char *name,
+ size_t length)
+{
+ bool status = false; /*return value */
+
+ if (length < sizeof(Application_Software_Version)) {
+ memmove(Application_Software_Version, name, length);
+ Application_Software_Version[length] = 0;
+ status = true;
+ }
+
+ return status;
+}
+
+const char *Device_Description(
+ void)
+{
+ return Description;
+}
+
+bool Device_Set_Description(
+ const char *name,
+ size_t length)
+{
+ bool status = false; /*return value */
+
+ if (length < sizeof(Description)) {
+ memmove(Description, name, length);
+ Description[length] = 0;
+ status = true;
+ }
+
+ return status;
+}
+
+const char *Device_Location(
+ void)
+{
+ return Location;
+}
+
+bool Device_Set_Location(
+ const char *name,
+ size_t length)
+{
+ bool status = false; /*return value */
+
+ if (length < sizeof(Location)) {
+ memmove(Location, name, length);
+ Location[length] = 0;
+ status = true;
+ }
+
+ return status;
+}
+
+uint8_t Device_Protocol_Version(
+ void)
+{
+ return BACNET_PROTOCOL_VERSION;
+}
+
+uint8_t Device_Protocol_Revision(
+ void)
+{
+ return BACNET_PROTOCOL_REVISION;
+}
+
+BACNET_SEGMENTATION Device_Segmentation_Supported(
+ void)
+{
+ return SEGMENTATION_NONE;
+}
+
+uint32_t Device_Database_Revision(
+ void)
+{
+ return Database_Revision;
+}
+
+void Device_Set_Database_Revision(
+ uint32_t revision)
+{
+ Database_Revision = revision;
+}
+
+/*
+ * Shortcut for incrementing database revision as this is potentially
+ * the most common operation if changing object names and ids is
+ * implemented.
+ */
+void Device_Inc_Database_Revision(
+ void)
+{
+ Database_Revision++;
+}
+
+/** Get the total count of objects supported by this Device Object.
+ * @note Since many network clients depend on the object list
+ * for discovery, it must be consistent!
+ * return: The count of objects, for all supported Object types.
+ */
+unsigned Device_Object_List_Count(
+ void)
+{
+ unsigned count = 0; /* number of objects */
+ struct object_functions *pObject = NULL;
+
+ /* initialize the default return values */
+ pObject = Object_Table;
+ while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) {
+ if (pObject->Object_Count) {
+ count += pObject->Object_Count();
+ }
+ pObject++;
+ }
+
+ return count;
+}
+
+/** Lookup the Object at the given array index in the Device's Object List.
+ * Even though we don't keep a single linear array of objects in the Device,
+ * this method acts as though we do and works through a virtual, concatenated
+ * array of all of our object type arrays.
+ *
+ * param: array_index [in] The desired array index (1 to N)
+ * param: object_type [out] The object's type, if found.
+ * param: instance [out] The object's instance number, if found.
+ * return: True if found, else false.
+ */
+bool Device_Object_List_Identifier(
+ unsigned array_index,
+ int *object_type,
+ uint32_t * instance)
+{
+ bool status = false;
+ unsigned count = 0;
+ unsigned object_index = 0;
+ unsigned temp_index = 0;
+ struct object_functions *pObject = NULL;
+
+ /* array index zero is length - so invalid */
+ if (array_index == 0) {
+ return status;
+ }
+ object_index = array_index - 1;
+ /* initialize the default return values */
+ pObject = Object_Table;
+ while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) {
+ if (pObject->Object_Count) {
+ object_index -= count;
+ count = pObject->Object_Count();
+ if (object_index < count) {
+ /* Use the iterator function if available otherwise
+ * look for the index to instance to get the ID */
+ if (pObject->Object_Iterator) {
+ /* First find the first object */
+ temp_index = pObject->Object_Iterator(~(unsigned) 0);
+ /* Then step through the objects to find the nth */
+ while (object_index != 0) {
+ temp_index = pObject->Object_Iterator(temp_index);
+ object_index--;
+ }
+ /* set the object_index up before falling through to next bit */
+ object_index = temp_index;
+ }
+ if (pObject->Object_Index_To_Instance) {
+ *object_type = pObject->Object_Type;
+ *instance =
+ pObject->Object_Index_To_Instance(object_index);
+ status = true;
+ break;
+ }
+ }
+ }
+ pObject++;
+ }
+
+ return status;
+}
+
+/** Determine if we have an object with the given object_name.
+ * If the object_type and object_instance pointers are not null,
+ * and the lookup succeeds, they will be given the resulting values.
+ * param: object_name [in] The desired Object Name to look for.
+ * param: object_type [out] The BACNET_OBJECT_TYPE of the matching Object.
+ * param: object_instance [out] The object instance number of the matching Object.
+ * return: True on success or else False if not found.
+ */
+bool Device_Valid_Object_Name(
+ BACNET_CHARACTER_STRING * object_name1,
+ int *object_type,
+ uint32_t * object_instance)
+{
+ bool found = false;
+ int type = 0;
+ uint32_t instance;
+ unsigned max_objects = 0, i = 0;
+ bool check_id = false;
+ BACNET_CHARACTER_STRING object_name2;
+ struct object_functions *pObject = NULL;
+
+ max_objects = Device_Object_List_Count();
+ for (i = 1; i <= max_objects; i++) {
+ check_id = Device_Object_List_Identifier(i, &type, &instance);
+ if (check_id) {
+ pObject = Device_Objects_Find_Functions(type);
+ if ((pObject != NULL) && (pObject->Object_Name != NULL) &&
+ (pObject->Object_Name(instance, &object_name2) &&
+ characterstring_same(object_name1, &object_name2))) {
+ found = true;
+ if (object_type) {
+ *object_type = type;
+ }
+ if (object_instance) {
+ *object_instance = instance;
+ }
+ break;
+ }
+ }
+ }
+
+ return found;
+}
+
+/** Determine if we have an object of this type and instance number.
+ * param: object_type [in] The desired BACNET_OBJECT_TYPE
+ * param: object_instance [in] The object instance number to be looked up.
+ * return: True if found, else False if no such Object in this device.
+ */
+bool Device_Valid_Object_Id(
+ int object_type,
+ uint32_t object_instance)
+{
+ bool status = false; /* return value */
+ struct object_functions *pObject = NULL;
+
+ pObject = Device_Objects_Find_Functions(object_type);
+ if ((pObject != NULL) && (pObject->Object_Valid_Instance != NULL)) {
+ status = pObject->Object_Valid_Instance(object_instance);
+ }
+
+ return status;
+}
+
+/** Copy a child object's object_name value, given its ID.
+ * param: object_type [in] The BACNET_OBJECT_TYPE of the child Object.
+ * param: object_instance [in] The object instance number of the child Object.
+ * param: object_name [out] The Object Name found for this child Object.
+ * return: True on success or else False if not found.
+ */
+bool Device_Object_Name_Copy(
+ BACNET_OBJECT_TYPE object_type,
+ uint32_t object_instance,
+ BACNET_CHARACTER_STRING * object_name)
+{
+ struct object_functions *pObject = NULL;
+ bool found = false;
+
+ pObject = Device_Objects_Find_Functions(object_type);
+ if ((pObject != NULL) && (pObject->Object_Name != NULL)) {
+ found = pObject->Object_Name(object_instance, object_name);
+ }
+
+ return found;
+}
+
+static void Device_Update_Current_Time(
+ void)
+{
+ struct tm *tblock = NULL;
+#if defined(_MSC_VER)
+ time_t tTemp;
+#else
+ struct timeval tv;
+#endif
+/*
+struct tm
+
+int tm_sec Seconds [0,60].
+int tm_min Minutes [0,59].
+int tm_hour Hour [0,23].
+int tm_mday Day of month [1,31].
+int tm_mon Month of year [0,11].
+int tm_year Years since 1900.
+int tm_wday Day of week [0,6] (Sunday =0).
+int tm_yday Day of year [0,365].
+int tm_isdst Daylight Savings flag.
+*/
+#if defined(_MSC_VER)
+ time(&tTemp);
+ tblock = localtime(&tTemp);
+#else
+ if (gettimeofday(&tv, NULL) == 0) {
+ tblock = localtime(&tv.tv_sec);
+ }
+#endif
+
+ if (tblock) {
+ datetime_set_date(&Local_Date, (uint16_t) tblock->tm_year + 1900,
+ (uint8_t) tblock->tm_mon + 1, (uint8_t) tblock->tm_mday);
+#if !defined(_MSC_VER)
+ datetime_set_time(&Local_Time, (uint8_t) tblock->tm_hour,
+ (uint8_t) tblock->tm_min, (uint8_t) tblock->tm_sec,
+ (uint8_t) (tv.tv_usec / 10000));
+#else
+ datetime_set_time(&Local_Time, (uint8_t) tblock->tm_hour,
+ (uint8_t) tblock->tm_min, (uint8_t) tblock->tm_sec, 0);
+#endif
+ if (tblock->tm_isdst) {
+ Daylight_Savings_Status = true;
+ } else {
+ Daylight_Savings_Status = false;
+ }
+ /* note: timezone is declared in <time.h> stdlib. */
+ UTC_Offset = timezone / 60;
+ } else {
+ datetime_date_wildcard_set(&Local_Date);
+ datetime_time_wildcard_set(&Local_Time);
+ Daylight_Savings_Status = false;
+ }
+}
+
+void Device_getCurrentDateTime(
+ BACNET_DATE_TIME * DateTime)
+{
+ Device_Update_Current_Time();
+
+ DateTime->date = Local_Date;
+ DateTime->time = Local_Time;
+}
+
+int32_t Device_UTC_Offset(void)
+{
+ Device_Update_Current_Time();
+
+ return UTC_Offset;
+}
+
+bool Device_Daylight_Savings_Status(void)
+{
+ return Daylight_Savings_Status;
+}
+
+/* return the length of the apdu encoded or BACNET_STATUS_ERROR for error or
+ BACNET_STATUS_ABORT for abort message */
+int Device_Read_Property_Local(
+ BACNET_READ_PROPERTY_DATA * rpdata)
+{
+ int apdu_len = 0; /* return value */
+ int len = 0; /* apdu len intermediate value */
+ BACNET_BIT_STRING bit_string = { 0 };
+ BACNET_CHARACTER_STRING char_string = { 0 };
+ unsigned i = 0;
+ int object_type = 0;
+ uint32_t instance = 0;
+ unsigned count = 0;
+ uint8_t *apdu = NULL;
+ struct object_functions *pObject = NULL;
+ bool found = false;
+
+ if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
+ (rpdata->application_data_len == 0)) {
+ return 0;
+ }
+ apdu = rpdata->application_data;
+ switch (rpdata->object_property) {
+ case PROP_OBJECT_IDENTIFIER:
+ apdu_len =
+ encode_application_object_id(&apdu[0], OBJECT_DEVICE,
+ Object_Instance_Number);
+ break;
+ case PROP_OBJECT_NAME:
+ apdu_len =
+ encode_application_character_string(&apdu[0], &My_Object_Name);
+ break;
+ case PROP_OBJECT_TYPE:
+ apdu_len = encode_application_enumerated(&apdu[0], OBJECT_DEVICE);
+ break;
+ case PROP_DESCRIPTION:
+ characterstring_init_ansi(&char_string, Description);
+ apdu_len =
+ encode_application_character_string(&apdu[0], &char_string);
+ break;
+ case PROP_SYSTEM_STATUS:
+ apdu_len = encode_application_enumerated(&apdu[0], System_Status);
+ break;
+ case PROP_VENDOR_NAME:
+ characterstring_init_ansi(&char_string, Vendor_Name);
+ apdu_len =
+ encode_application_character_string(&apdu[0], &char_string);
+ break;
+ case PROP_VENDOR_IDENTIFIER:
+ apdu_len =
+ encode_application_unsigned(&apdu[0], Vendor_Identifier);
+ break;
+ case PROP_MODEL_NAME:
+ characterstring_init_ansi(&char_string, Model_Name);
+ apdu_len =
+ encode_application_character_string(&apdu[0], &char_string);
+ break;
+ case PROP_FIRMWARE_REVISION:
+ characterstring_init_ansi(&char_string, Firmware_Revision);
+ apdu_len =
+ encode_application_character_string(&apdu[0], &char_string);
+ break;
+ case PROP_APPLICATION_SOFTWARE_VERSION:
+ characterstring_init_ansi(&char_string,
+ Application_Software_Version);
+ apdu_len =
+ encode_application_character_string(&apdu[0], &char_string);
+ break;
+ case PROP_LOCATION:
+ characterstring_init_ansi(&char_string, Location);
+ apdu_len =
+ encode_application_character_string(&apdu[0], &char_string);
+ break;
+ case PROP_LOCAL_TIME:
+ Device_Update_Current_Time();
+ apdu_len = encode_application_time(&apdu[0], &Local_Time);
+ break;
+ case PROP_UTC_OFFSET:
+ Device_Update_Current_Time();
+ apdu_len = encode_application_signed(&apdu[0], UTC_Offset);
+ break;
+ case PROP_LOCAL_DATE:
+ Device_Update_Current_Time();
+ apdu_len = encode_application_date(&apdu[0], &Local_Date);
+ break;
+ case PROP_DAYLIGHT_SAVINGS_STATUS:
+ Device_Update_Current_Time();
+ apdu_len =
+ encode_application_boolean(&apdu[0], Daylight_Savings_Status);
+ break;
+ case PROP_PROTOCOL_VERSION:
+ apdu_len =
+ encode_application_unsigned(&apdu[0],
+ Device_Protocol_Version());
+ break;
+ case PROP_PROTOCOL_REVISION:
+ apdu_len =
+ encode_application_unsigned(&apdu[0],
+ Device_Protocol_Revision());
+ break;
+ case PROP_PROTOCOL_SERVICES_SUPPORTED:
+ /* Note: list of services that are executed, not initiated. */
+ bitstring_init(&bit_string);
+ for (i = 0; i < MAX_BACNET_SERVICES_SUPPORTED; i++) {
+ /* automatic lookup based on handlers set */
+ bitstring_set_bit(&bit_string, (uint8_t) i,
+ apdu_service_supported((BACNET_SERVICES_SUPPORTED) i));
+ }
+ apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
+ break;
+ case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED:
+ /* Note: this is the list of objects that can be in this device,
+ not a list of objects that this device can access */
+ bitstring_init(&bit_string);
+ for (i = 0; i < MAX_ASHRAE_OBJECT_TYPE; i++) {
+ /* initialize all the object types to not-supported */
+ bitstring_set_bit(&bit_string, (uint8_t) i, false);
+ }
+ /* set the object types with objects to supported */
+
+ pObject = Object_Table;
+ while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) {
+ if ((pObject->Object_Count) && (pObject->Object_Count() > 0)) {
+ bitstring_set_bit(&bit_string, pObject->Object_Type, true);
+ }
+ pObject++;
+ }
+ apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
+ break;
+ case PROP_OBJECT_LIST:
+ count = Device_Object_List_Count();
+ /* Array element zero is the number of objects in the list */
+ if (rpdata->array_index == 0)
+ apdu_len = encode_application_unsigned(&apdu[0], count);
+ /* if no index was specified, then try to encode the entire list */
+ /* into one packet. Note that more than likely you will have */
+ /* to return an error if the number of encoded objects exceeds */
+ /* your maximum APDU size. */
+ else if (rpdata->array_index == BACNET_ARRAY_ALL) {
+ for (i = 1; i <= count; i++) {
+ found =
+ Device_Object_List_Identifier(i, &object_type,
+ &instance);
+ if (found) {
+ len =
+ encode_application_object_id(&apdu[apdu_len],
+ object_type, instance);
+ apdu_len += len;
+ /* assume next one is the same size as this one */
+ /* can we all fit into the APDU? Don't check for last entry */
+ if ((i != count) && (apdu_len + len) >= MAX_APDU) {
+ /* Abort response */
+ rpdata->error_code =
+ ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
+ apdu_len = BACNET_STATUS_ABORT;
+ break;
+ }
+ } else {
+ /* error: internal error? */
+ rpdata->error_class = ERROR_CLASS_SERVICES;
+ rpdata->error_code = ERROR_CODE_OTHER;
+ apdu_len = BACNET_STATUS_ERROR;
+ break;
+ }
+ }
+ } else {
+ found =
+ Device_Object_List_Identifier(rpdata->array_index,
+ &object_type, &instance);
+ if (found) {
+ apdu_len =
+ encode_application_object_id(&apdu[0], object_type,
+ instance);
+ } else {
+ rpdata->error_class = ERROR_CLASS_PROPERTY;
+ rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
+ apdu_len = BACNET_STATUS_ERROR;
+ }
+ }
+ break;
+ case PROP_MAX_APDU_LENGTH_ACCEPTED:
+ apdu_len = encode_application_unsigned(&apdu[0], MAX_APDU);
+ break;
+ case PROP_SEGMENTATION_SUPPORTED:
+ apdu_len =
+ encode_application_enumerated(&apdu[0],
+ Device_Segmentation_Supported());
+ break;
+ case PROP_APDU_TIMEOUT:
+ apdu_len = encode_application_unsigned(&apdu[0], apdu_timeout());
+ break;
+ case PROP_NUMBER_OF_APDU_RETRIES:
+ apdu_len = encode_application_unsigned(&apdu[0], apdu_retries());
+ break;
+ case PROP_DEVICE_ADDRESS_BINDING:
+ /* FIXME: the real max apdu remaining should be passed into function */
+ apdu_len = address_list_encode(&apdu[0], MAX_APDU);
+ break;
+ case PROP_DATABASE_REVISION:
+ apdu_len =
+ encode_application_unsigned(&apdu[0], Database_Revision);
+ break;
+// case PROP_PROPERTY_LIST:
+// BACnet_encode_array(Device_Properties_List,
+// property_list_count(Device_Properties_List),
+// retfalse, encode_application_enumerated);
+// break;
+ default:
+ rpdata->error_class = ERROR_CLASS_PROPERTY;
+ rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
+ apdu_len = BACNET_STATUS_ERROR;
+ break;
+ }
+ /* only array properties can have array options */
+ if ((apdu_len >= 0) && (rpdata->object_property != PROP_OBJECT_LIST) &&
+// (rpdata->object_property != PROP_PROPERTY_LIST) &&
+ (rpdata->array_index != BACNET_ARRAY_ALL)) {
+ rpdata->error_class = ERROR_CLASS_PROPERTY;
+ rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
+ apdu_len = BACNET_STATUS_ERROR;
+ }
+
+ return apdu_len;
+}
+
+/** Looks up the requested Object and Property, and encodes its Value in an APDU.
+ * If the Object or Property can't be found, sets the error class and code.
+ *
+ * param: rpdata [in,out] Structure with the desired Object and Property info
+ * on entry, and APDU message on return.
+ * return: The length of the APDU on success, else BACNET_STATUS_ERROR
+ */
+int Device_Read_Property(
+ BACNET_READ_PROPERTY_DATA * rpdata)
+{
+ int apdu_len = BACNET_STATUS_ERROR;
+ struct object_functions *pObject = NULL;
+
+ /* initialize the default return values */
+ rpdata->error_class = ERROR_CLASS_OBJECT;
+ rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT;
+ pObject = Device_Objects_Find_Functions(rpdata->object_type);
+ if (pObject != NULL) {
+ if (pObject->Object_Valid_Instance &&
+ pObject->Object_Valid_Instance(rpdata->object_instance)) {
+ if (pObject->Object_Read_Property) {
+ apdu_len = pObject->Object_Read_Property(rpdata);
+ }
+ }
+ }
+
+ return apdu_len;
+}
+
+/* returns true if successful */
+bool Device_Write_Property_Local(
+ BACNET_WRITE_PROPERTY_DATA * wp_data)
+{
+ bool status = false; /* return value */
+ int len = 0;
+ BACNET_APPLICATION_DATA_VALUE value;
+ int object_type = 0;
+ uint32_t object_instance = 0;
+ int temp;
+
+ /* decode the some of the request */
+ len =
+ bacapp_decode_application_data(wp_data->application_data,
+ wp_data->application_data_len, &value);
+ if (len < 0) {
+ /* error while decoding - a value larger than we can handle */
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
+ return false;
+ }
+ if ((wp_data->object_property != PROP_OBJECT_LIST) &&
+ (wp_data->array_index != BACNET_ARRAY_ALL)) {
+ /* only array properties can have array options */
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
+ return false;
+ }
+ /* FIXME: len < application_data_len: more data? */
+ switch (wp_data->object_property) {
+ case PROP_OBJECT_IDENTIFIER:
+ status =
+ WPValidateArgType(&value, BACNET_APPLICATION_TAG_OBJECT_ID,
+ &wp_data->error_class, &wp_data->error_code);
+ if (status) {
+ if ((value.type.Object_Id.type == OBJECT_DEVICE) &&
+ (Device_Set_Object_Instance_Number(value.type.
+ Object_Id.instance))) {
+ /* FIXME: we could send an I-Am broadcast to let the world know */
+ } else {
+ status = false;
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
+ }
+ }
+ break;
+ case PROP_NUMBER_OF_APDU_RETRIES:
+ status =
+ WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT,
+ &wp_data->error_class, &wp_data->error_code);
+ if (status) {
+ /* FIXME: bounds check? */
+ apdu_retries_set((uint8_t) value.type.Unsigned_Int);
+ }
+ break;
+ case PROP_APDU_TIMEOUT:
+ status =
+ WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT,
+ &wp_data->error_class, &wp_data->error_code);
+ if (status) {
+ /* FIXME: bounds check? */
+ apdu_timeout_set((uint16_t) value.type.Unsigned_Int);
+ }
+ break;
+ case PROP_VENDOR_IDENTIFIER:
+ status =
+ WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT,
+ &wp_data->error_class, &wp_data->error_code);
+ if (status) {
+ /* FIXME: bounds check? */
+ Device_Set_Vendor_Identifier((uint16_t) value.
+ type.Unsigned_Int);
+ }
+ break;
+// case PROP_SYSTEM_STATUS:
+// status =
+// WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED,
+// &wp_data->error_class, &wp_data->error_code);
+// if (status) {
+// temp = Device_Set_System_Status((BACNET_DEVICE_STATUS)
+// value.type.Enumerated, false);
+// if (temp != 0) {
+// status = false;
+// wp_data->error_class = ERROR_CLASS_PROPERTY;
+// if (temp == -1) {
+// wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
+// } else {
+// wp_data->error_code =
+// ERROR_CODE_OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED;
+// }
+// }
+// }
+// break;
+ case PROP_OBJECT_NAME:
+ status =
+ WPValidateString(&value,
+ characterstring_capacity(&My_Object_Name), false,
+ &wp_data->error_class, &wp_data->error_code);
+ if (status) {
+ /* All the object names in a device must be unique */
+ if (Device_Valid_Object_Name(&value.type.Character_String,
+ &object_type, &object_instance)) {
+ if ((object_type == wp_data->object_type) &&
+ (object_instance == wp_data->object_instance)) {
+ /* writing same name to same object */
+ status = true;
+ } else {
+ status = false;
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_DUPLICATE_NAME;
+ }
+ } else {
+ Device_Set_Object_Name(&value.type.Character_String);
+ }
+ }
+ break;
+ case PROP_LOCATION:
+ status =
+ WPValidateString(&value, MAX_DEV_LOC_LEN, true,
+ &wp_data->error_class, &wp_data->error_code);
+ if (status) {
+ Device_Set_Location(characterstring_value(&value.
+ type.Character_String),
+ characterstring_length(&value.type.Character_String));
+ }
+ break;
+
+ case PROP_DESCRIPTION:
+ status =
+ WPValidateString(&value, MAX_DEV_DESC_LEN, true,
+ &wp_data->error_class, &wp_data->error_code);
+ if (status) {
+ Device_Set_Description(characterstring_value(&value.
+ type.Character_String),
+ characterstring_length(&value.type.Character_String));
+ }
+ break;
+ case PROP_MODEL_NAME:
+ status =
+ WPValidateString(&value, MAX_DEV_MOD_LEN, true,
+ &wp_data->error_class, &wp_data->error_code);
+ if (status) {
+ Device_Set_Model_Name(characterstring_value(&value.
+ type.Character_String),
+ characterstring_length(&value.type.Character_String));
+ }
+ break;
+
+ case PROP_OBJECT_TYPE:
+ case PROP_SYSTEM_STATUS:
+ case PROP_VENDOR_NAME:
+ case PROP_FIRMWARE_REVISION:
+ case PROP_APPLICATION_SOFTWARE_VERSION:
+ case PROP_LOCAL_TIME:
+ case PROP_UTC_OFFSET:
+ case PROP_LOCAL_DATE:
+ case PROP_DAYLIGHT_SAVINGS_STATUS:
+ case PROP_PROTOCOL_VERSION:
+ case PROP_PROTOCOL_REVISION:
+ case PROP_PROTOCOL_SERVICES_SUPPORTED:
+ case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED:
+ case PROP_OBJECT_LIST:
+ case PROP_MAX_APDU_LENGTH_ACCEPTED:
+ case PROP_SEGMENTATION_SUPPORTED:
+ case PROP_DEVICE_ADDRESS_BINDING:
+ case PROP_DATABASE_REVISION:
+// case PROP_PROPERTY_LIST:
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
+ break;
+ default:
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
+ break;
+ }
+
+ return status;
+}
+
+/** Looks up the requested Object and Property, and set the new Value in it,
+ * if allowed.
+ * If the Object or Property can't be found, sets the error class and code.
+ *
+ * param: wp_data [in,out] Structure with the desired Object and Property info
+ * and new Value on entry, and APDU message on return.
+ * return: True on success, else False if there is an error.
+ */
+bool Device_Write_Property(
+ BACNET_WRITE_PROPERTY_DATA * wp_data)
+{
+ bool status = false; /* Ever the pessamist! */
+ struct object_functions *pObject = NULL;
+
+ /* initialize the default return values */
+ wp_data->error_class = ERROR_CLASS_OBJECT;
+ wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT;
+ pObject = Device_Objects_Find_Functions(wp_data->object_type);
+ if (pObject != NULL) {
+ if (pObject->Object_Valid_Instance &&
+ pObject->Object_Valid_Instance(wp_data->object_instance)) {
+ if (pObject->Object_Write_Property) {
+ status = pObject->Object_Write_Property(wp_data);
+ } else {
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
+ }
+ } else {
+ wp_data->error_class = ERROR_CLASS_OBJECT;
+ wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT;
+ }
+ } else {
+ wp_data->error_class = ERROR_CLASS_OBJECT;
+ wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT;
+ }
+
+ return (status);
+}
+
+/** Looks up the requested Object, and fills the Property Value list.
+ * If the Object or Property can't be found, returns false.
+ * param: [in] The object type to be looked up.
+ * param: [in] The object instance number to be looked up.
+ * param: [out] The value list
+ * return: True if the object instance supports this feature and value changed.
+ */
+bool Device_Encode_Value_List(
+ BACNET_OBJECT_TYPE object_type,
+ uint32_t object_instance,
+ BACNET_PROPERTY_VALUE * value_list)
+{
+ bool status = false; /* Ever the pessamist! */
+ struct object_functions *pObject = NULL;
+
+ pObject = Device_Objects_Find_Functions(object_type);
+ if (pObject != NULL) {
+ if (pObject->Object_Valid_Instance &&
+ pObject->Object_Valid_Instance(object_instance)) {
+ if (pObject->Object_Value_List) {
+ status =
+ pObject->Object_Value_List(object_instance, value_list);
+ }
+ }
+ }
+
+ return (status);
+}
+
+/** Checks the COV flag in the requested Object
+ * param: [in] The object type to be looked up.
+ * param: [in] The object instance to be looked up.
+ * return: True if the COV flag is set
+ */
+bool Device_COV(
+ BACNET_OBJECT_TYPE object_type,
+ uint32_t object_instance)
+{
+ bool status = false; /* Ever the pessamist! */
+ struct object_functions *pObject = NULL;
+
+ pObject = Device_Objects_Find_Functions(object_type);
+ if (pObject != NULL) {
+ if (pObject->Object_Valid_Instance &&
+ pObject->Object_Valid_Instance(object_instance)) {
+ if (pObject->Object_COV) {
+ status = pObject->Object_COV(object_instance);
+ }
+ }
+ }
+
+ return (status);
+}
+
+/** Clears the COV flag in the requested Object
+ * param: [in] The object type to be looked up.
+ * param: [in] The object instance to be looked up.
+ */
+void Device_COV_Clear(
+ BACNET_OBJECT_TYPE object_type,
+ uint32_t object_instance)
+{
+ struct object_functions *pObject = NULL;
+
+ pObject = Device_Objects_Find_Functions(object_type);
+ if (pObject != NULL) {
+ if (pObject->Object_Valid_Instance &&
+ pObject->Object_Valid_Instance(object_instance)) {
+ if (pObject->Object_COV_Clear) {
+ pObject->Object_COV_Clear(object_instance);
+ }
+ }
+ }
+}
+
+
+/** Looks up the requested Object to see if the functionality is supported.
+ * param: [in] The object type to be looked up.
+ * return: True if the object instance supports this feature.
+ */
+bool Device_Value_List_Supported(
+ BACNET_OBJECT_TYPE object_type)
+{
+ bool status = false; /* Ever the pessamist! */
+ struct object_functions *pObject = NULL;
+
+ pObject = Device_Objects_Find_Functions(object_type);
+ if (pObject != NULL) {
+ if (pObject->Object_Value_List) {
+ status = true;
+ }
+ }
+
+ return (status);
+}
+
+/** Initialize the Device Object.
+ Initialize the group of object helper functions for any supported Object.
+ Initialize each of the Device Object child Object instances.
+ * param: The BACnet Object Name of the bacnet server
+ */
+void Device_Init(
+ const char * Device_Object_Name)
+{
+ struct object_functions *pObject = NULL;
+
+ /* initialize the Device_Properties_List array */
+ int len = 0;
+ len += BACnet_Init_Properties_List(Device_Properties_List + len,
+ Device_Properties_Required);
+ len += BACnet_Init_Properties_List(Device_Properties_List + len,
+ Device_Properties_Optional);
+ len += BACnet_Init_Properties_List(Device_Properties_List + len,
+ Device_Properties_Proprietary);
+
+ characterstring_init_ansi(&My_Object_Name, Device_Object_Name);
+ Object_Table = &My_Object_Table[0]; // sets glogbal variable!
+ pObject = Object_Table;
+ while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) {
+ if (pObject->Object_Init) {
+ pObject->Object_Init();
+ }
+ pObject++;
+ }
+}
+
+bool DeviceGetRRInfo(
+ BACNET_READ_RANGE_DATA * pRequest, /* Info on the request */
+ RR_PROP_INFO * pInfo)
+{ /* Where to put the response */
+ bool status = false; /* return value */
+
+ switch (pRequest->object_property) {
+ case PROP_VT_CLASSES_SUPPORTED:
+ case PROP_ACTIVE_VT_SESSIONS:
+ case PROP_LIST_OF_SESSION_KEYS:
+ case PROP_TIME_SYNCHRONIZATION_RECIPIENTS:
+ case PROP_MANUAL_SLAVE_ADDRESS_BINDING:
+ case PROP_SLAVE_ADDRESS_BINDING:
+ case PROP_RESTART_NOTIFICATION_RECIPIENTS:
+ case PROP_UTC_TIME_SYNCHRONIZATION_RECIPIENTS:
+ pInfo->RequestTypes = RR_BY_POSITION;
+ pRequest->error_class = ERROR_CLASS_PROPERTY;
+ pRequest->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
+ break;
+
+ case PROP_DEVICE_ADDRESS_BINDING:
+ pInfo->RequestTypes = RR_BY_POSITION;
+ pInfo->Handler = rr_address_list_encode;
+ status = true;
+ break;
+
+ default:
+ pRequest->error_class = ERROR_CLASS_SERVICES;
+ pRequest->error_code = ERROR_CODE_PROPERTY_IS_NOT_A_LIST;
+ break;
+ }
+
+ return status;
+}
+
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bacnet/runtime/device.h Fri Jun 08 13:28:00 2018 +0200
@@ -0,0 +1,369 @@
+/**************************************************************************
+*
+* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
+* Copyright (C) 2017 Mario de Sousa <msousa@fe.up.pt>
+*
+* Permission is hereby granted, free of charge, to any person obtaining
+* a copy of this software and associated documentation files (the
+* "Software"), to deal in the Software without restriction, including
+* without limitation the rights to use, copy, modify, merge, publish,
+* distribute, sublicense, and/or sell copies of the Software, and to
+* permit persons to whom the Software is furnished to do so, subject to
+* the following conditions:
+*
+* The above copyright notice and this permission notice shall be included
+* in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*
+*********************************************************************/
+
+/** device.h Defines functions for handling all BACnet objects belonging
+ * to a BACnet device, as well as Device-specific properties. */
+
+#ifndef DEVICE_H
+#define DEVICE_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include "bacdef.h"
+#include "bacenum.h"
+#include "wp.h"
+#include "rd.h"
+#include "rp.h"
+#include "rpm.h"
+#include "readrange.h"
+
+/** Called so a BACnet object can perform any necessary initialization.
+ */
+typedef void (
+ *object_init_function) (
+ void);
+
+/** Counts the number of objects of this type.
+ * return: Count of implemented objects of this type.
+ */
+typedef unsigned (
+ *object_count_function) (
+ void);
+
+/** Maps an object index position to its corresponding BACnet object instance number.
+ * param: index [in] The index of the object, in the array of objects of its type.
+ * return: The BACnet object instance number to be used in a BACNET_OBJECT_ID.
+ */
+typedef uint32_t(
+ *object_index_to_instance_function)
+ (
+ unsigned index);
+
+/** Provides the BACnet Object_Name for a given object instance of this type.
+ * param: object_instance [in] The object instance number to be looked up.
+ * param: object_name [in,out] Pointer to a character_string structure that
+ * will hold a copy of the object name if this is a valid object_instance.
+ * return: True if the object_instance is valid and object_name has been
+ * filled with a copy of the Object's name.
+ */
+typedef bool(
+ *object_name_function)
+ (
+ uint32_t object_instance,
+ BACNET_CHARACTER_STRING * object_name);
+
+/** Look in the table of objects of this type, and see if this is a valid
+ * instance number.
+ * param: [in] The object instance number to be looked up.
+ * return: True if the object instance refers to a valid object of this type.
+ */
+typedef bool(
+ *object_valid_instance_function) (
+ uint32_t object_instance);
+
+/** Helper function to step through an array of objects and find either the
+ * first one or the next one of a given type. Used to step through an array
+ * of objects which is not necessarily contiguious for each type i.e. the
+ * index for the 'n'th object of a given type is not necessarily 'n'.
+ * param: [in] The index of the current object or a value of ~0 to indicate
+ * start at the beginning.
+ * return: The index of the next object of the required type or ~0 (all bits
+ * == 1) to indicate no more objects found.
+ */
+typedef unsigned (
+ *object_iterate_function) (
+ unsigned current_index);
+
+/** Look in the table of objects of this type, and get the COV Value List.
+ * param: [in] The object instance number to be looked up.
+ * param: [out] The value list
+ * return: True if the object instance supports this feature, and has changed.
+ */
+typedef bool(
+ *object_value_list_function) (
+ uint32_t object_instance,
+ BACNET_PROPERTY_VALUE * value_list);
+
+/** Look in the table of objects for this instance to see if value changed.
+ * param: [in] The object instance number to be looked up.
+ * return: True if the object instance has changed.
+ */
+typedef bool(
+ *object_cov_function) (
+ uint32_t object_instance);
+
+/** Look in the table of objects for this instance to clear the changed flag.
+ * param: [in] The object instance number to be looked up.
+ */
+typedef void (
+ *object_cov_clear_function) (
+ uint32_t object_instance);
+
+/** Intrinsic Reporting funcionality.
+ * param: [in] Object instance.
+ */
+typedef void (
+ *object_intrinsic_reporting_function) (
+ uint32_t object_instance);
+
+
+/** Defines the group of object helper functions for any supported Object.
+ * Each Object must provide some implementation of each of these helpers
+ * in order to properly support the handlers. Eg, the ReadProperty handler
+ * handler_read_property() relies on the instance of Object_Read_Property
+ * for each Object type, or configure the function as NULL.
+ * In both appearance and operation, this group of functions acts like
+ * they are member functions of a C++ Object base class.
+ */
+typedef struct object_functions {
+ BACNET_OBJECT_TYPE Object_Type;
+ object_init_function Object_Init;
+ object_count_function Object_Count;
+ object_index_to_instance_function Object_Index_To_Instance;
+ object_valid_instance_function Object_Valid_Instance;
+ object_name_function Object_Name;
+ read_property_function Object_Read_Property;
+ write_property_function Object_Write_Property;
+ rpm_property_lists_function Object_RPM_List;
+ rr_info_function Object_RR_Info;
+ object_iterate_function Object_Iterator;
+ object_value_list_function Object_Value_List;
+ object_cov_function Object_COV;
+ object_cov_clear_function Object_COV_Clear;
+ object_intrinsic_reporting_function Object_Intrinsic_Reporting;
+} object_functions_t;
+
+/* String Lengths for Device Object properties that may be changed
+ * (written to) over the BACnet network.
+ * Maximum sizes excluding nul terminator .
+ */
+#define STRLEN_X(minlen, str) (((minlen)>sizeof(str))?(minlen):sizeof(str))
+#define MAX_DEV_NAME_LEN STRLEN_X(32, "%(BACnet_Device_Name)s") /* Device name */
+#define MAX_DEV_LOC_LEN STRLEN_X(64, BACNET_DEVICE_LOCATION) /* Device location */
+#define MAX_DEV_MOD_LEN STRLEN_X(32, BACNET_DEVICE_MODEL_NAME) /* Device model name */
+#define MAX_DEV_VER_LEN STRLEN_X(16, BACNET_DEVICE_APPSOFT_VER) /* Device application software version */
+#define MAX_DEV_DESC_LEN STRLEN_X(64, BACNET_DEVICE_DESCRIPTION) /* Device description */
+
+/** Structure to define the Object Properties common to all Objects. */
+typedef struct commonBacObj_s {
+
+ /** The BACnet type of this object (ie, what class is this object from?).
+ * This property, of type BACnetObjectType, indicates membership in a
+ * particular object type class. Each inherited class will be of one type.
+ */
+ BACNET_OBJECT_TYPE mObject_Type;
+
+ /** The instance number for this class instance. */
+ uint32_t Object_Instance_Number;
+
+ /** Object Name; must be unique.
+ * This property, of type CharacterString, shall represent a name for
+ * the object that is unique within the BACnet Device that maintains it.
+ */
+ char Object_Name[MAX_DEV_NAME_LEN];
+
+} COMMON_BAC_OBJECT;
+
+
+/** Structure to define the Properties of Device Objects which distinguish
+ * one instance from another.
+ * This structure only defines fields for properties that are unique to
+ * a given Device object. The rest may be fixed in device.c or hard-coded
+ * into the read-property encoding.
+ * This may be useful for implementations which manage multiple Devices,
+ * eg, a Gateway.
+ */
+typedef struct devObj_s {
+ /** The BACnet Device Address for this device; ->len depends on DLL type. */
+ BACNET_ADDRESS bacDevAddr;
+
+ /** Structure for the Object Properties common to all Objects. */
+ COMMON_BAC_OBJECT bacObj;
+
+ /** Device Description. */
+ char Description[MAX_DEV_DESC_LEN];
+
+ /** The upcounter that shows if the Device ID or object structure has changed. */
+ uint32_t Database_Revision;
+} DEVICE_OBJECT_DATA;
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+ void Device_Init(
+ const char * Device_Object_Name);
+
+ bool Device_Reinitialize(
+ BACNET_REINITIALIZE_DEVICE_DATA * rd_data);
+
+ BACNET_REINITIALIZED_STATE Device_Reinitialized_State(
+ void);
+
+ rr_info_function Device_Objects_RR_Info(
+ BACNET_OBJECT_TYPE object_type);
+
+ void Device_getCurrentDateTime(
+ BACNET_DATE_TIME * DateTime);
+ int32_t Device_UTC_Offset(void);
+ bool Device_Daylight_Savings_Status(void);
+
+ void Device_Property_Lists(
+ const int **pRequired,
+ const int **pOptional,
+ const int **pProprietary);
+ void Device_Objects_Property_List(
+ BACNET_OBJECT_TYPE object_type,
+ struct special_property_list_t *pPropertyList);
+ /* functions to support COV */
+ bool Device_Encode_Value_List(
+ BACNET_OBJECT_TYPE object_type,
+ uint32_t object_instance,
+ BACNET_PROPERTY_VALUE * value_list);
+ bool Device_Value_List_Supported(
+ BACNET_OBJECT_TYPE object_type);
+ bool Device_COV(
+ BACNET_OBJECT_TYPE object_type,
+ uint32_t object_instance);
+ void Device_COV_Clear(
+ BACNET_OBJECT_TYPE object_type,
+ uint32_t object_instance);
+
+ uint32_t Device_Object_Instance_Number(
+ void);
+ bool Device_Set_Object_Instance_Number(
+ uint32_t object_id);
+ bool Device_Valid_Object_Instance_Number(
+ uint32_t object_id);
+ unsigned Device_Object_List_Count(
+ void);
+ bool Device_Object_List_Identifier(
+ unsigned array_index,
+ int *object_type,
+ uint32_t * instance);
+
+ unsigned Device_Count(
+ void);
+ uint32_t Device_Index_To_Instance(
+ unsigned index);
+
+ bool Device_Object_Name(
+ uint32_t object_instance,
+ BACNET_CHARACTER_STRING * object_name);
+ bool Device_Set_Object_Name(
+ BACNET_CHARACTER_STRING * object_name);
+ /* Copy a child object name, given its ID. */
+ bool Device_Object_Name_Copy(
+ BACNET_OBJECT_TYPE object_type,
+ uint32_t object_instance,
+ BACNET_CHARACTER_STRING * object_name);
+ bool Device_Object_Name_ANSI_Init(const char * value);
+
+ BACNET_DEVICE_STATUS Device_System_Status(
+ void);
+ int Device_Set_System_Status(
+ BACNET_DEVICE_STATUS status,
+ bool local);
+
+ const char *Device_Vendor_Name(
+ void);
+
+ uint16_t Device_Vendor_Identifier(
+ void);
+ void Device_Set_Vendor_Identifier(
+ uint16_t vendor_id);
+
+ const char *Device_Model_Name(
+ void);
+ bool Device_Set_Model_Name(
+ const char *name,
+ size_t length);
+
+ const char *Device_Firmware_Revision(
+ void);
+
+ const char *Device_Application_Software_Version(
+ void);
+ bool Device_Set_Application_Software_Version(
+ const char *name,
+ size_t length);
+
+ const char *Device_Description(
+ void);
+ bool Device_Set_Description(
+ const char *name,
+ size_t length);
+
+ const char *Device_Location(
+ void);
+ bool Device_Set_Location(
+ const char *name,
+ size_t length);
+
+ /* some stack-centric constant values - no set methods */
+ uint8_t Device_Protocol_Version(
+ void);
+ uint8_t Device_Protocol_Revision(
+ void);
+ BACNET_SEGMENTATION Device_Segmentation_Supported(
+ void);
+
+ uint32_t Device_Database_Revision(
+ void);
+ void Device_Set_Database_Revision(
+ uint32_t revision);
+ void Device_Inc_Database_Revision(
+ void);
+
+ bool Device_Valid_Object_Name(
+ BACNET_CHARACTER_STRING * object_name,
+ int *object_type,
+ uint32_t * object_instance);
+ bool Device_Valid_Object_Id(
+ int object_type,
+ uint32_t object_instance);
+
+ int Device_Read_Property(
+ BACNET_READ_PROPERTY_DATA * rpdata);
+ bool Device_Write_Property(
+ BACNET_WRITE_PROPERTY_DATA * wp_data);
+
+ bool DeviceGetRRInfo(
+ BACNET_READ_RANGE_DATA * pRequest, /* Info on the request */
+ RR_PROP_INFO * pInfo); /* Where to put the information */
+
+ int Device_Read_Property_Local(
+ BACNET_READ_PROPERTY_DATA * rpdata);
+ bool Device_Write_Property_Local(
+ BACNET_WRITE_PROPERTY_DATA * wp_data);
+
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bacnet/runtime/msi.c Fri Jun 08 13:28:00 2018 +0200
@@ -0,0 +1,556 @@
+/**************************************************************************
+*
+* Copyright (C) 2012 Steve Karg <skarg@users.sourceforge.net>
+* Copyright (C) 2017 Mario de Sousa <msousa@fe.up.pt>
+*
+* Permission is hereby granted, free of charge, to any person obtaining
+* a copy of this software and associated documentation files (the
+* "Software"), to deal in the Software without restriction, including
+* without limitation the rights to use, copy, modify, merge, publish,
+* distribute, sublicense, and/or sell copies of the Software, and to
+* permit persons to whom the Software is furnished to do so, subject to
+* the following conditions:
+*
+* The above copyright notice and this permission notice shall be included
+* in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*
+*********************************************************************/
+
+/* Multi-state Input Objects */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "config_bacnet_for_beremiz_%(locstr)s.h" /* the custom configuration for beremiz pluginh */
+#include "bacdef.h"
+#include "bacdcode.h"
+#include "bacenum.h"
+#include "bacapp.h"
+#include "rp.h"
+#include "wp.h"
+#include "msi_%(locstr)s.h"
+#include "handlers.h"
+
+
+/* initial value for present_value property of each object */
+#define MSI_VALUE_INIT (1)
+
+/* The IEC 61131-3 located variables mapped onto the Multi-state Input objects of BACnet protocol */
+%(MSI_lvars)s
+
+
+/* The array where we keep all the state related to the Multi-state Input Objects */
+#define MAX_MULTISTATE_INPUTS %(MSI_count)s
+static MULTISTATE_INPUT_DESCR MSI_Descr[MAX_MULTISTATE_INPUTS] = {
+%(MSI_param)s
+};
+
+
+
+/* These three arrays are used by the ReadPropertyMultiple handler,
+ * as well as to initialize the XXX_Property_List used by the
+ * Property List (PROP_PROPERTY_LIST) property.
+ */
+static const int Multistate_Input_Properties_Required[] = {
+ /* (1) Currently Supported */
+ /* (2) Required by standard ASHRAE 135-2016 */
+ /*(1)(2) */
+ PROP_OBJECT_IDENTIFIER, /* R R ( 75) */
+ PROP_OBJECT_NAME, /* R R ( 77) */
+ PROP_OBJECT_TYPE, /* R R ( 79) */
+ PROP_PRESENT_VALUE, /* W R ( 85) */
+ PROP_STATUS_FLAGS, /* R R (111) */
+ PROP_EVENT_STATE, /* R R ( 36) */
+ PROP_OUT_OF_SERVICE, /* W R ( 81) */
+ PROP_NUMBER_OF_STATES, /* R R ( 74) */
+// PROP_PROPERTY_LIST, /* R R (371) */
+ -1
+};
+
+static const int Multistate_Input_Properties_Optional[] = {
+ /* (1) Currently Supported */
+ /* (2) Required by standard ASHRAE 135-2016 */
+ /*(1)(2) */
+ PROP_DESCRIPTION, /* R O ( 28) */
+ PROP_STATE_TEXT, /* R O (110) */
+ -1
+};
+
+static const int Multistate_Input_Properties_Proprietary[] = {
+ -1
+};
+
+
+/* This array stores the PROPERTY_LIST which may be read by clients.
+ * End of list is marked by following the last element with the value '-1'
+ *
+ * It is initialized by Multistate_Input_Init() based off the values
+ * stored in Multistate_Input_Properties_Required
+ * Multistate_Input_Properties_Optional
+ * Multistate_Input_Properties_Proprietary
+ */
+/* TODO: Allocate memory for this array with malloc() at startup */
+static int Multistate_Input_Properties_List[64];
+
+
+
+
+
+/********************************************************/
+/** Callback functions. **/
+/** Functions required by BACnet devie implementation. **/
+/********************************************************/
+
+
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+void Multistate_Input_Property_Lists(
+ const int **pRequired,
+ const int **pOptional,
+ const int **pProprietary)
+{
+ if (pRequired)
+ *pRequired = Multistate_Input_Properties_Required;
+ if (pOptional)
+ *pOptional = Multistate_Input_Properties_Optional;
+ if (pProprietary)
+ *pProprietary = Multistate_Input_Properties_Proprietary;
+
+ return;
+}
+
+
+
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+void Multistate_Input_Init(
+ void)
+{
+ unsigned int i, j;
+
+ /* initialize the Multistate_Input_Properties_List array */
+ int len = 0;
+ len += BACnet_Init_Properties_List(Multistate_Input_Properties_List + len,
+ Multistate_Input_Properties_Required);
+ len += BACnet_Init_Properties_List(Multistate_Input_Properties_List + len,
+ Multistate_Input_Properties_Optional);
+ len += BACnet_Init_Properties_List(Multistate_Input_Properties_List + len,
+ Multistate_Input_Properties_Proprietary);
+
+ /* initialize all the analog output priority arrays to NULL */
+ for (i = 0; i < MAX_MULTISTATE_INPUTS; i++) {
+ MSI_Descr[i].Present_Value = MSI_VALUE_INIT;
+ for (j = 0; j < MSI_Descr[i].Number_Of_States; j++) {
+ sprintf(MSI_Descr[i].State_Text[j], "State %%d", j+1);
+ }
+ }
+ return;
+}
+
+
+
+/* validate that the given instance exists */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+bool Multistate_Input_Valid_Instance(
+ uint32_t object_instance)
+{
+ // fprintf(stderr, "BACnet plugin: Multistate_Input_Valid_Instance(obj_ID=%%u) called!\n", object _instance);
+ return (Multistate_Input_Instance_To_Index(object_instance) < MAX_MULTISTATE_INPUTS);
+}
+
+
+/* the number of Multistate Value Objects */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+unsigned Multistate_Input_Count(void) {return MAX_MULTISTATE_INPUTS;}
+
+
+/* returns the instance (i.e. Object Identifier) that correlates to the correct index */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+uint32_t Multistate_Input_Index_To_Instance(unsigned index) {return MSI_Descr[index].Object_Identifier;}
+
+
+
+
+
+
+/* returns the index that correlates to the correct instance number (Object Identifier) */
+unsigned Multistate_Input_Instance_To_Index(
+ uint32_t object_instance)
+{
+ unsigned index = 0;
+
+ for (index = 0; index < MAX_MULTISTATE_INPUTS; index++)
+ if (object_instance == MSI_Descr[index].Object_Identifier)
+ return index;
+
+ /* error, this object ID is not in our list! */
+ return MAX_MULTISTATE_INPUTS;
+}
+
+
+
+
+
+uint32_t Multistate_Input_Present_Value(
+ uint32_t object_instance)
+{
+ uint32_t value = MSI_VALUE_INIT;
+ unsigned index = 0; /* offset from instance lookup */
+
+ index = Multistate_Input_Instance_To_Index(object_instance);
+ if (index < MAX_MULTISTATE_INPUTS)
+ value = MSI_Descr[index].Present_Value;
+
+ return value;
+}
+
+
+
+bool Multistate_Input_Present_Value_Set(
+ uint32_t object_instance,
+ uint32_t value)
+{
+ unsigned index = 0; /* offset from instance lookup */
+
+ index = Multistate_Input_Instance_To_Index(object_instance);
+ if (index >= MAX_MULTISTATE_INPUTS)
+ return false;
+
+ if ((value == 0) || (value > MSI_Descr[index].Number_Of_States))
+ return false;
+
+ MSI_Descr[index].Present_Value = (uint8_t) value;
+ return true;
+}
+
+
+
+
+bool Multistate_Input_Out_Of_Service(
+ uint32_t object_instance)
+{
+ bool value = false;
+ unsigned index = 0;
+
+ index = Multistate_Input_Instance_To_Index(object_instance);
+ if (index < MAX_MULTISTATE_INPUTS) {
+ value = MSI_Descr[index].Out_Of_Service;
+ }
+
+ return value;
+}
+
+
+
+void Multistate_Input_Out_Of_Service_Set(
+ uint32_t object_instance,
+ bool value)
+{
+ unsigned index = 0;
+
+ index = Multistate_Input_Instance_To_Index(object_instance);
+ if (index < MAX_MULTISTATE_INPUTS) {
+ MSI_Descr[index].Out_Of_Service = value;
+ }
+
+ return;
+}
+
+
+
+static char *Multistate_Input_Description(
+ uint32_t object_instance)
+{
+ unsigned index = 0; /* offset from instance lookup */
+ char *pName = NULL; /* return value */
+
+ index = Multistate_Input_Instance_To_Index(object_instance);
+ if (index < MAX_MULTISTATE_INPUTS) {
+ pName = MSI_Descr[index].Description;
+ }
+
+ return pName;
+}
+
+
+
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+bool Multistate_Input_Object_Name(
+ uint32_t object_instance,
+ BACNET_CHARACTER_STRING * object_name)
+{
+ unsigned index = 0; /* offset from instance lookup */
+ bool status = false;
+
+ index = Multistate_Input_Instance_To_Index(object_instance);
+ if (index < MAX_MULTISTATE_INPUTS) {
+ status = characterstring_init_ansi(object_name, MSI_Descr[index].Object_Name);
+ }
+
+ return status;
+}
+
+
+
+/* return apdu len, or BACNET_STATUS_ERROR on error */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+int Multistate_Input_Read_Property(
+ BACNET_READ_PROPERTY_DATA * rpdata)
+{
+ int len = 0;
+ int apdu_len = 0; /* return value */
+ BACNET_BIT_STRING bit_string;
+ BACNET_CHARACTER_STRING char_string;
+ uint32_t present_value = 0;
+ unsigned object_index = 0;
+ unsigned i = 0;
+ bool state = false;
+ uint8_t *apdu = NULL;
+
+ if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
+ (rpdata->application_data_len == 0)) {
+ return 0;
+ }
+
+ object_index = Multistate_Input_Instance_To_Index(rpdata->object_instance);
+ if (object_index >= MAX_MULTISTATE_INPUTS) {
+ rpdata->error_class = ERROR_CLASS_OBJECT;
+ rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT;
+ return BACNET_STATUS_ERROR;
+ }
+
+ apdu = rpdata->application_data;
+ switch (rpdata->object_property) {
+ case PROP_OBJECT_IDENTIFIER:
+ apdu_len =
+ encode_application_object_id(&apdu[0],
+ OBJECT_MULTI_STATE_INPUT, rpdata->object_instance);
+ break;
+ case PROP_OBJECT_NAME:
+ Multistate_Input_Object_Name(rpdata->object_instance,
+ &char_string);
+ apdu_len =
+ encode_application_character_string(&apdu[0], &char_string);
+ break;
+ case PROP_DESCRIPTION:
+ characterstring_init_ansi(&char_string,
+ Multistate_Input_Description(rpdata->object_instance));
+ apdu_len =
+ encode_application_character_string(&apdu[0], &char_string);
+ break;
+ case PROP_OBJECT_TYPE:
+ apdu_len =
+ encode_application_enumerated(&apdu[0],
+ OBJECT_MULTI_STATE_INPUT);
+ break;
+ case PROP_PRESENT_VALUE:
+ present_value =
+ Multistate_Input_Present_Value(rpdata->object_instance);
+ apdu_len = encode_application_unsigned(&apdu[0], present_value);
+ break;
+ case PROP_STATUS_FLAGS:
+ /* note: see the details in the standard on how to use these */
+ bitstring_init(&bit_string);
+ bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false);
+ bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false);
+ bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false);
+ if (Multistate_Input_Out_Of_Service(rpdata->object_instance)) {
+ bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE,
+ true);
+ } else {
+ bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE,
+ false);
+ }
+ apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
+ break;
+ case PROP_EVENT_STATE:
+ /* note: see the details in the standard on how to use this */
+ apdu_len =
+ encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL);
+ break;
+ case PROP_OUT_OF_SERVICE:
+ state = MSI_Descr[object_index].Out_Of_Service;
+ apdu_len = encode_application_boolean(&apdu[0], state);
+ break;
+ case PROP_NUMBER_OF_STATES:
+ apdu_len =
+ encode_application_unsigned(&apdu[apdu_len],
+ MSI_Descr[object_index].Number_Of_States);
+ break;
+ case PROP_STATE_TEXT:
+ BACnet_encode_array(MSI_Descr[object_index].State_Text,
+ MSI_Descr[object_index].Number_Of_States,
+ retfalse, BACnet_encode_character_string);
+ break;
+// case PROP_PROPERTY_LIST:
+// BACnet_encode_array(Multistate_Input_Properties_List,
+// property_list_count(Multistate_Input_Properties_List),
+// retfalse, encode_application_enumerated);
+// break;
+ default:
+ rpdata->error_class = ERROR_CLASS_PROPERTY;
+ rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
+ apdu_len = BACNET_STATUS_ERROR;
+ break;
+ }
+ /* only array properties can have array options */
+ if ((apdu_len >= 0) && (rpdata->object_property != PROP_STATE_TEXT) &&
+// (rpdata->object_property != PROP_PROPERTY_LIST) &&
+ (rpdata->array_index != BACNET_ARRAY_ALL)) {
+ rpdata->error_class = ERROR_CLASS_PROPERTY;
+ rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
+ apdu_len = BACNET_STATUS_ERROR;
+ }
+
+ return apdu_len;
+}
+
+/* returns true if successful */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+bool Multistate_Input_Write_Property(
+ BACNET_WRITE_PROPERTY_DATA * wp_data)
+{
+ bool status = false; /* return value */
+ int len = 0;
+ unsigned object_index = 0;
+ BACNET_APPLICATION_DATA_VALUE value;
+
+ /* decode the some of the request */
+ len =
+ bacapp_decode_application_data(wp_data->application_data,
+ wp_data->application_data_len, &value);
+ /* FIXME: len < application_data_len: more data? */
+ if (len < 0) {
+ /* error while decoding - a value larger than we can handle */
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
+ return false;
+ }
+ if ((wp_data->object_property != PROP_STATE_TEXT) &&
+ (wp_data->array_index != BACNET_ARRAY_ALL)) {
+ /* only array properties can have array options */
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
+ return false;
+ }
+ /* No need to check whether object_index is within bounds.
+ * Has already been checked before Multistate_Input_Write_Property() is called
+ */
+ object_index = Multistate_Input_Instance_To_Index(wp_data->object_instance);
+
+ switch (wp_data->object_property) {
+ case PROP_PRESENT_VALUE:
+ status =
+ WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT,
+ &wp_data->error_class, &wp_data->error_code);
+ if (!status) {
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
+ status = false; // not really necessary here.
+ } else {
+ if (!MSI_Descr[object_index].Out_Of_Service) {
+ /* input objects can only be written to when Out_Of_Service is true! */
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
+ status = false;
+ } else {
+ status =
+ Multistate_Input_Present_Value_Set
+ (wp_data->object_instance, value.type.Unsigned_Int);
+ if (!status) {
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
+ status = false; // not really necessary here.
+ }
+ }
+ }
+ break;
+ case PROP_OUT_OF_SERVICE:
+ {
+ bool Previous_Out_Of_Service = MSI_Descr[object_index].Out_Of_Service;
+ status =
+ WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN,
+ &wp_data->error_class, &wp_data->error_code);
+ if (status) {
+ MSI_Descr[object_index].Out_Of_Service = value.type.Boolean;
+ if (Previous_Out_Of_Service && !MSI_Descr[object_index].Out_Of_Service)
+ /* We have just changed from Out_of_Service -> In Service */
+ /* We need to update the Present_Value to the value
+ * currently in the PLC...
+ */
+ MSI_Descr[object_index].Present_Value =
+ *(MSI_Descr[object_index].Located_Var_ptr);
+ }
+ break;
+ }
+ case PROP_OBJECT_IDENTIFIER:
+ case PROP_OBJECT_NAME:
+ case PROP_OBJECT_TYPE:
+ case PROP_STATUS_FLAGS:
+ case PROP_EVENT_STATE:
+ case PROP_NUMBER_OF_STATES:
+ case PROP_DESCRIPTION:
+ case PROP_STATE_TEXT:
+// case PROP_PROPERTY_LIST:
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
+ break;
+ default:
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
+ break;
+ }
+
+ return status;
+}
+
+
+
+
+
+/********************************************/
+/** Functions required for Beremiz plugin **/
+/********************************************/
+
+
+
+void Multistate_Input_Copy_Present_Value_to_Located_Var(void) {
+ unsigned i;
+ for (i = 0; i < MAX_MULTISTATE_INPUTS; i++) {
+ // decouple PLC's Located Variable from Bacnet Object's Present Value if Out_Of_Service is true
+ if (MSI_Descr[i].Out_Of_Service)
+ continue;
+
+ // copy the value
+ *(MSI_Descr[i].Located_Var_ptr) = Multistate_Input_Present_Value(MSI_Descr[i].Object_Identifier);
+ }
+}
+
+
+
+void Multistate_Input_Copy_Located_Var_to_Present_Value(void) {
+ unsigned i;
+ for (i = 0; i < MAX_MULTISTATE_INPUTS; i++) {
+ // decouple PLC's Located Variable from Bacnet Object's Present Value if Out_Of_Service is true
+ if (MSI_Descr[i].Out_Of_Service)
+ continue;
+
+ // Make sure local device does not set Present_Value to 0, nor higher than Number_Of_States
+ if ((*(MSI_Descr[i].Located_Var_ptr) > MSI_Descr[i].Number_Of_States) ||
+ (*(MSI_Descr[i].Located_Var_ptr) == 0))
+ continue;
+
+ // Everything seems to be OK. Copy the value
+ MSI_Descr[i].Present_Value = *(MSI_Descr[i].Located_Var_ptr);
+ }
+}
+
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bacnet/runtime/msi.h Fri Jun 08 13:28:00 2018 +0200
@@ -0,0 +1,109 @@
+/**************************************************************************
+*
+* Copyright (C) 2012 Steve Karg <skarg@users.sourceforge.net>
+* Copyright (C) 2017 Mario de Sousa <msousa@fe.up.pt>
+*
+* Permission is hereby granted, free of charge, to any person obtaining
+* a copy of this software and associated documentation files (the
+* "Software"), to deal in the Software without restriction, including
+* without limitation the rights to use, copy, modify, merge, publish,
+* distribute, sublicense, and/or sell copies of the Software, and to
+* permit persons to whom the Software is furnished to do so, subject to
+* the following conditions:
+*
+* The above copyright notice and this permission notice shall be included
+* in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*
+*********************************************************************/
+#ifndef MULTISTATE_INPUT_H
+#define MULTISTATE_INPUT_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include "bacdef.h"
+#include "bacerror.h"
+#include "rp.h"
+#include "wp.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+/* MultiState Inputs are encoded in unsigned integer
+ * (in BACnet => uint8_t), and can not be 0.
+ * See ASHRAE 135-2016, section 12.20.4
+ */
+#define MULTISTATE_MAX_NUMBER_OF_STATES (255)
+
+
+ typedef struct Multistate_Input_descr {
+ /* pointer to IEC 61131-3 located variable that is mapped onto this BACnet object */
+ uint8_t *Located_Var_ptr;
+ uint32_t Object_Identifier; /* called object 'Instance' in the source code! */
+ char *Object_Name;
+ char *Description;
+ uint8_t Number_Of_States;
+
+ /* stores the current value */
+ uint8_t Present_Value;
+ /* Writable out-of-service allows others to manipulate our Present Value */
+ bool Out_Of_Service;
+ char State_Text[MULTISTATE_MAX_NUMBER_OF_STATES][64];
+ } MULTISTATE_INPUT_DESCR;
+
+
+ void Multistate_Input_Property_Lists(
+ const int **pRequired,
+ const int **pOptional,
+ const int **pProprietary);
+
+ bool Multistate_Input_Valid_Instance(
+ uint32_t object_instance);
+ unsigned Multistate_Input_Count(
+ void);
+ uint32_t Multistate_Input_Index_To_Instance(
+ unsigned index);
+ unsigned Multistate_Input_Instance_To_Index(
+ uint32_t instance);
+
+ int Multistate_Input_Read_Property(
+ BACNET_READ_PROPERTY_DATA * rpdata);
+
+ bool Multistate_Input_Write_Property(
+ BACNET_WRITE_PROPERTY_DATA * wp_data);
+
+ /* optional API */
+ bool Multistate_Input_Object_Name(
+ uint32_t object_instance,
+ BACNET_CHARACTER_STRING * object_name);
+
+ uint32_t Multistate_Input_Present_Value(
+ uint32_t object_instance);
+ bool Multistate_Input_Present_Value_Set(
+ uint32_t object_instance,
+ uint32_t value);
+
+ bool Multistate_Input_Out_Of_Service(
+ uint32_t object_instance);
+ void Multistate_Input_Out_Of_Service_Set(
+ uint32_t object_instance,
+ bool value);
+
+ void Multistate_Input_Init(
+ void);
+
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bacnet/runtime/mso.c Fri Jun 08 13:28:00 2018 +0200
@@ -0,0 +1,668 @@
+/**************************************************************************
+*
+* Copyright (C) 2012 Steve Karg <skarg@users.sourceforge.net>
+* Copyright (C) 2017 Mario de Sousa <msousa@fe.up.pt>
+*
+* Permission is hereby granted, free of charge, to any person obtaining
+* a copy of this software and associated documentation files (the
+* "Software"), to deal in the Software without restriction, including
+* without limitation the rights to use, copy, modify, merge, publish,
+* distribute, sublicense, and/or sell copies of the Software, and to
+* permit persons to whom the Software is furnished to do so, subject to
+* the following conditions:
+*
+* The above copyright notice and this permission notice shall be included
+* in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*
+*********************************************************************/
+
+/* Multi-state Output Objects */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "config_bacnet_for_beremiz_%(locstr)s.h" /* the custom configuration for beremiz pluginh */
+#include "bacdef.h"
+#include "bacdcode.h"
+#include "bacenum.h"
+#include "bacapp.h"
+#include "rp.h"
+#include "wp.h"
+#include "mso_%(locstr)s.h"
+#include "handlers.h"
+
+/* we choose to have a NULL level in our system represented by */
+/* a particular value. When the priorities are not in use, they */
+/* will be relinquished (i.e. set to the NULL level). */
+/* Since multi state value objects can never take the value 0, we
+ * use 0 as our NULL value.
+ * See ASHRAE 135-2016, section 12.20.4
+ */
+#define MSO_VALUE_NULL (0)
+#define MSO_VALUE_IS_NULL(x) ((x) == MSO_VALUE_NULL)
+/* When all the priorities are level null, the present value returns */
+/* the Relinquish Default value */
+#define MSO_VALUE_RELINQUISH_DEFAULT 1
+
+
+
+/* The IEC 61131-3 located variables mapped onto the Multi-state Output objects of BACnet protocol */
+%(MSO_lvars)s
+
+
+/* The array where we keep all the state related to the Multi-state Output Objects */
+#define MAX_MULTISTATE_OUTPUTS %(MSO_count)s
+static MULTISTATE_OUTPUT_DESCR MSO_Descr[MAX_MULTISTATE_OUTPUTS] = {
+%(MSO_param)s
+};
+
+
+
+/* These three arrays are used by the ReadPropertyMultiple handler,
+ * as well as to initialize the XXX_Property_List used by the
+ * Property List (PROP_PROPERTY_LIST) property.
+ */
+static const int Multistate_Output_Properties_Required[] = {
+ /* (1) Currently Supported */
+ /* (2) Required by standard ASHRAE 135-2016 */
+ /*(1)(2) */
+ PROP_OBJECT_IDENTIFIER, /* R R ( 75) */
+ PROP_OBJECT_NAME, /* R R ( 77) */
+ PROP_OBJECT_TYPE, /* R R ( 79) */
+ PROP_PRESENT_VALUE, /* W W ( 85) */
+ PROP_STATUS_FLAGS, /* R R (111) */
+ PROP_EVENT_STATE, /* R R ( 36) */
+ PROP_OUT_OF_SERVICE, /* W R ( 81) */
+ PROP_NUMBER_OF_STATES, /* R R ( 74) */
+ PROP_PRIORITY_ARRAY, /* R R ( 87) */
+ PROP_RELINQUISH_DEFAULT, /* R R (104) */
+// PROP_PROPERTY_LIST, /* R R (371) */
+// PROP_CURRENT_COMMAND_PRIORITY,/* R R (431) */
+ -1
+};
+
+static const int Multistate_Output_Properties_Optional[] = {
+ /* (1) Currently Supported */
+ /* (2) Required by standard ASHRAE 135-2016 */
+ /*(1)(2) */
+ PROP_DESCRIPTION, /* R O ( 28) */
+ PROP_STATE_TEXT, /* R O (110) */
+ -1
+};
+
+static const int Multistate_Output_Properties_Proprietary[] = {
+ -1
+};
+
+
+/* This array stores the PROPERTY_LIST which may be read by clients.
+ * End of list is marked by following the last element with the value '-1'
+ *
+ * It is initialized by Multistate_Output_Init() based off the values
+ * stored in Multistate_Output_Properties_Required
+ * Multistate_Output_Properties_Optional
+ * Multistate_Output_Properties_Proprietary
+ */
+/* TODO: Allocate memory for this array with malloc() at startup */
+static int Multistate_Output_Properties_List[64];
+
+
+
+
+
+/********************************************************/
+/** Callback functions. **/
+/** Functions required by BACnet devie implementation. **/
+/********************************************************/
+
+
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+void Multistate_Output_Property_Lists(
+ const int **pRequired,
+ const int **pOptional,
+ const int **pProprietary)
+{
+ if (pRequired)
+ *pRequired = Multistate_Output_Properties_Required;
+ if (pOptional)
+ *pOptional = Multistate_Output_Properties_Optional;
+ if (pProprietary)
+ *pProprietary = Multistate_Output_Properties_Proprietary;
+
+ return;
+}
+
+
+
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+void Multistate_Output_Init(
+ void)
+{
+ unsigned int i, j;
+
+ /* initialize the Multistate_Output_Properties_List array */
+ int len = 0;
+ len += BACnet_Init_Properties_List(Multistate_Output_Properties_List + len,
+ Multistate_Output_Properties_Required);
+ len += BACnet_Init_Properties_List(Multistate_Output_Properties_List + len,
+ Multistate_Output_Properties_Optional);
+ len += BACnet_Init_Properties_List(Multistate_Output_Properties_List + len,
+ Multistate_Output_Properties_Proprietary);
+
+ /* initialize all the analog output priority arrays to NULL */
+ for (i = 0; i < MAX_MULTISTATE_OUTPUTS; i++) {
+ for (j = 0; j < BACNET_MAX_PRIORITY; j++) {
+ MSO_Descr[i].Present_Value[j] = MSO_VALUE_NULL;
+ }
+ for (j = 0; j < MSO_Descr[i].Number_Of_States; j++) {
+ sprintf(MSO_Descr[i].State_Text[j], "State %%d", j+1);
+ }
+ }
+ return;
+}
+
+
+
+/* validate that the given instance exists */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+bool Multistate_Output_Valid_Instance(
+ uint32_t object_instance)
+{
+ // fprintf(stderr, "BACnet plugin: Multistate_Output_Valid_Instance(obj_ID=%%u) called!\n", object _instance);
+ return (Multistate_Output_Instance_To_Index(object_instance) < MAX_MULTISTATE_OUTPUTS);
+}
+
+
+/* the number of Multistate Value Objects */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+unsigned Multistate_Output_Count(void) {return MAX_MULTISTATE_OUTPUTS;}
+
+
+/* returns the instance (i.e. Object Identifier) that correlates to the correct index */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+uint32_t Multistate_Output_Index_To_Instance(unsigned index) {return MSO_Descr[index].Object_Identifier;}
+
+
+
+
+
+
+/* returns the index that correlates to the correct instance number (Object Identifier) */
+unsigned Multistate_Output_Instance_To_Index(
+ uint32_t object_instance)
+{
+ unsigned index = 0;
+
+ for (index = 0; index < MAX_MULTISTATE_OUTPUTS; index++)
+ if (object_instance == MSO_Descr[index].Object_Identifier)
+ return index;
+
+ /* error, this object ID is not in our list! */
+ return MAX_MULTISTATE_OUTPUTS;
+}
+
+
+
+
+
+uint32_t Multistate_Output_Present_Value(
+ uint32_t object_instance)
+{
+ uint32_t value = MSO_VALUE_RELINQUISH_DEFAULT;
+ unsigned index = 0; /* offset from instance lookup */
+ unsigned i = 0;
+
+ index = Multistate_Output_Instance_To_Index(object_instance);
+ if (index < MAX_MULTISTATE_OUTPUTS) {
+ for (i = 0; i < BACNET_MAX_PRIORITY; i++) {
+ if (!MSO_VALUE_IS_NULL(MSO_Descr[index].Present_Value[i])) {
+ value = MSO_Descr[index].Present_Value[i];
+ break;
+ }
+ }
+ }
+
+ return value;
+}
+
+
+
+bool Multistate_Output_Present_Value_Set(
+ uint32_t object_instance,
+ uint32_t value,
+ uint8_t priority)
+{
+ unsigned index = 0; /* offset from instance lookup */
+
+ index = Multistate_Output_Instance_To_Index(object_instance);
+ if (index >= MAX_MULTISTATE_OUTPUTS)
+ return false;
+
+ if ((priority == 0) || (priority > BACNET_MAX_PRIORITY) ||
+ (priority == 6 /* reserved, ASHRAE 135-2016, section 19.2.2 */))
+ return false;
+
+ if ((value == 0) || (value > MSO_Descr[index].Number_Of_States))
+ return false;
+
+ priority--;
+ MSO_Descr[index].Present_Value[priority] = (uint8_t) value;
+
+ return true;
+}
+
+
+
+/* returns command priority (1..16), or 0 if all priority values are at NULL */
+int Multistate_Output_Current_Command_Priority(
+ uint32_t object_instance)
+{
+ unsigned index = 0;
+ unsigned i = 0;
+
+ index = Multistate_Output_Instance_To_Index(object_instance);
+ if (index < MAX_MULTISTATE_OUTPUTS) {
+ for (i = 0; i < BACNET_MAX_PRIORITY; i++) {
+ if (!MSO_VALUE_IS_NULL(MSO_Descr[index].Present_Value[i])) {
+ return i+1; // +1 since priority is 1..16, and not 0..15
+ }
+ }
+ }
+ // command values in all priorities are set to NULL
+ return 0;
+}
+
+
+
+
+bool Multistate_Output_Present_Value_Relinquish(
+ uint32_t object_instance,
+ uint8_t priority)
+{
+ unsigned index = 0; /* offset from instance lookup */
+
+ index = Multistate_Output_Instance_To_Index(object_instance);
+ if (index >= MAX_MULTISTATE_OUTPUTS)
+ return false;
+
+ if ((priority == 0) || (priority > BACNET_MAX_PRIORITY) ||
+ (priority == 6 /* reserved, ASHRAE 135-2016, section 19.2.2 */))
+ return false;
+
+ priority--;
+ MSO_Descr[index].Present_Value[priority] = MSO_VALUE_NULL;
+
+ return true;
+}
+
+
+
+bool Multistate_Output_Out_Of_Service(
+ uint32_t object_instance)
+{
+ bool value = false;
+ unsigned index = 0;
+
+ index = Multistate_Output_Instance_To_Index(object_instance);
+ if (index < MAX_MULTISTATE_OUTPUTS) {
+ value = MSO_Descr[index].Out_Of_Service;
+ }
+
+ return value;
+}
+
+
+
+void Multistate_Output_Out_Of_Service_Set(
+ uint32_t object_instance,
+ bool value)
+{
+ unsigned index = 0;
+
+ index = Multistate_Output_Instance_To_Index(object_instance);
+ if (index < MAX_MULTISTATE_OUTPUTS) {
+ MSO_Descr[index].Out_Of_Service = value;
+ }
+
+ return;
+}
+
+
+
+static char *Multistate_Output_Description(
+ uint32_t object_instance)
+{
+ unsigned index = 0; /* offset from instance lookup */
+ char *pName = NULL; /* return value */
+
+ index = Multistate_Output_Instance_To_Index(object_instance);
+ if (index < MAX_MULTISTATE_OUTPUTS) {
+ pName = MSO_Descr[index].Description;
+ }
+
+ return pName;
+}
+
+
+
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+bool Multistate_Output_Object_Name(
+ uint32_t object_instance,
+ BACNET_CHARACTER_STRING * object_name)
+{
+ unsigned index = 0; /* offset from instance lookup */
+ bool status = false;
+
+ index = Multistate_Output_Instance_To_Index(object_instance);
+ if (index < MAX_MULTISTATE_OUTPUTS) {
+ status = characterstring_init_ansi(object_name, MSO_Descr[index].Object_Name);
+ }
+
+ return status;
+}
+
+
+
+/* return apdu len, or BACNET_STATUS_ERROR on error */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+int Multistate_Output_Read_Property(
+ BACNET_READ_PROPERTY_DATA * rpdata)
+{
+ int len = 0;
+ int apdu_len = 0; /* return value */
+ BACNET_BIT_STRING bit_string;
+ BACNET_CHARACTER_STRING char_string;
+ uint32_t present_value = 0;
+ unsigned object_index = 0;
+ unsigned i = 0;
+ bool state = false;
+ uint8_t *apdu = NULL;
+
+ if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
+ (rpdata->application_data_len == 0)) {
+ return 0;
+ }
+
+ object_index = Multistate_Output_Instance_To_Index(rpdata->object_instance);
+ if (object_index >= MAX_MULTISTATE_OUTPUTS) {
+ rpdata->error_class = ERROR_CLASS_OBJECT;
+ rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT;
+ return BACNET_STATUS_ERROR;
+ }
+
+ apdu = rpdata->application_data;
+ switch (rpdata->object_property) {
+ case PROP_OBJECT_IDENTIFIER:
+ apdu_len =
+ encode_application_object_id(&apdu[0],
+ OBJECT_MULTI_STATE_OUTPUT, rpdata->object_instance);
+ break;
+ case PROP_OBJECT_NAME:
+ Multistate_Output_Object_Name(rpdata->object_instance,
+ &char_string);
+ apdu_len =
+ encode_application_character_string(&apdu[0], &char_string);
+ break;
+ case PROP_DESCRIPTION:
+ characterstring_init_ansi(&char_string,
+ Multistate_Output_Description(rpdata->object_instance));
+ apdu_len =
+ encode_application_character_string(&apdu[0], &char_string);
+ break;
+ case PROP_OBJECT_TYPE:
+ apdu_len =
+ encode_application_enumerated(&apdu[0],
+ OBJECT_MULTI_STATE_OUTPUT);
+ break;
+ case PROP_PRESENT_VALUE:
+ present_value =
+ Multistate_Output_Present_Value(rpdata->object_instance);
+ apdu_len = encode_application_unsigned(&apdu[0], present_value);
+ break;
+ case PROP_STATUS_FLAGS:
+ /* note: see the details in the standard on how to use these */
+ bitstring_init(&bit_string);
+ bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false);
+ bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false);
+ bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false);
+ if (Multistate_Output_Out_Of_Service(rpdata->object_instance)) {
+ bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE,
+ true);
+ } else {
+ bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE,
+ false);
+ }
+ apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
+ break;
+ case PROP_EVENT_STATE:
+ /* note: see the details in the standard on how to use this */
+ apdu_len =
+ encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL);
+ break;
+ case PROP_OUT_OF_SERVICE:
+ state = MSO_Descr[object_index].Out_Of_Service;
+ apdu_len = encode_application_boolean(&apdu[0], state);
+ break;
+ case PROP_PRIORITY_ARRAY:
+ BACnet_encode_array(MSO_Descr[object_index].Present_Value,
+ BACNET_MAX_PRIORITY,
+ MSO_VALUE_IS_NULL,
+ encode_application_unsigned);
+ break;
+// case PROP_CURRENT_COMMAND_PRIORITY: {
+// unsigned i = Multistate_Output_Current_Command_Priority(rpdata->object_instance);
+// if (i == 0) apdu_len = encode_application_null (&apdu[0]);
+// else apdu_len = encode_application_unsigned(&apdu[0], i);
+// break;
+// }
+
+ case PROP_RELINQUISH_DEFAULT:
+ present_value = MSO_VALUE_RELINQUISH_DEFAULT;
+ apdu_len = encode_application_unsigned(&apdu[0], present_value);
+ break;
+ case PROP_NUMBER_OF_STATES:
+ apdu_len =
+ encode_application_unsigned(&apdu[apdu_len],
+ MSO_Descr[object_index].Number_Of_States);
+ break;
+ case PROP_STATE_TEXT:
+ BACnet_encode_array(MSO_Descr[object_index].State_Text,
+ MSO_Descr[object_index].Number_Of_States,
+ retfalse, BACnet_encode_character_string);
+ break;
+// case PROP_PROPERTY_LIST:
+// BACnet_encode_array(Multistate_Output_Properties_List,
+// property_list_count(Multistate_Output_Properties_List),
+// retfalse, encode_application_enumerated);
+// break;
+ default:
+ rpdata->error_class = ERROR_CLASS_PROPERTY;
+ rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
+ apdu_len = BACNET_STATUS_ERROR;
+ break;
+ }
+ /* only array properties can have array options */
+ if ((apdu_len >= 0) && (rpdata->object_property != PROP_STATE_TEXT) &&
+ (rpdata->object_property != PROP_PRIORITY_ARRAY) &&
+// (rpdata->object_property != PROP_PROPERTY_LIST) &&
+ (rpdata->array_index != BACNET_ARRAY_ALL)) {
+ rpdata->error_class = ERROR_CLASS_PROPERTY;
+ rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
+ apdu_len = BACNET_STATUS_ERROR;
+ }
+
+ return apdu_len;
+}
+
+/* returns true if successful */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+bool Multistate_Output_Write_Property(
+ BACNET_WRITE_PROPERTY_DATA * wp_data)
+{
+ bool status = false; /* return value */
+ int len = 0;
+ unsigned object_index = 0;
+ BACNET_APPLICATION_DATA_VALUE value;
+
+ /* decode the some of the request */
+ len =
+ bacapp_decode_application_data(wp_data->application_data,
+ wp_data->application_data_len, &value);
+ /* FIXME: len < application_data_len: more data? */
+ if (len < 0) {
+ /* error while decoding - a value larger than we can handle */
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
+ return false;
+ }
+ if ((wp_data->object_property != PROP_STATE_TEXT) &&
+ (wp_data->object_property != PROP_PRIORITY_ARRAY) &&
+ (wp_data->array_index != BACNET_ARRAY_ALL)) {
+ /* only array properties can have array options */
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
+ return false;
+ }
+ /* No need to check whether object_index is within bounds.
+ * Has already been checked before Multistate_Output_Write_Property() is called
+ */
+ object_index = Multistate_Output_Instance_To_Index(wp_data->object_instance);
+
+ switch (wp_data->object_property) {
+ case PROP_PRESENT_VALUE:
+ status =
+ WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT,
+ &wp_data->error_class, &wp_data->error_code);
+ if (status) {
+ status =
+ Multistate_Output_Present_Value_Set
+ (wp_data->object_instance, value.type.Unsigned_Int, wp_data->priority);
+ if (!status) {
+ if (wp_data->priority == 6) {
+ /* Command priority 6 is reserved for use by Minimum On/Off
+ algorithm and may not be used for other purposes in any
+ object. */
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
+ } else {
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
+ }
+ }
+ } else {
+ status =
+ WPValidateArgType(&value, BACNET_APPLICATION_TAG_NULL,
+ &wp_data->error_class, &wp_data->error_code);
+ if (status) {
+ status =
+ Multistate_Output_Present_Value_Relinquish
+ (wp_data->object_instance, wp_data->priority);
+ }
+ if (!status) {
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
+ }
+ }
+ break;
+ case PROP_OUT_OF_SERVICE:
+ {
+ bool Previous_Out_Of_Service = MSO_Descr[object_index].Out_Of_Service;
+ status =
+ WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN,
+ &wp_data->error_class, &wp_data->error_code);
+ if (status) {
+ MSO_Descr[object_index].Out_Of_Service = value.type.Boolean;
+ if (Previous_Out_Of_Service && !MSO_Descr[object_index].Out_Of_Service)
+ /* We have just changed from Out_of_Service -> In Service */
+ /* We need to update the Present_Value to the value
+ * currently in the PLC...
+ */
+ MSO_Descr[object_index].Present_Value[BACNET_MAX_PRIORITY-1] =
+ *(MSO_Descr[object_index].Located_Var_ptr);
+ }
+ break;
+ }
+ case PROP_OBJECT_IDENTIFIER:
+ case PROP_OBJECT_NAME:
+ case PROP_OBJECT_TYPE:
+ case PROP_STATUS_FLAGS:
+ case PROP_EVENT_STATE:
+ case PROP_NUMBER_OF_STATES:
+ case PROP_PRIORITY_ARRAY:
+// case PROP_CURRENT_COMMAND_PRIORITY:
+ case PROP_RELINQUISH_DEFAULT:
+ case PROP_DESCRIPTION:
+ case PROP_STATE_TEXT:
+// case PROP_PROPERTY_LIST:
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
+ break;
+ default:
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
+ break;
+ }
+
+ return status;
+}
+
+
+
+
+
+/********************************************/
+/** Functions required for Beremiz plugin **/
+/********************************************/
+
+
+
+void Multistate_Output_Copy_Present_Value_to_Located_Var(void) {
+ unsigned i;
+ for (i = 0; i < MAX_MULTISTATE_OUTPUTS; i++) {
+ // decouple PLC's Located Variable from Bacnet Object's Present Value if Out_Of_Service is true
+ if (MSO_Descr[i].Out_Of_Service)
+ continue;
+
+ // copy the value
+ *(MSO_Descr[i].Located_Var_ptr) = Multistate_Output_Present_Value(MSO_Descr[i].Object_Identifier);
+ }
+}
+
+
+
+void Multistate_Output_Copy_Located_Var_to_Present_Value(void) {
+ unsigned i;
+ for (i = 0; i < MAX_MULTISTATE_OUTPUTS; i++) {
+ // decouple PLC's Located Variable from Bacnet Object's Present Value if Out_Of_Service is true
+ if (MSO_Descr[i].Out_Of_Service)
+ continue;
+
+ // Make sure local device does not set Present_Value to a value higher than Number_Of_States
+ /* NOTE: The following comparison (Present_Value > Number_Of_States) is OK as the
+ * MSO_VALUE_NULL is currently set to 0. This means that the local controller
+ * can safely relinquish control by setting the Present_Value to 0.
+ * The comparison would not be safe if for some reason we later change
+ * MSO_VALUE_NULL to a value that may be higher than Number_Of_States.
+ */
+ #if MSO_VALUE_NULL != 0
+ #error Implementation assumes that MSO_VALUE_NULL is set to 0, which is currently not the case.
+ #endif
+ if (*(MSO_Descr[i].Located_Var_ptr) > MSO_Descr[i].Number_Of_States)
+ continue;
+
+ // Everything seems to be OK. Copy the value
+ MSO_Descr[i].Present_Value[BACNET_MAX_PRIORITY-1] = *(MSO_Descr[i].Located_Var_ptr);
+ }
+}
+
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bacnet/runtime/mso.h Fri Jun 08 13:28:00 2018 +0200
@@ -0,0 +1,110 @@
+/**************************************************************************
+*
+* Copyright (C) 2012 Steve Karg <skarg@users.sourceforge.net>
+* Copyright (C) 2017 Mario de Sousa <msousa@fe.up.pt>
+*
+* Permission is hereby granted, free of charge, to any person obtaining
+* a copy of this software and associated documentation files (the
+* "Software"), to deal in the Software without restriction, including
+* without limitation the rights to use, copy, modify, merge, publish,
+* distribute, sublicense, and/or sell copies of the Software, and to
+* permit persons to whom the Software is furnished to do so, subject to
+* the following conditions:
+*
+* The above copyright notice and this permission notice shall be included
+* in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*
+*********************************************************************/
+#ifndef MULTISTATE_OUTPUT_H
+#define MULTISTATE_OUTPUT_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include "bacdef.h"
+#include "bacerror.h"
+#include "rp.h"
+#include "wp.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+/* MultiState Outputs are encoded in unsigned integer
+ * (in BACnet => uint8_t), and can not be 0.
+ * See ASHRAE 135-2016, section 12.20.4
+ */
+#define MULTISTATE_MAX_NUMBER_OF_STATES (255)
+
+
+ typedef struct Multistate_Output_descr {
+ /* pointer to IEC 61131-3 located variable that is mapped onto this BACnet object */
+ uint8_t *Located_Var_ptr;
+ uint32_t Object_Identifier; /* called object 'Instance' in the source code! */
+ char *Object_Name;
+ char *Description;
+ uint8_t Number_Of_States;
+
+ /* stores the current value */
+ /* one entry per priority value */
+ uint8_t Present_Value[BACNET_MAX_PRIORITY];
+ /* Writable out-of-service allows others to manipulate our Present Value */
+ bool Out_Of_Service;
+ char State_Text[MULTISTATE_MAX_NUMBER_OF_STATES][64];
+ } MULTISTATE_OUTPUT_DESCR;
+
+
+ void Multistate_Output_Property_Lists(
+ const int **pRequired,
+ const int **pOptional,
+ const int **pProprietary);
+
+ bool Multistate_Output_Valid_Instance(
+ uint32_t object_instance);
+ unsigned Multistate_Output_Count(
+ void);
+ uint32_t Multistate_Output_Index_To_Instance(
+ unsigned index);
+ unsigned Multistate_Output_Instance_To_Index(
+ uint32_t instance);
+
+ int Multistate_Output_Read_Property(
+ BACNET_READ_PROPERTY_DATA * rpdata);
+
+ bool Multistate_Output_Write_Property(
+ BACNET_WRITE_PROPERTY_DATA * wp_data);
+
+ /* optional API */
+ bool Multistate_Output_Object_Name(
+ uint32_t object_instance,
+ BACNET_CHARACTER_STRING * object_name);
+
+ uint32_t Multistate_Output_Present_Value(
+ uint32_t object_instance);
+ bool Multistate_Output_Present_Value_Set(
+ uint32_t object_instance,
+ uint32_t value,
+ uint8_t priority);
+
+ bool Multistate_Output_Out_Of_Service(
+ uint32_t object_instance);
+ void Multistate_Output_Out_Of_Service_Set(
+ uint32_t object_instance,
+ bool value);
+
+ void Multistate_Output_Init(
+ void);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bacnet/runtime/msv.c Fri Jun 08 13:28:00 2018 +0200
@@ -0,0 +1,666 @@
+/**************************************************************************
+*
+* Copyright (C) 2012 Steve Karg <skarg@users.sourceforge.net>
+* Copyright (C) 2017 Mario de Sousa <msousa@fe.up.pt>
+*
+* Permission is hereby granted, free of charge, to any person obtaining
+* a copy of this software and associated documentation files (the
+* "Software"), to deal in the Software without restriction, including
+* without limitation the rights to use, copy, modify, merge, publish,
+* distribute, sublicense, and/or sell copies of the Software, and to
+* permit persons to whom the Software is furnished to do so, subject to
+* the following conditions:
+*
+* The above copyright notice and this permission notice shall be included
+* in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*
+*********************************************************************/
+
+/* Multi-state Value Objects */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "config_bacnet_for_beremiz_%(locstr)s.h" /* the custom configuration for beremiz pluginh */
+#include "bacdef.h"
+#include "bacdcode.h"
+#include "bacenum.h"
+#include "bacapp.h"
+#include "rp.h"
+#include "wp.h"
+#include "msv_%(locstr)s.h"
+#include "handlers.h"
+
+/* we choose to have a NULL level in our system represented by */
+/* a particular value. When the priorities are not in use, they */
+/* will be relinquished (i.e. set to the NULL level). */
+/* Since multi state value objects can never take the value 0, we
+ * use 0 as our NULL value.
+ * See ASHRAE 135-2016, section 12.20.4
+ */
+#define MSV_VALUE_NULL (0)
+#define MSV_VALUE_IS_NULL(x) ((x) == MSV_VALUE_NULL)
+/* When all the priorities are level null, the present value returns */
+/* the Relinquish Default value */
+#define MSV_VALUE_RELINQUISH_DEFAULT 1
+
+
+
+/* The IEC 61131-3 located variables mapped onto the Multi-state Value objects of BACnet protocol */
+%(MSV_lvars)s
+
+
+/* The array where we keep all the state related to the Multi-state Value Objects */
+#define MAX_MULTISTATE_VALUES %(MSV_count)s
+static MULTISTATE_VALUE_DESCR MSV_Descr[MAX_MULTISTATE_VALUES] = {
+%(MSV_param)s
+};
+
+
+
+/* These three arrays are used by the ReadPropertyMultiple handler,
+ * as well as to initialize the XXX_Property_List used by the
+ * Property List (PROP_PROPERTY_LIST) property.
+ */
+static const int Multistate_Value_Properties_Required[] = {
+ /* (1) Currently Supported */
+ /* (2) Required by standard ASHRAE 135-2016 */
+ /*(1)(2) */
+ PROP_OBJECT_IDENTIFIER, /* R R ( 75) */
+ PROP_OBJECT_NAME, /* R R ( 77) */
+ PROP_OBJECT_TYPE, /* R R ( 79) */
+ PROP_PRESENT_VALUE, /* W R ( 85) */
+ PROP_STATUS_FLAGS, /* R R (111) */
+ PROP_EVENT_STATE, /* R R ( 36) */
+ PROP_OUT_OF_SERVICE, /* W R ( 81) */
+ PROP_NUMBER_OF_STATES, /* R R ( 74) */
+// PROP_PROPERTY_LIST, /* R R (371) */
+ -1
+};
+
+static const int Multistate_Value_Properties_Optional[] = {
+ PROP_DESCRIPTION, /* R O ( 28) */
+ PROP_STATE_TEXT, /* R O (110) */
+ /* required if Present_Value is writable (which is true in our case!) */
+ PROP_PRIORITY_ARRAY, /* R O ( 87) */
+ PROP_RELINQUISH_DEFAULT, /* R O (104) */
+// PROP_CURRENT_COMMAND_PRIORITY,/* R O (431) */
+ -1
+};
+
+static const int Multistate_Value_Properties_Proprietary[] = {
+ -1
+};
+
+
+/* This array stores the PROPERTY_LIST which may be read by clients.
+ * End of list is marked by following the last element with the value '-1'
+ *
+ * It is initialized by Multistate_Value_Init() based off the values
+ * stored in Multistate_Value_Properties_Required
+ * Multistate_Value_Properties_Optional
+ * Multistate_Value_Properties_Proprietary
+ */
+/* TODO: Allocate memory for this array with malloc() at startup */
+static int Multistate_Value_Properties_List[64];
+
+
+
+
+
+/********************************************************/
+/** Callback functions. **/
+/** Functions required by BACnet devie implementation. **/
+/********************************************************/
+
+
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+void Multistate_Value_Property_Lists(
+ const int **pRequired,
+ const int **pOptional,
+ const int **pProprietary)
+{
+ if (pRequired)
+ *pRequired = Multistate_Value_Properties_Required;
+ if (pOptional)
+ *pOptional = Multistate_Value_Properties_Optional;
+ if (pProprietary)
+ *pProprietary = Multistate_Value_Properties_Proprietary;
+
+ return;
+}
+
+
+
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+void Multistate_Value_Init(
+ void)
+{
+ unsigned int i, j;
+
+ /* initialize the Multistate_Value_Properties_List array */
+ int len = 0;
+ len += BACnet_Init_Properties_List(Multistate_Value_Properties_List + len,
+ Multistate_Value_Properties_Required);
+ len += BACnet_Init_Properties_List(Multistate_Value_Properties_List + len,
+ Multistate_Value_Properties_Optional);
+ len += BACnet_Init_Properties_List(Multistate_Value_Properties_List + len,
+ Multistate_Value_Properties_Proprietary);
+
+ /* initialize all the analog output priority arrays to NULL */
+ for (i = 0; i < MAX_MULTISTATE_VALUES; i++) {
+ for (j = 0; j < BACNET_MAX_PRIORITY; j++) {
+ MSV_Descr[i].Present_Value[j] = MSV_VALUE_NULL;
+ }
+ for (j = 0; j < MSV_Descr[i].Number_Of_States; j++) {
+ sprintf(MSV_Descr[i].State_Text[j], "State %%d", j+1);
+ }
+ }
+ return;
+}
+
+
+
+/* validate that the given instance exists */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+bool Multistate_Value_Valid_Instance(
+ uint32_t object_instance)
+{
+ // fprintf(stderr, "BACnet plugin: Multistate_Value_Valid_Instance(obj_ID=%%u) called!\n", object _instance);
+ return (Multistate_Value_Instance_To_Index(object_instance) < MAX_MULTISTATE_VALUES);
+}
+
+
+/* the number of Multistate Value Objects */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+unsigned Multistate_Value_Count(void) {return MAX_MULTISTATE_VALUES;}
+
+
+/* returns the instance (i.e. Object Identifier) that correlates to the correct index */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+uint32_t Multistate_Value_Index_To_Instance(unsigned index) {return MSV_Descr[index].Object_Identifier;}
+
+
+
+
+
+
+/* returns the index that correlates to the correct instance number (Object Identifier) */
+unsigned Multistate_Value_Instance_To_Index(
+ uint32_t object_instance)
+{
+ unsigned index = 0;
+
+ for (index = 0; index < MAX_MULTISTATE_VALUES; index++)
+ if (object_instance == MSV_Descr[index].Object_Identifier)
+ return index;
+
+ /* error, this object ID is not in our list! */
+ return MAX_MULTISTATE_VALUES;
+}
+
+
+
+
+
+uint32_t Multistate_Value_Present_Value(
+ uint32_t object_instance)
+{
+ uint32_t value = MSV_VALUE_RELINQUISH_DEFAULT;
+ unsigned index = 0; /* offset from instance lookup */
+ unsigned i = 0;
+
+ index = Multistate_Value_Instance_To_Index(object_instance);
+ if (index < MAX_MULTISTATE_VALUES) {
+ for (i = 0; i < BACNET_MAX_PRIORITY; i++) {
+ if (!MSV_VALUE_IS_NULL(MSV_Descr[index].Present_Value[i])) {
+ value = MSV_Descr[index].Present_Value[i];
+ break;
+ }
+ }
+ }
+
+ return value;
+}
+
+
+
+bool Multistate_Value_Present_Value_Set(
+ uint32_t object_instance,
+ uint32_t value,
+ uint8_t priority)
+{
+ unsigned index = 0; /* offset from instance lookup */
+
+ index = Multistate_Value_Instance_To_Index(object_instance);
+ if (index >= MAX_MULTISTATE_VALUES)
+ return false;
+
+ if ((priority == 0) || (priority > BACNET_MAX_PRIORITY) ||
+ (priority == 6 /* reserved, ASHRAE 135-2016, section 19.2.2 */))
+ return false;
+
+ if ((value == 0) || (value > MSV_Descr[index].Number_Of_States))
+ return false;
+
+ priority--;
+ MSV_Descr[index].Present_Value[priority] = (uint8_t) value;
+
+ return true;
+}
+
+
+
+/* returns command priority (1..16), or 0 if all priority values are at NULL */
+int Multistate_Value_Current_Command_Priority(
+ uint32_t object_instance)
+{
+ unsigned index = 0;
+ unsigned i = 0;
+
+ index = Multistate_Value_Instance_To_Index(object_instance);
+ if (index < MAX_MULTISTATE_VALUES) {
+ for (i = 0; i < BACNET_MAX_PRIORITY; i++) {
+ if (!MSV_VALUE_IS_NULL(MSV_Descr[index].Present_Value[i])) {
+ return i+1; // +1 since priority is 1..16, and not 0..15
+ }
+ }
+ }
+ // command values in all priorities are set to NULL
+ return 0;
+}
+
+
+
+
+bool Multistate_Value_Present_Value_Relinquish(
+ uint32_t object_instance,
+ uint8_t priority)
+{
+ unsigned index = 0; /* offset from instance lookup */
+
+ index = Multistate_Value_Instance_To_Index(object_instance);
+ if (index >= MAX_MULTISTATE_VALUES)
+ return false;
+
+ if ((priority == 0) || (priority > BACNET_MAX_PRIORITY) ||
+ (priority == 6 /* reserved, ASHRAE 135-2016, section 19.2.2 */))
+ return false;
+
+ priority--;
+ MSV_Descr[index].Present_Value[priority] = MSV_VALUE_NULL;
+
+ return true;
+}
+
+
+
+bool Multistate_Value_Out_Of_Service(
+ uint32_t object_instance)
+{
+ bool value = false;
+ unsigned index = 0;
+
+ index = Multistate_Value_Instance_To_Index(object_instance);
+ if (index < MAX_MULTISTATE_VALUES) {
+ value = MSV_Descr[index].Out_Of_Service;
+ }
+
+ return value;
+}
+
+
+
+void Multistate_Value_Out_Of_Service_Set(
+ uint32_t object_instance,
+ bool value)
+{
+ unsigned index = 0;
+
+ index = Multistate_Value_Instance_To_Index(object_instance);
+ if (index < MAX_MULTISTATE_VALUES) {
+ MSV_Descr[index].Out_Of_Service = value;
+ }
+
+ return;
+}
+
+
+
+static char *Multistate_Value_Description(
+ uint32_t object_instance)
+{
+ unsigned index = 0; /* offset from instance lookup */
+ char *pName = NULL; /* return value */
+
+ index = Multistate_Value_Instance_To_Index(object_instance);
+ if (index < MAX_MULTISTATE_VALUES) {
+ pName = MSV_Descr[index].Description;
+ }
+
+ return pName;
+}
+
+
+
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+bool Multistate_Value_Object_Name(
+ uint32_t object_instance,
+ BACNET_CHARACTER_STRING * object_name)
+{
+ unsigned index = 0; /* offset from instance lookup */
+ bool status = false;
+
+ index = Multistate_Value_Instance_To_Index(object_instance);
+ if (index < MAX_MULTISTATE_VALUES) {
+ status = characterstring_init_ansi(object_name, MSV_Descr[index].Object_Name);
+ }
+
+ return status;
+}
+
+
+
+/* return apdu len, or BACNET_STATUS_ERROR on error */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+int Multistate_Value_Read_Property(
+ BACNET_READ_PROPERTY_DATA * rpdata)
+{
+ int len = 0;
+ int apdu_len = 0; /* return value */
+ BACNET_BIT_STRING bit_string;
+ BACNET_CHARACTER_STRING char_string;
+ uint32_t present_value = 0;
+ unsigned object_index = 0;
+ unsigned i = 0;
+ bool state = false;
+ uint8_t *apdu = NULL;
+
+ if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
+ (rpdata->application_data_len == 0)) {
+ return 0;
+ }
+
+ object_index = Multistate_Value_Instance_To_Index(rpdata->object_instance);
+ if (object_index >= MAX_MULTISTATE_VALUES) {
+ rpdata->error_class = ERROR_CLASS_OBJECT;
+ rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT;
+ return BACNET_STATUS_ERROR;
+ }
+
+ apdu = rpdata->application_data;
+ switch (rpdata->object_property) {
+ case PROP_OBJECT_IDENTIFIER:
+ apdu_len =
+ encode_application_object_id(&apdu[0],
+ OBJECT_MULTI_STATE_VALUE, rpdata->object_instance);
+ break;
+ case PROP_OBJECT_NAME:
+ Multistate_Value_Object_Name(rpdata->object_instance,
+ &char_string);
+ apdu_len =
+ encode_application_character_string(&apdu[0], &char_string);
+ break;
+ case PROP_DESCRIPTION:
+ characterstring_init_ansi(&char_string,
+ Multistate_Value_Description(rpdata->object_instance));
+ apdu_len =
+ encode_application_character_string(&apdu[0], &char_string);
+ break;
+ case PROP_OBJECT_TYPE:
+ apdu_len =
+ encode_application_enumerated(&apdu[0],
+ OBJECT_MULTI_STATE_VALUE);
+ break;
+ case PROP_PRESENT_VALUE:
+ present_value =
+ Multistate_Value_Present_Value(rpdata->object_instance);
+ apdu_len = encode_application_unsigned(&apdu[0], present_value);
+ break;
+ case PROP_STATUS_FLAGS:
+ /* note: see the details in the standard on how to use these */
+ bitstring_init(&bit_string);
+ bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false);
+ bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false);
+ bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false);
+ if (Multistate_Value_Out_Of_Service(rpdata->object_instance)) {
+ bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE,
+ true);
+ } else {
+ bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE,
+ false);
+ }
+ apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
+ break;
+ case PROP_EVENT_STATE:
+ /* note: see the details in the standard on how to use this */
+ apdu_len =
+ encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL);
+ break;
+ case PROP_OUT_OF_SERVICE:
+ state = MSV_Descr[object_index].Out_Of_Service;
+ apdu_len = encode_application_boolean(&apdu[0], state);
+ break;
+ case PROP_PRIORITY_ARRAY:
+ BACnet_encode_array(MSV_Descr[object_index].Present_Value,
+ BACNET_MAX_PRIORITY,
+ MSV_VALUE_IS_NULL,
+ encode_application_unsigned);
+ break;
+// case PROP_CURRENT_COMMAND_PRIORITY: {
+// unsigned i = Multistate_Value_Current_Command_Priority(rpdata->object_instance);
+// if (i == 0) apdu_len = encode_application_null (&apdu[0]);
+// else apdu_len = encode_application_unsigned(&apdu[0], i);
+// break;
+// }
+ case PROP_RELINQUISH_DEFAULT:
+ present_value = MSV_VALUE_RELINQUISH_DEFAULT;
+ apdu_len = encode_application_unsigned(&apdu[0], present_value);
+ break;
+ case PROP_NUMBER_OF_STATES:
+ apdu_len =
+ encode_application_unsigned(&apdu[apdu_len],
+ MSV_Descr[object_index].Number_Of_States);
+ break;
+ case PROP_STATE_TEXT:
+ BACnet_encode_array(MSV_Descr[object_index].State_Text,
+ MSV_Descr[object_index].Number_Of_States,
+ retfalse, BACnet_encode_character_string);
+ break;
+// case PROP_PROPERTY_LIST:
+// BACnet_encode_array(Multistate_Value_Properties_List,
+// property_list_count(Multistate_Value_Properties_List),
+// retfalse, encode_application_enumerated);
+// break;
+ default:
+ rpdata->error_class = ERROR_CLASS_PROPERTY;
+ rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
+ apdu_len = BACNET_STATUS_ERROR;
+ break;
+ }
+ /* only array properties can have array options */
+ if ((apdu_len >= 0) && (rpdata->object_property != PROP_STATE_TEXT) &&
+ (rpdata->object_property != PROP_PRIORITY_ARRAY) &&
+// (rpdata->object_property != PROP_PROPERTY_LIST) &&
+ (rpdata->array_index != BACNET_ARRAY_ALL)) {
+ rpdata->error_class = ERROR_CLASS_PROPERTY;
+ rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
+ apdu_len = BACNET_STATUS_ERROR;
+ }
+
+ return apdu_len;
+}
+
+/* returns true if successful */
+/* This is a callback function. Callback set in My_Object_Table[] array declared in device.c, */
+bool Multistate_Value_Write_Property(
+ BACNET_WRITE_PROPERTY_DATA * wp_data)
+{
+ bool status = false; /* return value */
+ int len = 0;
+ unsigned object_index = 0;
+ BACNET_APPLICATION_DATA_VALUE value;
+
+ /* decode the some of the request */
+ len =
+ bacapp_decode_application_data(wp_data->application_data,
+ wp_data->application_data_len, &value);
+ /* FIXME: len < application_data_len: more data? */
+ if (len < 0) {
+ /* error while decoding - a value larger than we can handle */
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
+ return false;
+ }
+ if ((wp_data->object_property != PROP_STATE_TEXT) &&
+ (wp_data->object_property != PROP_PRIORITY_ARRAY) &&
+ (wp_data->array_index != BACNET_ARRAY_ALL)) {
+ /* only array properties can have array options */
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
+ return false;
+ }
+ /* No need to check whether object_index is within bounds.
+ * Has already been checked before Multistate_Output_Write_Property() is called
+ */
+ object_index = Multistate_Value_Instance_To_Index(wp_data->object_instance);
+
+ switch (wp_data->object_property) {
+ case PROP_PRESENT_VALUE:
+ status =
+ WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT,
+ &wp_data->error_class, &wp_data->error_code);
+ if (status) {
+ status =
+ Multistate_Value_Present_Value_Set
+ (wp_data->object_instance, value.type.Unsigned_Int, wp_data->priority);
+ if (!status) {
+ if (wp_data->priority == 6) {
+ /* Command priority 6 is reserved for use by Minimum On/Off
+ algorithm and may not be used for other purposes in any
+ object. */
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
+ } else {
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
+ }
+ }
+ } else {
+ status =
+ WPValidateArgType(&value, BACNET_APPLICATION_TAG_NULL,
+ &wp_data->error_class, &wp_data->error_code);
+ if (status) {
+ status =
+ Multistate_Value_Present_Value_Relinquish
+ (wp_data->object_instance, wp_data->priority);
+ }
+ if (!status) {
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
+ }
+ }
+ break;
+ case PROP_OUT_OF_SERVICE:
+ {
+ bool Previous_Out_Of_Service = MSV_Descr[object_index].Out_Of_Service;
+ status =
+ WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN,
+ &wp_data->error_class, &wp_data->error_code);
+ if (status) {
+ MSV_Descr[object_index].Out_Of_Service = value.type.Boolean;
+ if (Previous_Out_Of_Service && !MSV_Descr[object_index].Out_Of_Service)
+ /* We have just changed from Out_of_Service -> In Service */
+ /* We need to update the Present_Value to the value
+ * currently in the PLC...
+ */
+ MSV_Descr[object_index].Present_Value[BACNET_MAX_PRIORITY-1] =
+ *(MSV_Descr[object_index].Located_Var_ptr);
+ }
+ break;
+ }
+ case PROP_OBJECT_IDENTIFIER:
+ case PROP_OBJECT_NAME:
+ case PROP_OBJECT_TYPE:
+ case PROP_STATUS_FLAGS:
+ case PROP_EVENT_STATE:
+ case PROP_NUMBER_OF_STATES:
+ case PROP_PRIORITY_ARRAY:
+// case PROP_CURRENT_COMMAND_PRIORITY:
+ case PROP_RELINQUISH_DEFAULT:
+ case PROP_DESCRIPTION:
+ case PROP_STATE_TEXT:
+// case PROP_PROPERTY_LIST:
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
+ break;
+ default:
+ wp_data->error_class = ERROR_CLASS_PROPERTY;
+ wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
+ break;
+ }
+
+ return status;
+}
+
+
+
+
+
+/********************************************/
+/** Functions required for Beremiz plugin **/
+/********************************************/
+
+
+
+void Multistate_Value_Copy_Present_Value_to_Located_Var(void) {
+ unsigned i;
+ for (i = 0; i < MAX_MULTISTATE_VALUES; i++) {
+ // decouple PLC's Located Variable from Bacnet Object's Present Value if Out_Of_Service is true
+ if (MSV_Descr[i].Out_Of_Service)
+ continue;
+
+ // copy the value
+ *(MSV_Descr[i].Located_Var_ptr) = Multistate_Value_Present_Value(MSV_Descr[i].Object_Identifier);
+ }
+}
+
+
+
+void Multistate_Value_Copy_Located_Var_to_Present_Value(void) {
+ unsigned i;
+ for (i = 0; i < MAX_MULTISTATE_VALUES; i++) {
+ // decouple PLC's Located Variable from Bacnet Object's Present Value if Out_Of_Service is true
+ if (MSV_Descr[i].Out_Of_Service)
+ continue;
+
+ // copy the value
+ // Make sure local device does not set Present_Value to a value higher than Number_Of_States
+ /* NOTE: The following comparison (Present_Value > Number_Of_States) is OK as the
+ * MSV_VALUE_NULL is currently set to 0. This means that the local controller
+ * can safely relinquish control by setting the Present_Value to 0.
+ * The comparison would not be safe if for some reason we later change
+ * MSV_VALUE_NULL to a value that may be higher than Number_Of_States.
+ */
+ #if MSV_VALUE_NULL != 0
+ #error Implementation assumes that MSV_VALUE_NULL is set to 0, which is currently not the case.
+ #endif
+ if (*(MSV_Descr[i].Located_Var_ptr) > MSV_Descr[i].Number_Of_States)
+ continue;
+
+ // Everything seems to be OK. Copy the value
+ MSV_Descr[i].Present_Value[BACNET_MAX_PRIORITY-1] = *(MSV_Descr[i].Located_Var_ptr);
+ }
+}
+
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bacnet/runtime/msv.h Fri Jun 08 13:28:00 2018 +0200
@@ -0,0 +1,110 @@
+/**************************************************************************
+*
+* Copyright (C) 2012 Steve Karg <skarg@users.sourceforge.net>
+* Copyright (C) 2017 Mario de Sousa <msousa@fe.up.pt>
+*
+* Permission is hereby granted, free of charge, to any person obtaining
+* a copy of this software and associated documentation files (the
+* "Software"), to deal in the Software without restriction, including
+* without limitation the rights to use, copy, modify, merge, publish,
+* distribute, sublicense, and/or sell copies of the Software, and to
+* permit persons to whom the Software is furnished to do so, subject to
+* the following conditions:
+*
+* The above copyright notice and this permission notice shall be included
+* in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*
+*********************************************************************/
+#ifndef MULTISTATE_VALUE_H
+#define MULTISTATE_VALUE_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include "bacdef.h"
+#include "bacerror.h"
+#include "rp.h"
+#include "wp.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+/* MultiState Values are encoded in unsigned integer
+ * (in BACnet => uint8_t), and can not be 0.
+ * See ASHRAE 135-2016, section 12.20.4
+ */
+#define MULTISTATE_MAX_NUMBER_OF_STATES (255)
+
+
+ typedef struct multistate_value_descr {
+ /* pointer to IEC 61131-3 located variable that is mapped onto this BACnet object */
+ uint8_t *Located_Var_ptr;
+ uint32_t Object_Identifier; /* called object 'Instance' in the source code! */
+ char *Object_Name;
+ char *Description;
+ uint8_t Number_Of_States;
+
+ /* stores the current value */
+ /* one entry per priority value */
+ uint8_t Present_Value[BACNET_MAX_PRIORITY];
+ /* Writable out-of-service allows others to manipulate our Present Value */
+ bool Out_Of_Service;
+ char State_Text[MULTISTATE_MAX_NUMBER_OF_STATES][64];
+ } MULTISTATE_VALUE_DESCR;
+
+
+ void Multistate_Value_Property_Lists(
+ const int **pRequired,
+ const int **pOptional,
+ const int **pProprietary);
+
+ bool Multistate_Value_Valid_Instance(
+ uint32_t object_instance);
+ unsigned Multistate_Value_Count(
+ void);
+ uint32_t Multistate_Value_Index_To_Instance(
+ unsigned index);
+ unsigned Multistate_Value_Instance_To_Index(
+ uint32_t instance);
+
+ int Multistate_Value_Read_Property(
+ BACNET_READ_PROPERTY_DATA * rpdata);
+
+ bool Multistate_Value_Write_Property(
+ BACNET_WRITE_PROPERTY_DATA * wp_data);
+
+ /* optional API */
+ bool Multistate_Value_Object_Name(
+ uint32_t object_instance,
+ BACNET_CHARACTER_STRING * object_name);
+
+ uint32_t Multistate_Value_Present_Value(
+ uint32_t object_instance);
+ bool Multistate_Value_Present_Value_Set(
+ uint32_t object_instance,
+ uint32_t value,
+ uint8_t priority);
+
+ bool Multistate_Value_Out_Of_Service(
+ uint32_t object_instance);
+ void Multistate_Value_Out_Of_Service_Set(
+ uint32_t object_instance,
+ bool value);
+
+ void Multistate_Value_Init(
+ void);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bacnet/runtime/server.c Fri Jun 08 13:28:00 2018 +0200
@@ -0,0 +1,645 @@
+/**************************************************************************
+*
+* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
+* Copyright (C) 2017 Mario de Sousa <msousa@fe.up.pt>
+*
+* Permission is hereby granted, free of charge, to any person obtaining
+* a copy of this software and associated documentation files (the
+* "Software"), to deal in the Software without restriction, including
+* without limitation the rights to use, copy, modify, merge, publish,
+* distribute, sublicense, and/or sell copies of the Software, and to
+* permit persons to whom the Software is furnished to do so, subject to
+* the following conditions:
+*
+* The above copyright notice and this permission notice shall be included
+* in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*
+*********************************************************************/
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <time.h>
+#include <inttypes.h> // uint32_t, ..., PRIu32, ...
+
+#include "config_bacnet_for_beremiz_%(locstr)s.h" /* the custom configuration for beremiz pluginh */
+#include "server_%(locstr)s.h"
+#include "address.h"
+#include "bacdef.h"
+#include "handlers.h"
+#include "client.h"
+#include "dlenv.h"
+#include "bacdcode.h"
+#include "npdu.h"
+#include "apdu.h"
+#include "iam.h"
+#include "tsm.h"
+#include "datalink.h"
+#include "dcc.h"
+#include "getevent.h"
+#include "net.h"
+#include "txbuf.h"
+#include "version.h"
+#include "timesync.h"
+
+
+/* A utility function used by most (all?) implementations of BACnet Objects */
+/* Adds to Prop_List all entries in Prop_List_XX that are not
+ * PROP_OBJECT_IDENTIFIER, PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_PROPERTY_LIST
+ * and returns the number of elements that were added
+ */
+int BACnet_Init_Properties_List(
+ int *Prop_List,
+ const int *Prop_List_XX) {
+
+ unsigned int i = 0;
+ unsigned int j = 0;
+
+ for (j = 0; Prop_List_XX[j] >= 0; j++)
+ // Add any propety, except for the following 4 which should not be included
+ // in the Property_List property array.
+ // (see ASHRAE 135-2016, for example section 12.4.34)
+ if ((Prop_List_XX[j] != PROP_OBJECT_IDENTIFIER) &&
+ (Prop_List_XX[j] != PROP_OBJECT_NAME) &&
+ (Prop_List_XX[j] != PROP_OBJECT_TYPE) &&
+ (Prop_List_XX[j] != PROP_PROPERTY_LIST)) {
+ Prop_List[i] = Prop_List_XX[j];
+ i++;
+ }
+ Prop_List[i] = -1; // marks the end of the list!
+ return i;
+}
+
+
+
+
+
+
+int BACnet_encode_character_string(uint8_t *apdu, const char *str) {
+ BACNET_CHARACTER_STRING char_string;
+ characterstring_init_ansi(&char_string, str);
+ /* FIXME: this might go beyond MAX_APDU length! */
+ return encode_application_character_string(apdu, &char_string);
+}
+
+/* macro that always returns false.
+ * To be used as the 'test_null' parameter to the BACnet_encode_array macro
+ * in situations where we should never encode_null() values.
+ */
+#define retfalse(x) (false)
+
+#define BACnet_encode_array(array, array_len, test_null, encode_function) \
+{ \
+ uint8_t *apdu = NULL; \
+ apdu = rpdata->application_data; \
+ \
+ switch (rpdata->array_index) { \
+ case 0: /* Array element zero is the number of elements in the array */ \
+ apdu_len = encode_application_unsigned(&apdu[0], array_len); \
+ break; \
+ case BACNET_ARRAY_ALL: { \
+ /* if no index was specified, then try to encode the entire list */ \
+ unsigned i = 0; \
+ apdu_len = 0; \
+ for (i = 0; i < array_len; i++) { \
+ /* FIXME: this might go beyond MAX_APDU length! */ \
+ if (!test_null(array[i])) \
+ apdu_len += encode_function (&apdu[apdu_len], array[i]); \
+ else apdu_len += encode_application_null(&apdu[apdu_len]); \
+ /* return error if it does not fit in the APDU */ \
+ if (apdu_len >= MAX_APDU) { \
+ rpdata->error_class = ERROR_CLASS_SERVICES; \
+ rpdata->error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; \
+ apdu_len = BACNET_STATUS_ERROR; \
+ break; /* for(;;) */ \
+ } \
+ } \
+ break; \
+ } \
+ default: \
+ if (rpdata->array_index <= array_len) { \
+ if (!test_null(array[rpdata->array_index - 1])) \
+ apdu_len += encode_function(&apdu[0], array[rpdata->array_index - 1]); \
+ else apdu_len += encode_application_null(&apdu[0]); \
+ } else { \
+ rpdata->error_class = ERROR_CLASS_PROPERTY; \
+ rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; \
+ apdu_len = BACNET_STATUS_ERROR; \
+ } \
+ break; \
+ } /* switch() */ \
+}
+
+
+/* include the device object */
+#include "device_%(locstr)s.c"
+#include "ai_%(locstr)s.c"
+#include "ao_%(locstr)s.c"
+#include "av_%(locstr)s.c"
+#include "bi_%(locstr)s.c"
+#include "bo_%(locstr)s.c"
+#include "bv_%(locstr)s.c"
+#include "msi_%(locstr)s.c"
+#include "mso_%(locstr)s.c"
+#include "msv_%(locstr)s.c"
+
+
+/** Buffer used for receiving */
+static uint8_t Rx_Buf[MAX_MPDU] = { 0 };
+
+
+
+
+/*******************************************************/
+/* BACnet Service Handlers taylored to Beremiz plugin */
+/*******************************************************/
+
+static void BACNET_show_date_time(
+ BACNET_DATE * bdate,
+ BACNET_TIME * btime)
+{
+ /* show the date received */
+ fprintf(stderr, "%%u", (unsigned) bdate->year);
+ fprintf(stderr, "/%%u", (unsigned) bdate->month);
+ fprintf(stderr, "/%%u", (unsigned) bdate->day);
+ /* show the time received */
+ fprintf(stderr, " %%02u", (unsigned) btime->hour);
+ fprintf(stderr, ":%%02u", (unsigned) btime->min);
+ fprintf(stderr, ":%%02u", (unsigned) btime->sec);
+ fprintf(stderr, ".%%02u", (unsigned) btime->hundredths);
+ fprintf(stderr, "\r\n");
+}
+
+static time_t __timegm(struct tm *new_time) {
+ time_t sec = mktime(new_time); /* assume new_time is in local time */
+
+ /* sec will be an aproximation of the correct value.
+ * We must now fix this with the current difference
+ * between UTC and localtime.
+ *
+ * WARNING: The following algorithm to determine the current
+ * difference between local time and UTC will not
+ * work if each value (lcl and utc) falls on a different
+ * side of a change to/from DST.
+ * For example, assume a change to DST is made at 12h00
+ * of day X. The following algorithm does not work if:
+ * - lcl falls before 12h00 of day X
+ * - utc falls after 12h00 of day X
+ */
+ struct tm lcl = *localtime(&sec); // lcl will be == new_time
+ struct tm utc = *gmtime (&sec);
+
+ if (lcl.tm_isdst == 1) utc.tm_isdst = 1;
+
+ time_t sec_lcl = mktime(&lcl);
+ time_t sec_utc = mktime(&utc);
+
+ /* difference in seconds between localtime and utc */
+ time_t sec_dif = sec_lcl - sec_utc;
+ return sec + sec_dif;
+}
+
+
+static void BACNET_set_date_time(
+ BACNET_DATE * bdate,
+ BACNET_TIME * btime,
+ int utc /* set to > 0 if date & time in UTC */)
+{
+ struct tm brokendown_time;
+ time_t seconds = 0;
+ struct timespec ts;
+
+ brokendown_time.tm_sec = btime->sec; /* seconds 0..60 */
+ brokendown_time.tm_min = btime->min; /* minutes 0..59 */
+ brokendown_time.tm_hour = btime->hour; /* hours 0..23 */
+ brokendown_time.tm_mday = bdate->day; /* day of the month 1..31 */
+ brokendown_time.tm_mon = bdate->month-1; /* month 0..11 */
+ brokendown_time.tm_year = bdate->year-1900; /* years since 1900 */
+// brokendown_time.tm_wday = ; /* day of the week */
+// brokendown_time.tm_yday = ; /* day in the year */
+ brokendown_time.tm_isdst = -1; /* daylight saving time (-1 => unknown) */
+
+ // Tranform time into format -> 'seconds since epoch'
+ /* WARNING: timegm() is a non standard GNU extension.
+ * If you do not have it on your build system then consider
+ * finding the source code for timegm() (it is LGPL) from GNU
+ * C library and copying it here
+ * (e.g. https://code.woboq.org/userspace/glibc/time/timegm.c.html)
+ * Another alternative is to use the fundion __timegm() above,
+ * which will mostly work but may have errors when the time being
+ * converted is close to the time in the year when changing
+ * to/from DST (daylight saving time)
+ */
+ if (utc > 0) seconds = timegm(&brokendown_time);
+ else seconds = mktime(&brokendown_time);
+
+ ts.tv_sec = seconds;
+ ts.tv_nsec = btime->hundredths*10*1000*1000;
+
+// fprintf(stderr, "clock_settime() s=%%ul, ns=%%u\n", ts.tv_sec, ts.tv_nsec);
+ clock_settime(CLOCK_REALTIME, &ts);
+// clock_gettime(CLOCK_REALTIME, &ts);
+// fprintf(stderr, "clock_gettime() s=%%ul, ns=%%u\n", ts.tv_sec, ts.tv_nsec);
+}
+
+
+
+void BACnet_handler_timesync(
+ uint8_t * service_request,
+ uint16_t service_len,
+ BACNET_ADDRESS * src)
+{
+ int len = 0;
+ BACNET_DATE bdate;
+ BACNET_TIME btime;
+
+ (void) src;
+ (void) service_len;
+ len =
+ timesync_decode_service_request(service_request, service_len, &bdate, &btime);
+ if (len > 0) {
+ fprintf(stderr, "BACnet plugin: Received TimeSyncronization Request -> ");
+ BACNET_show_date_time(&bdate, &btime);
+ /* set the time */
+ BACNET_set_date_time(&bdate, &btime, 0 /* time in local time */);
+ }
+
+ return;
+}
+
+void BACnet_handler_timesync_utc(
+ uint8_t * service_request,
+ uint16_t service_len,
+ BACNET_ADDRESS * src)
+{
+ int len = 0;
+ BACNET_DATE bdate;
+ BACNET_TIME btime;
+
+ (void) src;
+ (void) service_len;
+ len =
+ timesync_decode_service_request(service_request, service_len, &bdate, &btime);
+ if (len > 0) {
+ fprintf(stderr, "BACnet plugin: Received TimeSyncronizationUTC Request -> ");
+ BACNET_show_date_time(&bdate, &btime);
+ /* set the time */
+ BACNET_set_date_time(&bdate, &btime, 1 /* time in UTC */);
+ }
+
+ return;
+}
+
+
+
+/**********************************************/
+/** Initialize the handlers we will utilize. **/
+/**********************************************/
+/*
+ * TLDR: The functions that will create the __Resp.__ messages.
+ *
+ * The service handlers are the functions that will respond to BACnet requests this device receives.
+ * In essence, the service handlers will create and send the Resp. (Response) messages
+ * of the Req. -> Ind. -> Resp. -> Conf. service sequence defined in OSI
+ * (Request, Indication, Response, Confirmation)
+ *
+ */
+static int Init_Service_Handlers(
+ void)
+{
+ /* set the handler for all the services we don't implement */
+ /* It is required to send the proper reject message... */
+ apdu_set_unrecognized_service_handler_handler(handler_unrecognized_service);
+
+ /* Set the handlers for any unconfirmed services that we support. */
+ apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, // DM-DDB-B - Dynamic Device Binding B (Resp.)
+ handler_who_is); // (see ASHRAE 135-2016, section K5.1 and K5.2)
+// apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, // DM-DDB-A - Dynamic Device Binding A (Resp.)
+// handler_i_am_bind); // Responding to I_AM requests is for clients (A)!
+// // (see ASHRAE 135-2016, section K5.1 and K5.2)
+ apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_HAS, // DM-DOB-B - Dynamic Object Binding B (Resp.)
+ handler_who_has); // (see ASHRAE 135-2016, section K5.3 and K5.4)
+// apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_HAVE, // DM-DOB-A - Dynamic Object Binding A (Resp.)
+// handler_i_have); // Responding to I_HAVE requests is for clients (A)!
+// // (see ASHRAE 135-2016, section K5.3 and K5.4)
+ apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_UTC_TIME_SYNCHRONIZATION, // DM-UTC-B -UTCTimeSynchronization-B (Resp.)
+ BACnet_handler_timesync_utc); // (see ASHRAE 135-2016, section K5.14)
+ apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_TIME_SYNCHRONIZATION, // DM-TS-B - TimeSynchronization-B (Resp.)
+ BACnet_handler_timesync); // (see ASHRAE 135-2016, section K5.12)
+
+ /* Set the handlers for any confirmed services that we support. */
+ apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, // DS-RP-B - Read Property B (Resp.)
+ handler_read_property); // (see ASHRAE 135-2016, section K1.2)
+// apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROP_MULTIPLE, // DS-RPM-B -Read Property Multiple-B (Resp.)
+// handler_read_property_multiple); // (see ASHRAE 135-2016, section K1.4)
+ apdu_set_confirmed_handler(SERVICE_CONFIRMED_WRITE_PROPERTY, // DS-WP-B - Write Property B (Resp.)
+ handler_write_property); // (see ASHRAE 135-2016, section K1.8)
+// apdu_set_confirmed_handler(SERVICE_CONFIRMED_WRITE_PROP_MULTIPLE,// DS-WPM-B -Write Property Multiple B (Resp.)
+// handler_write_property_multiple); // (see ASHRAE 135-2016, section K1.10)
+// apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_RANGE,
+// handler_read_range);
+// apdu_set_confirmed_handler(SERVICE_CONFIRMED_REINITIALIZE_DEVICE,
+// handler_reinitialize_device);
+
+// apdu_set_confirmed_handler(SERVICE_CONFIRMED_SUBSCRIBE_COV,
+// handler_cov_subscribe);
+// apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_COV_NOTIFICATION,
+// handler_ucov_notification);
+ /* handle communication so we can shutup when asked */
+ apdu_set_confirmed_handler(SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL, // DM-DCC-B - Device Communication Control B
+ handler_device_communication_control); // (see ASHRAE 135-2016, section K5.6)
+// /* handle the data coming back from private requests */
+// apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_PRIVATE_TRANSFER,
+// handler_unconfirmed_private_transfer);
+
+ // success
+ return 0;
+}
+
+
+
+static int Init_Network_Interface(
+ const char *interface, // for linux: /dev/eth0, /dev/eth1, /dev/wlan0, ...
+ // for windows: 192.168.0.1 (IP addr. of interface)
+ // NULL => use default!
+ const char *port, // Port the server will listen on. (NULL => use default)
+ const char *apdu_timeout, // (NULL => use default)
+ const char *apdu_retries // (NULL => use default)
+ )
+{
+ char datalink_layer[4];
+
+ strcpy(datalink_layer, "BIP"); // datalink_set() does not accpet const char *, so we copy it...
+// BIP_Debug = true;
+
+ if (port != NULL) {
+ bip_set_port(htons((uint16_t) strtol(port, NULL, 0)));
+ } else {
+ bip_set_port(htons(0xBAC0));
+ }
+
+ if (apdu_timeout != NULL) {
+ apdu_timeout_set((uint16_t) strtol(apdu_timeout, NULL, 0));
+ }
+
+ if (apdu_retries != NULL) {
+ apdu_retries_set((uint8_t) strtol(apdu_retries, NULL, 0));
+ }
+
+ // datalink_init is a pointer that will actually call bip_init()
+ // datalink_init pointer is set by the call datalink_set("BIP")
+ /* NOTE: current implementation of BACnet stack uses the interface
+ * only to determine the server's local IP address and broacast address.
+ * The local IP addr is later used to discard (broadcast) messages
+ * it receives that were sent by itself.
+ * The broadcast IP addr is used for broadcast messages.
+ * WARNING: The socket itself is created to listen on all
+ * available interfaces (INADDR_ANY), so this setting may induce
+ * the user in error as we will accept messages arriving on other
+ * interfaces (if they exist)
+ * (see bip_init() in ports/linux/bip-init.c)
+ * (see bip_****() in src/bip.c)
+ */
+ char *tmp = (char *)malloc(strlen(interface) + 1);
+ if (tmp == NULL) return -1;
+ strncpy(tmp, interface, strlen(interface) + 1);
+ if (!datalink_init(tmp)) {
+ return -1;
+ }
+// #if (MAX_TSM_TRANSACTIONS)
+// pEnv = getenv("BACNET_INVOKE_ID");
+// if (pEnv) {
+// tsm_invokeID_set((uint8_t) strtol(pEnv, NULL, 0));
+// }
+// #endif
+ dlenv_register_as_foreign_device();
+
+ // success
+ return 0;
+}
+
+
+
+/** Main function of server demo. **/
+int bn_server_run(server_node_t *server_node) {
+ int res = 0;
+ BACNET_ADDRESS src = {0}; /* address where message came from */
+ uint16_t pdu_len = 0;
+ unsigned timeout = 1000; /* milliseconds */
+ time_t last_seconds = 0;
+ time_t current_seconds = 0;
+ uint32_t elapsed_seconds = 0;
+ uint32_t elapsed_milliseconds = 0;
+ uint32_t address_binding_tmr = 0;
+ uint32_t recipient_scan_tmr = 0;
+
+ /* allow the device ID to be set */
+ Device_Set_Object_Instance_Number(server_node->device_id);
+ /* load any static address bindings in our device bindings list */
+ address_init();
+ /* load any entries in the BDT table from backup file */
+ bvlc_bdt_restore_local();
+ /* Initiliaze the bacnet server 'device' */
+ Device_Init(server_node->device_name);
+ /* Set the password (max 31 chars) for Device Communication Control request. */
+ /* Default in the BACnet stack is hardcoded as "filister" */
+ /* (char *) cast is to remove the cast. The function is incorrectly declared/defined in the BACnet stack! */
+ /* BACnet stack needs to change demo/handler/h_dcc.c and include/handlers.h */
+ handler_dcc_password_set((char *)server_node->comm_control_passwd);
+ /* Set callbacks and configure network interface */
+ res = Init_Service_Handlers();
+ if (res < 0) exit(1);
+ res = Init_Network_Interface(
+ server_node->network_interface, // interface (NULL => use default (eth0))
+ server_node->port_number, // Port number (NULL => use default (0xBAC0))
+ NULL, // apdu_timeout (NULL => use default)
+ NULL // apdu_retries (NULL => use default)
+ );
+ if (res < 0) {
+ fprintf(stderr, "BACnet plugin: error initializing bacnet server node %%s!\n", server_node->location);
+ exit(1); // kill the server thread!
+ }
+ /* BACnet stack correcly configured. Give user some feedback! */
+ struct in_addr my_addr, broadcast_addr;
+ my_addr. s_addr = bip_get_addr();
+ broadcast_addr.s_addr = bip_get_broadcast_addr();
+ printf("BACnet plugin:"
+ " Local IP addr: %%s"
+ ", Broadcast IP addr: %%s"
+ ", Port number: 0x%%04X [%%hu]"
+ ", BACnet Device ID: %%d"
+ ", Max APDU: %%d"
+ ", BACnet Stack Version %%s\n",
+ inet_ntoa(my_addr),
+ inet_ntoa(broadcast_addr),
+ ntohs(bip_get_port()), ntohs(bip_get_port()),
+ Device_Object_Instance_Number(),
+ MAX_APDU,
+ Device_Firmware_Revision()
+ );
+ /* configure the timeout values */
+ last_seconds = time(NULL);
+ /* broadcast an I-Am on startup */
+ Send_I_Am(&Handler_Transmit_Buffer[0]);
+ /* loop forever */
+ for (;;) {
+ /* input */
+ current_seconds = time(NULL);
+
+ /* returns 0 bytes on timeout */
+ pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout);
+
+ /* process */
+ if (pdu_len) {
+ npdu_handler(&src, &Rx_Buf[0], pdu_len);
+ }
+ /* at least one second has passed */
+ elapsed_seconds = (uint32_t) (current_seconds - last_seconds);
+ if (elapsed_seconds) {
+ last_seconds = current_seconds;
+ dcc_timer_seconds(elapsed_seconds);
+ bvlc_maintenance_timer(elapsed_seconds);
+ dlenv_maintenance_timer(elapsed_seconds);
+ elapsed_milliseconds = elapsed_seconds * 1000;
+ tsm_timer_milliseconds(elapsed_milliseconds);
+ }
+ handler_cov_task();
+ /* scan cache address */
+ address_binding_tmr += elapsed_seconds;
+ if (address_binding_tmr >= 60) {
+ address_cache_timer(address_binding_tmr);
+ address_binding_tmr = 0;
+ }
+ }
+
+ /* should never occur!! */
+ return 0;
+}
+
+
+
+
+
+#include <pthread.h>
+
+static void *__bn_server_thread(void *_server_node) {
+ server_node_t *server_node = _server_node;
+
+ // Enable thread cancelation. Enabled is default, but set it anyway to be safe.
+ pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+
+ // bn_server_run() should never return!
+ bn_server_run(server_node);
+ fprintf(stderr, "BACnet plugin: bacnet server for node %%s died unexpectedly!\n", server_node->location); /* should never occur */
+ return NULL;
+}
+
+
+int __cleanup_%(locstr)s ();
+
+int __init_%(locstr)s (int argc, char **argv){
+ int index;
+
+ /* init each local server */
+ /* NOTE: All server_nodes[].init_state are initialised to 0 in the code
+ * generated by the BACnet plugin
+ */
+ /* create the BACnet server */
+ server_node.init_state = 1; // we have created the node
+
+ /* launch a thread to handle this server node */
+ {
+ int res = 0;
+ pthread_attr_t attr;
+ res |= pthread_attr_init(&attr);
+ res |= pthread_create(&(server_node.thread_id), &attr, &__bn_server_thread, (void *)&(server_node));
+ if (res != 0) {
+ fprintf(stderr, "BACnet plugin: Error starting bacnet server thread for node %%s\n", server_node.location);
+ goto error_exit;
+ }
+ }
+ server_node.init_state = 2; // we have created the node and thread
+
+ return 0;
+
+error_exit:
+ __cleanup_%(locstr)s ();
+ return -1;
+}
+
+
+
+
+
+void __publish_%(locstr)s (){
+ Analog_Value_Copy_Located_Var_to_Present_Value();
+ Analog_Input_Copy_Located_Var_to_Present_Value();
+ Analog_Output_Copy_Located_Var_to_Present_Value();
+ Binary_Value_Copy_Located_Var_to_Present_Value();
+ Binary_Input_Copy_Located_Var_to_Present_Value();
+ Binary_Output_Copy_Located_Var_to_Present_Value();
+ Multistate_Value_Copy_Located_Var_to_Present_Value();
+ Multistate_Input_Copy_Located_Var_to_Present_Value();
+ Multistate_Output_Copy_Located_Var_to_Present_Value();
+}
+
+
+
+
+
+void __retrieve_%(locstr)s (){
+ Analog_Value_Copy_Present_Value_to_Located_Var();
+ Analog_Input_Copy_Present_Value_to_Located_Var();
+ Analog_Output_Copy_Present_Value_to_Located_Var();
+ Binary_Value_Copy_Present_Value_to_Located_Var();
+ Binary_Input_Copy_Present_Value_to_Located_Var();
+ Binary_Output_Copy_Present_Value_to_Located_Var();
+ Multistate_Value_Copy_Present_Value_to_Located_Var();
+ Multistate_Input_Copy_Present_Value_to_Located_Var();
+ Multistate_Output_Copy_Present_Value_to_Located_Var();
+}
+
+
+
+
+
+int __cleanup_%(locstr)s (){
+ int index, close;
+ int res = 0;
+
+ /* kill thread and close connections of each modbus server node */
+ close = 0;
+ if (server_node.init_state >= 2) {
+ // thread was launched, so we try to cancel it!
+ close = pthread_cancel(server_node.thread_id);
+ close |= pthread_join (server_node.thread_id, NULL);
+ if (close < 0)
+ fprintf(stderr, "BACnet plugin: Error closing thread for bacnet server %%s\n", server_node.location);
+ }
+ res |= close;
+
+ close = 0;
+ if (server_node.init_state >= 1) {
+ // bacnet server node was created, so we try to close it!
+ // datalink_cleanup is a pointer that will actually call bip_cleanup()
+ // datalink_cleanup pointer is set by the call datalink_set("BIP")
+ datalink_cleanup();
+ }
+ res |= close;
+ server_node.init_state = 0;
+
+ /* bacnet library close */
+ // Nothing to do ???
+
+ return res;
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bacnet/runtime/server.h Fri Jun 08 13:28:00 2018 +0200
@@ -0,0 +1,61 @@
+/**************************************************************************
+*
+* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
+* Copyright (C) 2017 Mario de Sousa <msousa@fe.up.pt>
+*
+* Permission is hereby granted, free of charge, to any person obtaining
+* a copy of this software and associated documentation files (the
+* "Software"), to deal in the Software without restriction, including
+* without limitation the rights to use, copy, modify, merge, publish,
+* distribute, sublicense, and/or sell copies of the Software, and to
+* permit persons to whom the Software is furnished to do so, subject to
+* the following conditions:
+*
+* The above copyright notice and this permission notice shall be included
+* in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*
+*********************************************************************/
+
+#ifndef SERVER_H_
+#define SERVER_H_
+
+
+#include <stddef.h>
+#include <stdint.h>
+
+
+
+typedef struct{
+ const char *location;
+ const char *network_interface;
+ const char *port_number;
+ const char *device_name;
+ const char *comm_control_passwd;
+ uint32_t device_id; // device ID is 22 bits long! uint16_t is not enough!
+ int init_state; // store how far along the server's initialization has progressed
+ pthread_t thread_id; // thread handling this server
+ } server_node_t;
+
+
+
+/*initialization following all parameters given by user in application*/
+static server_node_t server_node = {
+ "%(locstr)s",
+ "%(network_interface)s", // interface (NULL => use default (eth0))
+ "%(port_number)s", // Port number (NULL => use default)
+ "%(BACnet_Device_Name)s", // BACnet server's device (object) name
+ "%(BACnet_Comm_Control_Password)s",// BACnet server's device (object) name
+ %(BACnet_Device_ID)s // BACnet server's device (object) ID
+};
+
+
+
+#endif /* SERVER_H_ */
--- a/features.py Fri Jun 08 09:43:48 2018 +0200
+++ b/features.py Fri Jun 08 13:28:00 2018 +0200
@@ -29,6 +29,7 @@
catalog = [
('canfestival', _('CANopen support'), _('Map located variables over CANopen'), 'canfestival.canfestival.RootClass'),
+ ('bacnet', _('Bacnet support'), _('Map located variables over Bacnet'), 'bacnet.bacnet.RootClass'),
('modbus', _('Modbus support'), _('Map located variables over Modbus'), 'modbus.modbus.RootClass'),
('c_ext', _('C extension'), _('Add C code accessing located variables synchronously'), 'c_ext.CFile'),
('py_ext', _('Python file'), _('Add Python code executed asynchronously'), 'py_ext.PythonFile'),