├── .github └── workflows │ ├── pr.yml │ ├── push.yml │ └── release.yml ├── .gitignore ├── LICENSE ├── README.md ├── api ├── pom.xml └── src │ └── main │ └── java │ └── org │ └── code_house │ └── bacnet4j │ └── wrapper │ ├── api │ ├── BacNetClient.java │ ├── BacNetClientBase.java │ ├── BacNetClientException.java │ ├── BacNetObject.java │ ├── BacNetToJavaConverter.java │ ├── BlockingDiscoveryCallable.java │ ├── BypassBacnetConverter.java │ ├── CollectingDiscoveryListener.java │ ├── DefaultPriority.java │ ├── Device.java │ ├── DeviceDiscoveryListener.java │ ├── DiscoveryEventAdapter.java │ ├── JavaToBacNetConverter.java │ ├── NoopDiscoveryListener.java │ ├── Priorities.java │ ├── Priority.java │ ├── Property.java │ ├── Type.java │ ├── UnsupportedTypeException.java │ └── util │ │ └── ForwardingAdapter.java │ └── device │ ├── DefaultDeviceFactory.java │ ├── DeviceFactory.java │ ├── ip │ └── IpDevice.java │ └── mstp │ └── MstpDevice.java ├── assembly └── pom.xml ├── ip ├── pom.xml └── src │ ├── main │ └── java │ │ └── org │ │ └── code_house │ │ └── bacnet4j │ │ └── wrapper │ │ └── ip │ │ ├── BacNetIpClient.java │ │ ├── CsvMain.java │ │ ├── CsvVisitor.java │ │ ├── DiscoveryMain.java │ │ ├── GeneratorMain.java │ │ ├── NetworkProgram.java │ │ ├── OpenHabConfigurationVisitor.java │ │ ├── PrintingVisitor.java │ │ └── Visitor.java │ └── test │ └── resources │ └── log4j.properties ├── mstp ├── pom.xml └── src │ ├── main │ └── java │ │ └── org │ │ └── code_house │ │ └── bacnet4j │ │ └── wrapper │ │ └── mstp │ │ ├── BacNetMstpClient.java │ │ └── MstpNetworkBuilder.java │ └── test │ └── java │ └── org │ └── code_house │ └── bacnet4j │ └── wrapper │ └── mstp │ └── Main.java └── pom.xml /.github/workflows/pr.yml: -------------------------------------------------------------------------------- 1 | name: Build pull request 2 | 3 | on: 4 | pull_request: 5 | branches: [ "master", "1.0.x" , "1.1.x" , "1.2.x" , "1.3.x" ] 6 | 7 | jobs: 8 | build: 9 | permissions: 10 | checks: write 11 | contents: read 12 | uses: 'connectorio/gh-actions-shared/.github/workflows/maven.yml@master' 13 | secrets: 14 | CI_DEPLOY_USERNAME: ${{ secrets.CI_DEPLOY_USERNAME }} 15 | CI_DEPLOY_PASSWORD: ${{ secrets.CI_DEPLOY_PASSWORD }} 16 | with: 17 | openhab: false 18 | server_id: sonatype-nexus-snapshots 19 | master_branch: 1.3.x 20 | deploy: true 21 | skip_test_publish: true -------------------------------------------------------------------------------- /.github/workflows/push.yml: -------------------------------------------------------------------------------- 1 | name: Build pushed commit 2 | 3 | on: 4 | push: 5 | branches: [ "master", "1.0.x" , "1.1.x" , "1.2.x" , "1.3.x" ] 6 | 7 | jobs: 8 | build: 9 | #if: ${{ true }} 10 | permissions: 11 | checks: write 12 | contents: read 13 | uses: 'connectorio/gh-actions-shared/.github/workflows/maven.yml@master' 14 | secrets: 15 | CI_DEPLOY_USERNAME: ${{ secrets.CI_DEPLOY_USERNAME }} 16 | CI_DEPLOY_PASSWORD: ${{ secrets.CI_DEPLOY_PASSWORD }} 17 | with: 18 | openhab: false 19 | server_id: sonatype-nexus-snapshots 20 | master_branch: 1.3.x 21 | deploy: true 22 | skip_test_publish: true -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release artifacts 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | release_version: 7 | description: 'The version to be released from actual HEAD revision' 8 | required: false 9 | default: 'X.Y.Z' 10 | development_version: 11 | description: 'Version to be set as next, after release is made (appends commit to HEAD revision)' 12 | required: false 13 | default: 'X.Y.Z-SNAPSHOT' 14 | perform_version: 15 | description: 'Tag which should be used to perform release and publish its artifacts' 16 | required: false 17 | dry_run: 18 | description: 'Should execution abstain from mutating repository/remote state?' 19 | required: true 20 | default: 'true' 21 | 22 | jobs: 23 | build: 24 | permissions: 25 | contents: write 26 | uses: 'connectorio/gh-actions-shared/.github/workflows/release.yml@master' 27 | secrets: 28 | GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} 29 | GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} 30 | CI_RELEASE_USERNAME: ${{ secrets.CI_RELEASE_USERNAME }} 31 | CI_RELEASE_PASSWORD: ${{ secrets.CI_RELEASE_PASSWORD }} 32 | with: 33 | openhab: false 34 | server_id: 'sonatype-nexus-staging' 35 | master_branch: 1.3.x 36 | release_version: ${{ inputs.release_version }} 37 | development_version: ${{ inputs.development_version }} 38 | perform_version: ${{ inputs.perform_version }} 39 | dry_run: ${{ inputs.dry_run }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # Mobile Tools for Java (J2ME) 4 | .mtj.tmp/ 5 | 6 | # Package Files # 7 | *.jar 8 | *.war 9 | *.ear 10 | 11 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 12 | hs_err_pid* 13 | 14 | # IntelliJ Idea 15 | .idea 16 | 17 | */target 18 | assembly/dependency-reduced-pom.xml 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | {one line to give the program's name and a brief idea of what it does.} 635 | Copyright (C) {year} {name of author} 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | {project} Copyright (C) {year} {fullname} 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Code-House Bacnet4J wrapper 2 | 3 | [![Most recent build](https://github.com/Code-House/bacnet4j-wrapper/actions/workflows/push.yml/badge.svg)](https://github.com/Code-House/bacnet4j-wrapper/actions/workflows/push.yml) 4 | [![Release](https://github.com/Code-House/bacnet4j-wrapper/actions/workflows/release.yml/badge.svg)](https://github.com/Code-House/bacnet4j-wrapper/actions/workflows/release.yml) 5 | 6 | [![GitHub license](https://img.shields.io/github/license/Code-House/bacnet4j-wrapper.svg)](https://github.com/Code-House/bacnet4j-wrapper/blob/master/LICENSE) 7 | [![GitHub release](https://img.shields.io/github/release/Code-House/bacnet4j-wrapper.svg)](https://GitHub.com/Code-House/bacnet4j-wrapper/releases/) 8 | [![Github all releases](https://img.shields.io/github/downloads/Code-House/bacnet4j-wrapper/total.svg)](https://GitHub.com/Code-House/bacnet4j-wrapper/releases/) 9 | 10 | Bacnet4j is implementation of bacnet protocol in Java. Bacnet is an standard which is quite old and bacnet4j implementation 11 | is quite awful when it comes to interacting with devices. To avoid headaches with bacnet4j calls we provided this simple 12 | facade which hides most of weirdness's of bacnet4j. Thanks to this you can interact with devices, read and set property values, 13 | in simpler way. 14 | 15 | Many of initial surprises which most of developers experienced is cleaned up with 5.0 release (which is now in use), yet 16 | API of Bacnet4j remains a mystery if you do not follow spec. 17 | For this reason our wrapper is still maintained. Even if its documentation is not any better than original library, it still 18 | provides basic operations in less confusing way. 19 | 20 | IP: variant 21 | ```java 22 | BacNetClient client = new BacNetIpClient("", "", ); 23 | client.start(); 24 | Set devices = client.discoverDevices(5000); // given number is timeout in millis 25 | for (Device device : devices) { 26 | System.out.println(device); 27 | 28 | for (Property property : client.getDeviceProperties(device)) { 29 | System.out.println(property.getName() + " " + client.getPropertyValue(property)); 30 | } 31 | } 32 | 33 | client.stop(); 34 | ``` 35 | 36 | MSTP variant: 37 | ```java 38 | BacNetClient client = new BacNetMstpClient("", , ); 39 | client.start(); 40 | Set devices = client.discoverDevices(5000); // given number is timeout in millis 41 | for (Device device : devices) { 42 | System.out.println(device); 43 | 44 | for (Property property : client.getDeviceProperties(device)) { 45 | System.out.println(property.getName() + " " + client.getPropertyValue(property)); 46 | } 47 | } 48 | 49 | client.stop(); 50 | ``` 51 | 52 | 53 | ## Supported features 54 | 55 | This project is aimed to offer simple facade thus it is limited to device and following bacnet object types: 56 | 57 | * analogInput 58 | * analogOutput 59 | * analogValue 60 | * binaryInput 61 | * binaryOutput 62 | * binaryValue 63 | * multiStateInput 64 | * multiStateOutput 65 | * multiStateValue 66 | 67 | As of 1.2.x there is support for additional object types, however they are considered experimental: 68 | 69 | * calendar 70 | * pulseConverter 71 | * schedule 72 | * characterstringValue 73 | * dateTimeValue 74 | * largeAnalogValue 75 | * octetstringValue 76 | * timeValue 77 | * dateValue 78 | * integerValue 79 | * positiveIntegerValue 80 | * datetimePatternValue 81 | * timePatternValue 82 | * datePatternValue 83 | * accumulator 84 | * device 85 | * trendLog 86 | * file 87 | 88 | As of 1.3.x version there is support for following object types: 89 | 90 | * notificationClass 91 | 92 | Most of them is still commendable and supports access through `present value` property, but some of them bring additional properties 93 | which are not visible through wrapper APIs. 94 | 95 | If you are interested in adding other types of objects to API feel free to submit PR. 96 | 97 | Each property of type enlisted above can be read and attempted to set via `client.getPropertyValue` and `client.setPropertyValue` method calls. 98 | There are two variants of set/get property methods - with and without converter argument. 99 | If you know type of property then you can convert it upfront to bacnet4j structure. 100 | By default, library will try to guess, however in most of the cases it may fail. 101 | 102 | ### How to build 103 | 104 | To build project you need: 105 | * Java 8 106 | * Apache Maven 2+ 107 | 108 | ## Links 109 | 110 | * [Mango Automation BACnet4J](https://github.com/MangoAutomation/BACnet4J) - this is currently maintained version of BACnet4J used in this project. 111 | * [Bacnet4J site](http://bacnet.sourceforge.net) 112 | * [ConnectorIO Addons](https://github.com/connectorio/connectorio-addons) - consist a bacnet binding which integrates bacnet networks (mstp and ip) with openhab 2.x and above 113 | * [BACNet-openHAB-binding](https://github.com/openhab/org.openhab.binding.bacnet) - openhab 1.x binding which uses this project. 114 | -------------------------------------------------------------------------------- /api/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 4.0.0 5 | 6 | 7 | org.code-house.bacnet4j 8 | bacnet4j-wrapper 9 | 1.3.0-SNAPSHOT 10 | 11 | 12 | api 13 | bundle 14 | 15 | Code-House :: Bacnet4J Wrapper :: API 16 | General definition of software interface/API. 17 | 18 | 19 | 20 | jssc;resolution:=optional, 21 | * 22 | 23 | 24 | org.code_house.bacnet4j.wrapper.api.*, 25 | org.code_house.bacnet4j.wrapper.device.*, 26 | com.serotonin.bacnet4j.*;;version=${bacnet4j.version}, 27 | 28 | lohbihler.scheduler;version=${sero-scheduler.version}, 29 | lohbihler.warp;version=${sero-warp.version} 30 | 31 | 32 | 33 | 34 | 35 | org.slf4j 36 | slf4j-api 37 | 38 | 39 | 40 | org.apache.commons 41 | commons-lang3 42 | 43 | 44 | com.infiniteautomation 45 | bacnet4j 46 | compile 47 | true 48 | 49 | 50 | lohbihler 51 | sero-scheduler 52 | compile 53 | true 54 | 55 | 56 | lohbihler 57 | sero-warp 58 | compile 59 | true 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /api/src/main/java/org/code_house/bacnet4j/wrapper/api/BacNetClient.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 Code-House and others. 3 | * 4 | * bacnet4j-wrapper is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * https://www.gnu.org/licenses/gpl-3.0.txt 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with Foobar; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | package org.code_house.bacnet4j.wrapper.api; 21 | 22 | import java.util.List; 23 | import java.util.Set; 24 | import java.util.concurrent.CompletableFuture; 25 | import java.util.stream.Collectors; 26 | 27 | /** 28 | * Definition of bacnet client 29 | * 30 | * @author Łukasz Dywicki <luke@code-house.org> 31 | */ 32 | public interface BacNetClient { 33 | 34 | void start(); 35 | 36 | void stop(); 37 | 38 | /** 39 | * No timeout variant of discovery. 40 | * 41 | * This operation will send a discovery packet and wait for results as long as future is not cancelled. 42 | * 43 | * @param discoveryListener Listener which will get information about discovery devices. 44 | * @return Future which allows to stop discovery. 45 | */ 46 | CompletableFuture listenForDevices(DeviceDiscoveryListener discoveryListener); 47 | 48 | /** 49 | * No timeout variant of discovery with range options. 50 | * 51 | * This operation will send a discovery packet and wait for results as long as future is not cancelled. 52 | * Discovery request will contain instance limits. 53 | * 54 | * @param discoveryListener Listener which will get information about discovery devices. 55 | * @param min Optional minimum device number. 56 | * @param max Optional maximum device number. 57 | * @return Future which allows to stop discovery. 58 | */ 59 | CompletableFuture listenForDevices(DeviceDiscoveryListener discoveryListener, Integer min, Integer max); 60 | 61 | CompletableFuture> doDiscoverDevices(DeviceDiscoveryListener discoveryListener, long timeout); 62 | 63 | Set discoverDevices(long timeout); 64 | 65 | Set discoverDevices(DeviceDiscoveryListener discoveryListener, long timeout); 66 | 67 | /** 68 | * This method is deprecated in favor of properly named {{@link #getDeviceObjects(Device)}}. 69 | * 70 | * @param device Device. 71 | * @return List of bacnet objects (properties). 72 | */ 73 | @Deprecated 74 | default List getDeviceProperties(Device device) { 75 | return getDeviceObjects(device).stream() 76 | .map(obj -> new Property(obj.getDevice(), obj.getId(), obj.getType())) 77 | .collect(Collectors.toList()); 78 | } 79 | 80 | /** 81 | * Retrieve device objects. 82 | * 83 | * @param device Device. 84 | * @return Object. 85 | */ 86 | List getDeviceObjects(Device device); 87 | 88 | /** 89 | * Retrieves present values for given properties. 90 | * 91 | * @param properties Property list. 92 | * @return Present values. 93 | * @deprecated Please migrate code usage to {{@link #getPresentValues(List)}} 94 | */ 95 | @Deprecated 96 | default List getPropertyValues(List properties) { 97 | return getPresentValues(properties.stream() 98 | .map(Property::toBacNetObject) 99 | .collect(Collectors.toList()) 100 | ); 101 | } 102 | 103 | 104 | /** 105 | * Retrieve list with present values of passed objects. 106 | * 107 | * @param objects 108 | * @return 109 | */ 110 | List getPresentValues(List objects); 111 | 112 | // fetch present value 113 | @Deprecated 114 | default T getPropertyValue(Property property, BacNetToJavaConverter converter) { 115 | return getPresentValue(property.toBacNetObject(), converter); 116 | } 117 | T getPresentValue(BacNetObject object, BacNetToJavaConverter converter); 118 | 119 | @Deprecated 120 | default void setPropertyValue(Property property, T value, JavaToBacNetConverter converter) { 121 | setPresentValue(property.toBacNetObject(), value, converter); 122 | } 123 | 124 | @Deprecated 125 | default void setPropertyValue(Property property, T value, JavaToBacNetConverter converter, int priority) { 126 | setPresentValue(property.toBacNetObject(), value, converter, priority); 127 | } 128 | 129 | @Deprecated 130 | default void setPropertyValue(Property property, T value, JavaToBacNetConverter converter, Priority priority) { 131 | setPresentValue(property.toBacNetObject(), value, converter, priority); 132 | } 133 | 134 | void setPresentValue(BacNetObject object, T value, JavaToBacNetConverter converter); 135 | void setPresentValue(BacNetObject object, T value, JavaToBacNetConverter converter, int priority); 136 | void setPresentValue(BacNetObject object, T value, JavaToBacNetConverter converter, Priority priority); 137 | 138 | void setObjectPropertyValue(BacNetObject object, String property, T value, JavaToBacNetConverter converter); 139 | void setObjectPropertyValue(BacNetObject object, String property, T value, JavaToBacNetConverter converter, int priority); 140 | void setObjectPropertyValue(BacNetObject object, String property, T value, JavaToBacNetConverter converter, Priority priority); 141 | 142 | List getObjectPropertyNames(BacNetObject object); 143 | T getObjectPropertyValue(BacNetObject object, String attribute, BacNetToJavaConverter converter); 144 | 145 | List getObjectAttributeValues(BacNetObject object, List properties); 146 | 147 | } 148 | -------------------------------------------------------------------------------- /api/src/main/java/org/code_house/bacnet4j/wrapper/api/BacNetClientBase.java: -------------------------------------------------------------------------------- 1 | package org.code_house.bacnet4j.wrapper.api; 2 | 3 | import com.serotonin.bacnet4j.LocalDevice; 4 | import com.serotonin.bacnet4j.ServiceFuture; 5 | import com.serotonin.bacnet4j.exception.BACnetException; 6 | import com.serotonin.bacnet4j.service.acknowledgement.ReadPropertyAck; 7 | import com.serotonin.bacnet4j.service.acknowledgement.ReadPropertyMultipleAck; 8 | import com.serotonin.bacnet4j.service.confirmed.ReadPropertyMultipleRequest; 9 | import com.serotonin.bacnet4j.service.confirmed.ReadPropertyRequest; 10 | import com.serotonin.bacnet4j.service.confirmed.WritePropertyRequest; 11 | import com.serotonin.bacnet4j.service.unconfirmed.WhoIsRequest; 12 | import com.serotonin.bacnet4j.type.Encodable; 13 | import com.serotonin.bacnet4j.type.constructed.ReadAccessResult; 14 | import com.serotonin.bacnet4j.type.constructed.ReadAccessSpecification; 15 | import com.serotonin.bacnet4j.type.constructed.SequenceOf; 16 | import com.serotonin.bacnet4j.type.enumerated.ObjectType; 17 | import com.serotonin.bacnet4j.type.enumerated.PropertyIdentifier; 18 | import com.serotonin.bacnet4j.type.primitive.ObjectIdentifier; 19 | import com.serotonin.bacnet4j.type.primitive.UnsignedInteger; 20 | import java.util.ArrayList; 21 | import java.util.Collections; 22 | import java.util.List; 23 | import java.util.Optional; 24 | import java.util.Set; 25 | import java.util.concurrent.CompletableFuture; 26 | import java.util.concurrent.ExecutionException; 27 | import java.util.concurrent.ExecutorService; 28 | import java.util.concurrent.Executors; 29 | import java.util.concurrent.Future; 30 | import java.util.concurrent.atomic.AtomicInteger; 31 | import org.code_house.bacnet4j.wrapper.api.util.ForwardingAdapter; 32 | import org.code_house.bacnet4j.wrapper.device.DefaultDeviceFactory; 33 | import org.code_house.bacnet4j.wrapper.device.DeviceFactory; 34 | import org.slf4j.Logger; 35 | import org.slf4j.LoggerFactory; 36 | 37 | public abstract class BacNetClientBase implements BacNetClient { 38 | 39 | private final static AtomicInteger instances = new AtomicInteger(); 40 | 41 | protected final Logger logger = LoggerFactory.getLogger(getClass()); 42 | protected final LocalDevice localDevice; 43 | protected final DeviceFactory deviceFactory; 44 | 45 | protected final ExecutorService executor = Executors.newFixedThreadPool(2, r -> { 46 | Thread thread = new Thread(r, "bacnet-client-" + instances.incrementAndGet() + "-discovery"); 47 | thread.setDaemon(true); 48 | return thread; 49 | }); 50 | 51 | protected BacNetClientBase(LocalDevice device) { 52 | this(device, new DefaultDeviceFactory()); 53 | } 54 | 55 | protected BacNetClientBase(LocalDevice device, DeviceFactory deviceFactory) { 56 | this.localDevice = device; 57 | this.deviceFactory = deviceFactory; 58 | } 59 | 60 | @Override 61 | public void start() { 62 | try { 63 | localDevice.initialize(); 64 | } catch (Exception e) { 65 | throw new BacNetClientException("Unable to initialize client", e); 66 | } 67 | } 68 | 69 | @Override 70 | public void stop() { 71 | executor.shutdown(); 72 | localDevice.terminate(); 73 | } 74 | 75 | @Override 76 | public Set discoverDevices(final long timeout) { 77 | return discoverDevices(new NoopDiscoveryListener(), timeout); 78 | } 79 | 80 | @Override 81 | public CompletableFuture listenForDevices(DeviceDiscoveryListener discoveryListener) { 82 | return listenForDevices(discoveryListener, new WhoIsRequest()); 83 | } 84 | 85 | @Override 86 | public CompletableFuture listenForDevices(DeviceDiscoveryListener discoveryListener, Integer min, Integer max) { 87 | return listenForDevices(discoveryListener, new WhoIsRequest( 88 | min == null ? null : new UnsignedInteger(min), 89 | max == null ? null : new UnsignedInteger(max) 90 | )); 91 | } 92 | 93 | protected CompletableFuture listenForDevices(DeviceDiscoveryListener discoveryListener, WhoIsRequest request) { 94 | ForwardingAdapter listener = new ForwardingAdapter(executor, new DiscoveryEventAdapter(discoveryListener, deviceFactory, localDevice)); 95 | localDevice.getEventHandler().addListener(listener); 96 | localDevice.sendGlobalBroadcast(request); 97 | 98 | return new CompletableFuture<>() { 99 | @Override 100 | public boolean cancel(boolean b) { 101 | complete(null); 102 | localDevice.getEventHandler().removeListener(listener); 103 | return super.cancel(b); 104 | } 105 | }; 106 | } 107 | 108 | @Override 109 | public CompletableFuture> doDiscoverDevices(final DeviceDiscoveryListener discoveryListener, final long timeout) { 110 | BlockingDiscoveryCallable callable = new BlockingDiscoveryCallable(discoveryListener, deviceFactory, localDevice, timeout, timeout / 10); 111 | ForwardingAdapter listener = new ForwardingAdapter(executor, callable); 112 | localDevice.getEventHandler().addListener(listener); 113 | localDevice.sendGlobalBroadcast(new WhoIsRequest()); 114 | return CompletableFuture.supplyAsync(() -> { 115 | try { 116 | return callable.call(); 117 | } catch (Exception e) { 118 | throw new BacNetClientException("Could not complete discovery task", e); 119 | } 120 | }, executor).whenComplete((devices, throwable) -> { 121 | localDevice.getEventHandler().removeListener(listener); 122 | }); 123 | } 124 | 125 | @Override 126 | public Set discoverDevices(final DeviceDiscoveryListener discoveryListener, final long timeout) { 127 | BlockingDiscoveryCallable callable = new BlockingDiscoveryCallable(discoveryListener, deviceFactory, localDevice, timeout, timeout / 10); 128 | ForwardingAdapter listener = new ForwardingAdapter(executor, callable); 129 | try { 130 | localDevice.getEventHandler().addListener(listener); 131 | Future> future = executor.submit(callable); 132 | localDevice.sendGlobalBroadcast(new WhoIsRequest()); 133 | // this will block for at least timeout milliseconds 134 | return future.get(); 135 | } catch (ExecutionException e) { 136 | logger.error("Device discovery have failed", e); 137 | } catch (InterruptedException e) { 138 | logger.error("Could not discover devices due to timeout", e); 139 | } finally { 140 | localDevice.getEventHandler().removeListener(listener); 141 | } 142 | return Collections.emptySet(); 143 | } 144 | 145 | @Override 146 | public List getDeviceObjects(Device device) { 147 | try { 148 | ReadPropertyAck ack = localDevice.send(device.getBacNet4jAddress(), 149 | new ReadPropertyRequest(device.getObjectIdentifier(), PropertyIdentifier.objectList) 150 | ).get(); 151 | SequenceOf value = ack.getValue(); 152 | 153 | List objects = new ArrayList<>(); 154 | logger.debug("Received list of BACnet objects. Size {}, values {}", value.getCount(), value); 155 | for (ObjectIdentifier id : value) { 156 | logger.trace("Creating property from object identifier {}", id); 157 | try { 158 | if (ObjectType.device.equals(id.getObjectType())) { 159 | if (device.getInstanceNumber() != id.getInstanceNumber()) { 160 | // track only sub-devices, no point in tracking device itself again 161 | objects.add(createObject(device, id)); 162 | } 163 | continue; 164 | } 165 | objects.add(createObject(device, id)); 166 | } catch (UnsupportedTypeException e) { 167 | logger.warn("Discovered unsupported property, ignoring", e); 168 | } 169 | } 170 | return objects; 171 | } catch (BACnetException e) { 172 | throw new BacNetClientException("Unable to get device properties", e); 173 | } 174 | } 175 | 176 | @Override 177 | public T getPresentValue(BacNetObject object, BacNetToJavaConverter converter) { 178 | try { 179 | ReadPropertyAck presentValue = localDevice.send(object.getDevice().getBacNet4jAddress(), 180 | new ReadPropertyRequest(new ObjectIdentifier(object.getType().getBacNetType(), object.getId()), 181 | PropertyIdentifier.presentValue) 182 | ).get(); 183 | 184 | return (T) getJavaValue(presentValue.getValue(), converter); 185 | } catch (BACnetException e) { 186 | throw new BacNetClientException("Could not get property value", e); 187 | } 188 | } 189 | 190 | @Override 191 | public List getObjectPropertyNames(BacNetObject object) { 192 | try { 193 | ReadPropertyAck answer = localDevice.send(object.getDevice().getBacNet4jAddress(), 194 | new ReadPropertyRequest(new ObjectIdentifier(object.getType().getBacNetType(), object.getId()), 195 | PropertyIdentifier.propertyList) 196 | ).get(); 197 | 198 | if (answer.getValue() instanceof SequenceOf) { 199 | List attributes = new ArrayList<>(); 200 | for (PropertyIdentifier identifier : ((SequenceOf) answer.getValue())) { 201 | attributes.add(identifier.toString()); 202 | } 203 | return attributes; 204 | } 205 | logger.warn("Unexpected answer for object attribute list {}, forcing empty results", answer.getValue()); 206 | return Collections.emptyList(); 207 | } catch (BACnetException e) { 208 | throw new BacNetClientException("Could not get property value", e); 209 | } 210 | } 211 | 212 | @Override 213 | public T getObjectPropertyValue(BacNetObject object, String property, BacNetToJavaConverter converter) { 214 | try { 215 | ReadPropertyAck propertyVal = localDevice.send(object.getDevice().getBacNet4jAddress(), 216 | new ReadPropertyRequest(new ObjectIdentifier(object.getType().getBacNetType(), object.getId()), 217 | PropertyIdentifier.forName(property)) 218 | ).get(); 219 | 220 | return (T) getJavaValue(propertyVal.getValue(), converter); 221 | } catch (BACnetException e) { 222 | throw new BacNetClientException("Could not get property value", e); 223 | } 224 | } 225 | 226 | @Override 227 | public List getObjectAttributeValues(BacNetObject object, List properties) { 228 | BypassBacnetConverter converter = new BypassBacnetConverter(); 229 | Device device = object.getDevice(); 230 | List values = new ArrayList<>(); 231 | if (!device.isReadMultiple()) { 232 | for (String property : properties) { 233 | try { 234 | ReadPropertyAck answer = localDevice.send(device.getBacNet4jAddress(), 235 | new ReadPropertyRequest(object.getBacNet4jIdentifier(), PropertyIdentifier.forName(property)) 236 | ).get(); 237 | values.add(converter.fromBacNet(answer.getValue())); 238 | } catch (BACnetException e) { 239 | throw new BacNetClientException("Unable to read object properties.", e); 240 | } 241 | } 242 | return values; 243 | } 244 | 245 | List specifications = new ArrayList<>(); 246 | for (String property : properties) { 247 | specifications.add(new ReadAccessSpecification(new ObjectIdentifier(object.getType().getBacNetType(), 248 | object.getId()), PropertyIdentifier.forName(property)) 249 | ); 250 | } 251 | 252 | try { 253 | ReadPropertyMultipleAck readValues = localDevice.send(device.getBacNet4jAddress(), 254 | new ReadPropertyMultipleRequest(new SequenceOf<>(specifications)) 255 | ).get(); 256 | SequenceOf listOfReadAccessResults = readValues.getListOfReadAccessResults(); 257 | for (int index = 0; index < listOfReadAccessResults.getCount(); index++) { 258 | ReadAccessResult result = listOfReadAccessResults.get(index); 259 | logger.info("Reading object {} properties {}, values: {}", object, properties.get(index), result.getListOfResults()); 260 | values.add(getJavaValue(result.getListOfResults().get(0).getReadResult().getDatum(), converter)); 261 | } 262 | } catch (BACnetException e) { 263 | throw new BacNetClientException("Unable to read object properties.", e); 264 | } 265 | 266 | return values; 267 | } 268 | 269 | @Override 270 | public List getPresentValues(List objects) { 271 | BypassBacnetConverter converter = new BypassBacnetConverter(); 272 | Device device = objects.get(0).getDevice(); 273 | List values = new ArrayList<>(); 274 | if (!device.isReadMultiple()) { 275 | for (int objectIndex = 0; objectIndex < objects.size(); objectIndex++) { 276 | try { 277 | BacNetObject object = objects.get(objectIndex); 278 | ReadPropertyAck answer = localDevice.send(object.getDevice().getBacNet4jAddress(), 279 | new ReadPropertyRequest(new ObjectIdentifier(object.getType().getBacNetType(), object.getId()), 280 | PropertyIdentifier.presentValue) 281 | ).get(); 282 | values.add(converter.fromBacNet(answer.getValue())); 283 | } catch (BACnetException e) { 284 | throw new BacNetClientException("Sequential read of object present values failed", e); 285 | } 286 | } 287 | return values; 288 | } 289 | 290 | List specifications = new ArrayList<>(); 291 | for (int propertyIndex = 0; propertyIndex < objects.size(); propertyIndex++) { 292 | BacNetObject object = objects.get(propertyIndex); 293 | specifications.add(new ReadAccessSpecification(new ObjectIdentifier(object.getType().getBacNetType(), object.getId()), 294 | PropertyIdentifier.presentValue) 295 | ); 296 | 297 | if (propertyIndex % 3 == 0 || propertyIndex + 1 == objects.size()) { 298 | try { 299 | ReadPropertyMultipleAck readValues = localDevice.send(device.getBacNet4jAddress(), 300 | new ReadPropertyMultipleRequest(new SequenceOf<>(specifications))) 301 | .get(); 302 | specifications.clear(); 303 | SequenceOf listOfReadAccessResults = readValues.getListOfReadAccessResults(); 304 | for (int index = 0; index < listOfReadAccessResults.getCount(); index++) { 305 | ReadAccessResult result = listOfReadAccessResults.get(index); 306 | logger.info("Reading property {} value from {}", objects.get(propertyIndex), result.getListOfResults()); 307 | values.add(getJavaValue(result.getListOfResults().get(0).getReadResult().getDatum(), converter)); 308 | } 309 | } catch (BACnetException e) { 310 | throw new BacNetClientException("Unable to read properties.", e); 311 | } 312 | } 313 | } 314 | return values; 315 | } 316 | 317 | private T getJavaValue(Encodable datum, BacNetToJavaConverter converter) { 318 | if (converter == null) { 319 | return null; 320 | } 321 | return converter.fromBacNet(datum); 322 | } 323 | 324 | private Encodable getBacNetValue(T object, JavaToBacNetConverter converter) { 325 | if (converter == null) { 326 | return null; 327 | } 328 | return converter.toBacNet(object); 329 | } 330 | 331 | @Override 332 | public void setPresentValue(BacNetObject object, T value, JavaToBacNetConverter converter) { 333 | setPresentValue(object, value, converter, 0); 334 | } 335 | 336 | @Override 337 | public void setPresentValue(BacNetObject object, T value, JavaToBacNetConverter converter, int priority) { 338 | setPresentValue(object, value, converter, Priorities.get(priority).orElse(null)); 339 | } 340 | 341 | @Override 342 | public void setPresentValue(BacNetObject object, T value, JavaToBacNetConverter converter, Priority priority) { 343 | setObjectPropertyValue(object, "present-value", value, converter, priority); 344 | } 345 | 346 | @Override 347 | public void setObjectPropertyValue(BacNetObject object, String property, T value, JavaToBacNetConverter converter) { 348 | setObjectPropertyValue(object, property, value, converter, 0); 349 | } 350 | 351 | @Override 352 | public void setObjectPropertyValue(BacNetObject object, String property, T value, JavaToBacNetConverter converter, int priority) { 353 | setObjectPropertyValue(object, property, value, converter, Priorities.get(priority).orElse(null)); 354 | } 355 | 356 | @Override 357 | public void setObjectPropertyValue(BacNetObject object, String property, T value, JavaToBacNetConverter converter, Priority priority) { 358 | Encodable bacNetValue = getBacNetValue(value, converter); 359 | UnsignedInteger bacnetPriority = Optional.ofNullable(priority) 360 | .map(Priority::getPriority) 361 | .map(UnsignedInteger::new) 362 | .orElse(null); 363 | ServiceFuture send = localDevice.send(object.getDevice().getBacNet4jAddress(), 364 | new WritePropertyRequest(object.getBacNet4jIdentifier(), PropertyIdentifier.forName(property), null, bacNetValue, bacnetPriority) 365 | ); 366 | try { 367 | send.get(); 368 | } catch (BACnetException e) { 369 | if (!"Timeout waiting for response.".equals(e.getMessage())) { 370 | throw new BacNetClientException("Could not set property value", e); 371 | } 372 | logger.warn("Ignoring timeout for write property since bacnet4j misses simple ACK's."); 373 | } 374 | } 375 | 376 | private BacNetObject createObject(Device device, ObjectIdentifier id) { 377 | if (device.isReadMultiple()) { 378 | List specs = new ArrayList<>(); 379 | specs.add(new ReadAccessSpecification(id, PropertyIdentifier.presentValue)); 380 | specs.add(new ReadAccessSpecification(id, PropertyIdentifier.units)); 381 | specs.add(new ReadAccessSpecification(id, PropertyIdentifier.objectName)); 382 | specs.add(new ReadAccessSpecification(id, PropertyIdentifier.description)); 383 | 384 | try { 385 | ReadPropertyMultipleAck propertyDescriptorAck = localDevice.send(device.getBacNet4jAddress(), 386 | new ReadPropertyMultipleRequest(new SequenceOf<>(specs)) 387 | ).get(); 388 | SequenceOf readAccessResults = propertyDescriptorAck.getListOfReadAccessResults(); 389 | return createObject(device, id.getInstanceNumber(), Type.valueOf(id.getObjectType()), 390 | readAccessResults 391 | ); 392 | } catch (BACnetException e) { 393 | throw new BacNetClientException("Unable to fetch property description", e); 394 | } 395 | } 396 | 397 | return createObjectSingleRead(device, id); 398 | } 399 | 400 | private BacNetObject createObjectSingleRead(Device device, ObjectIdentifier id) { 401 | Encodable presentValue = readOrNull(device, id, PropertyIdentifier.presentValue); 402 | Encodable units = readOrNull(device, id, PropertyIdentifier.units); 403 | Encodable objectName = readOrNull(device, id, PropertyIdentifier.objectName); 404 | Encodable description = readOrNull(device, id, PropertyIdentifier.description); 405 | if (presentValue == null && units == null && objectName == null && description == null) { 406 | //throw new BacNetClientException("Could not construct object " + id + " for device " + device, new IllegalArgumentException()); 407 | return new BacNetObject(device, id.getInstanceNumber(), Type.valueOf(id.getObjectType()), null, null, null); 408 | } 409 | return new BacNetObject( 410 | device, id.getInstanceNumber(), Type.valueOf(id.getObjectType()), 411 | objectName == null ? "" : objectName.toString(), 412 | description == null ? "" : description.toString(), 413 | units == null ? null : units.toString() 414 | ); 415 | } 416 | 417 | protected abstract BacNetObject createObject(Device device, int instance, Type type, SequenceOf readAccessResults); 418 | 419 | protected T readOrNull(Device device, ObjectIdentifier object, PropertyIdentifier identifier) { 420 | ObjectIdentifier id = new ObjectIdentifier(object.getObjectType(), object.getInstanceNumber()); 421 | try { 422 | ReadPropertyAck presentValue = localDevice.send(device.getBacNet4jAddress(), 423 | new ReadPropertyRequest(id, identifier) 424 | ).get(); 425 | return (T) presentValue.getValue(); 426 | } catch (BACnetException e) { 427 | logger.trace("Failed to retrieve property {} for device {} object {}", identifier, device, object, e); 428 | return null; 429 | } 430 | } 431 | 432 | } 433 | -------------------------------------------------------------------------------- /api/src/main/java/org/code_house/bacnet4j/wrapper/api/BacNetClientException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 Code-House and others. 3 | * 4 | * bacnet4j-wrapper is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * https://www.gnu.org/licenses/gpl-3.0.txt 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with Foobar; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | package org.code_house.bacnet4j.wrapper.api; 21 | 22 | /** 23 | * Exception thrown by client in case of failures. 24 | * 25 | * @author Łukasz Dywicki <luke@code-house.org> 26 | */ 27 | public class BacNetClientException extends RuntimeException { 28 | 29 | public BacNetClientException(String message, Throwable cause) { 30 | super(message, cause); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /api/src/main/java/org/code_house/bacnet4j/wrapper/api/BacNetObject.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2022 Code-House and others. 3 | * 4 | * bacnet4j-wrapper is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * https://www.gnu.org/licenses/gpl-3.0.txt 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with Foobar; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | package org.code_house.bacnet4j.wrapper.api; 21 | 22 | import com.serotonin.bacnet4j.type.primitive.ObjectIdentifier; 23 | import java.util.Objects; 24 | 25 | public class BacNetObject { 26 | 27 | private final Device device; 28 | private final int id; 29 | private final Type type; 30 | 31 | private final String name; 32 | private final String description; 33 | private final String units; 34 | 35 | public BacNetObject(Device device, int id, Type type) { 36 | this(device, id, type, "", "", ""); 37 | } 38 | 39 | public BacNetObject(Device device, int id, Type type, String name, String description, String units) { 40 | this.device = device; 41 | this.id = id; 42 | this.type = type; 43 | this.name = name; 44 | this.description = description; 45 | this.units = units; 46 | } 47 | 48 | public Device getDevice() { 49 | return device; 50 | } 51 | 52 | public int getId() { 53 | return id; 54 | } 55 | 56 | public Type getType() { 57 | return type; 58 | } 59 | 60 | public String getName() { 61 | return name; 62 | } 63 | 64 | public String getDescription() { 65 | return description; 66 | } 67 | 68 | public String getUnits() { 69 | return units; 70 | } 71 | 72 | public ObjectIdentifier getBacNet4jIdentifier() { 73 | return new ObjectIdentifier(type.getBacNetType(), id); 74 | } 75 | 76 | @Override 77 | public boolean equals(Object o) { 78 | if (this == o) { 79 | return true; 80 | } 81 | if (!(o instanceof BacNetObject)) { 82 | return false; 83 | } 84 | BacNetObject that = (BacNetObject) o; 85 | return getId() == that.getId() && Objects.equals(getDevice(), that.getDevice()) 86 | && getType() == that.getType() && Objects.equals(name, that.name) 87 | && Objects.equals(description, that.description) && Objects.equals(units, 88 | that.units); 89 | } 90 | 91 | @Override 92 | public int hashCode() { 93 | return Objects.hash(getDevice(), getId(), getType(), name, description, units); 94 | } 95 | 96 | @Override 97 | public String toString() { 98 | return "BacNet Object[" + device.getInstanceNumber() + "." + type.name() + "." + id + "]"; 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /api/src/main/java/org/code_house/bacnet4j/wrapper/api/BacNetToJavaConverter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 Code-House and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.code_house.bacnet4j.wrapper.api; 17 | 18 | import com.serotonin.bacnet4j.type.Encodable; 19 | 20 | /** 21 | * @author Łukasz Dywicki <luke@code-house.org> 22 | */ 23 | public interface BacNetToJavaConverter { 24 | 25 | T fromBacNet(Encodable encodable); 26 | 27 | } 28 | -------------------------------------------------------------------------------- /api/src/main/java/org/code_house/bacnet4j/wrapper/api/BlockingDiscoveryCallable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 Code-House and others. 3 | * 4 | * bacnet4j-wrapper is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * https://www.gnu.org/licenses/gpl-3.0.txt 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with Foobar; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | package org.code_house.bacnet4j.wrapper.api; 21 | 22 | import com.serotonin.bacnet4j.LocalDevice; 23 | import com.serotonin.bacnet4j.RemoteDevice; 24 | import com.serotonin.bacnet4j.event.DeviceEventAdapter; 25 | import com.serotonin.bacnet4j.exception.BACnetException; 26 | import com.serotonin.bacnet4j.service.acknowledgement.ReadPropertyAck; 27 | import com.serotonin.bacnet4j.service.confirmed.ReadPropertyRequest; 28 | import com.serotonin.bacnet4j.type.Encodable; 29 | import com.serotonin.bacnet4j.type.enumerated.PropertyIdentifier; 30 | import com.serotonin.bacnet4j.type.primitive.ObjectIdentifier; 31 | import com.serotonin.bacnet4j.util.PropertyReferences; 32 | import com.serotonin.bacnet4j.util.PropertyValues; 33 | import com.serotonin.bacnet4j.util.RequestUtils; 34 | import org.code_house.bacnet4j.wrapper.device.DeviceFactory; 35 | import org.slf4j.Logger; 36 | import org.slf4j.LoggerFactory; 37 | 38 | import java.util.LinkedHashSet; 39 | import java.util.Set; 40 | import java.util.concurrent.Callable; 41 | 42 | /** 43 | * Callable responsible for calling collecting discovered devices and also sending discovery notifications. 44 | * 45 | * @author Łukasz Dywicki <luke@code-house.org> 46 | */ 47 | public class BlockingDiscoveryCallable extends DiscoveryEventAdapter implements Callable> { 48 | 49 | private final long timeout; 50 | private final long sleep; 51 | private final Set devices = new LinkedHashSet<>(); 52 | private Throwable exception; 53 | 54 | public BlockingDiscoveryCallable(DeviceDiscoveryListener listener, DeviceFactory deviceFactory, LocalDevice localDevice, long timeout, long sleep) { 55 | super(listener, deviceFactory, localDevice); 56 | this.timeout = timeout; 57 | this.sleep = sleep; 58 | } 59 | 60 | @Override 61 | public Set call() throws Exception { 62 | long time = 0; 63 | do { 64 | time += sleep; 65 | Thread.sleep(sleep); 66 | } while (time < timeout); 67 | 68 | if (exception != null) { 69 | throw new BacNetClientException("Could not finish discovery due to error", exception); 70 | } 71 | return devices; 72 | } 73 | 74 | @Override 75 | public void iAmReceived(RemoteDevice d) { 76 | Device device = createDevice(d); 77 | devices.add(device); 78 | this.listener.deviceDiscovered(device); 79 | } 80 | 81 | @Override 82 | public void listenerException(Throwable e) { 83 | this.exception = e; 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /api/src/main/java/org/code_house/bacnet4j/wrapper/api/BypassBacnetConverter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 Code-House and others. 3 | * 4 | * bacnet4j-wrapper is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * https://www.gnu.org/licenses/gpl-3.0.txt 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with Foobar; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | package org.code_house.bacnet4j.wrapper.api; 21 | 22 | import com.serotonin.bacnet4j.type.Encodable; 23 | 24 | public class BypassBacnetConverter implements BacNetToJavaConverter { 25 | 26 | @Override 27 | public Encodable fromBacNet(Encodable datum) { 28 | return datum; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /api/src/main/java/org/code_house/bacnet4j/wrapper/api/CollectingDiscoveryListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 Code-House and others. 3 | * 4 | * bacnet4j-wrapper is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * https://www.gnu.org/licenses/gpl-3.0.txt 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with Foobar; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | package org.code_house.bacnet4j.wrapper.api; 21 | 22 | import java.util.LinkedHashSet; 23 | import java.util.Set; 24 | 25 | /** 26 | * Simple discovery listener which just returns all devices. 27 | * 28 | * @author Łukasz Dywicki <luke@code-house.org> 29 | */ 30 | public class CollectingDiscoveryListener implements DeviceDiscoveryListener { 31 | 32 | private final Set devices = new LinkedHashSet<>(); 33 | 34 | @Override 35 | public void deviceDiscovered(Device device) { 36 | devices.add(device); 37 | } 38 | 39 | public Set getDevices() { 40 | return devices; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /api/src/main/java/org/code_house/bacnet4j/wrapper/api/DefaultPriority.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 Code-House and others. 3 | * 4 | * bacnet4j-wrapper is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * https://www.gnu.org/licenses/gpl-3.0.txt 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with Foobar; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | package org.code_house.bacnet4j.wrapper.api; 21 | 22 | /** 23 | * Immutable priority implementation. 24 | * 25 | * @author Łukasz Dywicki <luke@code-house.org> 26 | */ 27 | public class DefaultPriority implements Priority { 28 | 29 | private final int priority; 30 | private final String label; 31 | 32 | public DefaultPriority(int priority, String label) { 33 | this.priority = priority; 34 | this.label = label; 35 | } 36 | 37 | @Override 38 | public String getLabel() { 39 | return label; 40 | } 41 | 42 | @Override 43 | public int getPriority() { 44 | return priority; 45 | } 46 | 47 | @Override 48 | public String toString() { 49 | return "Priority " + priority + ", " + label; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /api/src/main/java/org/code_house/bacnet4j/wrapper/api/Device.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 Code-House and others. 3 | * 4 | * bacnet4j-wrapper is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * https://www.gnu.org/licenses/gpl-3.0.txt 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with Foobar; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | package org.code_house.bacnet4j.wrapper.api; 21 | 22 | import com.serotonin.bacnet4j.type.constructed.Address; 23 | import com.serotonin.bacnet4j.type.enumerated.ObjectType; 24 | import com.serotonin.bacnet4j.type.primitive.ObjectIdentifier; 25 | import com.serotonin.bacnet4j.type.primitive.OctetString; 26 | import java.util.Arrays; 27 | import java.util.Objects; 28 | 29 | /** 30 | * Representation of bacnet device. 31 | * 32 | * @author Łukasz Dywicki <luke@code-house.org> 33 | */ 34 | public class Device { 35 | 36 | private final int instanceNumber; 37 | private final byte[] address; 38 | private final int networkNumber; 39 | 40 | private String modelName = ""; 41 | private String vendorName = ""; 42 | private int vendorIdentifier ; 43 | private String serialNumber = ""; 44 | private String name = ""; 45 | private boolean readMultiple; 46 | 47 | 48 | public Device(int instanceNumber, byte[] address, int networkNumber) { 49 | this.instanceNumber = instanceNumber; 50 | this.address = address; 51 | this.networkNumber = networkNumber; 52 | } 53 | 54 | public Device(int instanceNumber, Address address) { 55 | this(instanceNumber, address.getMacAddress().getBytes(), address.getNetworkNumber().intValue()); 56 | } 57 | 58 | public ObjectIdentifier getObjectIdentifier() { 59 | return new ObjectIdentifier(ObjectType.device, instanceNumber); 60 | } 61 | 62 | public int getInstanceNumber() { 63 | return instanceNumber; 64 | } 65 | 66 | public byte[] getAddress() { 67 | return address; 68 | } 69 | 70 | public int getNetworkNumber() { 71 | return networkNumber; 72 | } 73 | 74 | public void setModelName(String modelName) { 75 | this.modelName = modelName; 76 | } 77 | 78 | public String getModelName() { 79 | return modelName; 80 | } 81 | 82 | public void setVendorName(String vendorName) { 83 | this.vendorName = vendorName; 84 | } 85 | 86 | public String getVendorName() { 87 | return vendorName; 88 | } 89 | 90 | public int getVendorIdentifier() { 91 | return vendorIdentifier; 92 | } 93 | 94 | public void setVendorIdentifier(int vendorIdentifier) { 95 | this.vendorIdentifier = vendorIdentifier; 96 | } 97 | 98 | public String getSerialNumber() { 99 | return serialNumber; 100 | } 101 | 102 | public void setSerialNumber(String serialNumber) { 103 | this.serialNumber = serialNumber; 104 | } 105 | 106 | public void setName(String name) { 107 | this.name = name; 108 | } 109 | 110 | public String getName() { 111 | return name; 112 | } 113 | 114 | public void setReadPropertyMultiple(boolean readMultiple) { 115 | this.readMultiple = readMultiple; 116 | } 117 | 118 | public boolean isReadMultiple() { 119 | return readMultiple; 120 | } 121 | 122 | public Address getBacNet4jAddress() { 123 | return new Address(networkNumber, address); 124 | } 125 | 126 | @Override 127 | public String toString() { 128 | return "Device [" + instanceNumber + " " + new OctetString(address) + " network " + networkNumber + "]"; 129 | } 130 | 131 | @Override 132 | public boolean equals(Object o) { 133 | if (this == o) { 134 | return true; 135 | } 136 | if (!(o instanceof Device)) { 137 | return false; 138 | } 139 | Device device = (Device) o; 140 | return getInstanceNumber() == device.getInstanceNumber() 141 | && getNetworkNumber() == device.getNetworkNumber() 142 | && Arrays.equals(getAddress(), device.getAddress()) && Objects.equals(getModelName(), device.getModelName()) 143 | && Objects.equals(getVendorName(), device.getVendorName()) 144 | && Objects.equals(getName(), device.getName()) 145 | && Objects.equals(isReadMultiple(), device.isReadMultiple()); 146 | } 147 | 148 | @Override 149 | public int hashCode() { 150 | int result = Objects.hash(getInstanceNumber(), 151 | getNetworkNumber(), 152 | getModelName(), 153 | getVendorName(), 154 | getName(), 155 | isReadMultiple() 156 | ); 157 | result = 31 * result + Arrays.hashCode(getAddress()); 158 | return result; 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /api/src/main/java/org/code_house/bacnet4j/wrapper/api/DeviceDiscoveryListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 Code-House and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.code_house.bacnet4j.wrapper.api; 17 | 18 | public interface DeviceDiscoveryListener { 19 | 20 | void deviceDiscovered(Device device); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /api/src/main/java/org/code_house/bacnet4j/wrapper/api/DiscoveryEventAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 Code-House and others. 3 | * 4 | * bacnet4j-wrapper is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * https://www.gnu.org/licenses/gpl-3.0.txt 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with Foobar; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | package org.code_house.bacnet4j.wrapper.api; 21 | 22 | import com.serotonin.bacnet4j.LocalDevice; 23 | import com.serotonin.bacnet4j.RemoteDevice; 24 | import com.serotonin.bacnet4j.event.DeviceEventAdapter; 25 | import com.serotonin.bacnet4j.exception.BACnetException; 26 | import com.serotonin.bacnet4j.service.acknowledgement.ReadPropertyAck; 27 | import com.serotonin.bacnet4j.service.confirmed.ReadPropertyRequest; 28 | import com.serotonin.bacnet4j.type.Encodable; 29 | import com.serotonin.bacnet4j.type.enumerated.PropertyIdentifier; 30 | import com.serotonin.bacnet4j.type.primitive.ObjectIdentifier; 31 | import com.serotonin.bacnet4j.util.PropertyReferences; 32 | import com.serotonin.bacnet4j.util.PropertyValues; 33 | import com.serotonin.bacnet4j.util.RequestUtils; 34 | import java.util.LinkedHashSet; 35 | import java.util.Set; 36 | import java.util.concurrent.Callable; 37 | import org.code_house.bacnet4j.wrapper.device.DeviceFactory; 38 | import org.slf4j.Logger; 39 | import org.slf4j.LoggerFactory; 40 | 41 | /** 42 | * A discovery event adapter responsible for calling {@link DeviceDiscoveryListener} which is declared 43 | * by bacnet4j-wrapper api. 44 | * 45 | * @author Łukasz Dywicki <luke@code-house.org> 46 | */ 47 | public class DiscoveryEventAdapter extends DeviceEventAdapter { 48 | 49 | private final Logger logger = LoggerFactory.getLogger(DiscoveryEventAdapter.class); 50 | 51 | protected final DeviceDiscoveryListener listener; 52 | protected final DeviceFactory deviceFactory; 53 | protected final LocalDevice localDevice; 54 | public DiscoveryEventAdapter(DeviceDiscoveryListener listener, DeviceFactory deviceFactory, LocalDevice localDevice) { 55 | this.listener = listener; 56 | this.deviceFactory = deviceFactory; 57 | this.localDevice = localDevice; 58 | } 59 | 60 | protected Device createDevice(RemoteDevice d) { 61 | try { 62 | final ObjectIdentifier oid = d.getObjectIdentifier(); 63 | 64 | // Get the device's supported services 65 | if (d.getServicesSupported() == null) { 66 | final ReadPropertyAck supportedServicesAck = (ReadPropertyAck) localDevice 67 | .send(d, new ReadPropertyRequest(oid, PropertyIdentifier.protocolServicesSupported)).get(); 68 | d.setDeviceProperty(PropertyIdentifier.protocolServicesSupported, supportedServicesAck.getValue()); 69 | } 70 | 71 | // Uses the readProperties method here because this list will probably be extended. 72 | final PropertyReferences properties = new PropertyReferences(); 73 | addIfMissing(d, properties, PropertyIdentifier.objectName); 74 | addIfMissing(d, properties, PropertyIdentifier.protocolVersion); 75 | addIfMissing(d, properties, PropertyIdentifier.vendorIdentifier); 76 | addIfMissing(d, properties, PropertyIdentifier.modelName); 77 | addIfMissing(d, properties, PropertyIdentifier.maxSegmentsAccepted); 78 | 79 | if (properties.size() > 0) { 80 | // Only send a request if we have to. 81 | final PropertyValues values = RequestUtils.readProperties(localDevice, d, properties, false, null); 82 | 83 | values.forEach((opr) -> { 84 | final Encodable value = values.getNullOnError(oid, opr.getPropertyIdentifier()); 85 | d.setDeviceProperty(opr.getPropertyIdentifier(), value); 86 | }); 87 | } 88 | } catch (BACnetException e) { 89 | logger.error("Could not collect additional device information", e); 90 | } 91 | Device device = deviceFactory.createDevice(d); 92 | if (d.getModelName() != null && !d.getModelName().isEmpty()) { 93 | device.setModelName(d.getModelName()); 94 | } 95 | if (d.getVendorName() != null && !d.getVendorName().isEmpty()) { 96 | device.setVendorName(d.getVendorName()); 97 | } 98 | if (d.getVendorIdentifier() != -1) { 99 | device.setVendorIdentifier(d.getVendorIdentifier()); 100 | } 101 | if (d.getCharacterStringProperty(PropertyIdentifier.serialNumber) != null 102 | && !d.getCharacterStringProperty(PropertyIdentifier.serialNumber).isEmpty()) { 103 | device.setSerialNumber(d.getCharacterStringProperty(PropertyIdentifier.serialNumber)); 104 | } 105 | if (d.getName() != null && !d.getName().isEmpty()) { 106 | device.setName(d.getName()); 107 | } 108 | if (!d.getServicesSupported().isReadPropertyMultiple()) { 109 | device.setReadPropertyMultiple(false); 110 | } 111 | return device; 112 | } 113 | 114 | @Override 115 | public void iAmReceived(RemoteDevice d) { 116 | Device device = createDevice(d); 117 | this.listener.deviceDiscovered(device); 118 | } 119 | 120 | @Override 121 | public void listenerException(Throwable e) { 122 | logger.warn("Error while running discovery process", e); 123 | } 124 | 125 | private static void addIfMissing(final RemoteDevice d, final PropertyReferences properties, final PropertyIdentifier pid) { 126 | if (d.getDeviceProperty(pid) == null) { 127 | properties.add(d.getObjectIdentifier(), pid); 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /api/src/main/java/org/code_house/bacnet4j/wrapper/api/JavaToBacNetConverter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 Code-House and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package org.code_house.bacnet4j.wrapper.api; 17 | 18 | import com.serotonin.bacnet4j.type.Encodable; 19 | 20 | /** 21 | * @author Łukasz Dywicki <luke@code-house.org> 22 | */ 23 | public interface JavaToBacNetConverter { 24 | 25 | Encodable toBacNet(T object); 26 | 27 | } 28 | -------------------------------------------------------------------------------- /api/src/main/java/org/code_house/bacnet4j/wrapper/api/NoopDiscoveryListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 Code-House and others. 3 | * 4 | * bacnet4j-wrapper is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * https://www.gnu.org/licenses/gpl-3.0.txt 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with Foobar; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | package org.code_house.bacnet4j.wrapper.api; 21 | 22 | /** 23 | * A dummy discovery listener used when no one is interested in receiving notifications. 24 | * 25 | * @author Łukasz Dywicki <luke@code-house.org> 26 | */ 27 | public class NoopDiscoveryListener implements DeviceDiscoveryListener { 28 | @Override 29 | public void deviceDiscovered(Device device) { 30 | 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /api/src/main/java/org/code_house/bacnet4j/wrapper/api/Priorities.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 Code-House and others. 3 | * 4 | * bacnet4j-wrapper is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * https://www.gnu.org/licenses/gpl-3.0.txt 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with Foobar; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | package org.code_house.bacnet4j.wrapper.api; 21 | 22 | import java.util.*; 23 | 24 | /** 25 | * Constants related to bacnet priorities. 26 | * 27 | * @author Łukasz Dywicki <luke@code-house.org> 28 | */ 29 | public interface Priorities { 30 | 31 | Priority PRIORITY_1 = new DefaultPriority(1, "Manual-Life Safety"); 32 | Priority PRIORITY_2 = new DefaultPriority(2, "Automatic-Life Safety"); 33 | Priority PRIORITY_3 = new DefaultPriority(3, "Available"); 34 | Priority PRIORITY_4 = new DefaultPriority(4, "Available"); 35 | Priority PRIORITY_5 = new DefaultPriority(5, "Critical Equipment Control"); 36 | Priority PRIORITY_6 = new DefaultPriority(6, "Minimum On/Off"); 37 | Priority PRIORITY_7 = new DefaultPriority(7, "Available"); 38 | Priority PRIORITY_8 = new DefaultPriority(8, "Manual Operator"); 39 | Priority PRIORITY_9 = new DefaultPriority(9, "Available"); 40 | Priority PRIORITY_10 = new DefaultPriority(10, "Available"); 41 | Priority PRIORITY_11 = new DefaultPriority(11, "Available"); 42 | Priority PRIORITY_12 = new DefaultPriority(12, "Available"); 43 | Priority PRIORITY_13 = new DefaultPriority(13, "Available"); 44 | Priority PRIORITY_14 = new DefaultPriority(14, "Available"); 45 | Priority PRIORITY_15 = new DefaultPriority(15, "Available"); 46 | Priority PRIORITY_16 = new DefaultPriority(16, "Available"); 47 | 48 | Map PRIORITIES = Collections.unmodifiableMap(new HashMap() {{ 49 | put(1, PRIORITY_1); 50 | put(2, PRIORITY_2); 51 | put(3, PRIORITY_3); 52 | put(4, PRIORITY_4); 53 | put(5, PRIORITY_5); 54 | put(6, PRIORITY_6); 55 | put(7, PRIORITY_7); 56 | put(8, PRIORITY_8); 57 | put(9, PRIORITY_9); 58 | put(10, PRIORITY_10); 59 | put(11, PRIORITY_11); 60 | put(12, PRIORITY_12); 61 | put(13, PRIORITY_13); 62 | put(14, PRIORITY_14); 63 | put(15, PRIORITY_15); 64 | put(16, PRIORITY_16); 65 | }}); 66 | 67 | static Optional get(int priority) { 68 | return Optional.ofNullable(PRIORITIES.get(priority)); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /api/src/main/java/org/code_house/bacnet4j/wrapper/api/Priority.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 Code-House and others. 3 | * 4 | * bacnet4j-wrapper is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * https://www.gnu.org/licenses/gpl-3.0.txt 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with Foobar; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | package org.code_house.bacnet4j.wrapper.api; 21 | 22 | /** 23 | * Representation of bacnet priority. 24 | * 25 | * @author Łukasz Dywicki <luke@code-house.org> 26 | */ 27 | public interface Priority { 28 | 29 | String getLabel(); 30 | int getPriority(); 31 | 32 | } 33 | -------------------------------------------------------------------------------- /api/src/main/java/org/code_house/bacnet4j/wrapper/api/Property.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 Code-House and others. 3 | * 4 | * bacnet4j-wrapper is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * https://www.gnu.org/licenses/gpl-3.0.txt 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with Foobar; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | package org.code_house.bacnet4j.wrapper.api; 21 | 22 | import com.serotonin.bacnet4j.type.primitive.ObjectIdentifier; 23 | 24 | /** 25 | * Represents an BACnet device property (object). 26 | * 27 | * @deprecated Please migrate use of this type to {@link BacNetObject}. 28 | */ 29 | @Deprecated 30 | public class Property { 31 | 32 | private final Device device; 33 | private final int id; 34 | private final String name; 35 | private final String description; 36 | private final String units; 37 | private final Type type; 38 | 39 | public Property(Device device, int id, Type type) { 40 | this(device, id, "", "", "", type); 41 | } 42 | 43 | public Property(Device device, int id, String name, String description, String units, Type type) { 44 | this.device = device; 45 | this.id = id; 46 | this.name = name; 47 | this.description = description; 48 | this.units = units; 49 | this.type = type; 50 | } 51 | 52 | public Device getDevice() { 53 | return device; 54 | } 55 | 56 | public int getId() { 57 | return id; 58 | } 59 | 60 | public String getName() { 61 | return name; 62 | } 63 | 64 | public String getDescription() { 65 | return description; 66 | } 67 | 68 | public String getUnits() { 69 | return units; 70 | } 71 | 72 | public Type getType() { 73 | return type; 74 | } 75 | 76 | public ObjectIdentifier getBacNet4jIdentifier() { 77 | return new ObjectIdentifier(type.getBacNetType(), id); 78 | } 79 | 80 | public BacNetObject toBacNetObject() { 81 | return new BacNetObject(device, id, type, name, description, units); 82 | } 83 | 84 | @Override 85 | public String toString() { 86 | return "Property[" + device.getInstanceNumber() + "." + type.name() + "." + id + "]"; 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /api/src/main/java/org/code_house/bacnet4j/wrapper/api/Type.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 Code-House and others. 3 | * 4 | * bacnet4j-wrapper is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * https://www.gnu.org/licenses/gpl-3.0.txt 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with Foobar; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | package org.code_house.bacnet4j.wrapper.api; 21 | 22 | import com.serotonin.bacnet4j.type.enumerated.ObjectType; 23 | 24 | /** 25 | * Bacnet property type. 26 | * 27 | * @author Łukasz Dywicki <luke@code-house.org> 28 | */ 29 | public enum Type { 30 | 31 | ANALOG_INPUT("analogInput", ObjectType.analogInput), 32 | ANALOG_OUTPUT("analogOutput", ObjectType.analogOutput), 33 | ANALOG_VALUE("analogValue", ObjectType.analogValue), 34 | BINARY_INPUT("binaryInput", ObjectType.binaryInput), 35 | BINARY_OUTPUT("binaryOutput", ObjectType.binaryOutput), 36 | BINARY_VALUE("binaryValue", ObjectType.binaryValue), 37 | MULTISTATE_INPUT("multiStateInput", ObjectType.multiStateInput), 38 | MULTISTATE_OUTPUT("multiStateOutput", ObjectType.multiStateOutput), 39 | MULTISTATE_VALUE("multiStateValue", ObjectType.multiStateValue), 40 | 41 | NOTIFICATION_CLASS("notificationClass", ObjectType.notificationClass), 42 | 43 | CALENDAR ("calendar", ObjectType.calendar), 44 | PULSE_CONVETHING_RTER("pulseCoverter", ObjectType.pulseConverter), 45 | 46 | SCHEDULE("schedule", ObjectType.schedule), 47 | CHARACTER_STRING("characterstringValue", ObjectType.characterstringValue), 48 | DATE_TIME("dateTimeValue", ObjectType.datetimeValue), 49 | LARGE_ANALOG("largeAnalogValue", ObjectType.largeAnalogValue), 50 | OCTET_STRING("octetstringValue", ObjectType.octetstringValue), 51 | TIME("timeValue", ObjectType.timeValue), 52 | DATE_VALUE("dateValue", ObjectType.dateValue), 53 | INTEGER("integerValue", ObjectType.integerValue), 54 | POSITIVE_INTEGER("positiveIntegerValue", ObjectType.positiveIntegerValue), 55 | DATE_TIME_PATTERN("datetimePatternValue", ObjectType.datetimePatternValue), 56 | TIME_PATTERN("timePatternValue", ObjectType.timePatternValue), 57 | DATE_PATTERN("datePatternValue", ObjectType.datePatternValue), 58 | ACCUMULATOR("accumulator", ObjectType.accumulator), 59 | 60 | DEVICE ("device", ObjectType.device), 61 | TREND_LOG ("trendLog", ObjectType.trendLog), 62 | FILE ("file", ObjectType.file); 63 | 64 | 65 | private final String name; 66 | private final ObjectType bacNetType; 67 | 68 | Type(String name, ObjectType bacNetType) { 69 | this.name = name; 70 | this.bacNetType = bacNetType; 71 | } 72 | 73 | public String getName() { 74 | return name; 75 | } 76 | 77 | public ObjectType getBacNetType() { 78 | return bacNetType; 79 | } 80 | 81 | @Override 82 | public String toString() { 83 | return bacNetType + " Type"; 84 | } 85 | 86 | public static Type valueOf(ObjectType objectType) { 87 | for (Type type : values()) { 88 | if (type.bacNetType.equals(objectType)) { 89 | return type; 90 | } 91 | } 92 | throw new UnsupportedTypeException("Unsuported bacnet object type " + objectType); 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /api/src/main/java/org/code_house/bacnet4j/wrapper/api/UnsupportedTypeException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 Code-House and others. 3 | * 4 | * bacnet4j-wrapper is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * https://www.gnu.org/licenses/gpl-3.0.txt 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with Foobar; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | package org.code_house.bacnet4j.wrapper.api; 21 | 22 | import com.serotonin.bacnet4j.type.enumerated.ObjectType; 23 | 24 | /** 25 | * Exception thrown by client when specific property type is not supported. 26 | * 27 | * @author Łukasz Dywicki <luke@code-house.org> 28 | */ 29 | public class UnsupportedTypeException extends RuntimeException { 30 | 31 | public UnsupportedTypeException(ObjectType objectType) { 32 | this("Type " + objectType + " is not supported"); 33 | } 34 | 35 | public UnsupportedTypeException(String message) { 36 | super(message); 37 | } 38 | 39 | public UnsupportedTypeException(String message, Throwable cause) { 40 | super(message, cause); 41 | } 42 | 43 | 44 | } 45 | -------------------------------------------------------------------------------- /api/src/main/java/org/code_house/bacnet4j/wrapper/api/util/ForwardingAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 Code-House and others. 3 | * 4 | * bacnet4j-wrapper is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * https://www.gnu.org/licenses/gpl-3.0.txt 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with Foobar; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | package org.code_house.bacnet4j.wrapper.api.util; 21 | 22 | import com.serotonin.bacnet4j.RemoteDevice; 23 | import com.serotonin.bacnet4j.RemoteObject; 24 | import com.serotonin.bacnet4j.event.DeviceEventAdapter; 25 | import com.serotonin.bacnet4j.event.DeviceEventListener; 26 | import com.serotonin.bacnet4j.obj.BACnetObject; 27 | import com.serotonin.bacnet4j.service.confirmed.ReinitializeDeviceRequest; 28 | import com.serotonin.bacnet4j.type.constructed.*; 29 | import com.serotonin.bacnet4j.type.enumerated.EventState; 30 | import com.serotonin.bacnet4j.type.enumerated.EventType; 31 | import com.serotonin.bacnet4j.type.enumerated.MessagePriority; 32 | import com.serotonin.bacnet4j.type.enumerated.NotifyType; 33 | import com.serotonin.bacnet4j.type.notificationParameters.NotificationParameters; 34 | import com.serotonin.bacnet4j.type.primitive.*; 35 | import org.slf4j.Logger; 36 | import org.slf4j.LoggerFactory; 37 | 38 | import java.lang.Boolean; 39 | import java.util.concurrent.Callable; 40 | import java.util.concurrent.ExecutionException; 41 | import java.util.concurrent.ExecutorService; 42 | 43 | /** 44 | * A messy way to allow event adapters/listeners execute additional requests when they receive information. 45 | * 46 | * By default notifications are handled by UDP transport thread which blocks outgoing communication. This simple bridge 47 | * is using given executor service to host ad-hoc threads handling notification. 48 | * 49 | * @author Łukasz Dywicki <luke@code-house.org> 50 | */ 51 | public class ForwardingAdapter extends DeviceEventAdapter { 52 | 53 | private final Logger logger = LoggerFactory.getLogger(ForwardingAdapter.class); 54 | private final ExecutorService executor; 55 | private final DeviceEventListener adapter; 56 | 57 | public ForwardingAdapter(ExecutorService executor, DeviceEventListener adapter) { 58 | this.executor = executor; 59 | this.adapter = adapter; 60 | } 61 | 62 | @Override 63 | public void listenerException(Throwable e) { 64 | executor.execute(new Runnable() { 65 | @Override 66 | public void run() { 67 | adapter.listenerException(e); 68 | } 69 | }); 70 | } 71 | 72 | @Override 73 | public void iAmReceived(RemoteDevice d) { 74 | executor.execute(new Runnable() { 75 | @Override 76 | public void run() { 77 | adapter.iAmReceived(d); 78 | } 79 | }); 80 | } 81 | 82 | @Override 83 | public boolean allowPropertyWrite(Address from, BACnetObject obj, PropertyValue pv) { 84 | try { 85 | executor.submit(new Callable() { 86 | @Override 87 | public Boolean call() throws Exception { 88 | return adapter.allowPropertyWrite(from, obj, pv); 89 | } 90 | }).get(); 91 | } catch (InterruptedException | ExecutionException e) { 92 | logger.error("Unable to complete operation", e); 93 | } 94 | return false; 95 | } 96 | 97 | @Override 98 | public void propertyWritten(Address from, BACnetObject obj, PropertyValue pv) { 99 | executor.execute(new Runnable() { 100 | @Override 101 | public void run() { 102 | adapter.propertyWritten(from, obj, pv); 103 | } 104 | }); 105 | } 106 | 107 | @Override 108 | public void iHaveReceived(RemoteDevice d, RemoteObject o) { 109 | executor.execute(new Runnable() { 110 | @Override 111 | public void run() { 112 | adapter.iHaveReceived(d, o); 113 | } 114 | }); 115 | } 116 | 117 | 118 | @Override 119 | public void covNotificationReceived(final UnsignedInteger subscriberProcessIdentifier, final ObjectIdentifier initiatingDeviceIdentifier, 120 | final ObjectIdentifier monitoredObjectIdentifier, final UnsignedInteger timeRemaining, final SequenceOf listOfValues) { 121 | executor.execute(new Runnable() { 122 | @Override 123 | public void run() { 124 | adapter.covNotificationReceived(subscriberProcessIdentifier, initiatingDeviceIdentifier, monitoredObjectIdentifier, timeRemaining, listOfValues); 125 | } 126 | }); 127 | } 128 | 129 | @Override 130 | public void eventNotificationReceived(final UnsignedInteger processIdentifier, 131 | final ObjectIdentifier initiatingDeviceIdentifier, final ObjectIdentifier eventObjectIdentifier, final TimeStamp timeStamp, 132 | final UnsignedInteger notificationClass, final UnsignedInteger priority, final EventType eventType, final CharacterString messageText, 133 | final NotifyType notifyType, final com.serotonin.bacnet4j.type.primitive.Boolean ackRequired, final EventState fromState, final EventState toState, 134 | final NotificationParameters eventValues) { 135 | // Override as required 136 | executor.execute(new Runnable() { 137 | @Override 138 | public void run() { 139 | adapter.eventNotificationReceived(processIdentifier, initiatingDeviceIdentifier, eventObjectIdentifier, 140 | timeStamp, notificationClass, priority, eventType, messageText, notifyType, ackRequired, fromState, toState, 141 | eventValues); 142 | } 143 | }); 144 | } 145 | 146 | @Override 147 | public void textMessageReceived(final ObjectIdentifier textMessageSourceDevice, final Choice messageClass, 148 | final MessagePriority messagePriority, final CharacterString message) { 149 | executor.execute(new Runnable() { 150 | @Override 151 | public void run() { 152 | adapter.textMessageReceived(textMessageSourceDevice, messageClass, messagePriority, message); 153 | } 154 | }); 155 | } 156 | 157 | @Override 158 | public void synchronizeTime(Address from, DateTime dateTime, boolean utc) { 159 | executor.execute(new Runnable() { 160 | @Override 161 | public void run() { 162 | adapter.synchronizeTime(from, dateTime, utc); 163 | } 164 | }); 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /api/src/main/java/org/code_house/bacnet4j/wrapper/device/DefaultDeviceFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 Code-House and others. 3 | * 4 | * bacnet4j-wrapper is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * https://www.gnu.org/licenses/gpl-3.0.txt 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with Foobar; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | package org.code_house.bacnet4j.wrapper.device; 21 | 22 | import com.serotonin.bacnet4j.RemoteDevice; 23 | import com.serotonin.bacnet4j.type.constructed.Address; 24 | import org.code_house.bacnet4j.wrapper.api.Device; 25 | import org.code_house.bacnet4j.wrapper.device.ip.IpDevice; 26 | import org.code_house.bacnet4j.wrapper.device.mstp.MstpDevice; 27 | 28 | public class DefaultDeviceFactory implements DeviceFactory { 29 | 30 | @Override 31 | public Device createDevice(RemoteDevice remoteDevice) { 32 | Address address = remoteDevice.getAddress(); 33 | byte[] bytes = address.getMacAddress().getBytes(); 34 | if (bytes.length == 1) { 35 | return new MstpDevice(remoteDevice.getInstanceNumber(), address); 36 | } 37 | return new IpDevice(remoteDevice.getInstanceNumber(), address); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /api/src/main/java/org/code_house/bacnet4j/wrapper/device/DeviceFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 Code-House and others. 3 | * 4 | * bacnet4j-wrapper is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * https://www.gnu.org/licenses/gpl-3.0.txt 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with Foobar; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | package org.code_house.bacnet4j.wrapper.device; 21 | 22 | import com.serotonin.bacnet4j.RemoteDevice; 23 | import org.code_house.bacnet4j.wrapper.api.Device; 24 | 25 | /** 26 | * Detection logic which can translate bacnet4j devices into representation of devices required by 27 | * wrapper. 28 | */ 29 | public interface DeviceFactory { 30 | 31 | Device createDevice(RemoteDevice remoteDevice); 32 | 33 | } 34 | -------------------------------------------------------------------------------- /api/src/main/java/org/code_house/bacnet4j/wrapper/device/ip/IpDevice.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 Code-House and others. 3 | * 4 | * bacnet4j-wrapper is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * https://www.gnu.org/licenses/gpl-3.0.txt 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with Foobar; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | package org.code_house.bacnet4j.wrapper.device.ip; 21 | 22 | import com.serotonin.bacnet4j.npdu.ip.IpNetworkUtils; 23 | import com.serotonin.bacnet4j.type.constructed.Address; 24 | import com.serotonin.bacnet4j.type.primitive.OctetString; 25 | import org.code_house.bacnet4j.wrapper.api.Device; 26 | 27 | /** 28 | * Device variant which assumes IP network operation, 29 | * 30 | * @author Łukasz Dywicki <luke@code-house.org> 31 | */ 32 | public class IpDevice extends Device { 33 | 34 | public IpDevice(int instanceNumber, byte[] address, int networkNumber) { 35 | super(instanceNumber, address, networkNumber); 36 | } 37 | 38 | public IpDevice(int instanceNumber, Address address) { 39 | super(instanceNumber, address); 40 | } 41 | 42 | public IpDevice(int instanceNumber, String ip, int port, int networkNumber) { 43 | this(instanceNumber, IpNetworkUtils.toAddress(networkNumber, ip, port)); 44 | } 45 | 46 | public String getHostAddress() { 47 | return IpNetworkUtils.getInetAddress(new OctetString(getAddress())).getHostAddress(); 48 | } 49 | 50 | public int getPort() { 51 | return IpNetworkUtils.getPort(new OctetString(getAddress())); 52 | } 53 | 54 | 55 | } 56 | -------------------------------------------------------------------------------- /api/src/main/java/org/code_house/bacnet4j/wrapper/device/mstp/MstpDevice.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 Code-House and others. 3 | * 4 | * bacnet4j-wrapper is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * https://www.gnu.org/licenses/gpl-3.0.txt 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with Foobar; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | package org.code_house.bacnet4j.wrapper.device.mstp; 21 | 22 | import org.code_house.bacnet4j.wrapper.api.Device; 23 | import com.serotonin.bacnet4j.type.constructed.Address; 24 | 25 | /** 26 | * Device variant which assumes MSTP network operation, 27 | * 28 | * @author Łukasz Dywicki <luke@code-house.org> 29 | */ 30 | public class MstpDevice extends Device { 31 | 32 | public MstpDevice(int instanceNumber, byte[] address, int networkNumber) { 33 | super(instanceNumber, address, networkNumber); 34 | } 35 | 36 | public MstpDevice(int instanceNumber, Address address) { 37 | super(instanceNumber, address); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /assembly/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 4.0.0 5 | 6 | 7 | org.code-house.bacnet4j 8 | bacnet4j-wrapper 9 | 1.3.0-SNAPSHOT 10 | 11 | 12 | assembly 13 | 14 | Code-House :: Bacnet4J Wrapper :: Assembly 15 | Executable version of bacnet4j-wrapper. 16 | 17 | 18 | 19 | org.code-house.bacnet4j 20 | api 21 | 22 | 23 | org.code-house.bacnet4j 24 | ip 25 | 26 | 27 | org.code-house.bacnet4j 28 | mstp 29 | 30 | 31 | org.slf4j 32 | slf4j-simple 33 | 34 | 35 | 36 | 37 | 38 | 39 | org.apache.maven.plugins 40 | maven-shade-plugin 41 | 3.1.0 42 | 43 | 44 | package 45 | 46 | shade 47 | 48 | 49 | 50 | 51 | org.code_house.bacnet4j.wrapper.ip.DiscoveryMain 52 | 53 | 54 | true 55 | 56 | 57 | 58 | 59 | 60 | 61 | org.apache.maven.plugins 62 | maven-jar-plugin 63 | 64 | 65 | javadoc-jar 66 | package 67 | 68 | jar 69 | 70 | 71 | javadoc 72 | 73 | 74 | 75 | sources-jar 76 | package 77 | 78 | jar 79 | 80 | 81 | sources 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /ip/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 4.0.0 5 | 6 | 7 | org.code-house.bacnet4j 8 | bacnet4j-wrapper 9 | 1.3.0-SNAPSHOT 10 | 11 | 12 | ip 13 | bundle 14 | 15 | Code-House :: Bacnet4J Wrapper :: IP 16 | Implementation of bacnet4j wrapper based on IP. 17 | 18 | 19 | * 20 | 21 | org.code_house.bacnet4j.wrapper.ip 22 | 23 | 24 | 25 | 26 | 27 | org.code-house.bacnet4j 28 | api 29 | 30 | 31 | 32 | org.slf4j 33 | slf4j-log4j12 34 | test 35 | 36 | 37 | log4j 38 | log4j 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /ip/src/main/java/org/code_house/bacnet4j/wrapper/ip/BacNetIpClient.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 Code-House and others. 3 | * 4 | * bacnet4j-wrapper is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * https://www.gnu.org/licenses/gpl-3.0.txt 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with Foobar; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | package org.code_house.bacnet4j.wrapper.ip; 21 | 22 | import com.serotonin.bacnet4j.LocalDevice; 23 | import com.serotonin.bacnet4j.npdu.ip.IpNetwork; 24 | import com.serotonin.bacnet4j.npdu.ip.IpNetworkBuilder; 25 | import com.serotonin.bacnet4j.npdu.ip.IpNetworkUtils; 26 | import com.serotonin.bacnet4j.transport.DefaultTransport; 27 | import com.serotonin.bacnet4j.type.constructed.ReadAccessResult; 28 | import com.serotonin.bacnet4j.type.constructed.ReadAccessResult.Result; 29 | import com.serotonin.bacnet4j.type.constructed.SequenceOf; 30 | import com.serotonin.bacnet4j.type.primitive.OctetString; 31 | import org.code_house.bacnet4j.wrapper.api.BacNetClientBase; 32 | import org.code_house.bacnet4j.wrapper.api.BacNetObject; 33 | import org.code_house.bacnet4j.wrapper.api.Device; 34 | import org.code_house.bacnet4j.wrapper.api.Type; 35 | 36 | /** 37 | * Implementation of bacnet client based on IpNetwork/UDP transport. 38 | * 39 | * @author Łukasz Dywicki <luke@code-house.org> 40 | */ 41 | public class BacNetIpClient extends BacNetClientBase { 42 | 43 | public BacNetIpClient(IpNetwork network, int deviceId) { 44 | super(new LocalDevice(deviceId, new DefaultTransport(network))); 45 | } 46 | 47 | public BacNetIpClient(String ip, String broadcast, int port, int deviceId, boolean reuseAddress) { 48 | this(new IpNetworkBuilder().withLocalBindAddress(ip).withBroadcast(broadcast, 24).withPort(port) 49 | .withReuseAddress(reuseAddress).build(), deviceId); 50 | } 51 | 52 | public BacNetIpClient(String ip, String broadcast, int port, int deviceId) { 53 | this(new IpNetworkBuilder().withLocalBindAddress(ip).withBroadcast(broadcast, 24).withPort(port).build(), deviceId); 54 | } 55 | 56 | public BacNetIpClient(String broadcast, int port, int deviceId) { 57 | this(new IpNetworkBuilder().withBroadcast(broadcast, 24).withPort(port).build(), deviceId); 58 | } 59 | 60 | public BacNetIpClient(String ip, String broadcast, int deviceId) { 61 | this(new IpNetworkBuilder().withLocalBindAddress(ip).withBroadcast(broadcast, 24).build(), deviceId); 62 | } 63 | 64 | public BacNetIpClient(String broadcast, int deviceId) { 65 | this(new IpNetworkBuilder().withBroadcast(broadcast, 24).build(), deviceId); 66 | } 67 | 68 | /** 69 | * Explicitly register a network router which is known beforehand. 70 | * 71 | * This method is introduced for situations where network is fixed and all details are known to 72 | * caller. Adding network router early on allow to communicate other networks without pre-flight 73 | * discovery requests to populate routing table. 74 | * 75 | * @param network Network number. 76 | * @param ipAddress Router ip address. 77 | * @param port Router port. 78 | */ 79 | public void addNetworkRouter(int network, String ipAddress, int port) { 80 | OctetString address = IpNetworkUtils.toOctetString(ipAddress, port); 81 | this.localDevice.getNetwork().getTransport().addNetworkRouter(network, address); 82 | } 83 | 84 | @Override 85 | protected BacNetObject createObject(Device device, int instance, Type type, SequenceOf readAccessResults) { 86 | if (readAccessResults.size() == 1) { 87 | SequenceOf results = readAccessResults.get(0).getListOfResults(); 88 | if (results.size() == 4) { 89 | String name = results.get(2).toString(); 90 | String units = results.get(1).toString(); 91 | String description = results.get(3).toString(); 92 | return new BacNetObject(device, instance, type, name, description, units); 93 | } 94 | throw new IllegalStateException("Unsupported response structure " + readAccessResults); 95 | } 96 | String name = getReadValue(readAccessResults.get(2)); 97 | String units = getReadValue(readAccessResults.get(1)); 98 | String description = getReadValue(readAccessResults.get(3)); 99 | return new BacNetObject(device, instance, type, name, description, units); 100 | } 101 | 102 | private String getReadValue(ReadAccessResult readAccessResult) { 103 | // first index contains 0 value.. I know it is weird, but that's how bacnet4j works 104 | return readAccessResult.getListOfResults().get(0).getReadResult().toString(); 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /ip/src/main/java/org/code_house/bacnet4j/wrapper/ip/CsvMain.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 Code-House and others. 3 | * 4 | * bacnet4j-wrapper is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * https://www.gnu.org/licenses/gpl-3.0.txt 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with Foobar; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | package org.code_house.bacnet4j.wrapper.ip; 21 | 22 | import java.io.FileOutputStream; 23 | import java.io.PrintStream; 24 | 25 | /** 26 | * Simple class to run discovery across all network interfaces, fetch discovered devices properties and print out csv 27 | * structured output. 28 | * 29 | * @author Łukasz Dywicki <luke@code-house.org> 30 | */ 31 | public class CsvMain { 32 | 33 | public static void main(String[] args) throws Exception { 34 | PrintStream output = args.length < 4 ? System.out : new PrintStream(new FileOutputStream(args[3])); 35 | new NetworkProgram(new CsvVisitor(output)).run(args); 36 | 37 | output.close(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /ip/src/main/java/org/code_house/bacnet4j/wrapper/ip/CsvVisitor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 Code-House and others. 3 | * 4 | * bacnet4j-wrapper is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * https://www.gnu.org/licenses/gpl-3.0.txt 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with Foobar; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | package org.code_house.bacnet4j.wrapper.ip; 21 | 22 | import com.serotonin.bacnet4j.npdu.NetworkUtils; 23 | import com.serotonin.bacnet4j.type.Encodable; 24 | import com.serotonin.bacnet4j.type.primitive.OctetString; 25 | import org.code_house.bacnet4j.wrapper.api.BacNetObject; 26 | import org.code_house.bacnet4j.wrapper.api.Device; 27 | import org.code_house.bacnet4j.wrapper.api.Property; 28 | 29 | import java.io.PrintStream; 30 | import java.util.StringJoiner; 31 | 32 | import static java.lang.String.format; 33 | 34 | /** 35 | * Very simple visitor - dumps network structure to csv file. 36 | * 37 | * @author Łukasz Dywicki <luke@code-house.org> 38 | */ 39 | class CsvVisitor implements Visitor { 40 | 41 | private final PrintStream output; 42 | 43 | CsvVisitor(PrintStream writer) { 44 | this.output = writer; 45 | 46 | output.println("Network,Address,DeviceId,ObjectType,ObjectIdentifier,Name,Units,Description"); 47 | } 48 | 49 | @Override 50 | public Flag visit(Device device) { 51 | return Flag.CONTINUE; 52 | } 53 | 54 | @Override 55 | public Flag visit(BacNetObject object) { 56 | Device device = object.getDevice(); 57 | String line = new StringJoiner(",") 58 | .add("" + device.getNetworkNumber()) 59 | .add(quote(NetworkUtils.toString(new OctetString(device.getAddress())))) 60 | .add("" + device.getInstanceNumber()) 61 | .add(object.getType().getName()) 62 | .add("" + object.getId()) 63 | //.add(quote(object.getName())) 64 | //.add(quote(object.getUnits())) 65 | //.add(quote(object.getDescription())) 66 | .toString(); 67 | output.println(line); 68 | return Flag.SKIP; 69 | } 70 | 71 | private String quote(String value) { 72 | return "\"" + value.replace("\"", "\\\"") + "\""; 73 | } 74 | 75 | @Override 76 | public Flag visit(Encodable propertyValue) { 77 | return Flag.SKIP; 78 | } 79 | 80 | @Override 81 | public Flag visitProperty(String property, Encodable propertyValue) { 82 | return Flag.SKIP; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /ip/src/main/java/org/code_house/bacnet4j/wrapper/ip/DiscoveryMain.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 Code-House and others. 3 | * 4 | * bacnet4j-wrapper is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * https://www.gnu.org/licenses/gpl-3.0.txt 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with Foobar; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | package org.code_house.bacnet4j.wrapper.ip; 21 | 22 | import java.io.FileOutputStream; 23 | import java.io.PrintStream; 24 | 25 | /** 26 | * Simple class to run discovery across all network interfaces, fetch discovered devices properties and it's values. 27 | * 28 | * @author Łukasz Dywicki <luke@code-house.org> 29 | */ 30 | public class DiscoveryMain { 31 | 32 | public static void main(String[] args) throws Exception { 33 | PrintStream output = args.length < 4 ? System.out : new PrintStream(new FileOutputStream(args[3])); 34 | new NetworkProgram(new PrintingVisitor(output)).run(args); 35 | output.close(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /ip/src/main/java/org/code_house/bacnet4j/wrapper/ip/GeneratorMain.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2017 Code-House and others. 3 | * 4 | * bacnet4j-wrapper is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * https://www.gnu.org/licenses/gpl-3.0.txt 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with Foobar; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | package org.code_house.bacnet4j.wrapper.ip; 21 | 22 | import java.io.FileOutputStream; 23 | import java.io.PrintStream; 24 | 25 | /** 26 | * Simple class to run discovery across all network interfaces, fetch discovered devices properties and print out openhab 27 | * configuration for it. 28 | * 29 | * @author Łukasz Dywicki <luke@code-house.org> 30 | */ 31 | public class GeneratorMain { 32 | 33 | public static void main(String[] args) throws Exception { 34 | PrintStream output = args.length < 6 ? System.out : new PrintStream(new FileOutputStream(args[5])); 35 | new NetworkProgram( 36 | new OpenHabConfigurationVisitor( 37 | args.length < 4 || Boolean.parseBoolean(args[3]), 38 | args.length < 5 || Boolean.parseBoolean(args[4]), 39 | output 40 | ) 41 | ).run(args); 42 | 43 | output.close(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /ip/src/main/java/org/code_house/bacnet4j/wrapper/ip/NetworkProgram.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2017 Code-House and others. 3 | * 4 | * bacnet4j-wrapper is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * https://www.gnu.org/licenses/gpl-3.0.txt 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with Foobar; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | package org.code_house.bacnet4j.wrapper.ip; 21 | 22 | import org.code_house.bacnet4j.wrapper.api.BacNetClientException; 23 | import org.code_house.bacnet4j.wrapper.api.BacNetObject; 24 | import org.code_house.bacnet4j.wrapper.api.BypassBacnetConverter; 25 | import org.code_house.bacnet4j.wrapper.api.Device; 26 | 27 | import java.net.InetAddress; 28 | import java.net.InterfaceAddress; 29 | import java.net.NetworkInterface; 30 | import java.util.*; 31 | import java.util.concurrent.TimeUnit; 32 | import org.code_house.bacnet4j.wrapper.api.Type; 33 | 34 | /** 35 | * Simple class to run discovery across all network interfaces, fetch discovered devices properties and it's values. 36 | * 37 | * @author Łukasz Dywicki <luke@code-house.org> 38 | */ 39 | public class NetworkProgram { 40 | 41 | private final Visitor visitor; 42 | 43 | NetworkProgram(Visitor visitor) { 44 | this.visitor = visitor; 45 | } 46 | 47 | public void run(String[] args) throws Exception {List interfaceIPs = new ArrayList<>(); 48 | if (args.length > 0) { 49 | String broadcasts = args[0].trim(); 50 | Arrays.stream(broadcasts.split(",")) 51 | .map(String::trim) 52 | .forEach(interfaceIPs::add); 53 | } 54 | 55 | Long timeout = 30L; 56 | if (args.length > 1) { 57 | timeout = Long.parseLong(args[1]); 58 | } 59 | 60 | int deviceId = 1441; 61 | if (args.length > 2) { 62 | deviceId = Integer.parseInt(args[2]); 63 | } 64 | 65 | if (interfaceIPs.isEmpty()) { 66 | // For each interface ... 67 | System.out.println("Fetching network interfaces"); 68 | for (Enumeration en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements(); ) { 69 | NetworkInterface networkInterface = en.nextElement(); 70 | if (!networkInterface.isLoopback()) { 71 | 72 | // .. and for each address ... 73 | for (Iterator it = networkInterface.getInterfaceAddresses().iterator(); it.hasNext(); ) { 74 | 75 | // ... get IP and Subnet 76 | InterfaceAddress interfaceAddress = it.next(); 77 | 78 | InetAddress broadcast = interfaceAddress.getBroadcast(); 79 | if (broadcast != null) { 80 | interfaceIPs.add(broadcast.getHostAddress()); 81 | } 82 | } 83 | } 84 | } 85 | } 86 | 87 | if (interfaceIPs.isEmpty()) { 88 | System.out.println("No broadcast interfaces found"); 89 | } 90 | 91 | for (String broadcast : interfaceIPs) { 92 | System.out.println("Device id " + deviceId); 93 | System.out.println("Fetching devices for " + broadcast + " address with " + timeout + " second timeout"); 94 | BacNetIpClient client = new BacNetIpClient(broadcast, deviceId); 95 | client.start(); 96 | 97 | Set devices = client.discoverDevices(TimeUnit.SECONDS.toMillis(timeout)); 98 | if (devices.isEmpty()) { 99 | System.out.println(" => No Devices found"); 100 | } else { 101 | for (Device device : devices) { 102 | visit(client, device); 103 | } 104 | } 105 | 106 | client.stop(); 107 | } 108 | 109 | System.out.println("Discovery complete"); 110 | } 111 | 112 | private void visit(BacNetIpClient client, Device device) { 113 | if (visitor.visit(device) == Visitor.Flag.CONTINUE) { 114 | List objects = client.getDeviceObjects(device); 115 | if (objects.isEmpty()) { 116 | System.out.println(" => No objects found"); 117 | } else { 118 | for (BacNetObject object : objects) { 119 | if (object.getType() == Type.DEVICE) { 120 | Device subDevice = new Device(object.getId(), device.getBacNet4jAddress()); 121 | if (!device.equals(subDevice)) { 122 | visit(client, subDevice); 123 | } 124 | } 125 | if (visitor.visit(object) == Visitor.Flag.CONTINUE) { 126 | try { 127 | visitor.visit(client.getPresentValue(object, new BypassBacnetConverter())); 128 | List properties = client.getObjectPropertyNames(object); 129 | for (String property : properties) { 130 | visitor.visitProperty(property, client.getObjectPropertyValue(object, property, new BypassBacnetConverter())); 131 | } 132 | } catch (BacNetClientException e) { 133 | //System.out.println(" => Error: " + e.getMessage()); 134 | } 135 | } 136 | } 137 | } 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /ip/src/main/java/org/code_house/bacnet4j/wrapper/ip/OpenHabConfigurationVisitor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2017 Code-House and others. 3 | * 4 | * bacnet4j-wrapper is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * https://www.gnu.org/licenses/gpl-3.0.txt 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with Foobar; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | package org.code_house.bacnet4j.wrapper.ip; 21 | 22 | import com.serotonin.bacnet4j.type.Encodable; 23 | import org.code_house.bacnet4j.wrapper.api.BacNetObject; 24 | import org.code_house.bacnet4j.wrapper.api.Device; 25 | import org.code_house.bacnet4j.wrapper.api.Property; 26 | 27 | import java.io.PrintStream; 28 | 29 | import static java.lang.String.format; 30 | 31 | /** 32 | * Very simple configuration configuration generator - dumps network to openhab items syntax. 33 | * 34 | * @author Łukasz Dywicki <luke@code-house.org> 35 | */ 36 | class OpenHabConfigurationVisitor implements Visitor { 37 | 38 | private final boolean groups; 39 | private final boolean tags; 40 | private final PrintStream output; 41 | private Device device; 42 | 43 | OpenHabConfigurationVisitor(boolean groups, boolean tags, PrintStream writer) { 44 | this.groups = groups; 45 | this.tags = tags; 46 | this.output = writer; 47 | } 48 | 49 | @Override 50 | public Flag visit(Device device) { 51 | this.device = device; 52 | return Flag.CONTINUE; 53 | } 54 | 55 | @Override 56 | public Flag visit(BacNetObject object) { 57 | output.println(format("%s %s \"%s\" %s %s {bacnet=\"device=%d,type=%s,id=%d\"}", 58 | openhabType(object), 59 | openhabName(object), 60 | openhabLabel(object), 61 | groups ? openhabGroups(object) : "", 62 | tags ? openhabTags(object) : "", 63 | device.getInstanceNumber(), 64 | object.getType().getName(), 65 | object.getId() 66 | )); 67 | 68 | return Flag.SKIP; 69 | } 70 | 71 | @Override 72 | public Flag visit(Encodable propertyValue) { 73 | return Flag.SKIP; 74 | } 75 | 76 | @Override 77 | public Flag visitProperty(String property, Encodable propertyValue) { 78 | return Flag.SKIP; 79 | } 80 | 81 | private static String openhabType(BacNetObject object) { 82 | switch (object.getType()) { 83 | case BINARY_INPUT: 84 | case BINARY_OUTPUT: 85 | case BINARY_VALUE: 86 | return "Switch"; 87 | } 88 | 89 | return "Number"; 90 | } 91 | 92 | private static String openhabName(BacNetObject object) { 93 | return object.getName().trim() 94 | .replaceAll("[^a-zA-Z0-9\\s]", "") 95 | .replaceAll("\\s", "_") 96 | // in the end - we replace non ascii characters 97 | .replaceAll("[^\\x00-\\x7F]", "_"); 98 | } 99 | 100 | private static String openhabLabel(BacNetObject object) { 101 | return object.getName(); 102 | } 103 | 104 | private String openhabGroups(BacNetObject object) { 105 | return "(" + 106 | "BacNet" + "," + 107 | "BacNet_" + object.getUnits() + "," + 108 | "Device_" + device.getInstanceNumber() + "," + 109 | openhabName(object) + "_" + object.getId() + "," + 110 | object.getType().getName() + "_" + object.getId() + 111 | ")"; 112 | } 113 | 114 | private Object openhabTags(BacNetObject object) { 115 | return "[" + 116 | "\"Device_" + device.getInstanceNumber() + "\"," + 117 | "\"" + object.getType().getName() +"\"" + 118 | "]"; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /ip/src/main/java/org/code_house/bacnet4j/wrapper/ip/PrintingVisitor.java: -------------------------------------------------------------------------------- 1 | package org.code_house.bacnet4j.wrapper.ip; 2 | 3 | import com.serotonin.bacnet4j.type.Encodable; 4 | import com.serotonin.bacnet4j.type.primitive.OctetString; 5 | import org.code_house.bacnet4j.wrapper.api.BacNetObject; 6 | import org.code_house.bacnet4j.wrapper.api.Device; 7 | 8 | import java.io.PrintStream; 9 | import org.code_house.bacnet4j.wrapper.device.ip.IpDevice; 10 | 11 | class PrintingVisitor implements Visitor { 12 | 13 | private final PrintStream output; 14 | 15 | PrintingVisitor(PrintStream output) { 16 | this.output = output; 17 | } 18 | 19 | @Override 20 | public Flag visit(Device device) { 21 | output.println(" => Device id " + device.getInstanceNumber() + " (" + device.getClass().getSimpleName() + ")"); 22 | output.println(" Metadata"); 23 | if (device instanceof IpDevice) { 24 | IpDevice ipDevice = (IpDevice) device; 25 | output.println(" Address: " + ipDevice.getHostAddress() + ":" + ipDevice.getPort()); 26 | } else { 27 | output.println(" Address: " + new OctetString(device.getAddress())); 28 | } 29 | output.println(" Name: " + device.getName()); 30 | output.println(" Model: " + device.getModelName()); 31 | output.println(" Vendor: " + device.getVendorName()); 32 | return Flag.CONTINUE; 33 | } 34 | 35 | @Override 36 | public Flag visit(BacNetObject object) { 37 | output.printf(" => Type %s id: %d%n", 38 | object.getType().name(), 39 | object.getId() 40 | ); 41 | output.println(" Metadata"); 42 | output.println(" Name: " + object.getName()); 43 | output.println(" Units: " + object.getUnits()); 44 | output.println(" Description: " + object.getDescription()); 45 | 46 | return Flag.CONTINUE; 47 | } 48 | 49 | @Override 50 | public Flag visit(Encodable propertyValue) { 51 | output.printf(" Present value %s, type: %s%n", 52 | propertyValue, 53 | propertyValue != null ? propertyValue.getClass().getName() : "" 54 | ); 55 | 56 | return Flag.SKIP; 57 | } 58 | 59 | 60 | @Override 61 | public Flag visitProperty(String property, Encodable propertyValue) { 62 | output.printf(" Attribute '%s' value %s, type: %s%n", 63 | property, 64 | propertyValue, 65 | propertyValue != null ? propertyValue.getClass().getName() : "" 66 | ); 67 | 68 | return Flag.SKIP; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /ip/src/main/java/org/code_house/bacnet4j/wrapper/ip/Visitor.java: -------------------------------------------------------------------------------- 1 | package org.code_house.bacnet4j.wrapper.ip; 2 | 3 | import com.serotonin.bacnet4j.type.Encodable; 4 | import org.code_house.bacnet4j.wrapper.api.BacNetObject; 5 | import org.code_house.bacnet4j.wrapper.api.Device; 6 | 7 | public interface Visitor { 8 | 9 | enum Flag { 10 | SKIP, 11 | CONTINUE 12 | } 13 | 14 | Flag visit(Device device); 15 | 16 | Flag visit(BacNetObject object); 17 | 18 | Flag visit(Encodable propertyValue); 19 | 20 | Flag visitProperty(String property, Encodable propertyValue); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /ip/src/test/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger = DEBUG, out 2 | 3 | log4j.appender.out=org.apache.log4j.ConsoleAppender 4 | log4j.appender.out.layout=org.apache.log4j.PatternLayout 5 | log4j.appender.out.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5.5p] [%-36.36c] - %m%n 6 | 7 | log4j.logger.com.serotonin.bacnet4j=INFO -------------------------------------------------------------------------------- /mstp/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 4.0.0 5 | 6 | 7 | org.code-house.bacnet4j 8 | bacnet4j-wrapper 9 | 1.3.0-SNAPSHOT 10 | 11 | 12 | mstp 13 | bundle 14 | 15 | Code-House :: Bacnet4J Wrapper :: MSTP 16 | Implementation of bacnet4j wrapper based on MSTP/serial interface. 17 | 18 | 19 | 20 | jssc;resolution:=optional, 21 | * 22 | 23 | 24 | org.code_house.bacnet4j.wrapper.mstp 25 | 26 | 27 | 28 | 29 | 30 | org.code-house.bacnet4j 31 | api 32 | 33 | 34 | org.scream3r 35 | jssc 36 | 2.8.0 37 | 38 | 39 | 40 | org.slf4j 41 | slf4j-log4j12 42 | test 43 | 44 | 45 | log4j 46 | log4j 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /mstp/src/main/java/org/code_house/bacnet4j/wrapper/mstp/BacNetMstpClient.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 Code-House and others. 3 | * 4 | * bacnet4j-wrapper is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * https://www.gnu.org/licenses/gpl-3.0.txt 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with Foobar; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | package org.code_house.bacnet4j.wrapper.mstp; 21 | 22 | import com.serotonin.bacnet4j.LocalDevice; 23 | import com.serotonin.bacnet4j.npdu.mstp.MstpNetwork; 24 | import com.serotonin.bacnet4j.transport.DefaultTransport; 25 | import com.serotonin.bacnet4j.type.constructed.ReadAccessResult; 26 | import com.serotonin.bacnet4j.type.constructed.ReadAccessResult.Result; 27 | import com.serotonin.bacnet4j.type.constructed.SequenceOf; 28 | import org.code_house.bacnet4j.wrapper.api.BacNetClientBase; 29 | import org.code_house.bacnet4j.wrapper.api.BacNetObject; 30 | import org.code_house.bacnet4j.wrapper.api.Device; 31 | import org.code_house.bacnet4j.wrapper.api.Type; 32 | import org.code_house.bacnet4j.wrapper.device.mstp.MstpDevice; 33 | 34 | /** 35 | * Implementation of bacnet client based on serial transport. 36 | * 37 | * @author Łukasz Dywicki <luke@code-house.org> 38 | */ 39 | public class BacNetMstpClient extends BacNetClientBase { 40 | 41 | public BacNetMstpClient(MstpNetwork network, int deviceId) { 42 | this(network, deviceId, 60_000, 5_000); 43 | } 44 | 45 | public BacNetMstpClient(MstpNetwork network, int deviceId, int timeout, int segTimeout) { 46 | super(new LocalDevice(deviceId, createTransport(network, timeout, segTimeout)), 47 | (remoteDevice) -> new MstpDevice(remoteDevice.getInstanceNumber(), remoteDevice.getAddress()) 48 | ); 49 | } 50 | 51 | @Override 52 | protected BacNetObject createObject(Device device, int instance, Type type, SequenceOf readAccessResults) { 53 | SequenceOf result = readAccessResults.get(0).getListOfResults(); 54 | String name = result.get(2).toString(); 55 | String units = result.get(1).toString(); 56 | String description = result.get(3).toString(); 57 | 58 | return new BacNetObject(device, instance, type, name, description, units); 59 | } 60 | 61 | private static DefaultTransport createTransport(MstpNetwork network, int timeout, int segTimeout) { 62 | DefaultTransport transport = new DefaultTransport(network); 63 | transport.setTimeout(timeout); 64 | transport.setSegTimeout(segTimeout); 65 | return transport; 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /mstp/src/main/java/org/code_house/bacnet4j/wrapper/mstp/MstpNetworkBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * (C) Copyright 2018 Code-House and others. 3 | * 4 | * bacnet4j-wrapper is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 2 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * https://www.gnu.org/licenses/gpl-3.0.txt 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with Foobar; if not, write to the Free Software 18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 | */ 20 | package org.code_house.bacnet4j.wrapper.mstp; 21 | 22 | import com.serotonin.bacnet4j.npdu.mstp.MstpNetwork; 23 | 24 | /** 25 | * Common mstp related options which include serial parameters. 26 | * 27 | * @author Łukasz Dywicki <luke@code-house.org> 28 | */ 29 | public abstract class MstpNetworkBuilder { 30 | 31 | private String serialPort; 32 | private int station; 33 | 34 | private int baud = 38_400; 35 | private short dataBits = 8; 36 | private short stopBits = 1; 37 | private short parity = 0; 38 | 39 | public MstpNetworkBuilder withStation(int station) { 40 | this.station = station; 41 | return this; 42 | } 43 | 44 | public int getStation() { 45 | return station; 46 | } 47 | 48 | public MstpNetworkBuilder withSerialPort(String port) { 49 | this.serialPort = port; 50 | return this; 51 | } 52 | 53 | public String getSerialPort() { 54 | return serialPort; 55 | } 56 | 57 | public MstpNetworkBuilder withBaud(int baud) { 58 | this.baud = baud; 59 | return this; 60 | } 61 | 62 | public int getBaud() { 63 | return baud; 64 | } 65 | 66 | public MstpNetworkBuilder withDataBits(short dataBits) { 67 | this.dataBits = dataBits; 68 | return this; 69 | } 70 | 71 | public short getDataBits() { 72 | return dataBits; 73 | } 74 | 75 | public MstpNetworkBuilder withStopBits(short stopBits) { 76 | this.stopBits = stopBits; 77 | return this; 78 | } 79 | 80 | public short getStopBits() { 81 | return stopBits; 82 | } 83 | 84 | public MstpNetworkBuilder withParity(short parity) { 85 | this.parity = parity; 86 | return this; 87 | } 88 | 89 | public short getParity() { 90 | return parity; 91 | } 92 | 93 | public abstract MstpNetwork build() throws Exception; 94 | 95 | } 96 | -------------------------------------------------------------------------------- /mstp/src/test/java/org/code_house/bacnet4j/wrapper/mstp/Main.java: -------------------------------------------------------------------------------- 1 | package org.code_house.bacnet4j.wrapper.mstp; 2 | 3 | public class Main { 4 | 5 | public static void main(String[] args) { 6 | 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 4.0.0 5 | 6 | 7 | org.connectorio 8 | connectorio-oss 9 | 5 10 | 11 | 12 | 13 | org.code-house.bacnet4j 14 | bacnet4j-wrapper 15 | pom 16 | 1.3.0-SNAPSHOT 17 | 18 | Code-House :: Bacnet4J Wrapper 19 | 20 | This small library provides thin and small abstraction layer over bacnet4j api to host more friendly application 21 | programming interface. It hides most of the bacnet internals behind BacNetClient class which is responsible for 22 | interacting with infrastructure. 23 | 24 | 25 | 26 | Code-House 27 | http://code-house.org 28 | 29 | 30 | 31 | 32 | GNU General Public License, version 3 33 | https://www.gnu.org/licenses/gpl-3.0.txt 34 | Due to bacnet4j dependency this library must be licensed under GNU GPL. 35 | 36 | 37 | 38 | 39 | scm:git:https://github.com/Code-House/bacnet4j-wrapper.git 40 | scm:git:git@github.com:Code-House/bacnet4j-wrapper.git 41 | https://github.com/Code-House/bacnet4j-wrapper 42 | HEAD 43 | 44 | 45 | 46 | github 47 | http://github.com/Code-House/bacnet4j-wrapper/issues 48 | 49 | 50 | 51 | api 52 | ip 53 | mstp 54 | assembly 55 | 56 | 57 | 58 | * 59 | 60 | 61 | 3.7 62 | 6.0.0 63 | 1.0.0 64 | 1.1.0 65 | 1.7.12 66 | 1.2.17 67 | 68 | 69 | 70 | 71 | ias-release 72 | true 73 | false 74 | Infinite Automation Release Repository 75 | https://maven.mangoautomation.net/repository/ias-release/ 76 | 77 | 78 | 79 | 80 | 81 | 82 | org.code-house.bacnet4j 83 | api 84 | ${project.version} 85 | 86 | 87 | com.infiniteautomation 88 | bacnet4j 89 | 90 | 91 | lohbihler 92 | sero-scheduler 93 | 94 | 95 | lohbihler 96 | sero-warp 97 | 98 | 99 | 100 | 101 | org.code-house.bacnet4j 102 | ip 103 | ${project.version} 104 | 105 | 106 | org.code-house.bacnet4j 107 | mstp 108 | ${project.version} 109 | 110 | 111 | org.slf4j 112 | slf4j-api 113 | ${slf4j.version} 114 | 115 | 116 | org.slf4j 117 | slf4j-simple 118 | ${slf4j.version} 119 | 120 | 121 | 122 | com.infiniteautomation 123 | bacnet4j 124 | ${bacnet4j.version} 125 | 126 | 127 | lohbihler 128 | sero-scheduler 129 | ${sero-scheduler.version} 130 | 131 | 132 | lohbihler 133 | sero-warp 134 | ${sero-warp.version} 135 | 136 | 137 | org.apache.commons 138 | commons-lang3 139 | ${commons-lang3.version} 140 | 141 | 142 | 143 | org.slf4j 144 | slf4j-log4j12 145 | ${slf4j.version} 146 | test 147 | 148 | 149 | log4j 150 | log4j 151 | ${log4j.version} 152 | test 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | org.apache.felix 162 | maven-bundle-plugin 163 | 4.2.1 164 | 165 | 166 | ${osgi.import} 167 | ${osgi.export} 168 | <_nouses>true 169 | 170 | 171 | 172 | 173 | org.apache.maven.plugins 174 | maven-release-plugin 175 | 176 | true 177 | false 178 | clean install 179 | deploy 180 | release 181 | 182 | 183 | 184 | 185 | 186 | 187 | org.apache.maven.plugins 188 | maven-compiler-plugin 189 | 190 | 11 191 | 11 192 | 193 | 194 | 195 | org.apache.felix 196 | maven-bundle-plugin 197 | true 198 | 199 | 200 | 201 | 202 | --------------------------------------------------------------------------------