Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
(ns clj-bacnet.application.prefix.header
(:require [clj-binary.codec.complex-bytes :as cb]
[clj-binary.core :as bin]
[clojure.spec.alpha :as s]
[orchestra.core :refer [defn-spec]]
[org.clojars.smee.binary.core :as sb]
[spec-coerce.core :as coerce]
[vertiv-common.spec :as vs]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def codec-config
[{::cb/bit-length 4
::cb/name ::number
::cb/enum-map {:extended 2r1111}
::cb/lenient? true}
{::cb/bit-length 1
::cb/name ::class
::cb/enum-map {:contextual 1
:application 0}}
{::cb/bit-length 3
::cb/name ::length-value-or-type
::cb/enum-map {:opening 2r110
:closing 2r111
:extended-length 2r101}
::cb/lenient? true}])
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Number
(s/def ::number
(s/or :compact-4-bit (vs/int-in 2r0000 2r1110)
:extended #{:extended}))
(s/def ::number-extended (s/and int? #(> % 2r1110)))
(coerce/def ::number
#(condp s/valid? %
::number %
::number-extended :extended))
;; Class
(s/def ::class
(s/or :application #{:application}
:contextual #{:contextual}))
;; Length
(s/def ::length
(s/or :compact-3-bit (vs/int-in 2r000 2r100)
:extended #{:extended-length}))
(s/def ::length-extended (s/and int? #(> % 2r100)))
(coerce/def ::length
#(condp s/valid? %
::length %
::length-extended :extended-length))
;; Boolean value
(s/def ::boolean-value #{0 1}) ;; Might be a boolean based on context
(coerce/def ::boolean-value
#(condp s/valid? %
::boolean-value %
boolean? ({true 1 false 0} %)))
;; Tag type
(s/def ::tag-type #{:opening :closing})
(coerce/def ::tag-type identity)
;; Length/value/type
(s/def ::length-value-or-type
(s/or :length ::length
:boolean-value ::boolean-value
:tag-type ::tag-type))
;; Attributes
(s/def ::attributes
(s/and (s/keys :req [::number ::class ::length-value-or-type])
(s/or :contextual-enclosing-tag
#(vs/keys-conform? {::class :contextual
::length-value-or-type :tag-type} %)
:contextual-value-prefix
#(vs/keys-conform? {::class :contextual
::length-value-or-type :length} %)
:application-value-prefix
#(vs/keys-conform? {::class :application
::length-value-or-type :length} %)
:application-boolean
#(vs/keys-conform? {::class :application
::length-value-or-type :boolean-value} %))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn-spec number-is-extended? boolean?
[attrs ::attributes]
(->> (s/conform ::attributes attrs)
(vs/keys-conform? {::number :extended})))
(defn-spec length-is-extended? boolean?
[attrs ::attributes]
(->> (s/conform ::attributes attrs)
(vs/keys-conform? {::length-value-or-type [:length :extended]})))
(defn-spec ^:no-gen decode ::attributes
[raw ::bin/ubyte]
(cb/decode codec-config raw))
(defn-spec encode ::bin/ubyte
[attrs ::attributes]
(cb/encode codec-config attrs))
(def codec (sb/compile-codec :ubyte encode decode))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.