From 9805b3514a6b5a4ab8404b8a93c3f96dc2d785ec Mon Sep 17 00:00:00 2001 From: FlorianMerz Date: Wed, 6 Feb 2019 12:34:46 +0100 Subject: [PATCH 1/4] add more info to the guides --- README.md | 11 +-- bin/{ => linux-amd64}/drivers | Bin bin/{ => windows-amd64}/drivers.exe | Bin docs/capabilities.md | 71 ------------------ docs/getting-started.md | 104 -------------------------- docs/golang.md | 11 +++ docs/javascript.md | 8 ++ docs/metadata.md | 29 +++++--- docs/quickstart.md | 109 ++++++++++++++++++++++++++++ docs/schema.md | 105 +++++++++++++++++++++++++-- 10 files changed, 249 insertions(+), 199 deletions(-) rename bin/{ => linux-amd64}/drivers (100%) rename bin/{ => windows-amd64}/drivers.exe (100%) delete mode 100644 docs/capabilities.md delete mode 100644 docs/getting-started.md create mode 100644 docs/quickstart.md diff --git a/README.md b/README.md index 7e56455..edaa1f4 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,6 @@ The SmartMakers driver SDK contains pre-built executables, documentation, and ex for developing and deploying your own IoT device driver for the thingsHub IoT middleware. -## Contents - -- [Quickstart](#quickstart) -- [Examples](#examples) -- [Contributing](#contributing) - - ## Quickstart Guide Check out the [Quickstart Guide](docs/quickstart.md) @@ -30,7 +23,7 @@ for more in-depth information on driver development. ## Driver Metadata Driver metadata is information about a driver which is not directly part of the driver's executable code itself. -This includes, for example, information about the driver'S name, author, as well as the devices supported by this driver. +This includes, for example, information about the driver's name, author, as well as the devices supported by this driver. See [here](docs/metadata.md) for more information about driver metadata. @@ -41,7 +34,7 @@ They provide the thingsHub with an understand on how it should interprete a devi and the different features a device provides, even before receiving actual uplinks from the device. -More information about driver schemas can be found [here](docs/schemas.md). +More information about driver schemas can be found [here](docs/schema.md). ## Driver Versioning diff --git a/bin/drivers b/bin/linux-amd64/drivers similarity index 100% rename from bin/drivers rename to bin/linux-amd64/drivers diff --git a/bin/drivers.exe b/bin/windows-amd64/drivers.exe similarity index 100% rename from bin/drivers.exe rename to bin/windows-amd64/drivers.exe diff --git a/docs/capabilities.md b/docs/capabilities.md deleted file mode 100644 index 24f7f9c..0000000 --- a/docs/capabilities.md +++ /dev/null @@ -1,71 +0,0 @@ -# Driver Capability Levels - -A driver's Capability Level describes a driver's capabilities -with respect to a set of predefined levels. -The Capability Level helps the thingsHub understand how the driver works, -and how it's data should be treated. - -The thingsHub uses a driver's capability level to understand if it -needs to persist a driver's state, -if it should validate the driver's data according to a schema, -if it can ask the driver to encode uplinks, -or if it can send data from a device to specific target applications. - -While a higher Capability Level indicates a more capable driver, -it is frequently not required for all devices. -In particular levels above level 3 are meant to be implemented -by drivers for very powerful and complex devices. - -| Level | Description | -|:------|:----------| -| 1 | Plain uplink decoding | -| 2 | Uplink decoding with a data schema | -| 3 | Downlink encoding | -| 4 | Statefulnes management | -| 5 | Declarative management | - -## Level 1 - Plain Uplink Decoding - -Capability Level 1 is designed -with existing javascript drivers in mind. -This level makes it possible to run off-the-web drivers with -minimal changes to the code. It frequently suffices -to a `decode` function as a well-defined entry point -to the code and the driver is ready to be used. - -Because of the lack of schema, the driver's output -cannot be validated as easily. -This makes it impossible to send data for such a device -to specific target applications, if those require -predefined data schemata or device and sensor models. - - -## Level 2 - Uplink decoding with a data schema - -Level 2 drivers are still designed for low effort, -though they require adding a data schema in addition -to the actual code of the driver. - -However, this data schema unlocks additional functionality -for the driver in the thingsHub, -which is not accessible for level 1 devices. - -For example, a level 2 device can encode additional types -and these types can be automatically stored in the different -target systems in the most suitable format. - - -## Level 3 - Downlink encoding - -Level 3 are similar to level 2 devices, -but in addition they support sending downlinks to the device. - -Level 3 drivers are still up-/downlink oriented. - - -## Level 4 - Stateful Management - -Level 4 drivers can store bits of state about a drivers - - -## Level 5 - Declarative management diff --git a/docs/getting-started.md b/docs/getting-started.md deleted file mode 100644 index 627183e..0000000 --- a/docs/getting-started.md +++ /dev/null @@ -1,104 +0,0 @@ -# Quickstart - -## 1. Installing the CLI - -In order to get started, you'll want to install SmartMakers drivers command line interface (CLI) globally. We recommend installing the drivers CLI by downloading the [pre-built binaries](https://storage.googleapis.com/sm-tools/drivers). Alternatively, you can download the CLI tool from the terminal: - -``` shell -$ wget https://storage.googleapis.com/sm-tools/drivers -``` - -Once installed, you will be able to open create and manage driver projects in Go and JavaScript. - -## How the CLI works - -Each time the CLI is run, it looks for the config file, .project, in the root directory of your project. It then applies the configuration from your, and executes any commands you've requested for it to run. - -``` shell -$ drivers -h -DESCRIPTION: - CLI tool for creating and managing driver projects - -COMMANDS: - help, h Shows a list of commands or help for one command - - Metadata: - get gets metadata by type - set sets metadata by type - del deletes metadata by type - - Project: - init command to initialize new driver project in empty working directory - build builds driver project - - Registry: - login authenticates to driver registry - push pushes the driver package to the driver registry - delete deletes the driver package with specific tag from the driver registry - search search for drivers in the registry - -GLOBAL OPTIONS: - --help, -h show help - --version, -v print the version -``` - - -#### 2. Start a new project - -Run the following command to get started: - -``` -$ drivers init golang -$ tree -a -├── .project -└── main.go -``` - -##### The Project configuration - -```yaml -type: golang -metadata: - name: "" - author: "" - labels: [] - supports: [] - platform: "" - os: "" -session: - server: "" - token: "" - API: "" -``` - -You can also generate a JavaScript boilerplate, by specifying 'javascript' instead of golang as in this example. - -## 3. Build project - -``` shell -$ drivers build -$ tree -aL 2 -├── .project -├── build -│   ├── main -│   └── package.zip -└── main.go -``` - -## 4. Authenticate to the Driver Registry - -``` shell -$ drivers login THINGS_HUB_INSTANCE_URL USERNAME -p PASSWORD -``` - -## 5. Upload package - -To upload thedriver package to the driver registry, run: - -``` shell -$ drivers push -t devel -``` - -For more on the push command, run `$ drivers push -h`. - - diff --git a/docs/golang.md b/docs/golang.md index f59743b..e76ca89 100644 --- a/docs/golang.md +++ b/docs/golang.md @@ -1,3 +1,14 @@ # Golang Driver Development +Use the following command to initialize a javascript driver development project: +``` +drivers init javascript +``` + +## Notes + +* A Golang driver development project requires golang's `go` command line tool + to be installed. +* Golang requires the driver development projects path to be a subpath of the + path set in the environment variable GOPATH. diff --git a/docs/javascript.md b/docs/javascript.md index f0a0724..530bb9e 100644 --- a/docs/javascript.md +++ b/docs/javascript.md @@ -1,3 +1,11 @@ # Javascript Driver Development +Use the following command to initialize a javascript driver development project: +``` +drivers init javascript +``` + +## Notes + +* Javascript drivers cannot currently handle state management. diff --git a/docs/metadata.md b/docs/metadata.md index ca55ff2..e0df17c 100644 --- a/docs/metadata.md +++ b/docs/metadata.md @@ -2,35 +2,46 @@ Driver metadata is information about a driver which is uploaded to the server along with the driver itself. -Metadata can, for example, -* help the system interface with the driver correctly, -* help the user understand what the driver is about, -* and can be used to search a suitable driver for a given device. +Metadata, for example, + +* helps the system interface with the driver correctly, +* helps the user understand what the driver is about, +* and can be used to identify a suitable driver for a given device in the registry. Metada is stored in the file `.projects`, which is created when a project is initialized. Following is an example for a metadata description: ```metadata: - name: imaginary-sensor - author: pupil + name: house-sensor + author: john supports: - - manufacturer: none - model: none + - manufacturer: jack + model: house firmwareversion: 0.1 platform: amd64 - os: linux``` + os: linux +``` * `name`: Name if the driver, usually indicates the supported manufaturer or device model, but can be used in any way the driver author considers useful. * `author`: The person or entity that wrote the driver. This is not necessarily the same as the manufacturer of the device. +* `platform`: The target platform for which a driver was built. + Currently, the only option here is `amd64`. +* `os`: The target operating system for which a driver was built. + Currently, the only two options here are `linux` and `amd64`. * `supports`: Indicates which devices and versions thereof the driver supports. See below for more information. ## Supports Declarations +A driver's author of a driver is not nececssarily the same entity as the manufacturer of a device. +For this reason, a driver's metadata allows explicitly listing the supported devices and their manufacturers. + A `supports` declaration indicates which devices a driver supports. Such a declaration can have three fields: * `manufacturer`: Name of the device's manufacturer (e.g. 'Elsys' or 'NKE') * `model`: Name of the device's model (e.g. 'ERS Lite' or 'In'O') +* `firmware_version`: The supported firmware version. As formats for firmware version vary widely, + this can be any string. Supports information is purely informational for the user and not used for any kind of validation. This implies that the system will not use his informaton to prevent usage of an unsuitable driver. diff --git a/docs/quickstart.md b/docs/quickstart.md new file mode 100644 index 0000000..39fafdf --- /dev/null +++ b/docs/quickstart.md @@ -0,0 +1,109 @@ +# Quickstart + +## Installing the Driver Development Kit + +In order to get started, download the contents of this repository as a zip file +or, preferrably, use `git clone` to clone the repository to your local disk. + +In this repository's directory `bin/` are subdirectory for each supported platform, +which in turn contain pre-built executables for the thingsHub's drivers command line tool. + +For simplicity, we assume that the right directory was added to the PATH variable. + +## Running the Command Line Tool + +Running the command line tool with the option `-h` gives the following output: + +``` shell +$ drivers -h +DESCRIPTION: + CLI tool for creating and managing driver projects + +COMMANDS: + help, h Shows a list of commands or help for one command + + Metadata: + get gets metadata by type + set sets metadata by type + del deletes metadata by type + + Project: + init command to initialize new driver project in empty working directory + build builds driver project + + Registry: + login authenticates to driver registry + push pushes the driver package to the driver registry + delete deletes the driver package with specific tag from the driver registry + search search for drivers in the registry + +GLOBAL OPTIONS: + --help, -h show help + --version, -v print the version +``` + +## Starting a New Driver Development Project + +Run the following command to get started: + +``` +$ drivers init javascript +$ tree -a +├── .project +└── script.js +``` + +## Project Configuration + +For project configuration use the following commands: + +```shell +$ drivers set author +$ drivers set name +$ drivers set supports +``` + +Or alternatively edit the `.project` file accordingly: + +```yaml +type: golang +metadata: + name: "" + author: "" + labels: + supports: + - manufacturer: + model: + firmware_version: + platform: "amd64" + os: "linux" +``` + +## Building the Driver Package + +Running the `build` subcommand builds and packages the driver. +```shell +$ drivers build +``` + +## Logging in to the Driver Registry + +Before being able to push anything to a driver registry, +it is necessary to login to the target registry first: + +```shell +$ drivers login -p +``` + +Note, that this will store an access token in the `.project` file, +which will be used in subsequent calls to the registry. + +## Uploading the Driver Package + +To upload the driver package to the driver registry, run: + +```shell +$ drivers push -t devel +``` + +For more on the push command, run `$ drivers push -h`. diff --git a/docs/schema.md b/docs/schema.md index 9aae36b..6e0af1c 100644 --- a/docs/schema.md +++ b/docs/schema.md @@ -2,11 +2,12 @@ Schemas provide a multitude of benefits: -* Reliability in the interaction between the system and the driver +* Reliability in the interaction between the system and the driver. +* Documentation of a device's capabilities. * Support for additional data types, e.g. dates, times, or distinction between floating point and integer numbers. -The following is an example schema: +The following is an example of such a schema: ```schema: properties: @@ -23,14 +24,106 @@ The following is an example schema: description: The heater's return temperature ``` -A device schema can contain the following types: +## Properties and Property Types + +A schema is a tree of properties. +Each property has a `type` and optionally a `description`. +Leaf properties can also have an optional `unit`. + +Schemas are commonly written as YAML or JSON code. +In this guide, we will stick to YAML for readability, +e.g. the following is a simple floating-point property +which represents a temperature: + +```yaml +temperature: + type: float64 + description: The temperature in degree celsius. Can be between -51,2°C and +51,1°C. + unit: °C +``` + +Valid `type`s are: * `object`: An object (aka dict or map) with named key value pairs. +* `boolean`: Either true or false. * `int64`: 64-bit integer number * `float64`: 64-bit floating point number * `string`: A sequence of Unicode characters -* To be completed -## Objects +The `description` should provide information about what the property represents. +The `unit` contains the physical unit, if applicable, of the measured phenomenon. + +The types `boolean`, `int64`, `float64`, `string` work as expected. + +An `object` is different in that it is used for grouping sub-properties: + +```yaml +room: + properties: + temperature: + type: float64 + humidity: + type: int64 +``` + +## Schemas and Data + +Schemas are used to validate the data returned by a driver. +A driver may send the data listed in the schema, +though all fields are considered optional. + +Let's assume the following schema: + +```yaml +room: + properties: + temperature: + type: float64 + humidity: + type: int64 +``` + +This will validate the following structure: + +```json +{ + "room": { + "temperature": 23.5, + "humidity": 70 + } +} +``` + +Also the following is perfectly valid, even though it might seem incomplete: + +```json +{ + "room": { + "temperature": 23.5 + } +} +``` + +Finally, an empty object is always valid: -Objects can contain sub-properties +```json +{} +``` + +But any property that is not listed in the schema is not valid: + +```json +{ + "engine": 20 +} +``` + +And also an incorrect type is not valid with respect to the schema: + +```json +{ + "room": { + "temperature": "I'm a string!" + } +} +``` From 6c5d774f791d40993a1bfb12addb7d4621950f4c Mon Sep 17 00:00:00 2001 From: FlorianMerz Date: Thu, 7 Feb 2019 15:20:01 +0100 Subject: [PATCH 2/4] add encoding library for bcd --- go/encoding/bcd/nonpacked.go | 50 +++++++++ go/encoding/bcd/nonpacked_test.go | 123 +++++++++++++++++++++ go/encoding/bcd/packed.go | 59 ++++++++++ go/encoding/bcd/packed_test.go | 177 ++++++++++++++++++++++++++++++ go/encoding/bytes/bits.go | 19 ++++ go/encoding/bytes/bits_test.go | 140 +++++++++++++++++++++++ go/encoding/bytes/hex.go | 13 +++ 7 files changed, 581 insertions(+) create mode 100644 go/encoding/bcd/nonpacked.go create mode 100644 go/encoding/bcd/nonpacked_test.go create mode 100644 go/encoding/bcd/packed.go create mode 100644 go/encoding/bcd/packed_test.go create mode 100644 go/encoding/bytes/bits.go create mode 100644 go/encoding/bytes/bits_test.go create mode 100644 go/encoding/bytes/hex.go diff --git a/go/encoding/bcd/nonpacked.go b/go/encoding/bcd/nonpacked.go new file mode 100644 index 0000000..e432cb9 --- /dev/null +++ b/go/encoding/bcd/nonpacked.go @@ -0,0 +1,50 @@ +package bcd + +import ( + "fmt" + "math" +) + +// NonPackedBigEndianBCD is a non-packed, little-endian, binary coded decimal value. +type NonPackedBigEndianBCD uint64 + +// UnmarshalBinary implements the binary.Unmarshaler interface. +func (bcd *NonPackedBigEndianBCD) UnmarshalBinary(bytes []byte) error { + var res uint64 + + for i := 0; i < len(bytes); i++ { + digit := uint64(bytes[i]) + if digit > 9 { + *bcd = 0 + return fmt.Errorf("Bad digit value for BCD: %d", digit) + } + + multiplier := uint64(math.Pow(10, float64(len(bytes)-i-1))) + res += multiplier * digit + } + + *bcd = NonPackedBigEndianBCD(res) + return nil +} + +// NonPackedLittleEndianBCD is a non-packed, little-endian, binary coded decimal value. +type NonPackedLittleEndianBCD uint64 + +// UnmarshalBinary implements the binary.Unmarshaler interface. +func (bcd *NonPackedLittleEndianBCD) UnmarshalBinary(bytes []byte) error { + var res uint64 + + for i := 0; i < len(bytes); i++ { + digit := uint64(bytes[i]) + if digit > 9 { + *bcd = 0 + return fmt.Errorf("Bad digit value for BCD: %d", digit) + } + + multiplier := uint64(math.Pow(10, float64(i))) + res += multiplier * digit + } + + *bcd = NonPackedLittleEndianBCD(res) + return nil +} diff --git a/go/encoding/bcd/nonpacked_test.go b/go/encoding/bcd/nonpacked_test.go new file mode 100644 index 0000000..04b33e0 --- /dev/null +++ b/go/encoding/bcd/nonpacked_test.go @@ -0,0 +1,123 @@ +package bcd + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestNonPackedBigEndianBCDDecoder(t *testing.T) { + Convey("Given a non-packed, big-endian BCD", t, func() { + var bcd NonPackedBigEndianBCD + + Convey("When unmarshaling 0x00", func() { + err := bcd.UnmarshalBinary([]byte{0x00}) + So(err, ShouldBeNil) + + Convey("Then the result should be 0", func() { + So(bcd, ShouldEqual, 0) + }) + }) + + Convey("When unmarshaling 0x01", func() { + err := bcd.UnmarshalBinary([]byte{0x01}) + So(err, ShouldBeNil) + + Convey("Then the result should be 1", func() { + So(bcd, ShouldEqual, 1) + }) + }) + + Convey("When unmarshaling 0x09", func() { + err := bcd.UnmarshalBinary([]byte{0x09}) + So(err, ShouldBeNil) + + Convey("Then the result should be 9", func() { + So(bcd, ShouldEqual, 9) + }) + }) + + Convey("When unmarshaling 0x0A", func() { + err := bcd.UnmarshalBinary([]byte{0x0A}) + + Convey("Then an error should happen", func() { + So(err, ShouldNotBeNil) + }) + }) + + Convey("When unmarshaling 0x10", func() { + err := bcd.UnmarshalBinary([]byte{0x10}) + + Convey("Then an error should happen", func() { + So(err, ShouldNotBeNil) + }) + }) + + Convey("When unmarshaling 0x0102", func() { + err := bcd.UnmarshalBinary([]byte{0x01, 0x02}) + So(err, ShouldBeNil) + + Convey("Then the result should be 12", func() { + So(bcd, ShouldEqual, 12) + }) + }) + }) +} + +func TestNonPackedLittleEndianBCDDecoder(t *testing.T) { + Convey("Given a non-packed, little-endian BCD", t, func() { + var bcd NonPackedLittleEndianBCD + + Convey("When unmarshaling 0x00", func() { + err := bcd.UnmarshalBinary([]byte{0x00}) + So(err, ShouldBeNil) + + Convey("Then the result should be 0", func() { + So(bcd, ShouldEqual, 0) + }) + }) + + Convey("When unmarshaling 0x01", func() { + err := bcd.UnmarshalBinary([]byte{0x01}) + So(err, ShouldBeNil) + + Convey("Then the result should be 1", func() { + So(bcd, ShouldEqual, 1) + }) + }) + + Convey("When unmarshaling 0x09", func() { + err := bcd.UnmarshalBinary([]byte{0x09}) + So(err, ShouldBeNil) + + Convey("Then the result should be 09", func() { + So(bcd, ShouldEqual, 9) + }) + }) + + Convey("When unmarshaling 0x0A", func() { + err := bcd.UnmarshalBinary([]byte{0x0A}) + + Convey("Then an error should happen", func() { + So(err, ShouldNotBeNil) + }) + }) + + Convey("When unmarshaling 0x10", func() { + err := bcd.UnmarshalBinary([]byte{0x10}) + + Convey("Then an error should happen", func() { + So(err, ShouldNotBeNil) + }) + }) + + Convey("When unmarshaling 0x0102", func() { + err := bcd.UnmarshalBinary([]byte{0x01, 0x02}) + So(err, ShouldBeNil) + + Convey("Then the result should be 21", func() { + So(bcd, ShouldEqual, 21) + }) + }) + }) +} diff --git a/go/encoding/bcd/packed.go b/go/encoding/bcd/packed.go new file mode 100644 index 0000000..d9d0974 --- /dev/null +++ b/go/encoding/bcd/packed.go @@ -0,0 +1,59 @@ +package bcd + +import ( + "fmt" + "math" + + "github.com/smartmakers/drivers/go/encoding/bytes" +) + +// PackedBigEndianBCD is a packed, big endian, binary coded decimal value. +type PackedBigEndianBCD uint64 + +// UnmarshalBinary implements the binary.Unmarshaler interface. +func (bcd *PackedBigEndianBCD) UnmarshalBinary(array []byte) error { + var res uint64 + + for i := 0; i < len(array); i++ { + // can ignore errors here, because they depend only on 'from' and 'to' + // which are hard coded to valid values + upperDigit, _ := bytes.Bits(array[i], 5, 8) + lowerDigit, _ := bytes.Bits(array[i], 1, 4) + if lowerDigit > 9 || upperDigit > 9 { + *bcd = 0 + return fmt.Errorf("Bad digit value for BCD: %x", array[i]) + } + + bytePos := len(array) - i - 1 + multiplier := uint64(math.Pow(100, float64(bytePos))) + res += (multiplier * uint64(lowerDigit)) + (10 * multiplier * uint64(upperDigit)) + } + + *bcd = PackedBigEndianBCD(res) + return nil +} + +// PackedLittleEndianBCD is a packed, little endian, binary coded decimal value. +type PackedLittleEndianBCD uint64 + +// UnmarshalBinary implements the binary.Unmarshaler interface. +func (bcd *PackedLittleEndianBCD) UnmarshalBinary(array []byte) error { + var res uint64 + + for i := 0; i < len(array); i++ { + // can ignore errors here, because they depend only on 'from' and 'to' + // which are hard coded to valid values + upperDigit, _ := bytes.Bits(array[i], 1, 4) + lowerDigit, _ := bytes.Bits(array[i], 5, 8) + if lowerDigit > 9 || upperDigit > 9 { + return fmt.Errorf("Bad digit value for BCD: %x", array[i]) + } + + bytePos := i + multiplier := uint64(math.Pow(100, float64(bytePos))) + res += (multiplier * uint64(lowerDigit)) + (10 * multiplier * uint64(upperDigit)) + } + + *bcd = PackedLittleEndianBCD(res) + return nil +} diff --git a/go/encoding/bcd/packed_test.go b/go/encoding/bcd/packed_test.go new file mode 100644 index 0000000..0b876ad --- /dev/null +++ b/go/encoding/bcd/packed_test.go @@ -0,0 +1,177 @@ +package bcd + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestPackedBigEndianBCDDecoder(t *testing.T) { + Convey("Given a packed big-endian BCD decoder", t, func() { + var bcd PackedBigEndianBCD + + Convey("When decoding 0x00", func() { + err := bcd.UnmarshalBinary([]byte{0x00}) + So(err, ShouldBeNil) + + Convey("Then the result should be 0", func() { + So(bcd, ShouldEqual, 0) + }) + }) + + Convey("When decoding 0x01", func() { + err := bcd.UnmarshalBinary([]byte{0x01}) + So(err, ShouldBeNil) + + Convey("Then the result should be 1", func() { + So(bcd, ShouldEqual, 1) + }) + }) + + Convey("When decoding 0x09", func() { + err := bcd.UnmarshalBinary([]byte{0x09}) + So(err, ShouldBeNil) + + Convey("Then the result should be 9", func() { + So(bcd, ShouldEqual, 9) + }) + }) + + Convey("When decoding 0x0A", func() { + err := bcd.UnmarshalBinary([]byte{0x0A}) + + Convey("Then an error should happen", func() { + So(err, ShouldNotBeNil) + }) + }) + + Convey("When decoding 0x10", func() { + err := bcd.UnmarshalBinary([]byte{0x10}) + So(err, ShouldBeNil) + + Convey("Then the result should be 10", func() { + So(bcd, ShouldEqual, 10) + }) + }) + + Convey("When decoding 0xA0", func() { + err := bcd.UnmarshalBinary([]byte{0xA0}) + + Convey("Then an error should happen", func() { + So(err, ShouldNotBeNil) + }) + }) + + Convey("When decoding 0x42", func() { + err := bcd.UnmarshalBinary([]byte{0x42}) + So(err, ShouldBeNil) + + Convey("Then the result should be 42", func() { + So(bcd, ShouldEqual, 42) + }) + }) + + Convey("When decoding 0x4711", func() { + err := bcd.UnmarshalBinary([]byte{0x47, 0x11}) + So(err, ShouldBeNil) + + Convey("Then the result should be 4711", func() { + So(bcd, ShouldEqual, 4711) + }) + }) + + Convey("When decoding 0x12345678", func() { + err := bcd.UnmarshalBinary([]byte{0x12, 0x34, 0x56, 0x78}) + So(err, ShouldBeNil) + + Convey("Then the result should be 12345678", func() { + So(bcd, ShouldEqual, 12345678) + }) + }) + }) +} + +func TestPackedLittleEndianBCDDecoder(t *testing.T) { + Convey("Given a packed little-endian BCD decoder", t, func() { + var bcd PackedLittleEndianBCD + + Convey("When decoding 0x00", func() { + err := bcd.UnmarshalBinary([]byte{0x00}) + So(err, ShouldBeNil) + + Convey("Then the result should be 0", func() { + So(bcd, ShouldEqual, 0) + }) + }) + + Convey("When decoding 0x01", func() { + err := bcd.UnmarshalBinary([]byte{0x01}) + So(err, ShouldBeNil) + + Convey("Then the result should be 10", func() { + So(bcd, ShouldEqual, 10) + }) + }) + + Convey("When decoding 0x09", func() { + err := bcd.UnmarshalBinary([]byte{0x09}) + So(err, ShouldBeNil) + + Convey("Then the result should be 90", func() { + So(bcd, ShouldEqual, 90) + }) + }) + + Convey("When decoding 0x0A", func() { + err := bcd.UnmarshalBinary([]byte{0x0A}) + + Convey("Then an error should happen", func() { + So(err, ShouldNotBeNil) + }) + }) + + Convey("When decoding 0x10", func() { + err := bcd.UnmarshalBinary([]byte{0x10}) + So(err, ShouldBeNil) + + Convey("Then the result should be 1", func() { + So(bcd, ShouldEqual, 1) + }) + }) + + Convey("When decoding 0xA0", func() { + err := bcd.UnmarshalBinary([]byte{0xA0}) + + Convey("Then an error should happen", func() { + So(err, ShouldNotBeNil) + }) + }) + + Convey("When decoding 0x42", func() { + err := bcd.UnmarshalBinary([]byte{0x42}) + So(err, ShouldBeNil) + + Convey("Then the result should be 24", func() { + So(bcd, ShouldEqual, 24) + }) + }) + + Convey("When decoding 0x4711", func() { + err := bcd.UnmarshalBinary([]byte{0x47, 0x11}) + So(err, ShouldBeNil) + + Convey("Then the result should be 1174", func() { + So(bcd, ShouldEqual, 1174) + }) + }) + + Convey("When decoding 0x12345678", func() { + err := bcd.UnmarshalBinary([]byte{0x12, 0x34, 0x56, 0x78}) + So(err, ShouldBeNil) + + Convey("Then the result should be 87654321", func() { + So(bcd, ShouldEqual, 87654321) + }) + }) + }) +} diff --git a/go/encoding/bytes/bits.go b/go/encoding/bytes/bits.go new file mode 100644 index 0000000..b2085a8 --- /dev/null +++ b/go/encoding/bytes/bits.go @@ -0,0 +1,19 @@ +package bytes + +import "errors" + +// Need to move this somewhere else +func Bits(input, from, to byte) (byte, error) { + if from == 0 || from > 8 { + return 0, errors.New("index out of range") + } + + if to == 0 || to > 8 { + return 0, errors.New("index out of range") + } + + bit := 1 << (to) + mask := byte(bit - 1) + shifted := (input & mask) >> (from - 1) + return shifted, nil +} diff --git a/go/encoding/bytes/bits_test.go b/go/encoding/bytes/bits_test.go new file mode 100644 index 0000000..e44b2b8 --- /dev/null +++ b/go/encoding/bytes/bits_test.go @@ -0,0 +1,140 @@ +package bytes + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestBits0x01(t *testing.T) { + Convey("Given the byte 0x01", t, func() { + in := byte(0x01) + + Convey("When extracting the first 1", func() { + out, err := Bits(in, 1, 1) + So(err, ShouldBeNil) + + Convey("Then the result should be 0x01", func() { + So(out, ShouldEqual, 0x01) + }) + }) + + Convey("When extractring the second bit", func() { + out, err := Bits(in, 2, 2) + So(err, ShouldBeNil) + + Convey("Then the result should be 0x00", func() { + So(out, ShouldEqual, 0x00) + }) + }) + }) +} + +func TestBits0x02(t *testing.T) { + Convey("Given the byte 0x02", t, func() { + in := byte(0x02) + + Convey("When extracting the first bit", func() { + out, err := Bits(in, 1, 1) + So(err, ShouldBeNil) + + Convey("Then the result should be 0x00", func() { + So(out, ShouldEqual, 0x00) + }) + }) + + Convey("When extracting the second bit", func() { + out, err := Bits(in, 2, 2) + So(err, ShouldBeNil) + + Convey("Then the result should be 0x01", func() { + So(out, ShouldEqual, 0x01) + }) + }) + }) +} + +func TestBits0x03(t *testing.T) { + Convey("Given the byte 0x03", t, func() { + in := byte(0x03) + + Convey("When extracting the first bit", func() { + out, err := Bits(in, 1, 1) + So(err, ShouldBeNil) + + Convey("Then the result should be 0x01", func() { + So(out, ShouldEqual, 0x01) + }) + }) + + Convey("When extracting the second bit", func() { + out, err := Bits(in, 2, 2) + So(err, ShouldBeNil) + + Convey("Then the result should be 0x01", func() { + So(out, ShouldEqual, 0x01) + }) + }) + + Convey("When extracting the third bit", func() { + out, err := bits(in, 3, 3) + So(err, ShouldBeNil) + + Convey("Then the result should be 0x00", func() { + So(out, ShouldEqual, 0x00) + }) + }) + + Convey("When extracting the first and second bit", func() { + out, err := Bits(in, 1, 2) + So(err, ShouldBeNil) + + Convey("Then the result should be 0x03", func() { + So(out, ShouldEqual, 0x03) + }) + }) + }) +} + +func TestBits0xA0(t *testing.T) { + Convey("Given the byte 0x80 (0b10...0)", t, func() { + in := byte(0x80) + + Convey("When extracting the first bit", func() { + out, err := Bits(in, 1, 1) + So(err, ShouldBeNil) + + Convey("Then the result should be 0x00", func() { + So(out, ShouldEqual, 0x00) + }) + }) + + Convey("When extracting the second to last bit", func() { + out, err := Bits(in, 7, 7) + So(err, ShouldBeNil) + + Convey("Then the result should be 0x00", func() { + So(out, ShouldEqual, 0x00) + }) + }) + + Convey("When extracting the last bit", func() { + out, err := Bits(in, 8, 8) + So(err, ShouldBeNil) + + Convey("Then the result should be 0x01", func() { + So(out, ShouldEqual, 0x01) + }) + }) + + Convey("When extracting the two last bits", func() { + out, err := Bits(in, 7, 8) + So(err, ShouldBeNil) + + Convey("Then the result should be 0x02", func() { + So(out, ShouldEqual, 0x02) + }) + }) + + }) +} diff --git a/go/encoding/bytes/hex.go b/go/encoding/bytes/hex.go new file mode 100644 index 0000000..2a4671a --- /dev/null +++ b/go/encoding/bytes/hex.go @@ -0,0 +1,13 @@ +package bytes + +import ( + "encoding/hex" +) + +type Bytes []byte + +func (b Bytes) MarshalText() ([]byte, error) { + dst := make([]byte, hex.EncodedLen(len(b))) + hex.Encode(dst, b) + return dst, nil +} From d7c726be31e5a1ffeba13c3ee92386fb530412ba Mon Sep 17 00:00:00 2001 From: flrnmrz Date: Thu, 7 Feb 2019 16:24:56 +0100 Subject: [PATCH 3/4] Update golang.md --- docs/golang.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/golang.md b/docs/golang.md index e76ca89..ccc0952 100644 --- a/docs/golang.md +++ b/docs/golang.md @@ -9,6 +9,6 @@ drivers init javascript ## Notes * A Golang driver development project requires golang's `go` command line tool - to be installed. + to be installed (https://golang.org/doc/install#install). * Golang requires the driver development projects path to be a subpath of the path set in the environment variable GOPATH. From 26dfeb79ec8bd7493b5d341b3068a323550dfeb8 Mon Sep 17 00:00:00 2001 From: flrnmrz Date: Thu, 7 Feb 2019 16:25:34 +0100 Subject: [PATCH 4/4] Update quickstart.md --- docs/quickstart.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/quickstart.md b/docs/quickstart.md index 39fafdf..a721af8 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -68,7 +68,7 @@ Or alternatively edit the `.project` file accordingly: ```yaml type: golang metadata: - name: "" + name: "" author: "" labels: supports: