First implementation
This commit is contained in:
parent
1cf57702cc
commit
b3de5ca724
1
.dockerignore
Normal file
1
.dockerignore
Normal file
@ -0,0 +1 @@
|
||||
rc-pca9685
|
284
.gitignore
vendored
Normal file
284
.gitignore
vendored
Normal file
@ -0,0 +1,284 @@
|
||||
# Created by .ignore support plugin (hsz.mobi)
|
||||
### Emacs template
|
||||
# -*- mode: gitignore; -*-
|
||||
*~
|
||||
\#*\#
|
||||
/.emacs.desktop
|
||||
/.emacs.desktop.lock
|
||||
*.elc
|
||||
auto-save-list
|
||||
tramp
|
||||
.\#*
|
||||
|
||||
# Org-mode
|
||||
.org-id-locations
|
||||
*_archive
|
||||
|
||||
# flymake-mode
|
||||
*_flymake.*
|
||||
|
||||
# eshell files
|
||||
/eshell/history
|
||||
/eshell/lastdir
|
||||
|
||||
# elpa packages
|
||||
/elpa/
|
||||
|
||||
# reftex files
|
||||
*.rel
|
||||
|
||||
# AUCTeX auto folder
|
||||
/auto/
|
||||
|
||||
# cask packages
|
||||
.cask/
|
||||
dist/
|
||||
|
||||
# Flycheck
|
||||
flycheck_*.el
|
||||
|
||||
# server auth directory
|
||||
/server/
|
||||
|
||||
# projectiles files
|
||||
.projectile
|
||||
|
||||
# directory configuration
|
||||
.dir-locals.el
|
||||
|
||||
# network security
|
||||
/network-security.data
|
||||
|
||||
|
||||
### JetBrains template
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/usage.statistics.xml
|
||||
.idea/**/dictionaries
|
||||
.idea/**/shelf
|
||||
|
||||
# Generated files
|
||||
.idea/**/contentModel.xml
|
||||
|
||||
# Sensitive or high-churn files
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/dbnavigator.xml
|
||||
|
||||
# Gradle
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Gradle and Maven with auto-import
|
||||
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||
# since they will be recreated, and may cause churn. Uncomment if using
|
||||
# auto-import.
|
||||
# .idea/artifacts
|
||||
# .idea/compiler.xml
|
||||
# .idea/modules.xml
|
||||
# .idea/*.iml
|
||||
# .idea/modules
|
||||
# *.iml
|
||||
# *.ipr
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
|
||||
# Mongo Explorer plugin
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
# File-based project format
|
||||
*.iws
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
# Editor-based Rest Client
|
||||
.idea/httpRequests
|
||||
|
||||
# Android studio 3.1+ serialized cache file
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
### Vim template
|
||||
# Swap
|
||||
[._]*.s[a-v][a-z]
|
||||
!*.svg # comment out if you don't need vector files
|
||||
[._]*.sw[a-p]
|
||||
[._]s[a-rt-v][a-z]
|
||||
[._]ss[a-gi-z]
|
||||
[._]sw[a-p]
|
||||
|
||||
# Session
|
||||
Session.vim
|
||||
Sessionx.vim
|
||||
|
||||
# Temporary
|
||||
.netrwhist
|
||||
*~
|
||||
# Auto-generated tag files
|
||||
tags
|
||||
# Persistent undo
|
||||
[._]*.un~
|
||||
|
||||
### macOS template
|
||||
# General
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
### Xcode template
|
||||
# Xcode
|
||||
#
|
||||
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
|
||||
|
||||
## User settings
|
||||
xcuserdata/
|
||||
|
||||
## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
|
||||
*.xcscmblueprint
|
||||
*.xccheckout
|
||||
|
||||
## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
|
||||
build/
|
||||
DerivedData/
|
||||
*.moved-aside
|
||||
*.pbxuser
|
||||
!default.pbxuser
|
||||
*.mode1v3
|
||||
!default.mode1v3
|
||||
*.mode2v3
|
||||
!default.mode2v3
|
||||
*.perspectivev3
|
||||
!default.perspectivev3
|
||||
|
||||
## Gcc Patch
|
||||
/*.gcno
|
||||
|
||||
### Go template
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
|
||||
### Eclipse template
|
||||
.metadata
|
||||
bin/
|
||||
tmp/
|
||||
*.tmp
|
||||
*.bak
|
||||
*.swp
|
||||
*~.nib
|
||||
local.properties
|
||||
.settings/
|
||||
.loadpath
|
||||
.recommenders
|
||||
|
||||
# External tool builders
|
||||
.externalToolBuilders/
|
||||
|
||||
# Locally stored "Eclipse launch configurations"
|
||||
*.launch
|
||||
|
||||
# PyDev specific (Python IDE for Eclipse)
|
||||
*.pydevproject
|
||||
|
||||
# CDT-specific (C/C++ Development Tooling)
|
||||
.cproject
|
||||
|
||||
# CDT- autotools
|
||||
.autotools
|
||||
|
||||
# Java annotation processor (APT)
|
||||
.factorypath
|
||||
|
||||
# PDT-specific (PHP Development Tools)
|
||||
.buildpath
|
||||
|
||||
# sbteclipse plugin
|
||||
.target
|
||||
|
||||
# Tern plugin
|
||||
.tern-project
|
||||
|
||||
# TeXlipse plugin
|
||||
.texlipse
|
||||
|
||||
# STS (Spring Tool Suite)
|
||||
.springBeans
|
||||
|
||||
# Code Recommenders
|
||||
.recommenders/
|
||||
|
||||
# Annotation Processing
|
||||
.apt_generated/
|
||||
.apt_generated_test/
|
||||
|
||||
# Scala IDE specific (Scala & Java development for Eclipse)
|
||||
.cache-main
|
||||
.scala_dependencies
|
||||
.worksheet
|
||||
|
||||
### VisualStudioCode template
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
*.code-workspace
|
||||
|
19
Dockerfile
Normal file
19
Dockerfile
Normal file
@ -0,0 +1,19 @@
|
||||
FROM --platform=$BUILDPLATFORM golang:alpine AS builder
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
ARG BUILDPLATFORM
|
||||
|
||||
WORKDIR /go/src
|
||||
ADD . .
|
||||
|
||||
RUN GOOS=$(echo $TARGETPLATFORM | cut -f1 -d/) && \
|
||||
GOARCH=$(echo $TARGETPLATFORM | cut -f2 -d/) && \
|
||||
GOARM=$(echo $TARGETPLATFORM | cut -f3 -d/ | sed "s/v//" ) && \
|
||||
CGO_ENABLED=0 GOOS=${GOOS} GOARCH=${GOARCH} GOARM=${GOARM} go build -mod vendor -tags netgo ./cmd/rc-pca9685/
|
||||
|
||||
|
||||
FROM gcr.io/distroless/static
|
||||
|
||||
USER 1234
|
||||
COPY --from=builder /go/src/rc-pca9685 /go/bin/rc-pca9685
|
||||
ENTRYPOINT ["/go/bin/rc-pca9685"]]
|
201
LICENSE
Normal file
201
LICENSE
Normal file
@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
3
README.md
Normal file
3
README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# robocar-pca9685
|
||||
|
||||
Microservice part for PCA9685 controller
|
39
actuator/pca9685.go
Normal file
39
actuator/pca9685.go
Normal file
@ -0,0 +1,39 @@
|
||||
package actuator
|
||||
|
||||
import (
|
||||
"log"
|
||||
"periph.io/x/periph/conn/i2c/i2creg"
|
||||
"periph.io/x/periph/conn/physic"
|
||||
"periph.io/x/periph/experimental/devices/pca9685"
|
||||
"periph.io/x/periph/host"
|
||||
)
|
||||
|
||||
var (
|
||||
device *pca9685.Dev
|
||||
)
|
||||
|
||||
func init() {
|
||||
log.Print("init pca9685 controller")
|
||||
_, err := host.Init()
|
||||
if err != nil {
|
||||
log.Fatalf("unable to init host: %v", err)
|
||||
}
|
||||
|
||||
log.Print("open i2c bus")
|
||||
bus, err := i2creg.Open("")
|
||||
if err != nil {
|
||||
log.Fatalf("unable to init i2c bus: %v", err)
|
||||
}
|
||||
log.Print("i2c bus opened")
|
||||
|
||||
device, err = pca9685.NewI2C(bus, pca9685.I2CAddr)
|
||||
if err != nil {
|
||||
log.Fatalf("unable to init pca9685 bus: %v", err)
|
||||
}
|
||||
log.Printf("set pwm frequency to %d", 60)
|
||||
err = device.SetPwmFreq(60 * physic.Hertz)
|
||||
if err != nil {
|
||||
log.Fatalf("unable to set pwm frequency: %v", err)
|
||||
}
|
||||
log.Print("init done")
|
||||
}
|
44
actuator/steering.go
Normal file
44
actuator/steering.go
Normal file
@ -0,0 +1,44 @@
|
||||
package actuator
|
||||
|
||||
import (
|
||||
"github.com/cyrilix/robocar-pca9685/util"
|
||||
"log"
|
||||
"periph.io/x/periph/conn/gpio"
|
||||
"periph.io/x/periph/experimental/devices/pca9685"
|
||||
)
|
||||
|
||||
const (
|
||||
LeftAngle = -1.
|
||||
RightAngle = 1.
|
||||
)
|
||||
|
||||
type Steering struct {
|
||||
channel int
|
||||
leftPWM, rightPWM int
|
||||
dev *pca9685.Dev
|
||||
}
|
||||
|
||||
func (s *Steering) SetPulse(pulse int) {
|
||||
err := s.dev.SetPwm(s.channel, 0, gpio.Duty(pulse))
|
||||
if err != nil {
|
||||
log.Printf("unable to set throttle pwm value: %v", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Set percent value steering
|
||||
func (s *Steering) SetPercentValue(p float64) {
|
||||
// map absolute angle to angle that vehicle can implement.
|
||||
pulse := util.MapRange(p, LeftAngle, RightAngle, float64(s.leftPWM), float64(s.rightPWM))
|
||||
s.SetPulse(pulse)
|
||||
}
|
||||
|
||||
func NewSteering(channel, leftPWM, rightPWM int) *Steering {
|
||||
t := Steering{
|
||||
channel: channel,
|
||||
dev: device,
|
||||
leftPWM: leftPWM,
|
||||
rightPWM: rightPWM,
|
||||
}
|
||||
return &t
|
||||
}
|
53
actuator/throttle.go
Normal file
53
actuator/throttle.go
Normal file
@ -0,0 +1,53 @@
|
||||
package actuator
|
||||
|
||||
import (
|
||||
"github.com/cyrilix/robocar-pca9685/util"
|
||||
"log"
|
||||
"periph.io/x/periph/conn/gpio"
|
||||
"periph.io/x/periph/experimental/devices/pca9685"
|
||||
)
|
||||
|
||||
const (
|
||||
MinThrottle = -1
|
||||
MaxThrottle = 1
|
||||
)
|
||||
|
||||
type Throttle struct {
|
||||
channel int
|
||||
zeroPulse, minPulse, maxPulse int
|
||||
dev *pca9685.Dev
|
||||
}
|
||||
|
||||
func (t *Throttle) SetPulse(pulse int) {
|
||||
err := t.dev.SetPwm(t.channel, 0, gpio.Duty(pulse))
|
||||
if err != nil {
|
||||
log.Printf("unable to set throttle pwm value: %v", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Set percent value throttle
|
||||
func (t *Throttle) SetPercentValue(p float64) {
|
||||
var pulse int
|
||||
if p > 0 {
|
||||
pulse = util.MapRange(p, 0, MaxThrottle, float64(t.zeroPulse), float64(t.maxPulse))
|
||||
} else {
|
||||
pulse = util.MapRange(p, MinThrottle, 0, float64(t.minPulse), float64(t.zeroPulse))
|
||||
}
|
||||
t.SetPulse(pulse)
|
||||
}
|
||||
|
||||
func NewThrottle(channel, zeroPulse, minPulse, maxPulse int) *Throttle {
|
||||
t := Throttle{
|
||||
channel: channel,
|
||||
dev: device,
|
||||
zeroPulse: zeroPulse,
|
||||
minPulse: minPulse,
|
||||
maxPulse: maxPulse,
|
||||
}
|
||||
|
||||
log.Printf("send zero pulse to calibrate ESC: %v", zeroPulse)
|
||||
t.SetPulse(zeroPulse)
|
||||
|
||||
return &t
|
||||
}
|
94
cmd/rc-pca9685/rc-pca9685.go
Normal file
94
cmd/rc-pca9685/rc-pca9685.go
Normal file
@ -0,0 +1,94 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"github.com/cyrilix/robocar-base/cli"
|
||||
rc "github.com/cyrilix/robocar-pca9685/actuator"
|
||||
"github.com/cyrilix/robocar-pca9685/part"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultClientId = "robocar-pca9685"
|
||||
SteeringChannel = 0
|
||||
ThrottleChannel = 1
|
||||
|
||||
ThrottleStoppedPWM = 1455
|
||||
ThrottleMinPWM = 1113
|
||||
ThrottleMaxPWM = 1800
|
||||
|
||||
SteeringLeftPWM = 1004
|
||||
SteeringRightPWM = 1986
|
||||
)
|
||||
|
||||
func main() {
|
||||
var mqttBroker, username, password, clientId, topicThrottle, topicSteering string
|
||||
|
||||
mqttQos := cli.InitIntFlag("MQTT_QOS", 0)
|
||||
_, mqttRetain := os.LookupEnv("MQTT_RETAIN")
|
||||
|
||||
cli.InitMqttFlags(DefaultClientId, &mqttBroker, &username, &password, &clientId, &mqttQos, &mqttRetain)
|
||||
|
||||
var throttleChannel, throttleStoppedPWM, throttleMinPWM, throttleMaxPWM int
|
||||
if err := cli.SetIntDefaultValueFromEnv(&throttleChannel, "THROTTLE_CHANNEL", ThrottleChannel); err != nil {
|
||||
log.Printf("unable to init throttleChannel arg: %v", err)
|
||||
}
|
||||
if err := cli.SetIntDefaultValueFromEnv(&throttleStoppedPWM, "THROTTLE_STOPPED_PWM", ThrottleStoppedPWM); err != nil {
|
||||
log.Printf("unable to init throttleStoppedPWM arg: %v", err)
|
||||
}
|
||||
if err := cli.SetIntDefaultValueFromEnv(&throttleMinPWM, "THROTTLE_MIN_PWM", ThrottleMinPWM); err != nil {
|
||||
log.Printf("unable to init throttleMinPWM arg: %v", err)
|
||||
}
|
||||
if err := cli.SetIntDefaultValueFromEnv(&throttleMaxPWM, "THROTTLE_MAX_PWM", ThrottleMaxPWM); err != nil {
|
||||
log.Printf("unable to init throttleMaxPWM arg: %v", err)
|
||||
}
|
||||
|
||||
var steeringChannel, steeringLeftPWM, steeringRightPWM int
|
||||
if err := cli.SetIntDefaultValueFromEnv(&steeringChannel, "STEERING_CHANNEL", SteeringChannel); err != nil {
|
||||
log.Printf("unable to init steeringChannel arg: %v", err)
|
||||
}
|
||||
if err := cli.SetIntDefaultValueFromEnv(&steeringLeftPWM, "STEERING_LEFT_PWM", SteeringLeftPWM); err != nil {
|
||||
log.Printf("unable to init steeringLeftPWM arg: %v", err)
|
||||
}
|
||||
if err := cli.SetIntDefaultValueFromEnv(&steeringRightPWM, "STEERING_RIGHT_PWM", SteeringRightPWM); err != nil {
|
||||
log.Printf("unable to init steeringRightPWM arg: %v", err)
|
||||
}
|
||||
|
||||
var updatePWMFrequency int
|
||||
if err := cli.SetIntDefaultValueFromEnv(&updatePWMFrequency, "UPDATE_PWM_FREQUENCY", 25); err != nil {
|
||||
log.Printf("unable to init updatePWMFrequency arg: %v", err)
|
||||
}
|
||||
|
||||
flag.StringVar(&topicThrottle, "mqtt-topic-throttle", os.Getenv("MQTT_TOPIC_THROTTLE"), "Mqtt topic that contains throttle value, use MQTT_TOPIC_THROTTLE if args not set")
|
||||
flag.StringVar(&topicSteering, "mqtt-topic-steering", os.Getenv("MQTT_TOPIC_STEERING"), "Mqtt topic that contains steering value, use MQTT_TOPIC_STEERING if args not set")
|
||||
flag.IntVar(&throttleChannel, "throttle-channel", throttleChannel, "I2C channel to use to control throttle, THROTTLE_CHANNEL env if args not set")
|
||||
flag.IntVar(&steeringChannel, "steering-channel", steeringChannel, "I2C channel to use to control steering, STEERING_CHANNEL env if args not set")
|
||||
flag.IntVar(&throttleStoppedPWM, "throttle-zero-pwm", throttleStoppedPWM, "Zero value for throttle PWM, THROTTLE_STOPPED_PWM env if args not set")
|
||||
flag.IntVar(&throttleMinPWM, "throttle-min-pwm", throttleMinPWM, "Min value for throttle PWM, THROTTLE_MIN_PWM env if args not set")
|
||||
flag.IntVar(&throttleMaxPWM, "throttle-max-pwm", throttleMaxPWM, "Max value for throttle PWM, THROTTLE_MAX_PWM env if args not set")
|
||||
flag.IntVar(&steeringLeftPWM, "steering-left-pwm", steeringLeftPWM, "Max left value for steering PWM, STEERING_STOPPED_PWM env if args not set")
|
||||
flag.IntVar(&steeringRightPWM, "steering-right-pwm", steeringRightPWM, "Max right value for steering PWM, STEERING_MIN_PWM env if args not set")
|
||||
flag.IntVar(&updatePWMFrequency, "update-pwm-frequency", updatePWMFrequency, "Number of update values per seconds, UPDATE_PWM_FREQUENCY env if args not set")
|
||||
|
||||
flag.Parse()
|
||||
if len(os.Args) <= 1 {
|
||||
flag.PrintDefaults()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
client, err := cli.Connect(mqttBroker, username, password, clientId)
|
||||
if err != nil {
|
||||
log.Fatalf("unable to connect to mqtt bus: %v", err)
|
||||
}
|
||||
defer client.Disconnect(50)
|
||||
|
||||
t := rc.NewThrottle(throttleChannel, throttleStoppedPWM, throttleMinPWM, throttleMaxPWM)
|
||||
s := rc.NewSteering(steeringChannel, steeringLeftPWM, steeringRightPWM)
|
||||
|
||||
p := part.NewPca9685Part(client, t, s, updatePWMFrequency, topicThrottle, topicSteering)
|
||||
err = p.Start()
|
||||
if err != nil {
|
||||
log.Fatalf("unable to start service: %v", err)
|
||||
}
|
||||
}
|
9
go.mod
Normal file
9
go.mod
Normal file
@ -0,0 +1,9 @@
|
||||
module github.com/cyrilix/robocar-pca9685
|
||||
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/cyrilix/robocar-base v0.0.0-20191227154304-47d48c39b0a2
|
||||
github.com/eclipse/paho.mqtt.golang v1.2.0
|
||||
periph.io/x/periph v3.6.2+incompatible
|
||||
)
|
119
go.sum
Normal file
119
go.sum
Normal file
@ -0,0 +1,119 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
||||
github.com/Microsoft/go-winio v0.4.11 h1:zoIOcVf0xPN1tnMVbTtEdI+P8OofVk3NObnwOQ6nK2Q=
|
||||
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
||||
github.com/Microsoft/hcsshim v0.8.6 h1:ZfF0+zZeYdzMIVMZHKtDKJvLHj76XCuVae/jNkjj0IA=
|
||||
github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc h1:TP+534wVlf61smEIq1nwLLAjQVEK2EADoW3CX9AuT+8=
|
||||
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
||||
github.com/cyrilix/robocar-base v0.0.0-20191227154304-47d48c39b0a2 h1:7E0P2+YXKtRM++vnBZtaNVlhKEMkp+X3qYdd0CzseJY=
|
||||
github.com/cyrilix/robocar-base v0.0.0-20191227154304-47d48c39b0a2/go.mod h1:/KZidG8Y4sKxCCkTcswpKz20oFN3j62tJvamEHcSgLM=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible h1:dvc1KSkIYTVjZgHf/CTC2diTYC8PzhaA5sFISRfNVrE=
|
||||
github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v0.7.3-0.20190506211059-b20a14b54661 h1:ZuxGvIvF01nfc/G9RJ5Q7Va1zQE2WJyG18Zv3DqCEf4=
|
||||
github.com/docker/docker v0.7.3-0.20190506211059-b20a14b54661/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/go-units v0.3.3 h1:Xk8S3Xj5sLGlG5g67hJmYMmUgXv5N4PhkjJHHqrwnTk=
|
||||
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/eclipse/paho.mqtt.golang v1.2.0 h1:1F8mhG9+aO5/xpdtFkW4SxOJB67ukuDC3t2y2qayIX0=
|
||||
github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/go-redis/redis v6.15.6+incompatible h1:H9evprGPLI8+ci7fxQx6WNZHJSb7be8FqJQRhdQZ5Sg=
|
||||
github.com/go-redis/redis v6.15.6+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
|
||||
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
|
||||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/gogo/protobuf v1.2.0 h1:xU6/SpYbvkNYiptHJYEDRseDLvYE7wSqhYYNy0QSUzI=
|
||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
|
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk=
|
||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c h1:nXxl5PrvVm2L/wCy8dQu6DMTwH4oIuGN8GJDAlqDdVE=
|
||||
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=
|
||||
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=
|
||||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
|
||||
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
github.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y=
|
||||
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/testcontainers/testcontainers-go v0.0.9 h1:mwvFz+FkuQMqQ9oLkG4cVzPsZTRmrCo2NcaerJNaptA=
|
||||
github.com/testcontainers/testcontainers-go v0.0.9/go.mod h1:0Qe9qqjNZgxHzzdHPWwmQ2D49FFO7920hLdJ4yUJXJI=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933 h1:e6HwijUxhDe+hPNjZQQn9bA5PW3vNmnN64U2ZW759Lk=
|
||||
golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180810170437-e96c4e24768d/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
google.golang.org/appengine v1.1.0 h1:igQkv0AAhEIvTEpD5LIpAfav2eeVO9HBTjvKHVJPRSs=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/grpc v1.17.0 h1:TRJYBgMclJvGYn2rIMjj+h9KtMt5r1Ij7ODVRIZkwhk=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gotest.tools v0.0.0-20181223230014-1083505acf35 h1:zpdCK+REwbk+rqjJmHhiCN6iBIigrZ39glqSF0P3KF0=
|
||||
gotest.tools v0.0.0-20181223230014-1083505acf35/go.mod h1:R//lfYlUuTOTfblYI3lGoAAAebUdzjvbmQsuB7Ykd90=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
periph.io/x/periph v3.6.2+incompatible h1:B9vqhYVuhKtr6bXua8N9GeBEvD7yanczCvE0wU2LEqw=
|
||||
periph.io/x/periph v3.6.2+incompatible/go.mod h1:EWr+FCIU2dBWz5/wSWeiIUJTriYv9v2j2ENBmgYyy7Y=
|
103
part/part.go
Normal file
103
part/part.go
Normal file
@ -0,0 +1,103 @@
|
||||
package part
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/cyrilix/robocar-base/service"
|
||||
"github.com/cyrilix/robocar-base/types"
|
||||
"github.com/cyrilix/robocar-pca9685/actuator"
|
||||
MQTT "github.com/eclipse/paho.mqtt.golang"
|
||||
"log"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Pca9685Part struct {
|
||||
client MQTT.Client
|
||||
throttleCtrl *actuator.Throttle
|
||||
steeringCtrl *actuator.Steering
|
||||
|
||||
muSteering sync.Mutex
|
||||
steeringValue float64
|
||||
muThrottle sync.Mutex
|
||||
throttleValue float64
|
||||
|
||||
updateFrequency int
|
||||
|
||||
throttleTopic string
|
||||
steeringTopic string
|
||||
}
|
||||
|
||||
func NewPca9685Part(client MQTT.Client, throttleCtrl *actuator.Throttle, steeringCtrl *actuator.Steering, updateFrequency int, throttleTopic, steeringTopic string) *Pca9685Part {
|
||||
return &Pca9685Part{
|
||||
client: client,
|
||||
throttleCtrl: throttleCtrl,
|
||||
steeringCtrl: steeringCtrl,
|
||||
updateFrequency: updateFrequency,
|
||||
throttleTopic: throttleTopic,
|
||||
steeringTopic: steeringTopic,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Pca9685Part) Start() error {
|
||||
if err := p.registerCallbacks(); err != nil {
|
||||
return fmt.Errorf("unable to start service: %v", err)
|
||||
}
|
||||
defer p.Stop()
|
||||
for {
|
||||
time.Sleep(time.Second / time.Duration(p.updateFrequency))
|
||||
p.updateCtrl()
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Pca9685Part) Stop() {
|
||||
service.StopService("pca9685", p.client, p.throttleTopic, p.steeringTopic)
|
||||
}
|
||||
|
||||
func (p *Pca9685Part) onThrottleChange(_ MQTT.Client, message MQTT.Message) {
|
||||
var throttle types.Throttle
|
||||
err := json.Unmarshal(message.Payload(), throttle)
|
||||
if err != nil {
|
||||
log.Printf("[%v] unable to unmarshall throttle msg: %v",message.Topic(), err)
|
||||
return
|
||||
}
|
||||
p.muThrottle.Lock()
|
||||
defer p.muThrottle.Unlock()
|
||||
p.throttleCtrl.SetPercentValue(throttle.Value)
|
||||
}
|
||||
|
||||
func (p *Pca9685Part) onSteeringChange(_ MQTT.Client, message MQTT.Message) {
|
||||
var steering types.Steering
|
||||
err := json.Unmarshal(message.Payload(), steering)
|
||||
if err != nil {
|
||||
log.Printf("[%v] unable to unmarshall steering msg: %v",message.Topic(), err)
|
||||
return
|
||||
}
|
||||
p.muSteering.Lock()
|
||||
defer p.muSteering.Unlock()
|
||||
p.steeringCtrl.SetPercentValue(steering.Value)
|
||||
}
|
||||
|
||||
func (p *Pca9685Part) registerCallbacks() error {
|
||||
err := service.RegisterCallback(p.client, p.throttleTopic, p.onThrottleChange)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to register throttle callback: %v", err)
|
||||
}
|
||||
|
||||
err = service.RegisterCallback(p.client, p.steeringTopic, p.onSteeringChange)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to register steering callback: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Pca9685Part) updateCtrl() {
|
||||
p.muThrottle.Lock()
|
||||
defer p.muThrottle.Unlock()
|
||||
p.throttleCtrl.SetPercentValue(p.throttleValue)
|
||||
|
||||
p.muSteering.Lock()
|
||||
defer p.muSteering.Unlock()
|
||||
p.steeringCtrl.SetPercentValue(p.steeringValue)
|
||||
}
|
12
util/util.go
Normal file
12
util/util.go
Normal file
@ -0,0 +1,12 @@
|
||||
package util
|
||||
|
||||
// Linear mapping between two ranges of values
|
||||
func MapRange(x, xmin, xmax, ymin, ymax float64) int {
|
||||
Xrange := xmax - xmin
|
||||
Yrange := ymax - ymin
|
||||
XYratio := Xrange / Yrange
|
||||
|
||||
y := (x-xmin)/XYratio + ymin
|
||||
|
||||
return int(y)
|
||||
}
|
32
util/util_test.go
Normal file
32
util/util_test.go
Normal file
@ -0,0 +1,32 @@
|
||||
package util
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestRangeMapping(t *testing.T) {
|
||||
cases := []struct {
|
||||
x, xmin, xmax, ymin, ymax float64
|
||||
expected int
|
||||
}{
|
||||
// Test positive
|
||||
{-100, -100, 100, 0, 1000, 0},
|
||||
{0, -100, 100, 0, 1000, 500},
|
||||
{100, -100, 100, 0, 1000, 1000},
|
||||
|
||||
// Test negative
|
||||
{0, 0, 100, 0, 1000, 0},
|
||||
{50, 0, 100, 0, 1000, 500},
|
||||
{100, 0, 100, 0, 1000, 1000},
|
||||
|
||||
// Reverse
|
||||
{0, 100, 0, 0, 1000, 1000},
|
||||
{50, 100, 0, 0, 1000, 500},
|
||||
{100, 100, 0, 0, 1000, 0},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
val := MapRange(c.x, c.xmin, c.xmax, c.ymin, c.ymax)
|
||||
if val != c.expected {
|
||||
t.Errorf("MapRange(%.0f, %.0f, %.0f, %.0f, %.0f): %d, wants %d", c.x, c.xmin, c.xmax, c.ymin, c.ymax, val, c.expected)
|
||||
}
|
||||
}
|
||||
}
|
201
vendor/github.com/cyrilix/robocar-base/LICENSE
generated
vendored
Normal file
201
vendor/github.com/cyrilix/robocar-base/LICENSE
generated
vendored
Normal file
@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
115
vendor/github.com/cyrilix/robocar-base/cli/cli.go
generated
vendored
Normal file
115
vendor/github.com/cyrilix/robocar-base/cli/cli.go
generated
vendored
Normal file
@ -0,0 +1,115 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/cyrilix/robocar-base/service"
|
||||
MQTT "github.com/eclipse/paho.mqtt.golang"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strconv"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func SetDefaultValueFromEnv(value *string, key string, defaultValue string) {
|
||||
if os.Getenv(key) != "" {
|
||||
*value = os.Getenv(key)
|
||||
} else {
|
||||
*value = defaultValue
|
||||
}
|
||||
}
|
||||
func SetIntDefaultValueFromEnv(value *int, key string, defaultValue int) error {
|
||||
var sVal string
|
||||
if os.Getenv(key) != "" {
|
||||
sVal = os.Getenv(key)
|
||||
val, err := strconv.Atoi(sVal)
|
||||
if err != nil {
|
||||
log.Printf("unable to convert string to int: %v", err)
|
||||
return err
|
||||
}
|
||||
*value = val
|
||||
} else {
|
||||
*value = defaultValue
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func SetFloat64DefaultValueFromEnv(value *float64, key string, defaultValue float64) error {
|
||||
var sVal string
|
||||
if os.Getenv(key) != "" {
|
||||
sVal = os.Getenv(key)
|
||||
val, err := strconv.ParseFloat(sVal, 64)
|
||||
if err != nil {
|
||||
log.Printf("unable to convert string to float: %v", err)
|
||||
return err
|
||||
}
|
||||
*value = val
|
||||
} else {
|
||||
*value = defaultValue
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func HandleExit(p service.Part) {
|
||||
signals := make(chan os.Signal, 1)
|
||||
signal.Notify(signals, os.Kill, os.Interrupt, syscall.SIGTERM)
|
||||
|
||||
go func() {
|
||||
<-signals
|
||||
p.Stop()
|
||||
os.Exit(0)
|
||||
}()
|
||||
}
|
||||
|
||||
func InitMqttFlags(defaultClientId string, mqttBroker, username, password, clientId *string, mqttQos *int, mqttRetain *bool) {
|
||||
SetDefaultValueFromEnv(clientId, "MQTT_CLIENT_ID", defaultClientId)
|
||||
SetDefaultValueFromEnv(mqttBroker, "MQTT_BROKER", "tcp://127.0.0.1:1883")
|
||||
|
||||
flag.StringVar(mqttBroker, "mqtt-broker", *mqttBroker, "Broker Uri, use MQTT_BROKER env if arg not set")
|
||||
flag.StringVar(username, "mqtt-username", os.Getenv("MQTT_USERNAME"), "Broker Username, use MQTT_USERNAME env if arg not set")
|
||||
flag.StringVar(password, "mqtt-password", os.Getenv("MQTT_PASSWORD"), "Broker Password, MQTT_PASSWORD env if args not set")
|
||||
flag.StringVar(clientId, "mqtt-client-id", *clientId, "Mqtt client id, use MQTT_CLIENT_ID env if args not set")
|
||||
flag.IntVar(mqttQos, "mqtt-qos", *mqttQos, "Qos to pusblish message, use MQTT_QOS env if arg not set")
|
||||
flag.BoolVar(mqttRetain, "mqtt-retain", *mqttRetain, "Retain mqtt message, if not set, true if MQTT_RETAIN env variable is set")
|
||||
}
|
||||
|
||||
func InitIntFlag(key string, defValue int) int {
|
||||
var value int
|
||||
err := SetIntDefaultValueFromEnv(&value, key, defValue)
|
||||
if err != nil {
|
||||
log.Panicf("invalid int value: %v", err)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func InitFloat64Flag(key string, defValue float64) float64 {
|
||||
var value float64
|
||||
err := SetFloat64DefaultValueFromEnv(&value, key, defValue)
|
||||
if err != nil {
|
||||
log.Panicf("invalid value: %v", err)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func Connect(uri, username, password, clientId string) (MQTT.Client, error) {
|
||||
//create a ClientOptions struct setting the broker address, clientid, turn
|
||||
//off trace output and set the default message handler
|
||||
opts := MQTT.NewClientOptions().AddBroker(uri)
|
||||
opts.SetUsername(username)
|
||||
opts.SetPassword(password)
|
||||
opts.SetClientID(clientId)
|
||||
opts.SetAutoReconnect(true)
|
||||
opts.SetDefaultPublishHandler(
|
||||
//define a function for the default message handler
|
||||
func(client MQTT.Client, msg MQTT.Message) {
|
||||
fmt.Printf("TOPIC: %s\n", msg.Topic())
|
||||
fmt.Printf("MSG: %s\n", msg.Payload())
|
||||
})
|
||||
|
||||
//create and start a client using the above ClientOptions
|
||||
client := MQTT.NewClient(opts)
|
||||
if token := client.Connect(); token.Wait() && token.Error() != nil {
|
||||
return nil, fmt.Errorf("unable to connect to mqtt bus: %v", token.Error())
|
||||
}
|
||||
return client, nil
|
||||
}
|
156
vendor/github.com/cyrilix/robocar-base/mqttdevice/mqttdevice.go
generated
vendored
Normal file
156
vendor/github.com/cyrilix/robocar-base/mqttdevice/mqttdevice.go
generated
vendored
Normal file
@ -0,0 +1,156 @@
|
||||
package mqttdevice
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/cyrilix/robocar-base/types"
|
||||
MQTT "github.com/eclipse/paho.mqtt.golang"
|
||||
"io"
|
||||
"log"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type Publisher interface {
|
||||
Publish(topic string, payload MqttValue)
|
||||
}
|
||||
|
||||
type Subscriber interface {
|
||||
Subscribe(topic string, mh MQTT.MessageHandler)
|
||||
}
|
||||
|
||||
type MQTTPubSub interface {
|
||||
Publisher
|
||||
Subscriber
|
||||
io.Closer
|
||||
}
|
||||
|
||||
type pahoMqttPubSub struct {
|
||||
Uri string
|
||||
Username string
|
||||
Password string
|
||||
ClientId string
|
||||
Qos int
|
||||
Retain bool
|
||||
client MQTT.Client
|
||||
}
|
||||
|
||||
func NewPahoMqttPubSub(uri string, username string, password string, clientId string, qos int, retain bool) MQTTPubSub {
|
||||
p := pahoMqttPubSub{Uri: uri, Username: username, Password: password, ClientId: clientId, Qos: qos, Retain: retain}
|
||||
p.Connect()
|
||||
return &p
|
||||
}
|
||||
|
||||
// Publish message to broker
|
||||
func (p *pahoMqttPubSub) Publish(topic string, payload MqttValue) {
|
||||
tokenResp := p.client.Publish(topic, byte(p.Qos), p.Retain, string(payload))
|
||||
if tokenResp.Error() != nil {
|
||||
log.Fatalf("%+v\n", tokenResp.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Register func to execute on message
|
||||
func (p *pahoMqttPubSub) Subscribe(topic string, callback MQTT.MessageHandler) {
|
||||
tokenResp := p.client.Subscribe(topic, byte(p.Qos), callback)
|
||||
if tokenResp.Error() != nil {
|
||||
log.Fatalf("%+v\n", tokenResp.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Close connection to broker
|
||||
func (p *pahoMqttPubSub) Close() error {
|
||||
p.client.Disconnect(500)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *pahoMqttPubSub) Connect() {
|
||||
if p.client != nil && p.client.IsConnected() {
|
||||
return
|
||||
}
|
||||
//create a ClientOptions struct setting the broker address, clientid, turn
|
||||
//off trace output and set the default message handler
|
||||
opts := MQTT.NewClientOptions().AddBroker(p.Uri)
|
||||
opts.SetUsername(p.Username)
|
||||
opts.SetPassword(p.Password)
|
||||
opts.SetClientID(p.ClientId)
|
||||
opts.SetAutoReconnect(true)
|
||||
opts.SetDefaultPublishHandler(
|
||||
//define a function for the default message handler
|
||||
func(client MQTT.Client, msg MQTT.Message) {
|
||||
fmt.Printf("TOPIC: %s\n", msg.Topic())
|
||||
fmt.Printf("MSG: %s\n", msg.Payload())
|
||||
})
|
||||
|
||||
//create and start a client using the above ClientOptions
|
||||
p.client = MQTT.NewClient(opts)
|
||||
if token := p.client.Connect(); token.Wait() && token.Error() != nil {
|
||||
panic(token.Error())
|
||||
}
|
||||
}
|
||||
|
||||
type MqttValue []byte
|
||||
|
||||
func NewMqttValue(v interface{}) MqttValue {
|
||||
switch val := v.(type) {
|
||||
case string:
|
||||
return MqttValue(val)
|
||||
case float32, float64:
|
||||
return MqttValue(fmt.Sprintf("%0.2f", val))
|
||||
case int, int8, int16, int32, int64:
|
||||
return MqttValue(fmt.Sprintf("%d", val))
|
||||
case bool:
|
||||
if val {
|
||||
return []byte("ON")
|
||||
} else {
|
||||
return []byte("OFF")
|
||||
}
|
||||
case []byte:
|
||||
return val
|
||||
case MqttValue:
|
||||
return val
|
||||
default:
|
||||
jsonValue, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
log.Printf("unable to mashall to json value '%v': %v", v, err)
|
||||
return nil
|
||||
}
|
||||
return jsonValue
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MqttValue) IntValue() (int, error) {
|
||||
return strconv.Atoi(string(*m))
|
||||
}
|
||||
|
||||
func (m *MqttValue) Float32Value() (float32, error) {
|
||||
val := string(*m)
|
||||
r, err := strconv.ParseFloat(val, 32)
|
||||
return float32(r), err
|
||||
}
|
||||
func (m *MqttValue) Float64Value() (float64, error) {
|
||||
val := string(*m)
|
||||
return strconv.ParseFloat(val, 64)
|
||||
}
|
||||
func (m *MqttValue) StringValue() (string, error) {
|
||||
return string(*m), nil
|
||||
}
|
||||
func (m *MqttValue) DriveModeValue() (types.DriveMode, error) {
|
||||
val, err := m.IntValue()
|
||||
if err != nil {
|
||||
return types.DriveModeInvalid, err
|
||||
}
|
||||
return types.DriveMode(val), nil
|
||||
}
|
||||
func (m *MqttValue) ByteSliceValue() ([]byte, error) {
|
||||
return *m, nil
|
||||
}
|
||||
func (m *MqttValue) BoolValue() (bool, error) {
|
||||
val := string(*m)
|
||||
switch val {
|
||||
case "ON":
|
||||
return true, nil
|
||||
case "OFF":
|
||||
return false, nil
|
||||
default:
|
||||
return false, fmt.Errorf("value %v can't be converted to bool", val)
|
||||
}
|
||||
}
|
33
vendor/github.com/cyrilix/robocar-base/service/part.go
generated
vendored
Normal file
33
vendor/github.com/cyrilix/robocar-base/service/part.go
generated
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
mqtt "github.com/eclipse/paho.mqtt.golang"
|
||||
"log"
|
||||
)
|
||||
|
||||
func StopService(name string, client mqtt.Client, topics ...string) {
|
||||
log.Printf("Stop %s service", name)
|
||||
token := client.Unsubscribe(topics...)
|
||||
token.Wait()
|
||||
if token.Error() != nil {
|
||||
log.Printf("unable to unsubscribe service: %v", token.Error())
|
||||
}
|
||||
client.Disconnect(50)
|
||||
}
|
||||
|
||||
func RegisterCallback(client mqtt.Client, topic string, callback mqtt.MessageHandler) error {
|
||||
log.Printf("Register callback on topic %v", topic)
|
||||
token := client.Subscribe(topic, 0, callback)
|
||||
token.Wait()
|
||||
if token.Error() != nil {
|
||||
return fmt.Errorf("unable to register callback on topic %s: %v", topic, token.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Part interface {
|
||||
Start() error
|
||||
Stop()
|
||||
}
|
||||
|
36
vendor/github.com/cyrilix/robocar-base/types/mode.go
generated
vendored
Normal file
36
vendor/github.com/cyrilix/robocar-base/types/mode.go
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"log"
|
||||
)
|
||||
|
||||
type DriveMode int
|
||||
|
||||
const (
|
||||
DriveModeInvalid = -1
|
||||
DriveModeUser = iota
|
||||
DriveModePilot
|
||||
)
|
||||
|
||||
func ToString(mode DriveMode) string {
|
||||
switch mode {
|
||||
case DriveModeUser:
|
||||
return "user"
|
||||
case DriveModePilot:
|
||||
return "pilot"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func ParseString(val string) DriveMode {
|
||||
switch val {
|
||||
case "user":
|
||||
return DriveModeUser
|
||||
case "pilot":
|
||||
return DriveModePilot
|
||||
default:
|
||||
log.Printf("invalid DriveMode: %v", val)
|
||||
return DriveModeInvalid
|
||||
}
|
||||
}
|
10
vendor/github.com/cyrilix/robocar-base/types/rc.go
generated
vendored
Normal file
10
vendor/github.com/cyrilix/robocar-base/types/rc.go
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
package types
|
||||
|
||||
/* Radio control value */
|
||||
type RCValue struct {
|
||||
Value float64
|
||||
Confidence float64
|
||||
}
|
||||
|
||||
type Steering RCValue
|
||||
type Throttle RCValue
|
36
vendor/github.com/eclipse/paho.mqtt.golang/.gitignore
generated
vendored
Normal file
36
vendor/github.com/eclipse/paho.mqtt.golang/.gitignore
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
|
||||
*.msg
|
||||
*.lok
|
||||
|
||||
samples/trivial
|
||||
samples/trivial2
|
||||
samples/sample
|
||||
samples/reconnect
|
||||
samples/ssl
|
||||
samples/custom_store
|
||||
samples/simple
|
||||
samples/stdinpub
|
||||
samples/stdoutsub
|
||||
samples/routing
|
56
vendor/github.com/eclipse/paho.mqtt.golang/CONTRIBUTING.md
generated
vendored
Normal file
56
vendor/github.com/eclipse/paho.mqtt.golang/CONTRIBUTING.md
generated
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
Contributing to Paho
|
||||
====================
|
||||
|
||||
Thanks for your interest in this project.
|
||||
|
||||
Project description:
|
||||
--------------------
|
||||
|
||||
The Paho project has been created to provide scalable open-source implementations of open and standard messaging protocols aimed at new, existing, and emerging applications for Machine-to-Machine (M2M) and Internet of Things (IoT).
|
||||
Paho reflects the inherent physical and cost constraints of device connectivity. Its objectives include effective levels of decoupling between devices and applications, designed to keep markets open and encourage the rapid growth of scalable Web and Enterprise middleware and applications. Paho is being kicked off with MQTT publish/subscribe client implementations for use on embedded platforms, along with corresponding server support as determined by the community.
|
||||
|
||||
- https://projects.eclipse.org/projects/technology.paho
|
||||
|
||||
Developer resources:
|
||||
--------------------
|
||||
|
||||
Information regarding source code management, builds, coding standards, and more.
|
||||
|
||||
- https://projects.eclipse.org/projects/technology.paho/developer
|
||||
|
||||
Contributor License Agreement:
|
||||
------------------------------
|
||||
|
||||
Before your contribution can be accepted by the project, you need to create and electronically sign the Eclipse Foundation Contributor License Agreement (CLA).
|
||||
|
||||
- http://www.eclipse.org/legal/CLA.php
|
||||
|
||||
Contributing Code:
|
||||
------------------
|
||||
|
||||
The Go client is developed in Github, see their documentation on the process of forking and pull requests; https://help.github.com/categories/collaborating-on-projects-using-pull-requests/
|
||||
|
||||
Git commit messages should follow the style described here;
|
||||
|
||||
http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
|
||||
|
||||
Contact:
|
||||
--------
|
||||
|
||||
Contact the project developers via the project's "dev" list.
|
||||
|
||||
- https://dev.eclipse.org/mailman/listinfo/paho-dev
|
||||
|
||||
Search for bugs:
|
||||
----------------
|
||||
|
||||
This project uses Github issues to track ongoing development and issues.
|
||||
|
||||
- https://github.com/eclipse/paho.mqtt.golang/issues
|
||||
|
||||
Create a new bug:
|
||||
-----------------
|
||||
|
||||
Be sure to search for existing bugs before you create another one. Remember that contributions are always welcome!
|
||||
|
||||
- https://github.com/eclipse/paho.mqtt.golang/issues
|
15
vendor/github.com/eclipse/paho.mqtt.golang/DISTRIBUTION
generated
vendored
Normal file
15
vendor/github.com/eclipse/paho.mqtt.golang/DISTRIBUTION
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
|
||||
|
||||
Eclipse Distribution License - v 1.0
|
||||
|
||||
Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors.
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
Neither the name of the Eclipse Foundation, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
87
vendor/github.com/eclipse/paho.mqtt.golang/LICENSE
generated
vendored
Normal file
87
vendor/github.com/eclipse/paho.mqtt.golang/LICENSE
generated
vendored
Normal file
@ -0,0 +1,87 @@
|
||||
Eclipse Public License - v 1.0
|
||||
|
||||
THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
|
||||
|
||||
1. DEFINITIONS
|
||||
|
||||
"Contribution" means:
|
||||
|
||||
a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and
|
||||
|
||||
b) in the case of each subsequent Contributor:
|
||||
|
||||
i) changes to the Program, and
|
||||
|
||||
ii) additions to the Program;
|
||||
|
||||
where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program.
|
||||
|
||||
"Contributor" means any person or entity that distributes the Program.
|
||||
|
||||
"Licensed Patents" mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program.
|
||||
|
||||
"Program" means the Contributions distributed in accordance with this Agreement.
|
||||
|
||||
"Recipient" means anyone who receives the Program under this Agreement, including all Contributors.
|
||||
|
||||
2. GRANT OF RIGHTS
|
||||
|
||||
a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form.
|
||||
|
||||
b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder.
|
||||
|
||||
c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program.
|
||||
|
||||
d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement.
|
||||
|
||||
3. REQUIREMENTS
|
||||
|
||||
A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that:
|
||||
|
||||
a) it complies with the terms and conditions of this Agreement; and
|
||||
|
||||
b) its license agreement:
|
||||
|
||||
i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose;
|
||||
|
||||
ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits;
|
||||
|
||||
iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and
|
||||
|
||||
iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange.
|
||||
|
||||
When the Program is made available in source code form:
|
||||
|
||||
a) it must be made available under this Agreement; and
|
||||
|
||||
b) a copy of this Agreement must be included with each copy of the Program.
|
||||
|
||||
Contributors may not remove or alter any copyright notices contained within the Program.
|
||||
|
||||
Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution.
|
||||
|
||||
4. COMMERCIAL DISTRIBUTION
|
||||
|
||||
Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense.
|
||||
|
||||
For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages.
|
||||
|
||||
5. NO WARRANTY
|
||||
|
||||
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement , including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations.
|
||||
|
||||
6. DISCLAIMER OF LIABILITY
|
||||
|
||||
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
7. GENERAL
|
||||
|
||||
If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
|
||||
|
||||
If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed.
|
||||
|
||||
All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive.
|
||||
|
||||
Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved.
|
||||
|
||||
This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation.
|
67
vendor/github.com/eclipse/paho.mqtt.golang/README.md
generated
vendored
Normal file
67
vendor/github.com/eclipse/paho.mqtt.golang/README.md
generated
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
|
||||
[](https://godoc.org/github.com/eclipse/paho.mqtt.golang)
|
||||
[](https://goreportcard.com/report/github.com/eclipse/paho.mqtt.golang)
|
||||
|
||||
Eclipse Paho MQTT Go client
|
||||
===========================
|
||||
|
||||
|
||||
This repository contains the source code for the [Eclipse Paho](http://eclipse.org/paho) MQTT Go client library.
|
||||
|
||||
This code builds a library which enable applications to connect to an [MQTT](http://mqtt.org) broker to publish messages, and to subscribe to topics and receive published messages.
|
||||
|
||||
This library supports a fully asynchronous mode of operation.
|
||||
|
||||
|
||||
Installation and Build
|
||||
----------------------
|
||||
|
||||
This client is designed to work with the standard Go tools, so installation is as easy as:
|
||||
|
||||
```
|
||||
go get github.com/eclipse/paho.mqtt.golang
|
||||
```
|
||||
|
||||
The client depends on Google's [websockets](https://godoc.org/golang.org/x/net/websocket) and [proxy](https://godoc.org/golang.org/x/net/proxy) package,
|
||||
also easily installed with the commands:
|
||||
|
||||
```
|
||||
go get golang.org/x/net/websocket
|
||||
go get golang.org/x/net/proxy
|
||||
```
|
||||
|
||||
|
||||
Usage and API
|
||||
-------------
|
||||
|
||||
Detailed API documentation is available by using to godoc tool, or can be browsed online
|
||||
using the [godoc.org](http://godoc.org/github.com/eclipse/paho.mqtt.golang) service.
|
||||
|
||||
Make use of the library by importing it in your Go client source code. For example,
|
||||
```
|
||||
import "github.com/eclipse/paho.mqtt.golang"
|
||||
```
|
||||
|
||||
Samples are available in the `cmd` directory for reference.
|
||||
|
||||
|
||||
Runtime tracing
|
||||
---------------
|
||||
|
||||
Tracing is enabled by assigning logs (from the Go log package) to the logging endpoints, ERROR, CRITICAL, WARN and DEBUG
|
||||
|
||||
|
||||
Reporting bugs
|
||||
--------------
|
||||
|
||||
Please report bugs by raising issues for this project in github https://github.com/eclipse/paho.mqtt.golang/issues
|
||||
|
||||
|
||||
More information
|
||||
----------------
|
||||
|
||||
Discussion of the Paho clients takes place on the [Eclipse paho-dev mailing list](https://dev.eclipse.org/mailman/listinfo/paho-dev).
|
||||
|
||||
General questions about the MQTT protocol are discussed in the [MQTT Google Group](https://groups.google.com/forum/?hl=en-US&fromgroups#!forum/mqtt).
|
||||
|
||||
There is much more information available via the [MQTT community site](http://mqtt.org).
|
41
vendor/github.com/eclipse/paho.mqtt.golang/about.html
generated
vendored
Normal file
41
vendor/github.com/eclipse/paho.mqtt.golang/about.html
generated
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml"><head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
||||
<title>About</title>
|
||||
</head>
|
||||
<body lang="EN-US">
|
||||
<h2>About This Content</h2>
|
||||
|
||||
<p><em>December 9, 2013</em></p>
|
||||
<h3>License</h3>
|
||||
|
||||
<p>The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise
|
||||
indicated below, the Content is provided to you under the terms and conditions of the
|
||||
Eclipse Public License Version 1.0 ("EPL") and Eclipse Distribution License Version 1.0 ("EDL").
|
||||
A copy of the EPL is available at
|
||||
<a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>
|
||||
and a copy of the EDL is available at
|
||||
<a href="http://www.eclipse.org/org/documents/edl-v10.php">http://www.eclipse.org/org/documents/edl-v10.php</a>.
|
||||
For purposes of the EPL, "Program" will mean the Content.</p>
|
||||
|
||||
<p>If you did not receive this Content directly from the Eclipse Foundation, the Content is
|
||||
being redistributed by another party ("Redistributor") and different terms and conditions may
|
||||
apply to your use of any object code in the Content. Check the Redistributor's license that was
|
||||
provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise
|
||||
indicated below, the terms and conditions of the EPL still apply to any source code in the Content
|
||||
and such source code may be obtained at <a href="http://www.eclipse.org/">http://www.eclipse.org</a>.</p>
|
||||
|
||||
|
||||
<h3>Third Party Content</h3>
|
||||
<p>The Content includes items that have been sourced from third parties as set out below. If you
|
||||
did not receive this Content directly from the Eclipse Foundation, the following is provided
|
||||
for informational purposes only, and you should look to the Redistributor's license for
|
||||
terms and conditions of use.</p>
|
||||
<p><em>
|
||||
<strong>None</strong> <br><br>
|
||||
<br><br>
|
||||
</em></p>
|
||||
|
||||
|
||||
|
||||
</body></html>
|
759
vendor/github.com/eclipse/paho.mqtt.golang/client.go
generated
vendored
Normal file
759
vendor/github.com/eclipse/paho.mqtt.golang/client.go
generated
vendored
Normal file
@ -0,0 +1,759 @@
|
||||
/*
|
||||
* Copyright (c) 2013 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Seth Hoenig
|
||||
* Allan Stockdill-Mander
|
||||
* Mike Robertson
|
||||
*/
|
||||
|
||||
// Portions copyright © 2018 TIBCO Software Inc.
|
||||
|
||||
// Package mqtt provides an MQTT v3.1.1 client library.
|
||||
package mqtt
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/eclipse/paho.mqtt.golang/packets"
|
||||
)
|
||||
|
||||
const (
|
||||
disconnected uint32 = iota
|
||||
connecting
|
||||
reconnecting
|
||||
connected
|
||||
)
|
||||
|
||||
// Client is the interface definition for a Client as used by this
|
||||
// library, the interface is primarily to allow mocking tests.
|
||||
//
|
||||
// It is an MQTT v3.1.1 client for communicating
|
||||
// with an MQTT server using non-blocking methods that allow work
|
||||
// to be done in the background.
|
||||
// An application may connect to an MQTT server using:
|
||||
// A plain TCP socket
|
||||
// A secure SSL/TLS socket
|
||||
// A websocket
|
||||
// To enable ensured message delivery at Quality of Service (QoS) levels
|
||||
// described in the MQTT spec, a message persistence mechanism must be
|
||||
// used. This is done by providing a type which implements the Store
|
||||
// interface. For convenience, FileStore and MemoryStore are provided
|
||||
// implementations that should be sufficient for most use cases. More
|
||||
// information can be found in their respective documentation.
|
||||
// Numerous connection options may be specified by configuring a
|
||||
// and then supplying a ClientOptions type.
|
||||
type Client interface {
|
||||
// IsConnected returns a bool signifying whether
|
||||
// the client is connected or not.
|
||||
IsConnected() bool
|
||||
// IsConnectionOpen return a bool signifying wether the client has an active
|
||||
// connection to mqtt broker, i.e not in disconnected or reconnect mode
|
||||
IsConnectionOpen() bool
|
||||
// Connect will create a connection to the message broker, by default
|
||||
// it will attempt to connect at v3.1.1 and auto retry at v3.1 if that
|
||||
// fails
|
||||
Connect() Token
|
||||
// Disconnect will end the connection with the server, but not before waiting
|
||||
// the specified number of milliseconds to wait for existing work to be
|
||||
// completed.
|
||||
Disconnect(quiesce uint)
|
||||
// Publish will publish a message with the specified QoS and content
|
||||
// to the specified topic.
|
||||
// Returns a token to track delivery of the message to the broker
|
||||
Publish(topic string, qos byte, retained bool, payload interface{}) Token
|
||||
// Subscribe starts a new subscription. Provide a MessageHandler to be executed when
|
||||
// a message is published on the topic provided, or nil for the default handler
|
||||
Subscribe(topic string, qos byte, callback MessageHandler) Token
|
||||
// SubscribeMultiple starts a new subscription for multiple topics. Provide a MessageHandler to
|
||||
// be executed when a message is published on one of the topics provided, or nil for the
|
||||
// default handler
|
||||
SubscribeMultiple(filters map[string]byte, callback MessageHandler) Token
|
||||
// Unsubscribe will end the subscription from each of the topics provided.
|
||||
// Messages published to those topics from other clients will no longer be
|
||||
// received.
|
||||
Unsubscribe(topics ...string) Token
|
||||
// AddRoute allows you to add a handler for messages on a specific topic
|
||||
// without making a subscription. For example having a different handler
|
||||
// for parts of a wildcard subscription
|
||||
AddRoute(topic string, callback MessageHandler)
|
||||
// OptionsReader returns a ClientOptionsReader which is a copy of the clientoptions
|
||||
// in use by the client.
|
||||
OptionsReader() ClientOptionsReader
|
||||
}
|
||||
|
||||
// client implements the Client interface
|
||||
type client struct {
|
||||
lastSent atomic.Value
|
||||
lastReceived atomic.Value
|
||||
pingOutstanding int32
|
||||
status uint32
|
||||
sync.RWMutex
|
||||
messageIds
|
||||
conn net.Conn
|
||||
ibound chan packets.ControlPacket
|
||||
obound chan *PacketAndToken
|
||||
oboundP chan *PacketAndToken
|
||||
msgRouter *router
|
||||
stopRouter chan bool
|
||||
incomingPubChan chan *packets.PublishPacket
|
||||
errors chan error
|
||||
stop chan struct{}
|
||||
persist Store
|
||||
options ClientOptions
|
||||
workers sync.WaitGroup
|
||||
}
|
||||
|
||||
// NewClient will create an MQTT v3.1.1 client with all of the options specified
|
||||
// in the provided ClientOptions. The client must have the Connect method called
|
||||
// on it before it may be used. This is to make sure resources (such as a net
|
||||
// connection) are created before the application is actually ready.
|
||||
func NewClient(o *ClientOptions) Client {
|
||||
c := &client{}
|
||||
c.options = *o
|
||||
|
||||
if c.options.Store == nil {
|
||||
c.options.Store = NewMemoryStore()
|
||||
}
|
||||
switch c.options.ProtocolVersion {
|
||||
case 3, 4:
|
||||
c.options.protocolVersionExplicit = true
|
||||
case 0x83, 0x84:
|
||||
c.options.protocolVersionExplicit = true
|
||||
default:
|
||||
c.options.ProtocolVersion = 4
|
||||
c.options.protocolVersionExplicit = false
|
||||
}
|
||||
c.persist = c.options.Store
|
||||
c.status = disconnected
|
||||
c.messageIds = messageIds{index: make(map[uint16]tokenCompletor)}
|
||||
c.msgRouter, c.stopRouter = newRouter()
|
||||
c.msgRouter.setDefaultHandler(c.options.DefaultPublishHandler)
|
||||
if !c.options.AutoReconnect {
|
||||
c.options.MessageChannelDepth = 0
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// AddRoute allows you to add a handler for messages on a specific topic
|
||||
// without making a subscription. For example having a different handler
|
||||
// for parts of a wildcard subscription
|
||||
func (c *client) AddRoute(topic string, callback MessageHandler) {
|
||||
if callback != nil {
|
||||
c.msgRouter.addRoute(topic, callback)
|
||||
}
|
||||
}
|
||||
|
||||
// IsConnected returns a bool signifying whether
|
||||
// the client is connected or not.
|
||||
func (c *client) IsConnected() bool {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
status := atomic.LoadUint32(&c.status)
|
||||
switch {
|
||||
case status == connected:
|
||||
return true
|
||||
case c.options.AutoReconnect && status > connecting:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// IsConnectionOpen return a bool signifying whether the client has an active
|
||||
// connection to mqtt broker, i.e not in disconnected or reconnect mode
|
||||
func (c *client) IsConnectionOpen() bool {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
status := atomic.LoadUint32(&c.status)
|
||||
switch {
|
||||
case status == connected:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (c *client) connectionStatus() uint32 {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
status := atomic.LoadUint32(&c.status)
|
||||
return status
|
||||
}
|
||||
|
||||
func (c *client) setConnected(status uint32) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
atomic.StoreUint32(&c.status, uint32(status))
|
||||
}
|
||||
|
||||
//ErrNotConnected is the error returned from function calls that are
|
||||
//made when the client is not connected to a broker
|
||||
var ErrNotConnected = errors.New("Not Connected")
|
||||
|
||||
// Connect will create a connection to the message broker, by default
|
||||
// it will attempt to connect at v3.1.1 and auto retry at v3.1 if that
|
||||
// fails
|
||||
func (c *client) Connect() Token {
|
||||
var err error
|
||||
t := newToken(packets.Connect).(*ConnectToken)
|
||||
DEBUG.Println(CLI, "Connect()")
|
||||
|
||||
c.obound = make(chan *PacketAndToken, c.options.MessageChannelDepth)
|
||||
c.oboundP = make(chan *PacketAndToken, c.options.MessageChannelDepth)
|
||||
c.ibound = make(chan packets.ControlPacket)
|
||||
|
||||
go func() {
|
||||
c.persist.Open()
|
||||
|
||||
c.setConnected(connecting)
|
||||
c.errors = make(chan error, 1)
|
||||
c.stop = make(chan struct{})
|
||||
|
||||
var rc byte
|
||||
protocolVersion := c.options.ProtocolVersion
|
||||
|
||||
if len(c.options.Servers) == 0 {
|
||||
t.setError(fmt.Errorf("No servers defined to connect to"))
|
||||
return
|
||||
}
|
||||
|
||||
for _, broker := range c.options.Servers {
|
||||
cm := newConnectMsgFromOptions(&c.options, broker)
|
||||
c.options.ProtocolVersion = protocolVersion
|
||||
CONN:
|
||||
DEBUG.Println(CLI, "about to write new connect msg")
|
||||
c.conn, err = openConnection(broker, c.options.TLSConfig, c.options.ConnectTimeout, c.options.HTTPHeaders)
|
||||
if err == nil {
|
||||
DEBUG.Println(CLI, "socket connected to broker")
|
||||
switch c.options.ProtocolVersion {
|
||||
case 3:
|
||||
DEBUG.Println(CLI, "Using MQTT 3.1 protocol")
|
||||
cm.ProtocolName = "MQIsdp"
|
||||
cm.ProtocolVersion = 3
|
||||
case 0x83:
|
||||
DEBUG.Println(CLI, "Using MQTT 3.1b protocol")
|
||||
cm.ProtocolName = "MQIsdp"
|
||||
cm.ProtocolVersion = 0x83
|
||||
case 0x84:
|
||||
DEBUG.Println(CLI, "Using MQTT 3.1.1b protocol")
|
||||
cm.ProtocolName = "MQTT"
|
||||
cm.ProtocolVersion = 0x84
|
||||
default:
|
||||
DEBUG.Println(CLI, "Using MQTT 3.1.1 protocol")
|
||||
c.options.ProtocolVersion = 4
|
||||
cm.ProtocolName = "MQTT"
|
||||
cm.ProtocolVersion = 4
|
||||
}
|
||||
cm.Write(c.conn)
|
||||
|
||||
rc, t.sessionPresent = c.connect()
|
||||
if rc != packets.Accepted {
|
||||
if c.conn != nil {
|
||||
c.conn.Close()
|
||||
c.conn = nil
|
||||
}
|
||||
//if the protocol version was explicitly set don't do any fallback
|
||||
if c.options.protocolVersionExplicit {
|
||||
ERROR.Println(CLI, "Connecting to", broker, "CONNACK was not CONN_ACCEPTED, but rather", packets.ConnackReturnCodes[rc])
|
||||
continue
|
||||
}
|
||||
if c.options.ProtocolVersion == 4 {
|
||||
DEBUG.Println(CLI, "Trying reconnect using MQTT 3.1 protocol")
|
||||
c.options.ProtocolVersion = 3
|
||||
goto CONN
|
||||
}
|
||||
}
|
||||
break
|
||||
} else {
|
||||
ERROR.Println(CLI, err.Error())
|
||||
WARN.Println(CLI, "failed to connect to broker, trying next")
|
||||
rc = packets.ErrNetworkError
|
||||
}
|
||||
}
|
||||
|
||||
if c.conn == nil {
|
||||
ERROR.Println(CLI, "Failed to connect to a broker")
|
||||
c.setConnected(disconnected)
|
||||
c.persist.Close()
|
||||
t.returnCode = rc
|
||||
if rc != packets.ErrNetworkError {
|
||||
t.setError(packets.ConnErrors[rc])
|
||||
} else {
|
||||
t.setError(fmt.Errorf("%s : %s", packets.ConnErrors[rc], err))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
c.options.protocolVersionExplicit = true
|
||||
|
||||
if c.options.KeepAlive != 0 {
|
||||
atomic.StoreInt32(&c.pingOutstanding, 0)
|
||||
c.lastReceived.Store(time.Now())
|
||||
c.lastSent.Store(time.Now())
|
||||
c.workers.Add(1)
|
||||
go keepalive(c)
|
||||
}
|
||||
|
||||
c.incomingPubChan = make(chan *packets.PublishPacket, c.options.MessageChannelDepth)
|
||||
c.msgRouter.matchAndDispatch(c.incomingPubChan, c.options.Order, c)
|
||||
|
||||
c.setConnected(connected)
|
||||
DEBUG.Println(CLI, "client is connected")
|
||||
if c.options.OnConnect != nil {
|
||||
go c.options.OnConnect(c)
|
||||
}
|
||||
|
||||
c.workers.Add(4)
|
||||
go errorWatch(c)
|
||||
go alllogic(c)
|
||||
go outgoing(c)
|
||||
go incoming(c)
|
||||
|
||||
// Take care of any messages in the store
|
||||
if c.options.CleanSession == false {
|
||||
c.resume(c.options.ResumeSubs)
|
||||
} else {
|
||||
c.persist.Reset()
|
||||
}
|
||||
|
||||
DEBUG.Println(CLI, "exit startClient")
|
||||
t.flowComplete()
|
||||
}()
|
||||
return t
|
||||
}
|
||||
|
||||
// internal function used to reconnect the client when it loses its connection
|
||||
func (c *client) reconnect() {
|
||||
DEBUG.Println(CLI, "enter reconnect")
|
||||
var (
|
||||
err error
|
||||
|
||||
rc = byte(1)
|
||||
sleep = time.Duration(1 * time.Second)
|
||||
)
|
||||
|
||||
for rc != 0 && atomic.LoadUint32(&c.status) != disconnected {
|
||||
for _, broker := range c.options.Servers {
|
||||
cm := newConnectMsgFromOptions(&c.options, broker)
|
||||
DEBUG.Println(CLI, "about to write new connect msg")
|
||||
c.Lock()
|
||||
c.conn, err = openConnection(broker, c.options.TLSConfig, c.options.ConnectTimeout, c.options.HTTPHeaders)
|
||||
c.Unlock()
|
||||
if err == nil {
|
||||
DEBUG.Println(CLI, "socket connected to broker")
|
||||
switch c.options.ProtocolVersion {
|
||||
case 0x83:
|
||||
DEBUG.Println(CLI, "Using MQTT 3.1b protocol")
|
||||
cm.ProtocolName = "MQIsdp"
|
||||
cm.ProtocolVersion = 0x83
|
||||
case 0x84:
|
||||
DEBUG.Println(CLI, "Using MQTT 3.1.1b protocol")
|
||||
cm.ProtocolName = "MQTT"
|
||||
cm.ProtocolVersion = 0x84
|
||||
case 3:
|
||||
DEBUG.Println(CLI, "Using MQTT 3.1 protocol")
|
||||
cm.ProtocolName = "MQIsdp"
|
||||
cm.ProtocolVersion = 3
|
||||
default:
|
||||
DEBUG.Println(CLI, "Using MQTT 3.1.1 protocol")
|
||||
cm.ProtocolName = "MQTT"
|
||||
cm.ProtocolVersion = 4
|
||||
}
|
||||
cm.Write(c.conn)
|
||||
|
||||
rc, _ = c.connect()
|
||||
if rc != packets.Accepted {
|
||||
c.conn.Close()
|
||||
c.conn = nil
|
||||
//if the protocol version was explicitly set don't do any fallback
|
||||
if c.options.protocolVersionExplicit {
|
||||
ERROR.Println(CLI, "Connecting to", broker, "CONNACK was not Accepted, but rather", packets.ConnackReturnCodes[rc])
|
||||
continue
|
||||
}
|
||||
}
|
||||
break
|
||||
} else {
|
||||
ERROR.Println(CLI, err.Error())
|
||||
WARN.Println(CLI, "failed to connect to broker, trying next")
|
||||
rc = packets.ErrNetworkError
|
||||
}
|
||||
}
|
||||
if rc != 0 {
|
||||
DEBUG.Println(CLI, "Reconnect failed, sleeping for", int(sleep.Seconds()), "seconds")
|
||||
time.Sleep(sleep)
|
||||
if sleep < c.options.MaxReconnectInterval {
|
||||
sleep *= 2
|
||||
}
|
||||
|
||||
if sleep > c.options.MaxReconnectInterval {
|
||||
sleep = c.options.MaxReconnectInterval
|
||||
}
|
||||
}
|
||||
}
|
||||
// Disconnect() must have been called while we were trying to reconnect.
|
||||
if c.connectionStatus() == disconnected {
|
||||
DEBUG.Println(CLI, "Client moved to disconnected state while reconnecting, abandoning reconnect")
|
||||
return
|
||||
}
|
||||
|
||||
c.stop = make(chan struct{})
|
||||
|
||||
if c.options.KeepAlive != 0 {
|
||||
atomic.StoreInt32(&c.pingOutstanding, 0)
|
||||
c.lastReceived.Store(time.Now())
|
||||
c.lastSent.Store(time.Now())
|
||||
c.workers.Add(1)
|
||||
go keepalive(c)
|
||||
}
|
||||
|
||||
c.setConnected(connected)
|
||||
DEBUG.Println(CLI, "client is reconnected")
|
||||
if c.options.OnConnect != nil {
|
||||
go c.options.OnConnect(c)
|
||||
}
|
||||
|
||||
c.workers.Add(4)
|
||||
go errorWatch(c)
|
||||
go alllogic(c)
|
||||
go outgoing(c)
|
||||
go incoming(c)
|
||||
|
||||
c.resume(false)
|
||||
}
|
||||
|
||||
// This function is only used for receiving a connack
|
||||
// when the connection is first started.
|
||||
// This prevents receiving incoming data while resume
|
||||
// is in progress if clean session is false.
|
||||
func (c *client) connect() (byte, bool) {
|
||||
DEBUG.Println(NET, "connect started")
|
||||
|
||||
ca, err := packets.ReadPacket(c.conn)
|
||||
if err != nil {
|
||||
ERROR.Println(NET, "connect got error", err)
|
||||
return packets.ErrNetworkError, false
|
||||
}
|
||||
if ca == nil {
|
||||
ERROR.Println(NET, "received nil packet")
|
||||
return packets.ErrNetworkError, false
|
||||
}
|
||||
|
||||
msg, ok := ca.(*packets.ConnackPacket)
|
||||
if !ok {
|
||||
ERROR.Println(NET, "received msg that was not CONNACK")
|
||||
return packets.ErrNetworkError, false
|
||||
}
|
||||
|
||||
DEBUG.Println(NET, "received connack")
|
||||
return msg.ReturnCode, msg.SessionPresent
|
||||
}
|
||||
|
||||
// Disconnect will end the connection with the server, but not before waiting
|
||||
// the specified number of milliseconds to wait for existing work to be
|
||||
// completed.
|
||||
func (c *client) Disconnect(quiesce uint) {
|
||||
status := atomic.LoadUint32(&c.status)
|
||||
if status == connected {
|
||||
DEBUG.Println(CLI, "disconnecting")
|
||||
c.setConnected(disconnected)
|
||||
|
||||
dm := packets.NewControlPacket(packets.Disconnect).(*packets.DisconnectPacket)
|
||||
dt := newToken(packets.Disconnect)
|
||||
c.oboundP <- &PacketAndToken{p: dm, t: dt}
|
||||
|
||||
// wait for work to finish, or quiesce time consumed
|
||||
dt.WaitTimeout(time.Duration(quiesce) * time.Millisecond)
|
||||
} else {
|
||||
WARN.Println(CLI, "Disconnect() called but not connected (disconnected/reconnecting)")
|
||||
c.setConnected(disconnected)
|
||||
}
|
||||
|
||||
c.disconnect()
|
||||
}
|
||||
|
||||
// ForceDisconnect will end the connection with the mqtt broker immediately.
|
||||
func (c *client) forceDisconnect() {
|
||||
if !c.IsConnected() {
|
||||
WARN.Println(CLI, "already disconnected")
|
||||
return
|
||||
}
|
||||
c.setConnected(disconnected)
|
||||
c.conn.Close()
|
||||
DEBUG.Println(CLI, "forcefully disconnecting")
|
||||
c.disconnect()
|
||||
}
|
||||
|
||||
func (c *client) internalConnLost(err error) {
|
||||
// Only do anything if this was called and we are still "connected"
|
||||
// forceDisconnect can cause incoming/outgoing/alllogic to end with
|
||||
// error from closing the socket but state will be "disconnected"
|
||||
if c.IsConnected() {
|
||||
c.closeStop()
|
||||
c.conn.Close()
|
||||
c.workers.Wait()
|
||||
if c.options.CleanSession && !c.options.AutoReconnect {
|
||||
c.messageIds.cleanUp()
|
||||
}
|
||||
if c.options.AutoReconnect {
|
||||
c.setConnected(reconnecting)
|
||||
go c.reconnect()
|
||||
} else {
|
||||
c.setConnected(disconnected)
|
||||
}
|
||||
if c.options.OnConnectionLost != nil {
|
||||
go c.options.OnConnectionLost(c, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *client) closeStop() {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
select {
|
||||
case <-c.stop:
|
||||
DEBUG.Println("In disconnect and stop channel is already closed")
|
||||
default:
|
||||
if c.stop != nil {
|
||||
close(c.stop)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *client) closeStopRouter() {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
select {
|
||||
case <-c.stopRouter:
|
||||
DEBUG.Println("In disconnect and stop channel is already closed")
|
||||
default:
|
||||
if c.stopRouter != nil {
|
||||
close(c.stopRouter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *client) closeConn() {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
if c.conn != nil {
|
||||
c.conn.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *client) disconnect() {
|
||||
c.closeStop()
|
||||
c.closeConn()
|
||||
c.workers.Wait()
|
||||
c.messageIds.cleanUp()
|
||||
c.closeStopRouter()
|
||||
DEBUG.Println(CLI, "disconnected")
|
||||
c.persist.Close()
|
||||
}
|
||||
|
||||
// Publish will publish a message with the specified QoS and content
|
||||
// to the specified topic.
|
||||
// Returns a token to track delivery of the message to the broker
|
||||
func (c *client) Publish(topic string, qos byte, retained bool, payload interface{}) Token {
|
||||
token := newToken(packets.Publish).(*PublishToken)
|
||||
DEBUG.Println(CLI, "enter Publish")
|
||||
switch {
|
||||
case !c.IsConnected():
|
||||
token.setError(ErrNotConnected)
|
||||
return token
|
||||
case c.connectionStatus() == reconnecting && qos == 0:
|
||||
token.flowComplete()
|
||||
return token
|
||||
}
|
||||
pub := packets.NewControlPacket(packets.Publish).(*packets.PublishPacket)
|
||||
pub.Qos = qos
|
||||
pub.TopicName = topic
|
||||
pub.Retain = retained
|
||||
switch payload.(type) {
|
||||
case string:
|
||||
pub.Payload = []byte(payload.(string))
|
||||
case []byte:
|
||||
pub.Payload = payload.([]byte)
|
||||
default:
|
||||
token.setError(fmt.Errorf("Unknown payload type"))
|
||||
return token
|
||||
}
|
||||
|
||||
if pub.Qos != 0 && pub.MessageID == 0 {
|
||||
pub.MessageID = c.getID(token)
|
||||
token.messageID = pub.MessageID
|
||||
}
|
||||
persistOutbound(c.persist, pub)
|
||||
if c.connectionStatus() == reconnecting {
|
||||
DEBUG.Println(CLI, "storing publish message (reconnecting), topic:", topic)
|
||||
} else {
|
||||
DEBUG.Println(CLI, "sending publish message, topic:", topic)
|
||||
c.obound <- &PacketAndToken{p: pub, t: token}
|
||||
}
|
||||
return token
|
||||
}
|
||||
|
||||
// Subscribe starts a new subscription. Provide a MessageHandler to be executed when
|
||||
// a message is published on the topic provided.
|
||||
func (c *client) Subscribe(topic string, qos byte, callback MessageHandler) Token {
|
||||
token := newToken(packets.Subscribe).(*SubscribeToken)
|
||||
DEBUG.Println(CLI, "enter Subscribe")
|
||||
if !c.IsConnected() {
|
||||
token.setError(ErrNotConnected)
|
||||
return token
|
||||
}
|
||||
sub := packets.NewControlPacket(packets.Subscribe).(*packets.SubscribePacket)
|
||||
if err := validateTopicAndQos(topic, qos); err != nil {
|
||||
token.setError(err)
|
||||
return token
|
||||
}
|
||||
sub.Topics = append(sub.Topics, topic)
|
||||
sub.Qoss = append(sub.Qoss, qos)
|
||||
DEBUG.Println(CLI, sub.String())
|
||||
|
||||
if strings.HasPrefix(topic, "$share") {
|
||||
topic = strings.Join(strings.Split(topic, "/")[2:], "/")
|
||||
}
|
||||
|
||||
if callback != nil {
|
||||
c.msgRouter.addRoute(topic, callback)
|
||||
}
|
||||
|
||||
token.subs = append(token.subs, topic)
|
||||
c.oboundP <- &PacketAndToken{p: sub, t: token}
|
||||
DEBUG.Println(CLI, "exit Subscribe")
|
||||
return token
|
||||
}
|
||||
|
||||
// SubscribeMultiple starts a new subscription for multiple topics. Provide a MessageHandler to
|
||||
// be executed when a message is published on one of the topics provided.
|
||||
func (c *client) SubscribeMultiple(filters map[string]byte, callback MessageHandler) Token {
|
||||
var err error
|
||||
token := newToken(packets.Subscribe).(*SubscribeToken)
|
||||
DEBUG.Println(CLI, "enter SubscribeMultiple")
|
||||
if !c.IsConnected() {
|
||||
token.setError(ErrNotConnected)
|
||||
return token
|
||||
}
|
||||
sub := packets.NewControlPacket(packets.Subscribe).(*packets.SubscribePacket)
|
||||
if sub.Topics, sub.Qoss, err = validateSubscribeMap(filters); err != nil {
|
||||
token.setError(err)
|
||||
return token
|
||||
}
|
||||
|
||||
if callback != nil {
|
||||
for topic := range filters {
|
||||
c.msgRouter.addRoute(topic, callback)
|
||||
}
|
||||
}
|
||||
token.subs = make([]string, len(sub.Topics))
|
||||
copy(token.subs, sub.Topics)
|
||||
c.oboundP <- &PacketAndToken{p: sub, t: token}
|
||||
DEBUG.Println(CLI, "exit SubscribeMultiple")
|
||||
return token
|
||||
}
|
||||
|
||||
// Load all stored messages and resend them
|
||||
// Call this to ensure QOS > 1,2 even after an application crash
|
||||
func (c *client) resume(subscription bool) {
|
||||
|
||||
storedKeys := c.persist.All()
|
||||
for _, key := range storedKeys {
|
||||
packet := c.persist.Get(key)
|
||||
if packet == nil {
|
||||
continue
|
||||
}
|
||||
details := packet.Details()
|
||||
if isKeyOutbound(key) {
|
||||
switch packet.(type) {
|
||||
case *packets.SubscribePacket:
|
||||
if subscription {
|
||||
DEBUG.Println(STR, fmt.Sprintf("loaded pending subscribe (%d)", details.MessageID))
|
||||
token := newToken(packets.Subscribe).(*SubscribeToken)
|
||||
c.oboundP <- &PacketAndToken{p: packet, t: token}
|
||||
}
|
||||
case *packets.UnsubscribePacket:
|
||||
if subscription {
|
||||
DEBUG.Println(STR, fmt.Sprintf("loaded pending unsubscribe (%d)", details.MessageID))
|
||||
token := newToken(packets.Unsubscribe).(*UnsubscribeToken)
|
||||
c.oboundP <- &PacketAndToken{p: packet, t: token}
|
||||
}
|
||||
case *packets.PubrelPacket:
|
||||
DEBUG.Println(STR, fmt.Sprintf("loaded pending pubrel (%d)", details.MessageID))
|
||||
select {
|
||||
case c.oboundP <- &PacketAndToken{p: packet, t: nil}:
|
||||
case <-c.stop:
|
||||
}
|
||||
case *packets.PublishPacket:
|
||||
token := newToken(packets.Publish).(*PublishToken)
|
||||
token.messageID = details.MessageID
|
||||
c.claimID(token, details.MessageID)
|
||||
DEBUG.Println(STR, fmt.Sprintf("loaded pending publish (%d)", details.MessageID))
|
||||
DEBUG.Println(STR, details)
|
||||
c.obound <- &PacketAndToken{p: packet, t: token}
|
||||
default:
|
||||
ERROR.Println(STR, "invalid message type in store (discarded)")
|
||||
c.persist.Del(key)
|
||||
}
|
||||
} else {
|
||||
switch packet.(type) {
|
||||
case *packets.PubrelPacket, *packets.PublishPacket:
|
||||
DEBUG.Println(STR, fmt.Sprintf("loaded pending incomming (%d)", details.MessageID))
|
||||
select {
|
||||
case c.ibound <- packet:
|
||||
case <-c.stop:
|
||||
}
|
||||
default:
|
||||
ERROR.Println(STR, "invalid message type in store (discarded)")
|
||||
c.persist.Del(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Unsubscribe will end the subscription from each of the topics provided.
|
||||
// Messages published to those topics from other clients will no longer be
|
||||
// received.
|
||||
func (c *client) Unsubscribe(topics ...string) Token {
|
||||
token := newToken(packets.Unsubscribe).(*UnsubscribeToken)
|
||||
DEBUG.Println(CLI, "enter Unsubscribe")
|
||||
if !c.IsConnected() {
|
||||
token.setError(ErrNotConnected)
|
||||
return token
|
||||
}
|
||||
unsub := packets.NewControlPacket(packets.Unsubscribe).(*packets.UnsubscribePacket)
|
||||
unsub.Topics = make([]string, len(topics))
|
||||
copy(unsub.Topics, topics)
|
||||
|
||||
c.oboundP <- &PacketAndToken{p: unsub, t: token}
|
||||
for _, topic := range topics {
|
||||
c.msgRouter.deleteRoute(topic)
|
||||
}
|
||||
|
||||
DEBUG.Println(CLI, "exit Unsubscribe")
|
||||
return token
|
||||
}
|
||||
|
||||
// OptionsReader returns a ClientOptionsReader which is a copy of the clientoptions
|
||||
// in use by the client.
|
||||
func (c *client) OptionsReader() ClientOptionsReader {
|
||||
r := ClientOptionsReader{options: &c.options}
|
||||
return r
|
||||
}
|
||||
|
||||
//DefaultConnectionLostHandler is a definition of a function that simply
|
||||
//reports to the DEBUG log the reason for the client losing a connection.
|
||||
func DefaultConnectionLostHandler(client Client, reason error) {
|
||||
DEBUG.Println("Connection lost:", reason.Error())
|
||||
}
|
31
vendor/github.com/eclipse/paho.mqtt.golang/components.go
generated
vendored
Normal file
31
vendor/github.com/eclipse/paho.mqtt.golang/components.go
generated
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2013 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Seth Hoenig
|
||||
* Allan Stockdill-Mander
|
||||
* Mike Robertson
|
||||
*/
|
||||
|
||||
package mqtt
|
||||
|
||||
type component string
|
||||
|
||||
// Component names for debug output
|
||||
const (
|
||||
NET component = "[net] "
|
||||
PNG component = "[pinger] "
|
||||
CLI component = "[client] "
|
||||
DEC component = "[decode] "
|
||||
MES component = "[message] "
|
||||
STR component = "[store] "
|
||||
MID component = "[msgids] "
|
||||
TST component = "[test] "
|
||||
STA component = "[state] "
|
||||
ERR component = "[error] "
|
||||
)
|
15
vendor/github.com/eclipse/paho.mqtt.golang/edl-v10
generated
vendored
Normal file
15
vendor/github.com/eclipse/paho.mqtt.golang/edl-v10
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
|
||||
Eclipse Distribution License - v 1.0
|
||||
|
||||
Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors.
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
Neither the name of the Eclipse Foundation, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
70
vendor/github.com/eclipse/paho.mqtt.golang/epl-v10
generated
vendored
Normal file
70
vendor/github.com/eclipse/paho.mqtt.golang/epl-v10
generated
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
Eclipse Public License - v 1.0
|
||||
|
||||
THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
|
||||
|
||||
1. DEFINITIONS
|
||||
|
||||
"Contribution" means:
|
||||
|
||||
a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and
|
||||
b) in the case of each subsequent Contributor:
|
||||
i) changes to the Program, and
|
||||
ii) additions to the Program;
|
||||
where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program.
|
||||
"Contributor" means any person or entity that distributes the Program.
|
||||
|
||||
"Licensed Patents" mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program.
|
||||
|
||||
"Program" means the Contributions distributed in accordance with this Agreement.
|
||||
|
||||
"Recipient" means anyone who receives the Program under this Agreement, including all Contributors.
|
||||
|
||||
2. GRANT OF RIGHTS
|
||||
|
||||
a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form.
|
||||
b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder.
|
||||
c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program.
|
||||
d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement.
|
||||
3. REQUIREMENTS
|
||||
|
||||
A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that:
|
||||
|
||||
a) it complies with the terms and conditions of this Agreement; and
|
||||
b) its license agreement:
|
||||
i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose;
|
||||
ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits;
|
||||
iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and
|
||||
iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange.
|
||||
When the Program is made available in source code form:
|
||||
|
||||
a) it must be made available under this Agreement; and
|
||||
b) a copy of this Agreement must be included with each copy of the Program.
|
||||
Contributors may not remove or alter any copyright notices contained within the Program.
|
||||
|
||||
Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution.
|
||||
|
||||
4. COMMERCIAL DISTRIBUTION
|
||||
|
||||
Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense.
|
||||
|
||||
For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages.
|
||||
|
||||
5. NO WARRANTY
|
||||
|
||||
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement , including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations.
|
||||
|
||||
6. DISCLAIMER OF LIABILITY
|
||||
|
||||
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
7. GENERAL
|
||||
|
||||
If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
|
||||
|
||||
If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed.
|
||||
|
||||
All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive.
|
||||
|
||||
Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved.
|
||||
|
||||
This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation.
|
255
vendor/github.com/eclipse/paho.mqtt.golang/filestore.go
generated
vendored
Normal file
255
vendor/github.com/eclipse/paho.mqtt.golang/filestore.go
generated
vendored
Normal file
@ -0,0 +1,255 @@
|
||||
/*
|
||||
* Copyright (c) 2013 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Seth Hoenig
|
||||
* Allan Stockdill-Mander
|
||||
* Mike Robertson
|
||||
*/
|
||||
|
||||
package mqtt
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"sort"
|
||||
"sync"
|
||||
|
||||
"github.com/eclipse/paho.mqtt.golang/packets"
|
||||
)
|
||||
|
||||
const (
|
||||
msgExt = ".msg"
|
||||
tmpExt = ".tmp"
|
||||
corruptExt = ".CORRUPT"
|
||||
)
|
||||
|
||||
// FileStore implements the store interface using the filesystem to provide
|
||||
// true persistence, even across client failure. This is designed to use a
|
||||
// single directory per running client. If you are running multiple clients
|
||||
// on the same filesystem, you will need to be careful to specify unique
|
||||
// store directories for each.
|
||||
type FileStore struct {
|
||||
sync.RWMutex
|
||||
directory string
|
||||
opened bool
|
||||
}
|
||||
|
||||
// NewFileStore will create a new FileStore which stores its messages in the
|
||||
// directory provided.
|
||||
func NewFileStore(directory string) *FileStore {
|
||||
store := &FileStore{
|
||||
directory: directory,
|
||||
opened: false,
|
||||
}
|
||||
return store
|
||||
}
|
||||
|
||||
// Open will allow the FileStore to be used.
|
||||
func (store *FileStore) Open() {
|
||||
store.Lock()
|
||||
defer store.Unlock()
|
||||
// if no store directory was specified in ClientOpts, by default use the
|
||||
// current working directory
|
||||
if store.directory == "" {
|
||||
store.directory, _ = os.Getwd()
|
||||
}
|
||||
|
||||
// if store dir exists, great, otherwise, create it
|
||||
if !exists(store.directory) {
|
||||
perms := os.FileMode(0770)
|
||||
merr := os.MkdirAll(store.directory, perms)
|
||||
chkerr(merr)
|
||||
}
|
||||
store.opened = true
|
||||
DEBUG.Println(STR, "store is opened at", store.directory)
|
||||
}
|
||||
|
||||
// Close will disallow the FileStore from being used.
|
||||
func (store *FileStore) Close() {
|
||||
store.Lock()
|
||||
defer store.Unlock()
|
||||
store.opened = false
|
||||
DEBUG.Println(STR, "store is closed")
|
||||
}
|
||||
|
||||
// Put will put a message into the store, associated with the provided
|
||||
// key value.
|
||||
func (store *FileStore) Put(key string, m packets.ControlPacket) {
|
||||
store.Lock()
|
||||
defer store.Unlock()
|
||||
if !store.opened {
|
||||
ERROR.Println(STR, "Trying to use file store, but not open")
|
||||
return
|
||||
}
|
||||
full := fullpath(store.directory, key)
|
||||
write(store.directory, key, m)
|
||||
if !exists(full) {
|
||||
ERROR.Println(STR, "file not created:", full)
|
||||
}
|
||||
}
|
||||
|
||||
// Get will retrieve a message from the store, the one associated with
|
||||
// the provided key value.
|
||||
func (store *FileStore) Get(key string) packets.ControlPacket {
|
||||
store.RLock()
|
||||
defer store.RUnlock()
|
||||
if !store.opened {
|
||||
ERROR.Println(STR, "Trying to use file store, but not open")
|
||||
return nil
|
||||
}
|
||||
filepath := fullpath(store.directory, key)
|
||||
if !exists(filepath) {
|
||||
return nil
|
||||
}
|
||||
mfile, oerr := os.Open(filepath)
|
||||
chkerr(oerr)
|
||||
msg, rerr := packets.ReadPacket(mfile)
|
||||
chkerr(mfile.Close())
|
||||
|
||||
// Message was unreadable, return nil
|
||||
if rerr != nil {
|
||||
newpath := corruptpath(store.directory, key)
|
||||
WARN.Println(STR, "corrupted file detected:", rerr.Error(), "archived at:", newpath)
|
||||
os.Rename(filepath, newpath)
|
||||
return nil
|
||||
}
|
||||
return msg
|
||||
}
|
||||
|
||||
// All will provide a list of all of the keys associated with messages
|
||||
// currenly residing in the FileStore.
|
||||
func (store *FileStore) All() []string {
|
||||
store.RLock()
|
||||
defer store.RUnlock()
|
||||
return store.all()
|
||||
}
|
||||
|
||||
// Del will remove the persisted message associated with the provided
|
||||
// key from the FileStore.
|
||||
func (store *FileStore) Del(key string) {
|
||||
store.Lock()
|
||||
defer store.Unlock()
|
||||
store.del(key)
|
||||
}
|
||||
|
||||
// Reset will remove all persisted messages from the FileStore.
|
||||
func (store *FileStore) Reset() {
|
||||
store.Lock()
|
||||
defer store.Unlock()
|
||||
WARN.Println(STR, "FileStore Reset")
|
||||
for _, key := range store.all() {
|
||||
store.del(key)
|
||||
}
|
||||
}
|
||||
|
||||
// lockless
|
||||
func (store *FileStore) all() []string {
|
||||
var err error
|
||||
var keys []string
|
||||
var files fileInfos
|
||||
|
||||
if !store.opened {
|
||||
ERROR.Println(STR, "Trying to use file store, but not open")
|
||||
return nil
|
||||
}
|
||||
|
||||
files, err = ioutil.ReadDir(store.directory)
|
||||
chkerr(err)
|
||||
sort.Sort(files)
|
||||
for _, f := range files {
|
||||
DEBUG.Println(STR, "file in All():", f.Name())
|
||||
name := f.Name()
|
||||
if name[len(name)-4:len(name)] != msgExt {
|
||||
DEBUG.Println(STR, "skipping file, doesn't have right extension: ", name)
|
||||
continue
|
||||
}
|
||||
key := name[0 : len(name)-4] // remove file extension
|
||||
keys = append(keys, key)
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
// lockless
|
||||
func (store *FileStore) del(key string) {
|
||||
if !store.opened {
|
||||
ERROR.Println(STR, "Trying to use file store, but not open")
|
||||
return
|
||||
}
|
||||
DEBUG.Println(STR, "store del filepath:", store.directory)
|
||||
DEBUG.Println(STR, "store delete key:", key)
|
||||
filepath := fullpath(store.directory, key)
|
||||
DEBUG.Println(STR, "path of deletion:", filepath)
|
||||
if !exists(filepath) {
|
||||
WARN.Println(STR, "store could not delete key:", key)
|
||||
return
|
||||
}
|
||||
rerr := os.Remove(filepath)
|
||||
chkerr(rerr)
|
||||
DEBUG.Println(STR, "del msg:", key)
|
||||
if exists(filepath) {
|
||||
ERROR.Println(STR, "file not deleted:", filepath)
|
||||
}
|
||||
}
|
||||
|
||||
func fullpath(store string, key string) string {
|
||||
p := path.Join(store, key+msgExt)
|
||||
return p
|
||||
}
|
||||
|
||||
func tmppath(store string, key string) string {
|
||||
p := path.Join(store, key+tmpExt)
|
||||
return p
|
||||
}
|
||||
|
||||
func corruptpath(store string, key string) string {
|
||||
p := path.Join(store, key+corruptExt)
|
||||
return p
|
||||
}
|
||||
|
||||
// create file called "X.[messageid].tmp" located in the store
|
||||
// the contents of the file is the bytes of the message, then
|
||||
// rename it to "X.[messageid].msg", overwriting any existing
|
||||
// message with the same id
|
||||
// X will be 'i' for inbound messages, and O for outbound messages
|
||||
func write(store, key string, m packets.ControlPacket) {
|
||||
temppath := tmppath(store, key)
|
||||
f, err := os.Create(temppath)
|
||||
chkerr(err)
|
||||
werr := m.Write(f)
|
||||
chkerr(werr)
|
||||
cerr := f.Close()
|
||||
chkerr(cerr)
|
||||
rerr := os.Rename(temppath, fullpath(store, key))
|
||||
chkerr(rerr)
|
||||
}
|
||||
|
||||
func exists(file string) bool {
|
||||
if _, err := os.Stat(file); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
chkerr(err)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type fileInfos []os.FileInfo
|
||||
|
||||
func (f fileInfos) Len() int {
|
||||
return len(f)
|
||||
}
|
||||
|
||||
func (f fileInfos) Swap(i, j int) {
|
||||
f[i], f[j] = f[j], f[i]
|
||||
}
|
||||
|
||||
func (f fileInfos) Less(i, j int) bool {
|
||||
return f[i].ModTime().Before(f[j].ModTime())
|
||||
}
|
138
vendor/github.com/eclipse/paho.mqtt.golang/memstore.go
generated
vendored
Normal file
138
vendor/github.com/eclipse/paho.mqtt.golang/memstore.go
generated
vendored
Normal file
@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright (c) 2013 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Seth Hoenig
|
||||
* Allan Stockdill-Mander
|
||||
* Mike Robertson
|
||||
*/
|
||||
|
||||
package mqtt
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/eclipse/paho.mqtt.golang/packets"
|
||||
)
|
||||
|
||||
// MemoryStore implements the store interface to provide a "persistence"
|
||||
// mechanism wholly stored in memory. This is only useful for
|
||||
// as long as the client instance exists.
|
||||
type MemoryStore struct {
|
||||
sync.RWMutex
|
||||
messages map[string]packets.ControlPacket
|
||||
opened bool
|
||||
}
|
||||
|
||||
// NewMemoryStore returns a pointer to a new instance of
|
||||
// MemoryStore, the instance is not initialized and ready to
|
||||
// use until Open() has been called on it.
|
||||
func NewMemoryStore() *MemoryStore {
|
||||
store := &MemoryStore{
|
||||
messages: make(map[string]packets.ControlPacket),
|
||||
opened: false,
|
||||
}
|
||||
return store
|
||||
}
|
||||
|
||||
// Open initializes a MemoryStore instance.
|
||||
func (store *MemoryStore) Open() {
|
||||
store.Lock()
|
||||
defer store.Unlock()
|
||||
store.opened = true
|
||||
DEBUG.Println(STR, "memorystore initialized")
|
||||
}
|
||||
|
||||
// Put takes a key and a pointer to a Message and stores the
|
||||
// message.
|
||||
func (store *MemoryStore) Put(key string, message packets.ControlPacket) {
|
||||
store.Lock()
|
||||
defer store.Unlock()
|
||||
if !store.opened {
|
||||
ERROR.Println(STR, "Trying to use memory store, but not open")
|
||||
return
|
||||
}
|
||||
store.messages[key] = message
|
||||
}
|
||||
|
||||
// Get takes a key and looks in the store for a matching Message
|
||||
// returning either the Message pointer or nil.
|
||||
func (store *MemoryStore) Get(key string) packets.ControlPacket {
|
||||
store.RLock()
|
||||
defer store.RUnlock()
|
||||
if !store.opened {
|
||||
ERROR.Println(STR, "Trying to use memory store, but not open")
|
||||
return nil
|
||||
}
|
||||
mid := mIDFromKey(key)
|
||||
m := store.messages[key]
|
||||
if m == nil {
|
||||
CRITICAL.Println(STR, "memorystore get: message", mid, "not found")
|
||||
} else {
|
||||
DEBUG.Println(STR, "memorystore get: message", mid, "found")
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// All returns a slice of strings containing all the keys currently
|
||||
// in the MemoryStore.
|
||||
func (store *MemoryStore) All() []string {
|
||||
store.RLock()
|
||||
defer store.RUnlock()
|
||||
if !store.opened {
|
||||
ERROR.Println(STR, "Trying to use memory store, but not open")
|
||||
return nil
|
||||
}
|
||||
keys := []string{}
|
||||
for k := range store.messages {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
// Del takes a key, searches the MemoryStore and if the key is found
|
||||
// deletes the Message pointer associated with it.
|
||||
func (store *MemoryStore) Del(key string) {
|
||||
store.Lock()
|
||||
defer store.Unlock()
|
||||
if !store.opened {
|
||||
ERROR.Println(STR, "Trying to use memory store, but not open")
|
||||
return
|
||||
}
|
||||
mid := mIDFromKey(key)
|
||||
m := store.messages[key]
|
||||
if m == nil {
|
||||
WARN.Println(STR, "memorystore del: message", mid, "not found")
|
||||
} else {
|
||||
delete(store.messages, key)
|
||||
DEBUG.Println(STR, "memorystore del: message", mid, "was deleted")
|
||||
}
|
||||
}
|
||||
|
||||
// Close will disallow modifications to the state of the store.
|
||||
func (store *MemoryStore) Close() {
|
||||
store.Lock()
|
||||
defer store.Unlock()
|
||||
if !store.opened {
|
||||
ERROR.Println(STR, "Trying to close memory store, but not open")
|
||||
return
|
||||
}
|
||||
store.opened = false
|
||||
DEBUG.Println(STR, "memorystore closed")
|
||||
}
|
||||
|
||||
// Reset eliminates all persisted message data in the store.
|
||||
func (store *MemoryStore) Reset() {
|
||||
store.Lock()
|
||||
defer store.Unlock()
|
||||
if !store.opened {
|
||||
ERROR.Println(STR, "Trying to reset memory store, but not open")
|
||||
}
|
||||
store.messages = make(map[string]packets.ControlPacket)
|
||||
WARN.Println(STR, "memorystore wiped")
|
||||
}
|
127
vendor/github.com/eclipse/paho.mqtt.golang/message.go
generated
vendored
Normal file
127
vendor/github.com/eclipse/paho.mqtt.golang/message.go
generated
vendored
Normal file
@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright (c) 2013 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Seth Hoenig
|
||||
* Allan Stockdill-Mander
|
||||
* Mike Robertson
|
||||
*/
|
||||
|
||||
package mqtt
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
|
||||
"github.com/eclipse/paho.mqtt.golang/packets"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Message defines the externals that a message implementation must support
|
||||
// these are received messages that are passed to the callbacks, not internal
|
||||
// messages
|
||||
type Message interface {
|
||||
Duplicate() bool
|
||||
Qos() byte
|
||||
Retained() bool
|
||||
Topic() string
|
||||
MessageID() uint16
|
||||
Payload() []byte
|
||||
Ack()
|
||||
}
|
||||
|
||||
type message struct {
|
||||
duplicate bool
|
||||
qos byte
|
||||
retained bool
|
||||
topic string
|
||||
messageID uint16
|
||||
payload []byte
|
||||
once sync.Once
|
||||
ack func()
|
||||
}
|
||||
|
||||
func (m *message) Duplicate() bool {
|
||||
return m.duplicate
|
||||
}
|
||||
|
||||
func (m *message) Qos() byte {
|
||||
return m.qos
|
||||
}
|
||||
|
||||
func (m *message) Retained() bool {
|
||||
return m.retained
|
||||
}
|
||||
|
||||
func (m *message) Topic() string {
|
||||
return m.topic
|
||||
}
|
||||
|
||||
func (m *message) MessageID() uint16 {
|
||||
return m.messageID
|
||||
}
|
||||
|
||||
func (m *message) Payload() []byte {
|
||||
return m.payload
|
||||
}
|
||||
|
||||
func (m *message) Ack() {
|
||||
m.once.Do(m.ack)
|
||||
}
|
||||
|
||||
func messageFromPublish(p *packets.PublishPacket, ack func()) Message {
|
||||
return &message{
|
||||
duplicate: p.Dup,
|
||||
qos: p.Qos,
|
||||
retained: p.Retain,
|
||||
topic: p.TopicName,
|
||||
messageID: p.MessageID,
|
||||
payload: p.Payload,
|
||||
ack: ack,
|
||||
}
|
||||
}
|
||||
|
||||
func newConnectMsgFromOptions(options *ClientOptions, broker *url.URL) *packets.ConnectPacket {
|
||||
m := packets.NewControlPacket(packets.Connect).(*packets.ConnectPacket)
|
||||
|
||||
m.CleanSession = options.CleanSession
|
||||
m.WillFlag = options.WillEnabled
|
||||
m.WillRetain = options.WillRetained
|
||||
m.ClientIdentifier = options.ClientID
|
||||
|
||||
if options.WillEnabled {
|
||||
m.WillQos = options.WillQos
|
||||
m.WillTopic = options.WillTopic
|
||||
m.WillMessage = options.WillPayload
|
||||
}
|
||||
|
||||
username := options.Username
|
||||
password := options.Password
|
||||
if broker.User != nil {
|
||||
username = broker.User.Username()
|
||||
if pwd, ok := broker.User.Password(); ok {
|
||||
password = pwd
|
||||
}
|
||||
}
|
||||
if options.CredentialsProvider != nil {
|
||||
username, password = options.CredentialsProvider()
|
||||
}
|
||||
|
||||
if username != "" {
|
||||
m.UsernameFlag = true
|
||||
m.Username = username
|
||||
//mustn't have password without user as well
|
||||
if password != "" {
|
||||
m.PasswordFlag = true
|
||||
m.Password = []byte(password)
|
||||
}
|
||||
}
|
||||
|
||||
m.Keepalive = uint16(options.KeepAlive)
|
||||
|
||||
return m
|
||||
}
|
117
vendor/github.com/eclipse/paho.mqtt.golang/messageids.go
generated
vendored
Normal file
117
vendor/github.com/eclipse/paho.mqtt.golang/messageids.go
generated
vendored
Normal file
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (c) 2013 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Seth Hoenig
|
||||
* Allan Stockdill-Mander
|
||||
* Mike Robertson
|
||||
*/
|
||||
|
||||
package mqtt
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// MId is 16 bit message id as specified by the MQTT spec.
|
||||
// In general, these values should not be depended upon by
|
||||
// the client application.
|
||||
type MId uint16
|
||||
|
||||
type messageIds struct {
|
||||
sync.RWMutex
|
||||
index map[uint16]tokenCompletor
|
||||
}
|
||||
|
||||
const (
|
||||
midMin uint16 = 1
|
||||
midMax uint16 = 65535
|
||||
)
|
||||
|
||||
func (mids *messageIds) cleanUp() {
|
||||
mids.Lock()
|
||||
for _, token := range mids.index {
|
||||
switch token.(type) {
|
||||
case *PublishToken:
|
||||
token.setError(fmt.Errorf("Connection lost before Publish completed"))
|
||||
case *SubscribeToken:
|
||||
token.setError(fmt.Errorf("Connection lost before Subscribe completed"))
|
||||
case *UnsubscribeToken:
|
||||
token.setError(fmt.Errorf("Connection lost before Unsubscribe completed"))
|
||||
case nil:
|
||||
continue
|
||||
}
|
||||
token.flowComplete()
|
||||
}
|
||||
mids.index = make(map[uint16]tokenCompletor)
|
||||
mids.Unlock()
|
||||
DEBUG.Println(MID, "cleaned up")
|
||||
}
|
||||
|
||||
func (mids *messageIds) freeID(id uint16) {
|
||||
mids.Lock()
|
||||
delete(mids.index, id)
|
||||
mids.Unlock()
|
||||
}
|
||||
|
||||
func (mids *messageIds) claimID(token tokenCompletor, id uint16) {
|
||||
mids.Lock()
|
||||
defer mids.Unlock()
|
||||
if _, ok := mids.index[id]; !ok {
|
||||
mids.index[id] = token
|
||||
} else {
|
||||
old := mids.index[id]
|
||||
old.flowComplete()
|
||||
mids.index[id] = token
|
||||
}
|
||||
}
|
||||
|
||||
func (mids *messageIds) getID(t tokenCompletor) uint16 {
|
||||
mids.Lock()
|
||||
defer mids.Unlock()
|
||||
for i := midMin; i < midMax; i++ {
|
||||
if _, ok := mids.index[i]; !ok {
|
||||
mids.index[i] = t
|
||||
return i
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (mids *messageIds) getToken(id uint16) tokenCompletor {
|
||||
mids.RLock()
|
||||
defer mids.RUnlock()
|
||||
if token, ok := mids.index[id]; ok {
|
||||
return token
|
||||
}
|
||||
return &DummyToken{id: id}
|
||||
}
|
||||
|
||||
type DummyToken struct {
|
||||
id uint16
|
||||
}
|
||||
|
||||
func (d *DummyToken) Wait() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (d *DummyToken) WaitTimeout(t time.Duration) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (d *DummyToken) flowComplete() {
|
||||
ERROR.Printf("A lookup for token %d returned nil\n", d.id)
|
||||
}
|
||||
|
||||
func (d *DummyToken) Error() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DummyToken) setError(e error) {}
|
355
vendor/github.com/eclipse/paho.mqtt.golang/net.go
generated
vendored
Normal file
355
vendor/github.com/eclipse/paho.mqtt.golang/net.go
generated
vendored
Normal file
@ -0,0 +1,355 @@
|
||||
/*
|
||||
* Copyright (c) 2013 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Seth Hoenig
|
||||
* Allan Stockdill-Mander
|
||||
* Mike Robertson
|
||||
*/
|
||||
|
||||
package mqtt
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"reflect"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/eclipse/paho.mqtt.golang/packets"
|
||||
"golang.org/x/net/proxy"
|
||||
"golang.org/x/net/websocket"
|
||||
)
|
||||
|
||||
func signalError(c chan<- error, err error) {
|
||||
select {
|
||||
case c <- err:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func openConnection(uri *url.URL, tlsc *tls.Config, timeout time.Duration, headers http.Header) (net.Conn, error) {
|
||||
switch uri.Scheme {
|
||||
case "ws":
|
||||
config, _ := websocket.NewConfig(uri.String(), fmt.Sprintf("http://%s", uri.Host))
|
||||
config.Protocol = []string{"mqtt"}
|
||||
config.Header = headers
|
||||
config.Dialer = &net.Dialer{Timeout: timeout}
|
||||
conn, err := websocket.DialConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conn.PayloadType = websocket.BinaryFrame
|
||||
return conn, err
|
||||
case "wss":
|
||||
config, _ := websocket.NewConfig(uri.String(), fmt.Sprintf("https://%s", uri.Host))
|
||||
config.Protocol = []string{"mqtt"}
|
||||
config.TlsConfig = tlsc
|
||||
config.Header = headers
|
||||
config.Dialer = &net.Dialer{Timeout: timeout}
|
||||
conn, err := websocket.DialConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conn.PayloadType = websocket.BinaryFrame
|
||||
return conn, err
|
||||
case "tcp":
|
||||
allProxy := os.Getenv("all_proxy")
|
||||
if len(allProxy) == 0 {
|
||||
conn, err := net.DialTimeout("tcp", uri.Host, timeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
proxyDialer := proxy.FromEnvironment()
|
||||
|
||||
conn, err := proxyDialer.Dial("tcp", uri.Host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return conn, nil
|
||||
case "unix":
|
||||
conn, err := net.DialTimeout("unix", uri.Host, timeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return conn, nil
|
||||
case "ssl":
|
||||
fallthrough
|
||||
case "tls":
|
||||
fallthrough
|
||||
case "tcps":
|
||||
allProxy := os.Getenv("all_proxy")
|
||||
if len(allProxy) == 0 {
|
||||
conn, err := tls.DialWithDialer(&net.Dialer{Timeout: timeout}, "tcp", uri.Host, tlsc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
proxyDialer := proxy.FromEnvironment()
|
||||
|
||||
conn, err := proxyDialer.Dial("tcp", uri.Host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tlsConn := tls.Client(conn, tlsc)
|
||||
|
||||
err = tlsConn.Handshake()
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return tlsConn, nil
|
||||
}
|
||||
return nil, errors.New("Unknown protocol")
|
||||
}
|
||||
|
||||
// actually read incoming messages off the wire
|
||||
// send Message object into ibound channel
|
||||
func incoming(c *client) {
|
||||
var err error
|
||||
var cp packets.ControlPacket
|
||||
|
||||
defer c.workers.Done()
|
||||
|
||||
DEBUG.Println(NET, "incoming started")
|
||||
|
||||
for {
|
||||
if cp, err = packets.ReadPacket(c.conn); err != nil {
|
||||
break
|
||||
}
|
||||
DEBUG.Println(NET, "Received Message")
|
||||
select {
|
||||
case c.ibound <- cp:
|
||||
// Notify keepalive logic that we recently received a packet
|
||||
if c.options.KeepAlive != 0 {
|
||||
c.lastReceived.Store(time.Now())
|
||||
}
|
||||
case <-c.stop:
|
||||
// This avoids a deadlock should a message arrive while shutting down.
|
||||
// In that case the "reader" of c.ibound might already be gone
|
||||
WARN.Println(NET, "incoming dropped a received message during shutdown")
|
||||
break
|
||||
}
|
||||
}
|
||||
// We received an error on read.
|
||||
// If disconnect is in progress, swallow error and return
|
||||
select {
|
||||
case <-c.stop:
|
||||
DEBUG.Println(NET, "incoming stopped")
|
||||
return
|
||||
// Not trying to disconnect, send the error to the errors channel
|
||||
default:
|
||||
ERROR.Println(NET, "incoming stopped with error", err)
|
||||
signalError(c.errors, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// receive a Message object on obound, and then
|
||||
// actually send outgoing message to the wire
|
||||
func outgoing(c *client) {
|
||||
defer c.workers.Done()
|
||||
DEBUG.Println(NET, "outgoing started")
|
||||
|
||||
for {
|
||||
DEBUG.Println(NET, "outgoing waiting for an outbound message")
|
||||
select {
|
||||
case <-c.stop:
|
||||
DEBUG.Println(NET, "outgoing stopped")
|
||||
return
|
||||
case pub := <-c.obound:
|
||||
msg := pub.p.(*packets.PublishPacket)
|
||||
|
||||
if c.options.WriteTimeout > 0 {
|
||||
c.conn.SetWriteDeadline(time.Now().Add(c.options.WriteTimeout))
|
||||
}
|
||||
|
||||
if err := msg.Write(c.conn); err != nil {
|
||||
ERROR.Println(NET, "outgoing stopped with error", err)
|
||||
pub.t.setError(err)
|
||||
signalError(c.errors, err)
|
||||
return
|
||||
}
|
||||
|
||||
if c.options.WriteTimeout > 0 {
|
||||
// If we successfully wrote, we don't want the timeout to happen during an idle period
|
||||
// so we reset it to infinite.
|
||||
c.conn.SetWriteDeadline(time.Time{})
|
||||
}
|
||||
|
||||
if msg.Qos == 0 {
|
||||
pub.t.flowComplete()
|
||||
}
|
||||
DEBUG.Println(NET, "obound wrote msg, id:", msg.MessageID)
|
||||
case msg := <-c.oboundP:
|
||||
switch msg.p.(type) {
|
||||
case *packets.SubscribePacket:
|
||||
msg.p.(*packets.SubscribePacket).MessageID = c.getID(msg.t)
|
||||
case *packets.UnsubscribePacket:
|
||||
msg.p.(*packets.UnsubscribePacket).MessageID = c.getID(msg.t)
|
||||
}
|
||||
DEBUG.Println(NET, "obound priority msg to write, type", reflect.TypeOf(msg.p))
|
||||
if err := msg.p.Write(c.conn); err != nil {
|
||||
ERROR.Println(NET, "outgoing stopped with error", err)
|
||||
if msg.t != nil {
|
||||
msg.t.setError(err)
|
||||
}
|
||||
signalError(c.errors, err)
|
||||
return
|
||||
}
|
||||
switch msg.p.(type) {
|
||||
case *packets.DisconnectPacket:
|
||||
msg.t.(*DisconnectToken).flowComplete()
|
||||
DEBUG.Println(NET, "outbound wrote disconnect, stopping")
|
||||
return
|
||||
}
|
||||
}
|
||||
// Reset ping timer after sending control packet.
|
||||
if c.options.KeepAlive != 0 {
|
||||
c.lastSent.Store(time.Now())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// receive Message objects on ibound
|
||||
// store messages if necessary
|
||||
// send replies on obound
|
||||
// delete messages from store if necessary
|
||||
func alllogic(c *client) {
|
||||
defer c.workers.Done()
|
||||
DEBUG.Println(NET, "logic started")
|
||||
|
||||
for {
|
||||
DEBUG.Println(NET, "logic waiting for msg on ibound")
|
||||
|
||||
select {
|
||||
case msg := <-c.ibound:
|
||||
DEBUG.Println(NET, "logic got msg on ibound")
|
||||
persistInbound(c.persist, msg)
|
||||
switch m := msg.(type) {
|
||||
case *packets.PingrespPacket:
|
||||
DEBUG.Println(NET, "received pingresp")
|
||||
atomic.StoreInt32(&c.pingOutstanding, 0)
|
||||
case *packets.SubackPacket:
|
||||
DEBUG.Println(NET, "received suback, id:", m.MessageID)
|
||||
token := c.getToken(m.MessageID)
|
||||
switch t := token.(type) {
|
||||
case *SubscribeToken:
|
||||
DEBUG.Println(NET, "granted qoss", m.ReturnCodes)
|
||||
for i, qos := range m.ReturnCodes {
|
||||
t.subResult[t.subs[i]] = qos
|
||||
}
|
||||
}
|
||||
token.flowComplete()
|
||||
c.freeID(m.MessageID)
|
||||
case *packets.UnsubackPacket:
|
||||
DEBUG.Println(NET, "received unsuback, id:", m.MessageID)
|
||||
c.getToken(m.MessageID).flowComplete()
|
||||
c.freeID(m.MessageID)
|
||||
case *packets.PublishPacket:
|
||||
DEBUG.Println(NET, "received publish, msgId:", m.MessageID)
|
||||
DEBUG.Println(NET, "putting msg on onPubChan")
|
||||
switch m.Qos {
|
||||
case 2:
|
||||
c.incomingPubChan <- m
|
||||
DEBUG.Println(NET, "done putting msg on incomingPubChan")
|
||||
case 1:
|
||||
c.incomingPubChan <- m
|
||||
DEBUG.Println(NET, "done putting msg on incomingPubChan")
|
||||
case 0:
|
||||
select {
|
||||
case c.incomingPubChan <- m:
|
||||
case <-c.stop:
|
||||
}
|
||||
DEBUG.Println(NET, "done putting msg on incomingPubChan")
|
||||
}
|
||||
case *packets.PubackPacket:
|
||||
DEBUG.Println(NET, "received puback, id:", m.MessageID)
|
||||
// c.receipts.get(msg.MsgId()) <- Receipt{}
|
||||
// c.receipts.end(msg.MsgId())
|
||||
c.getToken(m.MessageID).flowComplete()
|
||||
c.freeID(m.MessageID)
|
||||
case *packets.PubrecPacket:
|
||||
DEBUG.Println(NET, "received pubrec, id:", m.MessageID)
|
||||
prel := packets.NewControlPacket(packets.Pubrel).(*packets.PubrelPacket)
|
||||
prel.MessageID = m.MessageID
|
||||
select {
|
||||
case c.oboundP <- &PacketAndToken{p: prel, t: nil}:
|
||||
case <-c.stop:
|
||||
}
|
||||
case *packets.PubrelPacket:
|
||||
DEBUG.Println(NET, "received pubrel, id:", m.MessageID)
|
||||
pc := packets.NewControlPacket(packets.Pubcomp).(*packets.PubcompPacket)
|
||||
pc.MessageID = m.MessageID
|
||||
persistOutbound(c.persist, pc)
|
||||
select {
|
||||
case c.oboundP <- &PacketAndToken{p: pc, t: nil}:
|
||||
case <-c.stop:
|
||||
}
|
||||
case *packets.PubcompPacket:
|
||||
DEBUG.Println(NET, "received pubcomp, id:", m.MessageID)
|
||||
c.getToken(m.MessageID).flowComplete()
|
||||
c.freeID(m.MessageID)
|
||||
}
|
||||
case <-c.stop:
|
||||
WARN.Println(NET, "logic stopped")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *client) ackFunc(packet *packets.PublishPacket) func() {
|
||||
return func() {
|
||||
switch packet.Qos {
|
||||
case 2:
|
||||
pr := packets.NewControlPacket(packets.Pubrec).(*packets.PubrecPacket)
|
||||
pr.MessageID = packet.MessageID
|
||||
DEBUG.Println(NET, "putting pubrec msg on obound")
|
||||
select {
|
||||
case c.oboundP <- &PacketAndToken{p: pr, t: nil}:
|
||||
case <-c.stop:
|
||||
}
|
||||
DEBUG.Println(NET, "done putting pubrec msg on obound")
|
||||
case 1:
|
||||
pa := packets.NewControlPacket(packets.Puback).(*packets.PubackPacket)
|
||||
pa.MessageID = packet.MessageID
|
||||
DEBUG.Println(NET, "putting puback msg on obound")
|
||||
persistOutbound(c.persist, pa)
|
||||
select {
|
||||
case c.oboundP <- &PacketAndToken{p: pa, t: nil}:
|
||||
case <-c.stop:
|
||||
}
|
||||
DEBUG.Println(NET, "done putting puback msg on obound")
|
||||
case 0:
|
||||
// do nothing, since there is no need to send an ack packet back
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func errorWatch(c *client) {
|
||||
defer c.workers.Done()
|
||||
select {
|
||||
case <-c.stop:
|
||||
WARN.Println(NET, "errorWatch stopped")
|
||||
return
|
||||
case err := <-c.errors:
|
||||
ERROR.Println(NET, "error triggered, stopping")
|
||||
go c.internalConnLost(err)
|
||||
return
|
||||
}
|
||||
}
|
108
vendor/github.com/eclipse/paho.mqtt.golang/notice.html
generated
vendored
Normal file
108
vendor/github.com/eclipse/paho.mqtt.golang/notice.html
generated
vendored
Normal file
@ -0,0 +1,108 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
|
||||
<title>Eclipse Foundation Software User Agreement</title>
|
||||
</head>
|
||||
|
||||
<body lang="EN-US">
|
||||
<h2>Eclipse Foundation Software User Agreement</h2>
|
||||
<p>February 1, 2011</p>
|
||||
|
||||
<h3>Usage Of Content</h3>
|
||||
|
||||
<p>THE ECLIPSE FOUNDATION MAKES AVAILABLE SOFTWARE, DOCUMENTATION, INFORMATION AND/OR OTHER MATERIALS FOR OPEN SOURCE PROJECTS
|
||||
(COLLECTIVELY "CONTENT"). USE OF THE CONTENT IS GOVERNED BY THE TERMS AND CONDITIONS OF THIS AGREEMENT AND/OR THE TERMS AND
|
||||
CONDITIONS OF LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW. BY USING THE CONTENT, YOU AGREE THAT YOUR USE
|
||||
OF THE CONTENT IS GOVERNED BY THIS AGREEMENT AND/OR THE TERMS AND CONDITIONS OF ANY APPLICABLE LICENSE AGREEMENTS OR
|
||||
NOTICES INDICATED OR REFERENCED BELOW. IF YOU DO NOT AGREE TO THE TERMS AND CONDITIONS OF THIS AGREEMENT AND THE TERMS AND
|
||||
CONDITIONS OF ANY APPLICABLE LICENSE AGREEMENTS OR NOTICES INDICATED OR REFERENCED BELOW, THEN YOU MAY NOT USE THE CONTENT.</p>
|
||||
|
||||
<h3>Applicable Licenses</h3>
|
||||
|
||||
<p>Unless otherwise indicated, all Content made available by the Eclipse Foundation is provided to you under the terms and conditions of the Eclipse Public License Version 1.0
|
||||
("EPL"). A copy of the EPL is provided with this Content and is also available at <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a>.
|
||||
For purposes of the EPL, "Program" will mean the Content.</p>
|
||||
|
||||
<p>Content includes, but is not limited to, source code, object code, documentation and other files maintained in the Eclipse Foundation source code
|
||||
repository ("Repository") in software modules ("Modules") and made available as downloadable archives ("Downloads").</p>
|
||||
|
||||
<ul>
|
||||
<li>Content may be structured and packaged into modules to facilitate delivering, extending, and upgrading the Content. Typical modules may include plug-ins ("Plug-ins"), plug-in fragments ("Fragments"), and features ("Features").</li>
|
||||
<li>Each Plug-in or Fragment may be packaged as a sub-directory or JAR (Java™ ARchive) in a directory named "plugins".</li>
|
||||
<li>A Feature is a bundle of one or more Plug-ins and/or Fragments and associated material. Each Feature may be packaged as a sub-directory in a directory named "features". Within a Feature, files named "feature.xml" may contain a list of the names and version numbers of the Plug-ins
|
||||
and/or Fragments associated with that Feature.</li>
|
||||
<li>Features may also include other Features ("Included Features"). Within a Feature, files named "feature.xml" may contain a list of the names and version numbers of Included Features.</li>
|
||||
</ul>
|
||||
|
||||
<p>The terms and conditions governing Plug-ins and Fragments should be contained in files named "about.html" ("Abouts"). The terms and conditions governing Features and
|
||||
Included Features should be contained in files named "license.html" ("Feature Licenses"). Abouts and Feature Licenses may be located in any directory of a Download or Module
|
||||
including, but not limited to the following locations:</p>
|
||||
|
||||
<ul>
|
||||
<li>The top-level (root) directory</li>
|
||||
<li>Plug-in and Fragment directories</li>
|
||||
<li>Inside Plug-ins and Fragments packaged as JARs</li>
|
||||
<li>Sub-directories of the directory named "src" of certain Plug-ins</li>
|
||||
<li>Feature directories</li>
|
||||
</ul>
|
||||
|
||||
<p>Note: if a Feature made available by the Eclipse Foundation is installed using the Provisioning Technology (as defined below), you must agree to a license ("Feature Update License") during the
|
||||
installation process. If the Feature contains Included Features, the Feature Update License should either provide you with the terms and conditions governing the Included Features or
|
||||
inform you where you can locate them. Feature Update Licenses may be found in the "license" property of files named "feature.properties" found within a Feature.
|
||||
Such Abouts, Feature Licenses, and Feature Update Licenses contain the terms and conditions (or references to such terms and conditions) that govern your use of the associated Content in
|
||||
that directory.</p>
|
||||
|
||||
<p>THE ABOUTS, FEATURE LICENSES, AND FEATURE UPDATE LICENSES MAY REFER TO THE EPL OR OTHER LICENSE AGREEMENTS, NOTICES OR TERMS AND CONDITIONS. SOME OF THESE
|
||||
OTHER LICENSE AGREEMENTS MAY INCLUDE (BUT ARE NOT LIMITED TO):</p>
|
||||
|
||||
<ul>
|
||||
<li>Eclipse Distribution License Version 1.0 (available at <a href="http://www.eclipse.org/licenses/edl-v10.html">http://www.eclipse.org/licenses/edl-v1.0.html</a>)</li>
|
||||
<li>Common Public License Version 1.0 (available at <a href="http://www.eclipse.org/legal/cpl-v10.html">http://www.eclipse.org/legal/cpl-v10.html</a>)</li>
|
||||
<li>Apache Software License 1.1 (available at <a href="http://www.apache.org/licenses/LICENSE">http://www.apache.org/licenses/LICENSE</a>)</li>
|
||||
<li>Apache Software License 2.0 (available at <a href="http://www.apache.org/licenses/LICENSE-2.0">http://www.apache.org/licenses/LICENSE-2.0</a>)</li>
|
||||
<li>Metro Link Public License 1.00 (available at <a href="http://www.opengroup.org/openmotif/supporters/metrolink/license.html">http://www.opengroup.org/openmotif/supporters/metrolink/license.html</a>)</li>
|
||||
<li>Mozilla Public License Version 1.1 (available at <a href="http://www.mozilla.org/MPL/MPL-1.1.html">http://www.mozilla.org/MPL/MPL-1.1.html</a>)</li>
|
||||
</ul>
|
||||
|
||||
<p>IT IS YOUR OBLIGATION TO READ AND ACCEPT ALL SUCH TERMS AND CONDITIONS PRIOR TO USE OF THE CONTENT. If no About, Feature License, or Feature Update License is provided, please
|
||||
contact the Eclipse Foundation to determine what terms and conditions govern that particular Content.</p>
|
||||
|
||||
|
||||
<h3>Use of Provisioning Technology</h3>
|
||||
|
||||
<p>The Eclipse Foundation makes available provisioning software, examples of which include, but are not limited to, p2 and the Eclipse
|
||||
Update Manager ("Provisioning Technology") for the purpose of allowing users to install software, documentation, information and/or
|
||||
other materials (collectively "Installable Software"). This capability is provided with the intent of allowing such users to
|
||||
install, extend and update Eclipse-based products. Information about packaging Installable Software is available at <a
|
||||
href="http://eclipse.org/equinox/p2/repository_packaging.html">http://eclipse.org/equinox/p2/repository_packaging.html</a>
|
||||
("Specification").</p>
|
||||
|
||||
<p>You may use Provisioning Technology to allow other parties to install Installable Software. You shall be responsible for enabling the
|
||||
applicable license agreements relating to the Installable Software to be presented to, and accepted by, the users of the Provisioning Technology
|
||||
in accordance with the Specification. By using Provisioning Technology in such a manner and making it available in accordance with the
|
||||
Specification, you further acknowledge your agreement to, and the acquisition of all necessary rights to permit the following:</p>
|
||||
|
||||
<ol>
|
||||
<li>A series of actions may occur ("Provisioning Process") in which a user may execute the Provisioning Technology
|
||||
on a machine ("Target Machine") with the intent of installing, extending or updating the functionality of an Eclipse-based
|
||||
product.</li>
|
||||
<li>During the Provisioning Process, the Provisioning Technology may cause third party Installable Software or a portion thereof to be
|
||||
accessed and copied to the Target Machine.</li>
|
||||
<li>Pursuant to the Specification, you will provide to the user the terms and conditions that govern the use of the Installable
|
||||
Software ("Installable Software Agreement") and such Installable Software Agreement shall be accessed from the Target
|
||||
Machine in accordance with the Specification. Such Installable Software Agreement must inform the user of the terms and conditions that govern
|
||||
the Installable Software and must solicit acceptance by the end user in the manner prescribed in such Installable Software Agreement. Upon such
|
||||
indication of agreement by the user, the provisioning Technology will complete installation of the Installable Software.</li>
|
||||
</ol>
|
||||
|
||||
<h3>Cryptography</h3>
|
||||
|
||||
<p>Content may contain encryption software. The country in which you are currently may have restrictions on the import, possession, and use, and/or re-export to
|
||||
another country, of encryption software. BEFORE using any encryption software, please check the country's laws, regulations and policies concerning the import,
|
||||
possession, or use, and re-export of encryption software, to see if this is permitted.</p>
|
||||
|
||||
<p><small>Java and all Java-based trademarks are trademarks of Oracle Corporation in the United States, other countries, or both.</small></p>
|
||||
</body>
|
||||
</html>
|
21
vendor/github.com/eclipse/paho.mqtt.golang/oops.go
generated
vendored
Normal file
21
vendor/github.com/eclipse/paho.mqtt.golang/oops.go
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (c) 2013 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Seth Hoenig
|
||||
* Allan Stockdill-Mander
|
||||
* Mike Robertson
|
||||
*/
|
||||
|
||||
package mqtt
|
||||
|
||||
func chkerr(e error) {
|
||||
if e != nil {
|
||||
panic(e)
|
||||
}
|
||||
}
|
340
vendor/github.com/eclipse/paho.mqtt.golang/options.go
generated
vendored
Normal file
340
vendor/github.com/eclipse/paho.mqtt.golang/options.go
generated
vendored
Normal file
@ -0,0 +1,340 @@
|
||||
/*
|
||||
* Copyright (c) 2013 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Seth Hoenig
|
||||
* Allan Stockdill-Mander
|
||||
* Mike Robertson
|
||||
*/
|
||||
|
||||
// Portions copyright © 2018 TIBCO Software Inc.
|
||||
|
||||
package mqtt
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// CredentialsProvider allows the username and password to be updated
|
||||
// before reconnecting. It should return the current username and password.
|
||||
type CredentialsProvider func() (username string, password string)
|
||||
|
||||
// MessageHandler is a callback type which can be set to be
|
||||
// executed upon the arrival of messages published to topics
|
||||
// to which the client is subscribed.
|
||||
type MessageHandler func(Client, Message)
|
||||
|
||||
// ConnectionLostHandler is a callback type which can be set to be
|
||||
// executed upon an unintended disconnection from the MQTT broker.
|
||||
// Disconnects caused by calling Disconnect or ForceDisconnect will
|
||||
// not cause an OnConnectionLost callback to execute.
|
||||
type ConnectionLostHandler func(Client, error)
|
||||
|
||||
// OnConnectHandler is a callback that is called when the client
|
||||
// state changes from unconnected/disconnected to connected. Both
|
||||
// at initial connection and on reconnection
|
||||
type OnConnectHandler func(Client)
|
||||
|
||||
// ClientOptions contains configurable options for an Client.
|
||||
type ClientOptions struct {
|
||||
Servers []*url.URL
|
||||
ClientID string
|
||||
Username string
|
||||
Password string
|
||||
CredentialsProvider CredentialsProvider
|
||||
CleanSession bool
|
||||
Order bool
|
||||
WillEnabled bool
|
||||
WillTopic string
|
||||
WillPayload []byte
|
||||
WillQos byte
|
||||
WillRetained bool
|
||||
ProtocolVersion uint
|
||||
protocolVersionExplicit bool
|
||||
TLSConfig *tls.Config
|
||||
KeepAlive int64
|
||||
PingTimeout time.Duration
|
||||
ConnectTimeout time.Duration
|
||||
MaxReconnectInterval time.Duration
|
||||
AutoReconnect bool
|
||||
Store Store
|
||||
DefaultPublishHandler MessageHandler
|
||||
OnConnect OnConnectHandler
|
||||
OnConnectionLost ConnectionLostHandler
|
||||
WriteTimeout time.Duration
|
||||
MessageChannelDepth uint
|
||||
ResumeSubs bool
|
||||
HTTPHeaders http.Header
|
||||
}
|
||||
|
||||
// NewClientOptions will create a new ClientClientOptions type with some
|
||||
// default values.
|
||||
// Port: 1883
|
||||
// CleanSession: True
|
||||
// Order: True
|
||||
// KeepAlive: 30 (seconds)
|
||||
// ConnectTimeout: 30 (seconds)
|
||||
// MaxReconnectInterval 10 (minutes)
|
||||
// AutoReconnect: True
|
||||
func NewClientOptions() *ClientOptions {
|
||||
o := &ClientOptions{
|
||||
Servers: nil,
|
||||
ClientID: "",
|
||||
Username: "",
|
||||
Password: "",
|
||||
CleanSession: true,
|
||||
Order: true,
|
||||
WillEnabled: false,
|
||||
WillTopic: "",
|
||||
WillPayload: nil,
|
||||
WillQos: 0,
|
||||
WillRetained: false,
|
||||
ProtocolVersion: 0,
|
||||
protocolVersionExplicit: false,
|
||||
KeepAlive: 30,
|
||||
PingTimeout: 10 * time.Second,
|
||||
ConnectTimeout: 30 * time.Second,
|
||||
MaxReconnectInterval: 10 * time.Minute,
|
||||
AutoReconnect: true,
|
||||
Store: nil,
|
||||
OnConnect: nil,
|
||||
OnConnectionLost: DefaultConnectionLostHandler,
|
||||
WriteTimeout: 0, // 0 represents timeout disabled
|
||||
MessageChannelDepth: 100,
|
||||
ResumeSubs: false,
|
||||
HTTPHeaders: make(map[string][]string),
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
// AddBroker adds a broker URI to the list of brokers to be used. The format should be
|
||||
// scheme://host:port
|
||||
// Where "scheme" is one of "tcp", "ssl", or "ws", "host" is the ip-address (or hostname)
|
||||
// and "port" is the port on which the broker is accepting connections.
|
||||
//
|
||||
// Default values for hostname is "127.0.0.1", for schema is "tcp://".
|
||||
//
|
||||
// An example broker URI would look like: tcp://foobar.com:1883
|
||||
func (o *ClientOptions) AddBroker(server string) *ClientOptions {
|
||||
if len(server) > 0 && server[0] == ':' {
|
||||
server = "127.0.0.1" + server
|
||||
}
|
||||
if !strings.Contains(server, "://") {
|
||||
server = "tcp://" + server
|
||||
}
|
||||
brokerURI, err := url.Parse(server)
|
||||
if err != nil {
|
||||
ERROR.Println(CLI, "Failed to parse %q broker address: %s", server, err)
|
||||
return o
|
||||
}
|
||||
o.Servers = append(o.Servers, brokerURI)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetResumeSubs will enable resuming of stored (un)subscribe messages when connecting
|
||||
// but not reconnecting if CleanSession is false. Otherwise these messages are discarded.
|
||||
func (o *ClientOptions) SetResumeSubs(resume bool) *ClientOptions {
|
||||
o.ResumeSubs = resume
|
||||
return o
|
||||
}
|
||||
|
||||
// SetClientID will set the client id to be used by this client when
|
||||
// connecting to the MQTT broker. According to the MQTT v3.1 specification,
|
||||
// a client id mus be no longer than 23 characters.
|
||||
func (o *ClientOptions) SetClientID(id string) *ClientOptions {
|
||||
o.ClientID = id
|
||||
return o
|
||||
}
|
||||
|
||||
// SetUsername will set the username to be used by this client when connecting
|
||||
// to the MQTT broker. Note: without the use of SSL/TLS, this information will
|
||||
// be sent in plaintext accross the wire.
|
||||
func (o *ClientOptions) SetUsername(u string) *ClientOptions {
|
||||
o.Username = u
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPassword will set the password to be used by this client when connecting
|
||||
// to the MQTT broker. Note: without the use of SSL/TLS, this information will
|
||||
// be sent in plaintext accross the wire.
|
||||
func (o *ClientOptions) SetPassword(p string) *ClientOptions {
|
||||
o.Password = p
|
||||
return o
|
||||
}
|
||||
|
||||
// SetCredentialsProvider will set a method to be called by this client when
|
||||
// connecting to the MQTT broker that provide the current username and password.
|
||||
// Note: without the use of SSL/TLS, this information will be sent
|
||||
// in plaintext accross the wire.
|
||||
func (o *ClientOptions) SetCredentialsProvider(p CredentialsProvider) *ClientOptions {
|
||||
o.CredentialsProvider = p
|
||||
return o
|
||||
}
|
||||
|
||||
// SetCleanSession will set the "clean session" flag in the connect message
|
||||
// when this client connects to an MQTT broker. By setting this flag, you are
|
||||
// indicating that no messages saved by the broker for this client should be
|
||||
// delivered. Any messages that were going to be sent by this client before
|
||||
// diconnecting previously but didn't will not be sent upon connecting to the
|
||||
// broker.
|
||||
func (o *ClientOptions) SetCleanSession(clean bool) *ClientOptions {
|
||||
o.CleanSession = clean
|
||||
return o
|
||||
}
|
||||
|
||||
// SetOrderMatters will set the message routing to guarantee order within
|
||||
// each QoS level. By default, this value is true. If set to false,
|
||||
// this flag indicates that messages can be delivered asynchronously
|
||||
// from the client to the application and possibly arrive out of order.
|
||||
func (o *ClientOptions) SetOrderMatters(order bool) *ClientOptions {
|
||||
o.Order = order
|
||||
return o
|
||||
}
|
||||
|
||||
// SetTLSConfig will set an SSL/TLS configuration to be used when connecting
|
||||
// to an MQTT broker. Please read the official Go documentation for more
|
||||
// information.
|
||||
func (o *ClientOptions) SetTLSConfig(t *tls.Config) *ClientOptions {
|
||||
o.TLSConfig = t
|
||||
return o
|
||||
}
|
||||
|
||||
// SetStore will set the implementation of the Store interface
|
||||
// used to provide message persistence in cases where QoS levels
|
||||
// QoS_ONE or QoS_TWO are used. If no store is provided, then the
|
||||
// client will use MemoryStore by default.
|
||||
func (o *ClientOptions) SetStore(s Store) *ClientOptions {
|
||||
o.Store = s
|
||||
return o
|
||||
}
|
||||
|
||||
// SetKeepAlive will set the amount of time (in seconds) that the client
|
||||
// should wait before sending a PING request to the broker. This will
|
||||
// allow the client to know that a connection has not been lost with the
|
||||
// server.
|
||||
func (o *ClientOptions) SetKeepAlive(k time.Duration) *ClientOptions {
|
||||
o.KeepAlive = int64(k / time.Second)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetPingTimeout will set the amount of time (in seconds) that the client
|
||||
// will wait after sending a PING request to the broker, before deciding
|
||||
// that the connection has been lost. Default is 10 seconds.
|
||||
func (o *ClientOptions) SetPingTimeout(k time.Duration) *ClientOptions {
|
||||
o.PingTimeout = k
|
||||
return o
|
||||
}
|
||||
|
||||
// SetProtocolVersion sets the MQTT version to be used to connect to the
|
||||
// broker. Legitimate values are currently 3 - MQTT 3.1 or 4 - MQTT 3.1.1
|
||||
func (o *ClientOptions) SetProtocolVersion(pv uint) *ClientOptions {
|
||||
if (pv >= 3 && pv <= 4) || (pv > 0x80) {
|
||||
o.ProtocolVersion = pv
|
||||
o.protocolVersionExplicit = true
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
// UnsetWill will cause any set will message to be disregarded.
|
||||
func (o *ClientOptions) UnsetWill() *ClientOptions {
|
||||
o.WillEnabled = false
|
||||
return o
|
||||
}
|
||||
|
||||
// SetWill accepts a string will message to be set. When the client connects,
|
||||
// it will give this will message to the broker, which will then publish the
|
||||
// provided payload (the will) to any clients that are subscribed to the provided
|
||||
// topic.
|
||||
func (o *ClientOptions) SetWill(topic string, payload string, qos byte, retained bool) *ClientOptions {
|
||||
o.SetBinaryWill(topic, []byte(payload), qos, retained)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetBinaryWill accepts a []byte will message to be set. When the client connects,
|
||||
// it will give this will message to the broker, which will then publish the
|
||||
// provided payload (the will) to any clients that are subscribed to the provided
|
||||
// topic.
|
||||
func (o *ClientOptions) SetBinaryWill(topic string, payload []byte, qos byte, retained bool) *ClientOptions {
|
||||
o.WillEnabled = true
|
||||
o.WillTopic = topic
|
||||
o.WillPayload = payload
|
||||
o.WillQos = qos
|
||||
o.WillRetained = retained
|
||||
return o
|
||||
}
|
||||
|
||||
// SetDefaultPublishHandler sets the MessageHandler that will be called when a message
|
||||
// is received that does not match any known subscriptions.
|
||||
func (o *ClientOptions) SetDefaultPublishHandler(defaultHandler MessageHandler) *ClientOptions {
|
||||
o.DefaultPublishHandler = defaultHandler
|
||||
return o
|
||||
}
|
||||
|
||||
// SetOnConnectHandler sets the function to be called when the client is connected. Both
|
||||
// at initial connection time and upon automatic reconnect.
|
||||
func (o *ClientOptions) SetOnConnectHandler(onConn OnConnectHandler) *ClientOptions {
|
||||
o.OnConnect = onConn
|
||||
return o
|
||||
}
|
||||
|
||||
// SetConnectionLostHandler will set the OnConnectionLost callback to be executed
|
||||
// in the case where the client unexpectedly loses connection with the MQTT broker.
|
||||
func (o *ClientOptions) SetConnectionLostHandler(onLost ConnectionLostHandler) *ClientOptions {
|
||||
o.OnConnectionLost = onLost
|
||||
return o
|
||||
}
|
||||
|
||||
// SetWriteTimeout puts a limit on how long a mqtt publish should block until it unblocks with a
|
||||
// timeout error. A duration of 0 never times out. Default 30 seconds
|
||||
func (o *ClientOptions) SetWriteTimeout(t time.Duration) *ClientOptions {
|
||||
o.WriteTimeout = t
|
||||
return o
|
||||
}
|
||||
|
||||
// SetConnectTimeout limits how long the client will wait when trying to open a connection
|
||||
// to an MQTT server before timeing out and erroring the attempt. A duration of 0 never times out.
|
||||
// Default 30 seconds. Currently only operational on TCP/TLS connections.
|
||||
func (o *ClientOptions) SetConnectTimeout(t time.Duration) *ClientOptions {
|
||||
o.ConnectTimeout = t
|
||||
return o
|
||||
}
|
||||
|
||||
// SetMaxReconnectInterval sets the maximum time that will be waited between reconnection attempts
|
||||
// when connection is lost
|
||||
func (o *ClientOptions) SetMaxReconnectInterval(t time.Duration) *ClientOptions {
|
||||
o.MaxReconnectInterval = t
|
||||
return o
|
||||
}
|
||||
|
||||
// SetAutoReconnect sets whether the automatic reconnection logic should be used
|
||||
// when the connection is lost, even if disabled the ConnectionLostHandler is still
|
||||
// called
|
||||
func (o *ClientOptions) SetAutoReconnect(a bool) *ClientOptions {
|
||||
o.AutoReconnect = a
|
||||
return o
|
||||
}
|
||||
|
||||
// SetMessageChannelDepth sets the size of the internal queue that holds messages while the
|
||||
// client is temporairily offline, allowing the application to publish when the client is
|
||||
// reconnecting. This setting is only valid if AutoReconnect is set to true, it is otherwise
|
||||
// ignored.
|
||||
func (o *ClientOptions) SetMessageChannelDepth(s uint) *ClientOptions {
|
||||
o.MessageChannelDepth = s
|
||||
return o
|
||||
}
|
||||
|
||||
// SetHTTPHeaders sets the additional HTTP headers that will be sent in the WebSocket
|
||||
// opening handshake.
|
||||
func (o *ClientOptions) SetHTTPHeaders(h http.Header) *ClientOptions {
|
||||
o.HTTPHeaders = h
|
||||
return o
|
||||
}
|
149
vendor/github.com/eclipse/paho.mqtt.golang/options_reader.go
generated
vendored
Normal file
149
vendor/github.com/eclipse/paho.mqtt.golang/options_reader.go
generated
vendored
Normal file
@ -0,0 +1,149 @@
|
||||
/*
|
||||
* Copyright (c) 2013 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Seth Hoenig
|
||||
* Allan Stockdill-Mander
|
||||
* Mike Robertson
|
||||
*/
|
||||
|
||||
package mqtt
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ClientOptionsReader provides an interface for reading ClientOptions after the client has been initialized.
|
||||
type ClientOptionsReader struct {
|
||||
options *ClientOptions
|
||||
}
|
||||
|
||||
//Servers returns a slice of the servers defined in the clientoptions
|
||||
func (r *ClientOptionsReader) Servers() []*url.URL {
|
||||
s := make([]*url.URL, len(r.options.Servers))
|
||||
|
||||
for i, u := range r.options.Servers {
|
||||
nu := *u
|
||||
s[i] = &nu
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
//ResumeSubs returns true if resuming stored (un)sub is enabled
|
||||
func (r *ClientOptionsReader) ResumeSubs() bool {
|
||||
s := r.options.ResumeSubs
|
||||
return s
|
||||
}
|
||||
|
||||
//ClientID returns the set client id
|
||||
func (r *ClientOptionsReader) ClientID() string {
|
||||
s := r.options.ClientID
|
||||
return s
|
||||
}
|
||||
|
||||
//Username returns the set username
|
||||
func (r *ClientOptionsReader) Username() string {
|
||||
s := r.options.Username
|
||||
return s
|
||||
}
|
||||
|
||||
//Password returns the set password
|
||||
func (r *ClientOptionsReader) Password() string {
|
||||
s := r.options.Password
|
||||
return s
|
||||
}
|
||||
|
||||
//CleanSession returns whether Cleansession is set
|
||||
func (r *ClientOptionsReader) CleanSession() bool {
|
||||
s := r.options.CleanSession
|
||||
return s
|
||||
}
|
||||
|
||||
func (r *ClientOptionsReader) Order() bool {
|
||||
s := r.options.Order
|
||||
return s
|
||||
}
|
||||
|
||||
func (r *ClientOptionsReader) WillEnabled() bool {
|
||||
s := r.options.WillEnabled
|
||||
return s
|
||||
}
|
||||
|
||||
func (r *ClientOptionsReader) WillTopic() string {
|
||||
s := r.options.WillTopic
|
||||
return s
|
||||
}
|
||||
|
||||
func (r *ClientOptionsReader) WillPayload() []byte {
|
||||
s := r.options.WillPayload
|
||||
return s
|
||||
}
|
||||
|
||||
func (r *ClientOptionsReader) WillQos() byte {
|
||||
s := r.options.WillQos
|
||||
return s
|
||||
}
|
||||
|
||||
func (r *ClientOptionsReader) WillRetained() bool {
|
||||
s := r.options.WillRetained
|
||||
return s
|
||||
}
|
||||
|
||||
func (r *ClientOptionsReader) ProtocolVersion() uint {
|
||||
s := r.options.ProtocolVersion
|
||||
return s
|
||||
}
|
||||
|
||||
func (r *ClientOptionsReader) TLSConfig() *tls.Config {
|
||||
s := r.options.TLSConfig
|
||||
return s
|
||||
}
|
||||
|
||||
func (r *ClientOptionsReader) KeepAlive() time.Duration {
|
||||
s := time.Duration(r.options.KeepAlive * int64(time.Second))
|
||||
return s
|
||||
}
|
||||
|
||||
func (r *ClientOptionsReader) PingTimeout() time.Duration {
|
||||
s := r.options.PingTimeout
|
||||
return s
|
||||
}
|
||||
|
||||
func (r *ClientOptionsReader) ConnectTimeout() time.Duration {
|
||||
s := r.options.ConnectTimeout
|
||||
return s
|
||||
}
|
||||
|
||||
func (r *ClientOptionsReader) MaxReconnectInterval() time.Duration {
|
||||
s := r.options.MaxReconnectInterval
|
||||
return s
|
||||
}
|
||||
|
||||
func (r *ClientOptionsReader) AutoReconnect() bool {
|
||||
s := r.options.AutoReconnect
|
||||
return s
|
||||
}
|
||||
|
||||
func (r *ClientOptionsReader) WriteTimeout() time.Duration {
|
||||
s := r.options.WriteTimeout
|
||||
return s
|
||||
}
|
||||
|
||||
func (r *ClientOptionsReader) MessageChannelDepth() uint {
|
||||
s := r.options.MessageChannelDepth
|
||||
return s
|
||||
}
|
||||
|
||||
func (r *ClientOptionsReader) HTTPHeaders() http.Header {
|
||||
h := r.options.HTTPHeaders
|
||||
return h
|
||||
}
|
55
vendor/github.com/eclipse/paho.mqtt.golang/packets/connack.go
generated
vendored
Normal file
55
vendor/github.com/eclipse/paho.mqtt.golang/packets/connack.go
generated
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
package packets
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
//ConnackPacket is an internal representation of the fields of the
|
||||
//Connack MQTT packet
|
||||
type ConnackPacket struct {
|
||||
FixedHeader
|
||||
SessionPresent bool
|
||||
ReturnCode byte
|
||||
}
|
||||
|
||||
func (ca *ConnackPacket) String() string {
|
||||
str := fmt.Sprintf("%s", ca.FixedHeader)
|
||||
str += " "
|
||||
str += fmt.Sprintf("sessionpresent: %t returncode: %d", ca.SessionPresent, ca.ReturnCode)
|
||||
return str
|
||||
}
|
||||
|
||||
func (ca *ConnackPacket) Write(w io.Writer) error {
|
||||
var body bytes.Buffer
|
||||
var err error
|
||||
|
||||
body.WriteByte(boolToByte(ca.SessionPresent))
|
||||
body.WriteByte(ca.ReturnCode)
|
||||
ca.FixedHeader.RemainingLength = 2
|
||||
packet := ca.FixedHeader.pack()
|
||||
packet.Write(body.Bytes())
|
||||
_, err = packet.WriteTo(w)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
//Unpack decodes the details of a ControlPacket after the fixed
|
||||
//header has been read
|
||||
func (ca *ConnackPacket) Unpack(b io.Reader) error {
|
||||
flags, err := decodeByte(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ca.SessionPresent = 1&flags > 0
|
||||
ca.ReturnCode, err = decodeByte(b)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
//Details returns a Details struct containing the Qos and
|
||||
//MessageID of this ControlPacket
|
||||
func (ca *ConnackPacket) Details() Details {
|
||||
return Details{Qos: 0, MessageID: 0}
|
||||
}
|
154
vendor/github.com/eclipse/paho.mqtt.golang/packets/connect.go
generated
vendored
Normal file
154
vendor/github.com/eclipse/paho.mqtt.golang/packets/connect.go
generated
vendored
Normal file
@ -0,0 +1,154 @@
|
||||
package packets
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
//ConnectPacket is an internal representation of the fields of the
|
||||
//Connect MQTT packet
|
||||
type ConnectPacket struct {
|
||||
FixedHeader
|
||||
ProtocolName string
|
||||
ProtocolVersion byte
|
||||
CleanSession bool
|
||||
WillFlag bool
|
||||
WillQos byte
|
||||
WillRetain bool
|
||||
UsernameFlag bool
|
||||
PasswordFlag bool
|
||||
ReservedBit byte
|
||||
Keepalive uint16
|
||||
|
||||
ClientIdentifier string
|
||||
WillTopic string
|
||||
WillMessage []byte
|
||||
Username string
|
||||
Password []byte
|
||||
}
|
||||
|
||||
func (c *ConnectPacket) String() string {
|
||||
str := fmt.Sprintf("%s", c.FixedHeader)
|
||||
str += " "
|
||||
str += fmt.Sprintf("protocolversion: %d protocolname: %s cleansession: %t willflag: %t WillQos: %d WillRetain: %t Usernameflag: %t Passwordflag: %t keepalive: %d clientId: %s willtopic: %s willmessage: %s Username: %s Password: %s", c.ProtocolVersion, c.ProtocolName, c.CleanSession, c.WillFlag, c.WillQos, c.WillRetain, c.UsernameFlag, c.PasswordFlag, c.Keepalive, c.ClientIdentifier, c.WillTopic, c.WillMessage, c.Username, c.Password)
|
||||
return str
|
||||
}
|
||||
|
||||
func (c *ConnectPacket) Write(w io.Writer) error {
|
||||
var body bytes.Buffer
|
||||
var err error
|
||||
|
||||
body.Write(encodeString(c.ProtocolName))
|
||||
body.WriteByte(c.ProtocolVersion)
|
||||
body.WriteByte(boolToByte(c.CleanSession)<<1 | boolToByte(c.WillFlag)<<2 | c.WillQos<<3 | boolToByte(c.WillRetain)<<5 | boolToByte(c.PasswordFlag)<<6 | boolToByte(c.UsernameFlag)<<7)
|
||||
body.Write(encodeUint16(c.Keepalive))
|
||||
body.Write(encodeString(c.ClientIdentifier))
|
||||
if c.WillFlag {
|
||||
body.Write(encodeString(c.WillTopic))
|
||||
body.Write(encodeBytes(c.WillMessage))
|
||||
}
|
||||
if c.UsernameFlag {
|
||||
body.Write(encodeString(c.Username))
|
||||
}
|
||||
if c.PasswordFlag {
|
||||
body.Write(encodeBytes(c.Password))
|
||||
}
|
||||
c.FixedHeader.RemainingLength = body.Len()
|
||||
packet := c.FixedHeader.pack()
|
||||
packet.Write(body.Bytes())
|
||||
_, err = packet.WriteTo(w)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
//Unpack decodes the details of a ControlPacket after the fixed
|
||||
//header has been read
|
||||
func (c *ConnectPacket) Unpack(b io.Reader) error {
|
||||
var err error
|
||||
c.ProtocolName, err = decodeString(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.ProtocolVersion, err = decodeByte(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
options, err := decodeByte(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.ReservedBit = 1 & options
|
||||
c.CleanSession = 1&(options>>1) > 0
|
||||
c.WillFlag = 1&(options>>2) > 0
|
||||
c.WillQos = 3 & (options >> 3)
|
||||
c.WillRetain = 1&(options>>5) > 0
|
||||
c.PasswordFlag = 1&(options>>6) > 0
|
||||
c.UsernameFlag = 1&(options>>7) > 0
|
||||
c.Keepalive, err = decodeUint16(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.ClientIdentifier, err = decodeString(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if c.WillFlag {
|
||||
c.WillTopic, err = decodeString(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.WillMessage, err = decodeBytes(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if c.UsernameFlag {
|
||||
c.Username, err = decodeString(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if c.PasswordFlag {
|
||||
c.Password, err = decodeBytes(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//Validate performs validation of the fields of a Connect packet
|
||||
func (c *ConnectPacket) Validate() byte {
|
||||
if c.PasswordFlag && !c.UsernameFlag {
|
||||
return ErrRefusedBadUsernameOrPassword
|
||||
}
|
||||
if c.ReservedBit != 0 {
|
||||
//Bad reserved bit
|
||||
return ErrProtocolViolation
|
||||
}
|
||||
if (c.ProtocolName == "MQIsdp" && c.ProtocolVersion != 3) || (c.ProtocolName == "MQTT" && c.ProtocolVersion != 4) {
|
||||
//Mismatched or unsupported protocol version
|
||||
return ErrRefusedBadProtocolVersion
|
||||
}
|
||||
if c.ProtocolName != "MQIsdp" && c.ProtocolName != "MQTT" {
|
||||
//Bad protocol name
|
||||
return ErrProtocolViolation
|
||||
}
|
||||
if len(c.ClientIdentifier) > 65535 || len(c.Username) > 65535 || len(c.Password) > 65535 {
|
||||
//Bad size field
|
||||
return ErrProtocolViolation
|
||||
}
|
||||
if len(c.ClientIdentifier) == 0 && !c.CleanSession {
|
||||
//Bad client identifier
|
||||
return ErrRefusedIDRejected
|
||||
}
|
||||
return Accepted
|
||||
}
|
||||
|
||||
//Details returns a Details struct containing the Qos and
|
||||
//MessageID of this ControlPacket
|
||||
func (c *ConnectPacket) Details() Details {
|
||||
return Details{Qos: 0, MessageID: 0}
|
||||
}
|
36
vendor/github.com/eclipse/paho.mqtt.golang/packets/disconnect.go
generated
vendored
Normal file
36
vendor/github.com/eclipse/paho.mqtt.golang/packets/disconnect.go
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
package packets
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
//DisconnectPacket is an internal representation of the fields of the
|
||||
//Disconnect MQTT packet
|
||||
type DisconnectPacket struct {
|
||||
FixedHeader
|
||||
}
|
||||
|
||||
func (d *DisconnectPacket) String() string {
|
||||
str := fmt.Sprintf("%s", d.FixedHeader)
|
||||
return str
|
||||
}
|
||||
|
||||
func (d *DisconnectPacket) Write(w io.Writer) error {
|
||||
packet := d.FixedHeader.pack()
|
||||
_, err := packet.WriteTo(w)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
//Unpack decodes the details of a ControlPacket after the fixed
|
||||
//header has been read
|
||||
func (d *DisconnectPacket) Unpack(b io.Reader) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
//Details returns a Details struct containing the Qos and
|
||||
//MessageID of this ControlPacket
|
||||
func (d *DisconnectPacket) Details() Details {
|
||||
return Details{Qos: 0, MessageID: 0}
|
||||
}
|
346
vendor/github.com/eclipse/paho.mqtt.golang/packets/packets.go
generated
vendored
Normal file
346
vendor/github.com/eclipse/paho.mqtt.golang/packets/packets.go
generated
vendored
Normal file
@ -0,0 +1,346 @@
|
||||
package packets
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
//ControlPacket defines the interface for structs intended to hold
|
||||
//decoded MQTT packets, either from being read or before being
|
||||
//written
|
||||
type ControlPacket interface {
|
||||
Write(io.Writer) error
|
||||
Unpack(io.Reader) error
|
||||
String() string
|
||||
Details() Details
|
||||
}
|
||||
|
||||
//PacketNames maps the constants for each of the MQTT packet types
|
||||
//to a string representation of their name.
|
||||
var PacketNames = map[uint8]string{
|
||||
1: "CONNECT",
|
||||
2: "CONNACK",
|
||||
3: "PUBLISH",
|
||||
4: "PUBACK",
|
||||
5: "PUBREC",
|
||||
6: "PUBREL",
|
||||
7: "PUBCOMP",
|
||||
8: "SUBSCRIBE",
|
||||
9: "SUBACK",
|
||||
10: "UNSUBSCRIBE",
|
||||
11: "UNSUBACK",
|
||||
12: "PINGREQ",
|
||||
13: "PINGRESP",
|
||||
14: "DISCONNECT",
|
||||
}
|
||||
|
||||
//Below are the constants assigned to each of the MQTT packet types
|
||||
const (
|
||||
Connect = 1
|
||||
Connack = 2
|
||||
Publish = 3
|
||||
Puback = 4
|
||||
Pubrec = 5
|
||||
Pubrel = 6
|
||||
Pubcomp = 7
|
||||
Subscribe = 8
|
||||
Suback = 9
|
||||
Unsubscribe = 10
|
||||
Unsuback = 11
|
||||
Pingreq = 12
|
||||
Pingresp = 13
|
||||
Disconnect = 14
|
||||
)
|
||||
|
||||
//Below are the const definitions for error codes returned by
|
||||
//Connect()
|
||||
const (
|
||||
Accepted = 0x00
|
||||
ErrRefusedBadProtocolVersion = 0x01
|
||||
ErrRefusedIDRejected = 0x02
|
||||
ErrRefusedServerUnavailable = 0x03
|
||||
ErrRefusedBadUsernameOrPassword = 0x04
|
||||
ErrRefusedNotAuthorised = 0x05
|
||||
ErrNetworkError = 0xFE
|
||||
ErrProtocolViolation = 0xFF
|
||||
)
|
||||
|
||||
//ConnackReturnCodes is a map of the error codes constants for Connect()
|
||||
//to a string representation of the error
|
||||
var ConnackReturnCodes = map[uint8]string{
|
||||
0: "Connection Accepted",
|
||||
1: "Connection Refused: Bad Protocol Version",
|
||||
2: "Connection Refused: Client Identifier Rejected",
|
||||
3: "Connection Refused: Server Unavailable",
|
||||
4: "Connection Refused: Username or Password in unknown format",
|
||||
5: "Connection Refused: Not Authorised",
|
||||
254: "Connection Error",
|
||||
255: "Connection Refused: Protocol Violation",
|
||||
}
|
||||
|
||||
//ConnErrors is a map of the errors codes constants for Connect()
|
||||
//to a Go error
|
||||
var ConnErrors = map[byte]error{
|
||||
Accepted: nil,
|
||||
ErrRefusedBadProtocolVersion: errors.New("Unnacceptable protocol version"),
|
||||
ErrRefusedIDRejected: errors.New("Identifier rejected"),
|
||||
ErrRefusedServerUnavailable: errors.New("Server Unavailable"),
|
||||
ErrRefusedBadUsernameOrPassword: errors.New("Bad user name or password"),
|
||||
ErrRefusedNotAuthorised: errors.New("Not Authorized"),
|
||||
ErrNetworkError: errors.New("Network Error"),
|
||||
ErrProtocolViolation: errors.New("Protocol Violation"),
|
||||
}
|
||||
|
||||
//ReadPacket takes an instance of an io.Reader (such as net.Conn) and attempts
|
||||
//to read an MQTT packet from the stream. It returns a ControlPacket
|
||||
//representing the decoded MQTT packet and an error. One of these returns will
|
||||
//always be nil, a nil ControlPacket indicating an error occurred.
|
||||
func ReadPacket(r io.Reader) (ControlPacket, error) {
|
||||
var fh FixedHeader
|
||||
b := make([]byte, 1)
|
||||
|
||||
_, err := io.ReadFull(r, b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = fh.unpack(b[0], r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cp, err := NewControlPacketWithHeader(fh)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
packetBytes := make([]byte, fh.RemainingLength)
|
||||
n, err := io.ReadFull(r, packetBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if n != fh.RemainingLength {
|
||||
return nil, errors.New("Failed to read expected data")
|
||||
}
|
||||
|
||||
err = cp.Unpack(bytes.NewBuffer(packetBytes))
|
||||
return cp, err
|
||||
}
|
||||
|
||||
//NewControlPacket is used to create a new ControlPacket of the type specified
|
||||
//by packetType, this is usually done by reference to the packet type constants
|
||||
//defined in packets.go. The newly created ControlPacket is empty and a pointer
|
||||
//is returned.
|
||||
func NewControlPacket(packetType byte) ControlPacket {
|
||||
switch packetType {
|
||||
case Connect:
|
||||
return &ConnectPacket{FixedHeader: FixedHeader{MessageType: Connect}}
|
||||
case Connack:
|
||||
return &ConnackPacket{FixedHeader: FixedHeader{MessageType: Connack}}
|
||||
case Disconnect:
|
||||
return &DisconnectPacket{FixedHeader: FixedHeader{MessageType: Disconnect}}
|
||||
case Publish:
|
||||
return &PublishPacket{FixedHeader: FixedHeader{MessageType: Publish}}
|
||||
case Puback:
|
||||
return &PubackPacket{FixedHeader: FixedHeader{MessageType: Puback}}
|
||||
case Pubrec:
|
||||
return &PubrecPacket{FixedHeader: FixedHeader{MessageType: Pubrec}}
|
||||
case Pubrel:
|
||||
return &PubrelPacket{FixedHeader: FixedHeader{MessageType: Pubrel, Qos: 1}}
|
||||
case Pubcomp:
|
||||
return &PubcompPacket{FixedHeader: FixedHeader{MessageType: Pubcomp}}
|
||||
case Subscribe:
|
||||
return &SubscribePacket{FixedHeader: FixedHeader{MessageType: Subscribe, Qos: 1}}
|
||||
case Suback:
|
||||
return &SubackPacket{FixedHeader: FixedHeader{MessageType: Suback}}
|
||||
case Unsubscribe:
|
||||
return &UnsubscribePacket{FixedHeader: FixedHeader{MessageType: Unsubscribe, Qos: 1}}
|
||||
case Unsuback:
|
||||
return &UnsubackPacket{FixedHeader: FixedHeader{MessageType: Unsuback}}
|
||||
case Pingreq:
|
||||
return &PingreqPacket{FixedHeader: FixedHeader{MessageType: Pingreq}}
|
||||
case Pingresp:
|
||||
return &PingrespPacket{FixedHeader: FixedHeader{MessageType: Pingresp}}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//NewControlPacketWithHeader is used to create a new ControlPacket of the type
|
||||
//specified within the FixedHeader that is passed to the function.
|
||||
//The newly created ControlPacket is empty and a pointer is returned.
|
||||
func NewControlPacketWithHeader(fh FixedHeader) (ControlPacket, error) {
|
||||
switch fh.MessageType {
|
||||
case Connect:
|
||||
return &ConnectPacket{FixedHeader: fh}, nil
|
||||
case Connack:
|
||||
return &ConnackPacket{FixedHeader: fh}, nil
|
||||
case Disconnect:
|
||||
return &DisconnectPacket{FixedHeader: fh}, nil
|
||||
case Publish:
|
||||
return &PublishPacket{FixedHeader: fh}, nil
|
||||
case Puback:
|
||||
return &PubackPacket{FixedHeader: fh}, nil
|
||||
case Pubrec:
|
||||
return &PubrecPacket{FixedHeader: fh}, nil
|
||||
case Pubrel:
|
||||
return &PubrelPacket{FixedHeader: fh}, nil
|
||||
case Pubcomp:
|
||||
return &PubcompPacket{FixedHeader: fh}, nil
|
||||
case Subscribe:
|
||||
return &SubscribePacket{FixedHeader: fh}, nil
|
||||
case Suback:
|
||||
return &SubackPacket{FixedHeader: fh}, nil
|
||||
case Unsubscribe:
|
||||
return &UnsubscribePacket{FixedHeader: fh}, nil
|
||||
case Unsuback:
|
||||
return &UnsubackPacket{FixedHeader: fh}, nil
|
||||
case Pingreq:
|
||||
return &PingreqPacket{FixedHeader: fh}, nil
|
||||
case Pingresp:
|
||||
return &PingrespPacket{FixedHeader: fh}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("unsupported packet type 0x%x", fh.MessageType)
|
||||
}
|
||||
|
||||
//Details struct returned by the Details() function called on
|
||||
//ControlPackets to present details of the Qos and MessageID
|
||||
//of the ControlPacket
|
||||
type Details struct {
|
||||
Qos byte
|
||||
MessageID uint16
|
||||
}
|
||||
|
||||
//FixedHeader is a struct to hold the decoded information from
|
||||
//the fixed header of an MQTT ControlPacket
|
||||
type FixedHeader struct {
|
||||
MessageType byte
|
||||
Dup bool
|
||||
Qos byte
|
||||
Retain bool
|
||||
RemainingLength int
|
||||
}
|
||||
|
||||
func (fh FixedHeader) String() string {
|
||||
return fmt.Sprintf("%s: dup: %t qos: %d retain: %t rLength: %d", PacketNames[fh.MessageType], fh.Dup, fh.Qos, fh.Retain, fh.RemainingLength)
|
||||
}
|
||||
|
||||
func boolToByte(b bool) byte {
|
||||
switch b {
|
||||
case true:
|
||||
return 1
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func (fh *FixedHeader) pack() bytes.Buffer {
|
||||
var header bytes.Buffer
|
||||
header.WriteByte(fh.MessageType<<4 | boolToByte(fh.Dup)<<3 | fh.Qos<<1 | boolToByte(fh.Retain))
|
||||
header.Write(encodeLength(fh.RemainingLength))
|
||||
return header
|
||||
}
|
||||
|
||||
func (fh *FixedHeader) unpack(typeAndFlags byte, r io.Reader) error {
|
||||
fh.MessageType = typeAndFlags >> 4
|
||||
fh.Dup = (typeAndFlags>>3)&0x01 > 0
|
||||
fh.Qos = (typeAndFlags >> 1) & 0x03
|
||||
fh.Retain = typeAndFlags&0x01 > 0
|
||||
|
||||
var err error
|
||||
fh.RemainingLength, err = decodeLength(r)
|
||||
return err
|
||||
}
|
||||
|
||||
func decodeByte(b io.Reader) (byte, error) {
|
||||
num := make([]byte, 1)
|
||||
_, err := b.Read(num)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return num[0], nil
|
||||
}
|
||||
|
||||
func decodeUint16(b io.Reader) (uint16, error) {
|
||||
num := make([]byte, 2)
|
||||
_, err := b.Read(num)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return binary.BigEndian.Uint16(num), nil
|
||||
}
|
||||
|
||||
func encodeUint16(num uint16) []byte {
|
||||
bytes := make([]byte, 2)
|
||||
binary.BigEndian.PutUint16(bytes, num)
|
||||
return bytes
|
||||
}
|
||||
|
||||
func encodeString(field string) []byte {
|
||||
return encodeBytes([]byte(field))
|
||||
}
|
||||
|
||||
func decodeString(b io.Reader) (string, error) {
|
||||
buf, err := decodeBytes(b)
|
||||
return string(buf), err
|
||||
}
|
||||
|
||||
func decodeBytes(b io.Reader) ([]byte, error) {
|
||||
fieldLength, err := decodeUint16(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
field := make([]byte, fieldLength)
|
||||
_, err = b.Read(field)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return field, nil
|
||||
}
|
||||
|
||||
func encodeBytes(field []byte) []byte {
|
||||
fieldLength := make([]byte, 2)
|
||||
binary.BigEndian.PutUint16(fieldLength, uint16(len(field)))
|
||||
return append(fieldLength, field...)
|
||||
}
|
||||
|
||||
func encodeLength(length int) []byte {
|
||||
var encLength []byte
|
||||
for {
|
||||
digit := byte(length % 128)
|
||||
length /= 128
|
||||
if length > 0 {
|
||||
digit |= 0x80
|
||||
}
|
||||
encLength = append(encLength, digit)
|
||||
if length == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return encLength
|
||||
}
|
||||
|
||||
func decodeLength(r io.Reader) (int, error) {
|
||||
var rLength uint32
|
||||
var multiplier uint32
|
||||
b := make([]byte, 1)
|
||||
for multiplier < 27 { //fix: Infinite '(digit & 128) == 1' will cause the dead loop
|
||||
_, err := io.ReadFull(r, b)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
digit := b[0]
|
||||
rLength |= uint32(digit&127) << multiplier
|
||||
if (digit & 128) == 0 {
|
||||
break
|
||||
}
|
||||
multiplier += 7
|
||||
}
|
||||
return int(rLength), nil
|
||||
}
|
36
vendor/github.com/eclipse/paho.mqtt.golang/packets/pingreq.go
generated
vendored
Normal file
36
vendor/github.com/eclipse/paho.mqtt.golang/packets/pingreq.go
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
package packets
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
//PingreqPacket is an internal representation of the fields of the
|
||||
//Pingreq MQTT packet
|
||||
type PingreqPacket struct {
|
||||
FixedHeader
|
||||
}
|
||||
|
||||
func (pr *PingreqPacket) String() string {
|
||||
str := fmt.Sprintf("%s", pr.FixedHeader)
|
||||
return str
|
||||
}
|
||||
|
||||
func (pr *PingreqPacket) Write(w io.Writer) error {
|
||||
packet := pr.FixedHeader.pack()
|
||||
_, err := packet.WriteTo(w)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
//Unpack decodes the details of a ControlPacket after the fixed
|
||||
//header has been read
|
||||
func (pr *PingreqPacket) Unpack(b io.Reader) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
//Details returns a Details struct containing the Qos and
|
||||
//MessageID of this ControlPacket
|
||||
func (pr *PingreqPacket) Details() Details {
|
||||
return Details{Qos: 0, MessageID: 0}
|
||||
}
|
36
vendor/github.com/eclipse/paho.mqtt.golang/packets/pingresp.go
generated
vendored
Normal file
36
vendor/github.com/eclipse/paho.mqtt.golang/packets/pingresp.go
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
package packets
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
//PingrespPacket is an internal representation of the fields of the
|
||||
//Pingresp MQTT packet
|
||||
type PingrespPacket struct {
|
||||
FixedHeader
|
||||
}
|
||||
|
||||
func (pr *PingrespPacket) String() string {
|
||||
str := fmt.Sprintf("%s", pr.FixedHeader)
|
||||
return str
|
||||
}
|
||||
|
||||
func (pr *PingrespPacket) Write(w io.Writer) error {
|
||||
packet := pr.FixedHeader.pack()
|
||||
_, err := packet.WriteTo(w)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
//Unpack decodes the details of a ControlPacket after the fixed
|
||||
//header has been read
|
||||
func (pr *PingrespPacket) Unpack(b io.Reader) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
//Details returns a Details struct containing the Qos and
|
||||
//MessageID of this ControlPacket
|
||||
func (pr *PingrespPacket) Details() Details {
|
||||
return Details{Qos: 0, MessageID: 0}
|
||||
}
|
45
vendor/github.com/eclipse/paho.mqtt.golang/packets/puback.go
generated
vendored
Normal file
45
vendor/github.com/eclipse/paho.mqtt.golang/packets/puback.go
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
package packets
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
//PubackPacket is an internal representation of the fields of the
|
||||
//Puback MQTT packet
|
||||
type PubackPacket struct {
|
||||
FixedHeader
|
||||
MessageID uint16
|
||||
}
|
||||
|
||||
func (pa *PubackPacket) String() string {
|
||||
str := fmt.Sprintf("%s", pa.FixedHeader)
|
||||
str += " "
|
||||
str += fmt.Sprintf("MessageID: %d", pa.MessageID)
|
||||
return str
|
||||
}
|
||||
|
||||
func (pa *PubackPacket) Write(w io.Writer) error {
|
||||
var err error
|
||||
pa.FixedHeader.RemainingLength = 2
|
||||
packet := pa.FixedHeader.pack()
|
||||
packet.Write(encodeUint16(pa.MessageID))
|
||||
_, err = packet.WriteTo(w)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
//Unpack decodes the details of a ControlPacket after the fixed
|
||||
//header has been read
|
||||
func (pa *PubackPacket) Unpack(b io.Reader) error {
|
||||
var err error
|
||||
pa.MessageID, err = decodeUint16(b)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
//Details returns a Details struct containing the Qos and
|
||||
//MessageID of this ControlPacket
|
||||
func (pa *PubackPacket) Details() Details {
|
||||
return Details{Qos: pa.Qos, MessageID: pa.MessageID}
|
||||
}
|
45
vendor/github.com/eclipse/paho.mqtt.golang/packets/pubcomp.go
generated
vendored
Normal file
45
vendor/github.com/eclipse/paho.mqtt.golang/packets/pubcomp.go
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
package packets
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
//PubcompPacket is an internal representation of the fields of the
|
||||
//Pubcomp MQTT packet
|
||||
type PubcompPacket struct {
|
||||
FixedHeader
|
||||
MessageID uint16
|
||||
}
|
||||
|
||||
func (pc *PubcompPacket) String() string {
|
||||
str := fmt.Sprintf("%s", pc.FixedHeader)
|
||||
str += " "
|
||||
str += fmt.Sprintf("MessageID: %d", pc.MessageID)
|
||||
return str
|
||||
}
|
||||
|
||||
func (pc *PubcompPacket) Write(w io.Writer) error {
|
||||
var err error
|
||||
pc.FixedHeader.RemainingLength = 2
|
||||
packet := pc.FixedHeader.pack()
|
||||
packet.Write(encodeUint16(pc.MessageID))
|
||||
_, err = packet.WriteTo(w)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
//Unpack decodes the details of a ControlPacket after the fixed
|
||||
//header has been read
|
||||
func (pc *PubcompPacket) Unpack(b io.Reader) error {
|
||||
var err error
|
||||
pc.MessageID, err = decodeUint16(b)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
//Details returns a Details struct containing the Qos and
|
||||
//MessageID of this ControlPacket
|
||||
func (pc *PubcompPacket) Details() Details {
|
||||
return Details{Qos: pc.Qos, MessageID: pc.MessageID}
|
||||
}
|
88
vendor/github.com/eclipse/paho.mqtt.golang/packets/publish.go
generated
vendored
Normal file
88
vendor/github.com/eclipse/paho.mqtt.golang/packets/publish.go
generated
vendored
Normal file
@ -0,0 +1,88 @@
|
||||
package packets
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
//PublishPacket is an internal representation of the fields of the
|
||||
//Publish MQTT packet
|
||||
type PublishPacket struct {
|
||||
FixedHeader
|
||||
TopicName string
|
||||
MessageID uint16
|
||||
Payload []byte
|
||||
}
|
||||
|
||||
func (p *PublishPacket) String() string {
|
||||
str := fmt.Sprintf("%s", p.FixedHeader)
|
||||
str += " "
|
||||
str += fmt.Sprintf("topicName: %s MessageID: %d", p.TopicName, p.MessageID)
|
||||
str += " "
|
||||
str += fmt.Sprintf("payload: %s", string(p.Payload))
|
||||
return str
|
||||
}
|
||||
|
||||
func (p *PublishPacket) Write(w io.Writer) error {
|
||||
var body bytes.Buffer
|
||||
var err error
|
||||
|
||||
body.Write(encodeString(p.TopicName))
|
||||
if p.Qos > 0 {
|
||||
body.Write(encodeUint16(p.MessageID))
|
||||
}
|
||||
p.FixedHeader.RemainingLength = body.Len() + len(p.Payload)
|
||||
packet := p.FixedHeader.pack()
|
||||
packet.Write(body.Bytes())
|
||||
packet.Write(p.Payload)
|
||||
_, err = w.Write(packet.Bytes())
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
//Unpack decodes the details of a ControlPacket after the fixed
|
||||
//header has been read
|
||||
func (p *PublishPacket) Unpack(b io.Reader) error {
|
||||
var payloadLength = p.FixedHeader.RemainingLength
|
||||
var err error
|
||||
p.TopicName, err = decodeString(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if p.Qos > 0 {
|
||||
p.MessageID, err = decodeUint16(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
payloadLength -= len(p.TopicName) + 4
|
||||
} else {
|
||||
payloadLength -= len(p.TopicName) + 2
|
||||
}
|
||||
if payloadLength < 0 {
|
||||
return fmt.Errorf("Error unpacking publish, payload length < 0")
|
||||
}
|
||||
p.Payload = make([]byte, payloadLength)
|
||||
_, err = b.Read(p.Payload)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
//Copy creates a new PublishPacket with the same topic and payload
|
||||
//but an empty fixed header, useful for when you want to deliver
|
||||
//a message with different properties such as Qos but the same
|
||||
//content
|
||||
func (p *PublishPacket) Copy() *PublishPacket {
|
||||
newP := NewControlPacket(Publish).(*PublishPacket)
|
||||
newP.TopicName = p.TopicName
|
||||
newP.Payload = p.Payload
|
||||
|
||||
return newP
|
||||
}
|
||||
|
||||
//Details returns a Details struct containing the Qos and
|
||||
//MessageID of this ControlPacket
|
||||
func (p *PublishPacket) Details() Details {
|
||||
return Details{Qos: p.Qos, MessageID: p.MessageID}
|
||||
}
|
45
vendor/github.com/eclipse/paho.mqtt.golang/packets/pubrec.go
generated
vendored
Normal file
45
vendor/github.com/eclipse/paho.mqtt.golang/packets/pubrec.go
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
package packets
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
//PubrecPacket is an internal representation of the fields of the
|
||||
//Pubrec MQTT packet
|
||||
type PubrecPacket struct {
|
||||
FixedHeader
|
||||
MessageID uint16
|
||||
}
|
||||
|
||||
func (pr *PubrecPacket) String() string {
|
||||
str := fmt.Sprintf("%s", pr.FixedHeader)
|
||||
str += " "
|
||||
str += fmt.Sprintf("MessageID: %d", pr.MessageID)
|
||||
return str
|
||||
}
|
||||
|
||||
func (pr *PubrecPacket) Write(w io.Writer) error {
|
||||
var err error
|
||||
pr.FixedHeader.RemainingLength = 2
|
||||
packet := pr.FixedHeader.pack()
|
||||
packet.Write(encodeUint16(pr.MessageID))
|
||||
_, err = packet.WriteTo(w)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
//Unpack decodes the details of a ControlPacket after the fixed
|
||||
//header has been read
|
||||
func (pr *PubrecPacket) Unpack(b io.Reader) error {
|
||||
var err error
|
||||
pr.MessageID, err = decodeUint16(b)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
//Details returns a Details struct containing the Qos and
|
||||
//MessageID of this ControlPacket
|
||||
func (pr *PubrecPacket) Details() Details {
|
||||
return Details{Qos: pr.Qos, MessageID: pr.MessageID}
|
||||
}
|
45
vendor/github.com/eclipse/paho.mqtt.golang/packets/pubrel.go
generated
vendored
Normal file
45
vendor/github.com/eclipse/paho.mqtt.golang/packets/pubrel.go
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
package packets
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
//PubrelPacket is an internal representation of the fields of the
|
||||
//Pubrel MQTT packet
|
||||
type PubrelPacket struct {
|
||||
FixedHeader
|
||||
MessageID uint16
|
||||
}
|
||||
|
||||
func (pr *PubrelPacket) String() string {
|
||||
str := fmt.Sprintf("%s", pr.FixedHeader)
|
||||
str += " "
|
||||
str += fmt.Sprintf("MessageID: %d", pr.MessageID)
|
||||
return str
|
||||
}
|
||||
|
||||
func (pr *PubrelPacket) Write(w io.Writer) error {
|
||||
var err error
|
||||
pr.FixedHeader.RemainingLength = 2
|
||||
packet := pr.FixedHeader.pack()
|
||||
packet.Write(encodeUint16(pr.MessageID))
|
||||
_, err = packet.WriteTo(w)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
//Unpack decodes the details of a ControlPacket after the fixed
|
||||
//header has been read
|
||||
func (pr *PubrelPacket) Unpack(b io.Reader) error {
|
||||
var err error
|
||||
pr.MessageID, err = decodeUint16(b)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
//Details returns a Details struct containing the Qos and
|
||||
//MessageID of this ControlPacket
|
||||
func (pr *PubrelPacket) Details() Details {
|
||||
return Details{Qos: pr.Qos, MessageID: pr.MessageID}
|
||||
}
|
60
vendor/github.com/eclipse/paho.mqtt.golang/packets/suback.go
generated
vendored
Normal file
60
vendor/github.com/eclipse/paho.mqtt.golang/packets/suback.go
generated
vendored
Normal file
@ -0,0 +1,60 @@
|
||||
package packets
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
//SubackPacket is an internal representation of the fields of the
|
||||
//Suback MQTT packet
|
||||
type SubackPacket struct {
|
||||
FixedHeader
|
||||
MessageID uint16
|
||||
ReturnCodes []byte
|
||||
}
|
||||
|
||||
func (sa *SubackPacket) String() string {
|
||||
str := fmt.Sprintf("%s", sa.FixedHeader)
|
||||
str += " "
|
||||
str += fmt.Sprintf("MessageID: %d", sa.MessageID)
|
||||
return str
|
||||
}
|
||||
|
||||
func (sa *SubackPacket) Write(w io.Writer) error {
|
||||
var body bytes.Buffer
|
||||
var err error
|
||||
body.Write(encodeUint16(sa.MessageID))
|
||||
body.Write(sa.ReturnCodes)
|
||||
sa.FixedHeader.RemainingLength = body.Len()
|
||||
packet := sa.FixedHeader.pack()
|
||||
packet.Write(body.Bytes())
|
||||
_, err = packet.WriteTo(w)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
//Unpack decodes the details of a ControlPacket after the fixed
|
||||
//header has been read
|
||||
func (sa *SubackPacket) Unpack(b io.Reader) error {
|
||||
var qosBuffer bytes.Buffer
|
||||
var err error
|
||||
sa.MessageID, err = decodeUint16(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = qosBuffer.ReadFrom(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sa.ReturnCodes = qosBuffer.Bytes()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//Details returns a Details struct containing the Qos and
|
||||
//MessageID of this ControlPacket
|
||||
func (sa *SubackPacket) Details() Details {
|
||||
return Details{Qos: 0, MessageID: sa.MessageID}
|
||||
}
|
72
vendor/github.com/eclipse/paho.mqtt.golang/packets/subscribe.go
generated
vendored
Normal file
72
vendor/github.com/eclipse/paho.mqtt.golang/packets/subscribe.go
generated
vendored
Normal file
@ -0,0 +1,72 @@
|
||||
package packets
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
//SubscribePacket is an internal representation of the fields of the
|
||||
//Subscribe MQTT packet
|
||||
type SubscribePacket struct {
|
||||
FixedHeader
|
||||
MessageID uint16
|
||||
Topics []string
|
||||
Qoss []byte
|
||||
}
|
||||
|
||||
func (s *SubscribePacket) String() string {
|
||||
str := fmt.Sprintf("%s", s.FixedHeader)
|
||||
str += " "
|
||||
str += fmt.Sprintf("MessageID: %d topics: %s", s.MessageID, s.Topics)
|
||||
return str
|
||||
}
|
||||
|
||||
func (s *SubscribePacket) Write(w io.Writer) error {
|
||||
var body bytes.Buffer
|
||||
var err error
|
||||
|
||||
body.Write(encodeUint16(s.MessageID))
|
||||
for i, topic := range s.Topics {
|
||||
body.Write(encodeString(topic))
|
||||
body.WriteByte(s.Qoss[i])
|
||||
}
|
||||
s.FixedHeader.RemainingLength = body.Len()
|
||||
packet := s.FixedHeader.pack()
|
||||
packet.Write(body.Bytes())
|
||||
_, err = packet.WriteTo(w)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
//Unpack decodes the details of a ControlPacket after the fixed
|
||||
//header has been read
|
||||
func (s *SubscribePacket) Unpack(b io.Reader) error {
|
||||
var err error
|
||||
s.MessageID, err = decodeUint16(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
payloadLength := s.FixedHeader.RemainingLength - 2
|
||||
for payloadLength > 0 {
|
||||
topic, err := decodeString(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.Topics = append(s.Topics, topic)
|
||||
qos, err := decodeByte(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.Qoss = append(s.Qoss, qos)
|
||||
payloadLength -= 2 + len(topic) + 1 //2 bytes of string length, plus string, plus 1 byte for Qos
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//Details returns a Details struct containing the Qos and
|
||||
//MessageID of this ControlPacket
|
||||
func (s *SubscribePacket) Details() Details {
|
||||
return Details{Qos: 1, MessageID: s.MessageID}
|
||||
}
|
45
vendor/github.com/eclipse/paho.mqtt.golang/packets/unsuback.go
generated
vendored
Normal file
45
vendor/github.com/eclipse/paho.mqtt.golang/packets/unsuback.go
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
package packets
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
//UnsubackPacket is an internal representation of the fields of the
|
||||
//Unsuback MQTT packet
|
||||
type UnsubackPacket struct {
|
||||
FixedHeader
|
||||
MessageID uint16
|
||||
}
|
||||
|
||||
func (ua *UnsubackPacket) String() string {
|
||||
str := fmt.Sprintf("%s", ua.FixedHeader)
|
||||
str += " "
|
||||
str += fmt.Sprintf("MessageID: %d", ua.MessageID)
|
||||
return str
|
||||
}
|
||||
|
||||
func (ua *UnsubackPacket) Write(w io.Writer) error {
|
||||
var err error
|
||||
ua.FixedHeader.RemainingLength = 2
|
||||
packet := ua.FixedHeader.pack()
|
||||
packet.Write(encodeUint16(ua.MessageID))
|
||||
_, err = packet.WriteTo(w)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
//Unpack decodes the details of a ControlPacket after the fixed
|
||||
//header has been read
|
||||
func (ua *UnsubackPacket) Unpack(b io.Reader) error {
|
||||
var err error
|
||||
ua.MessageID, err = decodeUint16(b)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
//Details returns a Details struct containing the Qos and
|
||||
//MessageID of this ControlPacket
|
||||
func (ua *UnsubackPacket) Details() Details {
|
||||
return Details{Qos: 0, MessageID: ua.MessageID}
|
||||
}
|
59
vendor/github.com/eclipse/paho.mqtt.golang/packets/unsubscribe.go
generated
vendored
Normal file
59
vendor/github.com/eclipse/paho.mqtt.golang/packets/unsubscribe.go
generated
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
package packets
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
//UnsubscribePacket is an internal representation of the fields of the
|
||||
//Unsubscribe MQTT packet
|
||||
type UnsubscribePacket struct {
|
||||
FixedHeader
|
||||
MessageID uint16
|
||||
Topics []string
|
||||
}
|
||||
|
||||
func (u *UnsubscribePacket) String() string {
|
||||
str := fmt.Sprintf("%s", u.FixedHeader)
|
||||
str += " "
|
||||
str += fmt.Sprintf("MessageID: %d", u.MessageID)
|
||||
return str
|
||||
}
|
||||
|
||||
func (u *UnsubscribePacket) Write(w io.Writer) error {
|
||||
var body bytes.Buffer
|
||||
var err error
|
||||
body.Write(encodeUint16(u.MessageID))
|
||||
for _, topic := range u.Topics {
|
||||
body.Write(encodeString(topic))
|
||||
}
|
||||
u.FixedHeader.RemainingLength = body.Len()
|
||||
packet := u.FixedHeader.pack()
|
||||
packet.Write(body.Bytes())
|
||||
_, err = packet.WriteTo(w)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
//Unpack decodes the details of a ControlPacket after the fixed
|
||||
//header has been read
|
||||
func (u *UnsubscribePacket) Unpack(b io.Reader) error {
|
||||
var err error
|
||||
u.MessageID, err = decodeUint16(b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for topic, err := decodeString(b); err == nil && topic != ""; topic, err = decodeString(b) {
|
||||
u.Topics = append(u.Topics, topic)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
//Details returns a Details struct containing the Qos and
|
||||
//MessageID of this ControlPacket
|
||||
func (u *UnsubscribePacket) Details() Details {
|
||||
return Details{Qos: 1, MessageID: u.MessageID}
|
||||
}
|
69
vendor/github.com/eclipse/paho.mqtt.golang/ping.go
generated
vendored
Normal file
69
vendor/github.com/eclipse/paho.mqtt.golang/ping.go
generated
vendored
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (c) 2013 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Seth Hoenig
|
||||
* Allan Stockdill-Mander
|
||||
* Mike Robertson
|
||||
*/
|
||||
|
||||
package mqtt
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/eclipse/paho.mqtt.golang/packets"
|
||||
)
|
||||
|
||||
func keepalive(c *client) {
|
||||
defer c.workers.Done()
|
||||
DEBUG.Println(PNG, "keepalive starting")
|
||||
var checkInterval int64
|
||||
var pingSent time.Time
|
||||
|
||||
if c.options.KeepAlive > 10 {
|
||||
checkInterval = 5
|
||||
} else {
|
||||
checkInterval = c.options.KeepAlive / 2
|
||||
}
|
||||
|
||||
intervalTicker := time.NewTicker(time.Duration(checkInterval * int64(time.Second)))
|
||||
defer intervalTicker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-c.stop:
|
||||
DEBUG.Println(PNG, "keepalive stopped")
|
||||
return
|
||||
case <-intervalTicker.C:
|
||||
lastSent := c.lastSent.Load().(time.Time)
|
||||
lastReceived := c.lastReceived.Load().(time.Time)
|
||||
|
||||
DEBUG.Println(PNG, "ping check", time.Since(lastSent).Seconds())
|
||||
if time.Since(lastSent) >= time.Duration(c.options.KeepAlive*int64(time.Second)) || time.Since(lastReceived) >= time.Duration(c.options.KeepAlive*int64(time.Second)) {
|
||||
if atomic.LoadInt32(&c.pingOutstanding) == 0 {
|
||||
DEBUG.Println(PNG, "keepalive sending ping")
|
||||
ping := packets.NewControlPacket(packets.Pingreq).(*packets.PingreqPacket)
|
||||
//We don't want to wait behind large messages being sent, the Write call
|
||||
//will block until it it able to send the packet.
|
||||
atomic.StoreInt32(&c.pingOutstanding, 1)
|
||||
ping.Write(c.conn)
|
||||
c.lastSent.Store(time.Now())
|
||||
pingSent = time.Now()
|
||||
}
|
||||
}
|
||||
if atomic.LoadInt32(&c.pingOutstanding) > 0 && time.Now().Sub(pingSent) >= c.options.PingTimeout {
|
||||
CRITICAL.Println(PNG, "pingresp not received, disconnecting")
|
||||
c.errors <- errors.New("pingresp not received, disconnecting")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
187
vendor/github.com/eclipse/paho.mqtt.golang/router.go
generated
vendored
Normal file
187
vendor/github.com/eclipse/paho.mqtt.golang/router.go
generated
vendored
Normal file
@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Copyright (c) 2013 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Seth Hoenig
|
||||
* Allan Stockdill-Mander
|
||||
* Mike Robertson
|
||||
*/
|
||||
|
||||
package mqtt
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/eclipse/paho.mqtt.golang/packets"
|
||||
)
|
||||
|
||||
// route is a type which associates MQTT Topic strings with a
|
||||
// callback to be executed upon the arrival of a message associated
|
||||
// with a subscription to that topic.
|
||||
type route struct {
|
||||
topic string
|
||||
callback MessageHandler
|
||||
}
|
||||
|
||||
// match takes a slice of strings which represent the route being tested having been split on '/'
|
||||
// separators, and a slice of strings representing the topic string in the published message, similarly
|
||||
// split.
|
||||
// The function determines if the topic string matches the route according to the MQTT topic rules
|
||||
// and returns a boolean of the outcome
|
||||
func match(route []string, topic []string) bool {
|
||||
if len(route) == 0 {
|
||||
if len(topic) == 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if len(topic) == 0 {
|
||||
if route[0] == "#" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if route[0] == "#" {
|
||||
return true
|
||||
}
|
||||
|
||||
if (route[0] == "+") || (route[0] == topic[0]) {
|
||||
return match(route[1:], topic[1:])
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func routeIncludesTopic(route, topic string) bool {
|
||||
return match(routeSplit(route), strings.Split(topic, "/"))
|
||||
}
|
||||
|
||||
// removes $share and sharename when splitting the route to allow
|
||||
// shared subscription routes to correctly match the topic
|
||||
func routeSplit(route string) []string {
|
||||
var result []string
|
||||
if strings.HasPrefix(route, "$share") {
|
||||
result = strings.Split(route, "/")[2:]
|
||||
} else {
|
||||
result = strings.Split(route, "/")
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// match takes the topic string of the published message and does a basic compare to the
|
||||
// string of the current Route, if they match it returns true
|
||||
func (r *route) match(topic string) bool {
|
||||
return r.topic == topic || routeIncludesTopic(r.topic, topic)
|
||||
}
|
||||
|
||||
type router struct {
|
||||
sync.RWMutex
|
||||
routes *list.List
|
||||
defaultHandler MessageHandler
|
||||
messages chan *packets.PublishPacket
|
||||
stop chan bool
|
||||
}
|
||||
|
||||
// newRouter returns a new instance of a Router and channel which can be used to tell the Router
|
||||
// to stop
|
||||
func newRouter() (*router, chan bool) {
|
||||
router := &router{routes: list.New(), messages: make(chan *packets.PublishPacket), stop: make(chan bool)}
|
||||
stop := router.stop
|
||||
return router, stop
|
||||
}
|
||||
|
||||
// addRoute takes a topic string and MessageHandler callback. It looks in the current list of
|
||||
// routes to see if there is already a matching Route. If there is it replaces the current
|
||||
// callback with the new one. If not it add a new entry to the list of Routes.
|
||||
func (r *router) addRoute(topic string, callback MessageHandler) {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
for e := r.routes.Front(); e != nil; e = e.Next() {
|
||||
if e.Value.(*route).match(topic) {
|
||||
r := e.Value.(*route)
|
||||
r.callback = callback
|
||||
return
|
||||
}
|
||||
}
|
||||
r.routes.PushBack(&route{topic: topic, callback: callback})
|
||||
}
|
||||
|
||||
// deleteRoute takes a route string, looks for a matching Route in the list of Routes. If
|
||||
// found it removes the Route from the list.
|
||||
func (r *router) deleteRoute(topic string) {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
for e := r.routes.Front(); e != nil; e = e.Next() {
|
||||
if e.Value.(*route).match(topic) {
|
||||
r.routes.Remove(e)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// setDefaultHandler assigns a default callback that will be called if no matching Route
|
||||
// is found for an incoming Publish.
|
||||
func (r *router) setDefaultHandler(handler MessageHandler) {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
r.defaultHandler = handler
|
||||
}
|
||||
|
||||
// matchAndDispatch takes a channel of Message pointers as input and starts a go routine that
|
||||
// takes messages off the channel, matches them against the internal route list and calls the
|
||||
// associated callback (or the defaultHandler, if one exists and no other route matched). If
|
||||
// anything is sent down the stop channel the function will end.
|
||||
func (r *router) matchAndDispatch(messages <-chan *packets.PublishPacket, order bool, client *client) {
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case message := <-messages:
|
||||
sent := false
|
||||
r.RLock()
|
||||
m := messageFromPublish(message, client.ackFunc(message))
|
||||
handlers := []MessageHandler{}
|
||||
for e := r.routes.Front(); e != nil; e = e.Next() {
|
||||
if e.Value.(*route).match(message.TopicName) {
|
||||
if order {
|
||||
handlers = append(handlers, e.Value.(*route).callback)
|
||||
} else {
|
||||
hd := e.Value.(*route).callback
|
||||
go func() {
|
||||
hd(client, m)
|
||||
m.Ack()
|
||||
}()
|
||||
}
|
||||
sent = true
|
||||
}
|
||||
}
|
||||
if !sent && r.defaultHandler != nil {
|
||||
if order {
|
||||
handlers = append(handlers, r.defaultHandler)
|
||||
} else {
|
||||
go func() {
|
||||
r.defaultHandler(client, m)
|
||||
m.Ack()
|
||||
}()
|
||||
}
|
||||
}
|
||||
r.RUnlock()
|
||||
for _, handler := range handlers {
|
||||
func() {
|
||||
handler(client, m)
|
||||
m.Ack()
|
||||
}()
|
||||
}
|
||||
case <-r.stop:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
136
vendor/github.com/eclipse/paho.mqtt.golang/store.go
generated
vendored
Normal file
136
vendor/github.com/eclipse/paho.mqtt.golang/store.go
generated
vendored
Normal file
@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright (c) 2013 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Seth Hoenig
|
||||
* Allan Stockdill-Mander
|
||||
* Mike Robertson
|
||||
*/
|
||||
|
||||
package mqtt
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/eclipse/paho.mqtt.golang/packets"
|
||||
)
|
||||
|
||||
const (
|
||||
inboundPrefix = "i."
|
||||
outboundPrefix = "o."
|
||||
)
|
||||
|
||||
// Store is an interface which can be used to provide implementations
|
||||
// for message persistence.
|
||||
// Because we may have to store distinct messages with the same
|
||||
// message ID, we need a unique key for each message. This is
|
||||
// possible by prepending "i." or "o." to each message id
|
||||
type Store interface {
|
||||
Open()
|
||||
Put(key string, message packets.ControlPacket)
|
||||
Get(key string) packets.ControlPacket
|
||||
All() []string
|
||||
Del(key string)
|
||||
Close()
|
||||
Reset()
|
||||
}
|
||||
|
||||
// A key MUST have the form "X.[messageid]"
|
||||
// where X is 'i' or 'o'
|
||||
func mIDFromKey(key string) uint16 {
|
||||
s := key[2:]
|
||||
i, err := strconv.Atoi(s)
|
||||
chkerr(err)
|
||||
return uint16(i)
|
||||
}
|
||||
|
||||
// Return true if key prefix is outbound
|
||||
func isKeyOutbound(key string) bool {
|
||||
return key[:2] == outboundPrefix
|
||||
}
|
||||
|
||||
// Return true if key prefix is inbound
|
||||
func isKeyInbound(key string) bool {
|
||||
return key[:2] == inboundPrefix
|
||||
}
|
||||
|
||||
// Return a string of the form "i.[id]"
|
||||
func inboundKeyFromMID(id uint16) string {
|
||||
return fmt.Sprintf("%s%d", inboundPrefix, id)
|
||||
}
|
||||
|
||||
// Return a string of the form "o.[id]"
|
||||
func outboundKeyFromMID(id uint16) string {
|
||||
return fmt.Sprintf("%s%d", outboundPrefix, id)
|
||||
}
|
||||
|
||||
// govern which outgoing messages are persisted
|
||||
func persistOutbound(s Store, m packets.ControlPacket) {
|
||||
switch m.Details().Qos {
|
||||
case 0:
|
||||
switch m.(type) {
|
||||
case *packets.PubackPacket, *packets.PubcompPacket:
|
||||
// Sending puback. delete matching publish
|
||||
// from ibound
|
||||
s.Del(inboundKeyFromMID(m.Details().MessageID))
|
||||
}
|
||||
case 1:
|
||||
switch m.(type) {
|
||||
case *packets.PublishPacket, *packets.PubrelPacket, *packets.SubscribePacket, *packets.UnsubscribePacket:
|
||||
// Sending publish. store in obound
|
||||
// until puback received
|
||||
s.Put(outboundKeyFromMID(m.Details().MessageID), m)
|
||||
default:
|
||||
ERROR.Println(STR, "Asked to persist an invalid message type")
|
||||
}
|
||||
case 2:
|
||||
switch m.(type) {
|
||||
case *packets.PublishPacket:
|
||||
// Sending publish. store in obound
|
||||
// until pubrel received
|
||||
s.Put(outboundKeyFromMID(m.Details().MessageID), m)
|
||||
default:
|
||||
ERROR.Println(STR, "Asked to persist an invalid message type")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// govern which incoming messages are persisted
|
||||
func persistInbound(s Store, m packets.ControlPacket) {
|
||||
switch m.Details().Qos {
|
||||
case 0:
|
||||
switch m.(type) {
|
||||
case *packets.PubackPacket, *packets.SubackPacket, *packets.UnsubackPacket, *packets.PubcompPacket:
|
||||
// Received a puback. delete matching publish
|
||||
// from obound
|
||||
s.Del(outboundKeyFromMID(m.Details().MessageID))
|
||||
case *packets.PublishPacket, *packets.PubrecPacket, *packets.PingrespPacket, *packets.ConnackPacket:
|
||||
default:
|
||||
ERROR.Println(STR, "Asked to persist an invalid messages type")
|
||||
}
|
||||
case 1:
|
||||
switch m.(type) {
|
||||
case *packets.PublishPacket, *packets.PubrelPacket:
|
||||
// Received a publish. store it in ibound
|
||||
// until puback sent
|
||||
s.Put(inboundKeyFromMID(m.Details().MessageID), m)
|
||||
default:
|
||||
ERROR.Println(STR, "Asked to persist an invalid messages type")
|
||||
}
|
||||
case 2:
|
||||
switch m.(type) {
|
||||
case *packets.PublishPacket:
|
||||
// Received a publish. store it in ibound
|
||||
// until pubrel received
|
||||
s.Put(inboundKeyFromMID(m.Details().MessageID), m)
|
||||
default:
|
||||
ERROR.Println(STR, "Asked to persist an invalid messages type")
|
||||
}
|
||||
}
|
||||
}
|
184
vendor/github.com/eclipse/paho.mqtt.golang/token.go
generated
vendored
Normal file
184
vendor/github.com/eclipse/paho.mqtt.golang/token.go
generated
vendored
Normal file
@ -0,0 +1,184 @@
|
||||
/*
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Allan Stockdill-Mander
|
||||
*/
|
||||
|
||||
package mqtt
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/eclipse/paho.mqtt.golang/packets"
|
||||
)
|
||||
|
||||
// PacketAndToken is a struct that contains both a ControlPacket and a
|
||||
// Token. This struct is passed via channels between the client interface
|
||||
// code and the underlying code responsible for sending and receiving
|
||||
// MQTT messages.
|
||||
type PacketAndToken struct {
|
||||
p packets.ControlPacket
|
||||
t tokenCompletor
|
||||
}
|
||||
|
||||
// Token defines the interface for the tokens used to indicate when
|
||||
// actions have completed.
|
||||
type Token interface {
|
||||
Wait() bool
|
||||
WaitTimeout(time.Duration) bool
|
||||
Error() error
|
||||
}
|
||||
|
||||
type TokenErrorSetter interface {
|
||||
setError(error)
|
||||
}
|
||||
|
||||
type tokenCompletor interface {
|
||||
Token
|
||||
TokenErrorSetter
|
||||
flowComplete()
|
||||
}
|
||||
|
||||
type baseToken struct {
|
||||
m sync.RWMutex
|
||||
complete chan struct{}
|
||||
err error
|
||||
}
|
||||
|
||||
// Wait will wait indefinitely for the Token to complete, ie the Publish
|
||||
// to be sent and confirmed receipt from the broker
|
||||
func (b *baseToken) Wait() bool {
|
||||
<-b.complete
|
||||
return true
|
||||
}
|
||||
|
||||
// WaitTimeout takes a time.Duration to wait for the flow associated with the
|
||||
// Token to complete, returns true if it returned before the timeout or
|
||||
// returns false if the timeout occurred. In the case of a timeout the Token
|
||||
// does not have an error set in case the caller wishes to wait again
|
||||
func (b *baseToken) WaitTimeout(d time.Duration) bool {
|
||||
b.m.Lock()
|
||||
defer b.m.Unlock()
|
||||
|
||||
timer := time.NewTimer(d)
|
||||
select {
|
||||
case <-b.complete:
|
||||
if !timer.Stop() {
|
||||
<-timer.C
|
||||
}
|
||||
return true
|
||||
case <-timer.C:
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (b *baseToken) flowComplete() {
|
||||
select {
|
||||
case <-b.complete:
|
||||
default:
|
||||
close(b.complete)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *baseToken) Error() error {
|
||||
b.m.RLock()
|
||||
defer b.m.RUnlock()
|
||||
return b.err
|
||||
}
|
||||
|
||||
func (b *baseToken) setError(e error) {
|
||||
b.m.Lock()
|
||||
b.err = e
|
||||
b.flowComplete()
|
||||
b.m.Unlock()
|
||||
}
|
||||
|
||||
func newToken(tType byte) tokenCompletor {
|
||||
switch tType {
|
||||
case packets.Connect:
|
||||
return &ConnectToken{baseToken: baseToken{complete: make(chan struct{})}}
|
||||
case packets.Subscribe:
|
||||
return &SubscribeToken{baseToken: baseToken{complete: make(chan struct{})}, subResult: make(map[string]byte)}
|
||||
case packets.Publish:
|
||||
return &PublishToken{baseToken: baseToken{complete: make(chan struct{})}}
|
||||
case packets.Unsubscribe:
|
||||
return &UnsubscribeToken{baseToken: baseToken{complete: make(chan struct{})}}
|
||||
case packets.Disconnect:
|
||||
return &DisconnectToken{baseToken: baseToken{complete: make(chan struct{})}}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ConnectToken is an extension of Token containing the extra fields
|
||||
// required to provide information about calls to Connect()
|
||||
type ConnectToken struct {
|
||||
baseToken
|
||||
returnCode byte
|
||||
sessionPresent bool
|
||||
}
|
||||
|
||||
// ReturnCode returns the acknowlegement code in the connack sent
|
||||
// in response to a Connect()
|
||||
func (c *ConnectToken) ReturnCode() byte {
|
||||
c.m.RLock()
|
||||
defer c.m.RUnlock()
|
||||
return c.returnCode
|
||||
}
|
||||
|
||||
// SessionPresent returns a bool representing the value of the
|
||||
// session present field in the connack sent in response to a Connect()
|
||||
func (c *ConnectToken) SessionPresent() bool {
|
||||
c.m.RLock()
|
||||
defer c.m.RUnlock()
|
||||
return c.sessionPresent
|
||||
}
|
||||
|
||||
// PublishToken is an extension of Token containing the extra fields
|
||||
// required to provide information about calls to Publish()
|
||||
type PublishToken struct {
|
||||
baseToken
|
||||
messageID uint16
|
||||
}
|
||||
|
||||
// MessageID returns the MQTT message ID that was assigned to the
|
||||
// Publish packet when it was sent to the broker
|
||||
func (p *PublishToken) MessageID() uint16 {
|
||||
return p.messageID
|
||||
}
|
||||
|
||||
// SubscribeToken is an extension of Token containing the extra fields
|
||||
// required to provide information about calls to Subscribe()
|
||||
type SubscribeToken struct {
|
||||
baseToken
|
||||
subs []string
|
||||
subResult map[string]byte
|
||||
}
|
||||
|
||||
// Result returns a map of topics that were subscribed to along with
|
||||
// the matching return code from the broker. This is either the Qos
|
||||
// value of the subscription or an error code.
|
||||
func (s *SubscribeToken) Result() map[string]byte {
|
||||
s.m.RLock()
|
||||
defer s.m.RUnlock()
|
||||
return s.subResult
|
||||
}
|
||||
|
||||
// UnsubscribeToken is an extension of Token containing the extra fields
|
||||
// required to provide information about calls to Unsubscribe()
|
||||
type UnsubscribeToken struct {
|
||||
baseToken
|
||||
}
|
||||
|
||||
// DisconnectToken is an extension of Token containing the extra fields
|
||||
// required to provide information about calls to Disconnect()
|
||||
type DisconnectToken struct {
|
||||
baseToken
|
||||
}
|
82
vendor/github.com/eclipse/paho.mqtt.golang/topic.go
generated
vendored
Normal file
82
vendor/github.com/eclipse/paho.mqtt.golang/topic.go
generated
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (c) 2014 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Seth Hoenig
|
||||
* Allan Stockdill-Mander
|
||||
* Mike Robertson
|
||||
*/
|
||||
|
||||
package mqtt
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
//ErrInvalidQos is the error returned when an packet is to be sent
|
||||
//with an invalid Qos value
|
||||
var ErrInvalidQos = errors.New("Invalid QoS")
|
||||
|
||||
//ErrInvalidTopicEmptyString is the error returned when a topic string
|
||||
//is passed in that is 0 length
|
||||
var ErrInvalidTopicEmptyString = errors.New("Invalid Topic; empty string")
|
||||
|
||||
//ErrInvalidTopicMultilevel is the error returned when a topic string
|
||||
//is passed in that has the multi level wildcard in any position but
|
||||
//the last
|
||||
var ErrInvalidTopicMultilevel = errors.New("Invalid Topic; multi-level wildcard must be last level")
|
||||
|
||||
// Topic Names and Topic Filters
|
||||
// The MQTT v3.1.1 spec clarifies a number of ambiguities with regard
|
||||
// to the validity of Topic strings.
|
||||
// - A Topic must be between 1 and 65535 bytes.
|
||||
// - A Topic is case sensitive.
|
||||
// - A Topic may contain whitespace.
|
||||
// - A Topic containing a leading forward slash is different than a Topic without.
|
||||
// - A Topic may be "/" (two levels, both empty string).
|
||||
// - A Topic must be UTF-8 encoded.
|
||||
// - A Topic may contain any number of levels.
|
||||
// - A Topic may contain an empty level (two forward slashes in a row).
|
||||
// - A TopicName may not contain a wildcard.
|
||||
// - A TopicFilter may only have a # (multi-level) wildcard as the last level.
|
||||
// - A TopicFilter may contain any number of + (single-level) wildcards.
|
||||
// - A TopicFilter with a # will match the absense of a level
|
||||
// Example: a subscription to "foo/#" will match messages published to "foo".
|
||||
|
||||
func validateSubscribeMap(subs map[string]byte) ([]string, []byte, error) {
|
||||
var topics []string
|
||||
var qoss []byte
|
||||
for topic, qos := range subs {
|
||||
if err := validateTopicAndQos(topic, qos); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
topics = append(topics, topic)
|
||||
qoss = append(qoss, qos)
|
||||
}
|
||||
|
||||
return topics, qoss, nil
|
||||
}
|
||||
|
||||
func validateTopicAndQos(topic string, qos byte) error {
|
||||
if len(topic) == 0 {
|
||||
return ErrInvalidTopicEmptyString
|
||||
}
|
||||
|
||||
levels := strings.Split(topic, "/")
|
||||
for i, level := range levels {
|
||||
if level == "#" && i != len(levels)-1 {
|
||||
return ErrInvalidTopicMultilevel
|
||||
}
|
||||
}
|
||||
|
||||
if qos < 0 || qos > 2 {
|
||||
return ErrInvalidQos
|
||||
}
|
||||
return nil
|
||||
}
|
40
vendor/github.com/eclipse/paho.mqtt.golang/trace.go
generated
vendored
Normal file
40
vendor/github.com/eclipse/paho.mqtt.golang/trace.go
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) 2013 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Seth Hoenig
|
||||
* Allan Stockdill-Mander
|
||||
* Mike Robertson
|
||||
*/
|
||||
|
||||
package mqtt
|
||||
|
||||
type (
|
||||
// Logger interface allows implementations to provide to this package any
|
||||
// object that implements the methods defined in it.
|
||||
Logger interface {
|
||||
Println(v ...interface{})
|
||||
Printf(format string, v ...interface{})
|
||||
}
|
||||
|
||||
// NOOPLogger implements the logger that does not perform any operation
|
||||
// by default. This allows us to efficiently discard the unwanted messages.
|
||||
NOOPLogger struct{}
|
||||
)
|
||||
|
||||
func (NOOPLogger) Println(v ...interface{}) {}
|
||||
func (NOOPLogger) Printf(format string, v ...interface{}) {}
|
||||
|
||||
// Internal levels of library output that are initialised to not print
|
||||
// anything but can be overridden by programmer
|
||||
var (
|
||||
ERROR Logger = NOOPLogger{}
|
||||
CRITICAL Logger = NOOPLogger{}
|
||||
WARN Logger = NOOPLogger{}
|
||||
DEBUG Logger = NOOPLogger{}
|
||||
)
|
3
vendor/golang.org/x/net/AUTHORS
generated
vendored
Normal file
3
vendor/golang.org/x/net/AUTHORS
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
# This source code refers to The Go Authors for copyright purposes.
|
||||
# The master list of authors is in the main Go distribution,
|
||||
# visible at http://tip.golang.org/AUTHORS.
|
3
vendor/golang.org/x/net/CONTRIBUTORS
generated
vendored
Normal file
3
vendor/golang.org/x/net/CONTRIBUTORS
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
# This source code was written by the Go contributors.
|
||||
# The master list of contributors is in the main Go distribution,
|
||||
# visible at http://tip.golang.org/CONTRIBUTORS.
|
27
vendor/golang.org/x/net/LICENSE
generated
vendored
Normal file
27
vendor/golang.org/x/net/LICENSE
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
22
vendor/golang.org/x/net/PATENTS
generated
vendored
Normal file
22
vendor/golang.org/x/net/PATENTS
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
Additional IP Rights Grant (Patents)
|
||||
|
||||
"This implementation" means the copyrightable works distributed by
|
||||
Google as part of the Go project.
|
||||
|
||||
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||
no-charge, royalty-free, irrevocable (except as stated in this section)
|
||||
patent license to make, have made, use, offer to sell, sell, import,
|
||||
transfer and otherwise run, modify and propagate the contents of this
|
||||
implementation of Go, where such license applies only to those patent
|
||||
claims, both currently owned or controlled by Google and acquired in
|
||||
the future, licensable by Google that are necessarily infringed by this
|
||||
implementation of Go. This grant does not include claims that would be
|
||||
infringed only as a consequence of further modification of this
|
||||
implementation. If you or your agent or exclusive licensee institute or
|
||||
order or agree to the institution of patent litigation against any
|
||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||
that this implementation of Go or any code incorporated within this
|
||||
implementation of Go constitutes direct or contributory patent
|
||||
infringement, or inducement of patent infringement, then any patent
|
||||
rights granted to you under this License for this implementation of Go
|
||||
shall terminate as of the date such litigation is filed.
|
168
vendor/golang.org/x/net/internal/socks/client.go
generated
vendored
Normal file
168
vendor/golang.org/x/net/internal/socks/client.go
generated
vendored
Normal file
@ -0,0 +1,168 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package socks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
noDeadline = time.Time{}
|
||||
aLongTimeAgo = time.Unix(1, 0)
|
||||
)
|
||||
|
||||
func (d *Dialer) connect(ctx context.Context, c net.Conn, address string) (_ net.Addr, ctxErr error) {
|
||||
host, port, err := splitHostPort(address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if deadline, ok := ctx.Deadline(); ok && !deadline.IsZero() {
|
||||
c.SetDeadline(deadline)
|
||||
defer c.SetDeadline(noDeadline)
|
||||
}
|
||||
if ctx != context.Background() {
|
||||
errCh := make(chan error, 1)
|
||||
done := make(chan struct{})
|
||||
defer func() {
|
||||
close(done)
|
||||
if ctxErr == nil {
|
||||
ctxErr = <-errCh
|
||||
}
|
||||
}()
|
||||
go func() {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
c.SetDeadline(aLongTimeAgo)
|
||||
errCh <- ctx.Err()
|
||||
case <-done:
|
||||
errCh <- nil
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
b := make([]byte, 0, 6+len(host)) // the size here is just an estimate
|
||||
b = append(b, Version5)
|
||||
if len(d.AuthMethods) == 0 || d.Authenticate == nil {
|
||||
b = append(b, 1, byte(AuthMethodNotRequired))
|
||||
} else {
|
||||
ams := d.AuthMethods
|
||||
if len(ams) > 255 {
|
||||
return nil, errors.New("too many authentication methods")
|
||||
}
|
||||
b = append(b, byte(len(ams)))
|
||||
for _, am := range ams {
|
||||
b = append(b, byte(am))
|
||||
}
|
||||
}
|
||||
if _, ctxErr = c.Write(b); ctxErr != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if _, ctxErr = io.ReadFull(c, b[:2]); ctxErr != nil {
|
||||
return
|
||||
}
|
||||
if b[0] != Version5 {
|
||||
return nil, errors.New("unexpected protocol version " + strconv.Itoa(int(b[0])))
|
||||
}
|
||||
am := AuthMethod(b[1])
|
||||
if am == AuthMethodNoAcceptableMethods {
|
||||
return nil, errors.New("no acceptable authentication methods")
|
||||
}
|
||||
if d.Authenticate != nil {
|
||||
if ctxErr = d.Authenticate(ctx, c, am); ctxErr != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
b = b[:0]
|
||||
b = append(b, Version5, byte(d.cmd), 0)
|
||||
if ip := net.ParseIP(host); ip != nil {
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
b = append(b, AddrTypeIPv4)
|
||||
b = append(b, ip4...)
|
||||
} else if ip6 := ip.To16(); ip6 != nil {
|
||||
b = append(b, AddrTypeIPv6)
|
||||
b = append(b, ip6...)
|
||||
} else {
|
||||
return nil, errors.New("unknown address type")
|
||||
}
|
||||
} else {
|
||||
if len(host) > 255 {
|
||||
return nil, errors.New("FQDN too long")
|
||||
}
|
||||
b = append(b, AddrTypeFQDN)
|
||||
b = append(b, byte(len(host)))
|
||||
b = append(b, host...)
|
||||
}
|
||||
b = append(b, byte(port>>8), byte(port))
|
||||
if _, ctxErr = c.Write(b); ctxErr != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if _, ctxErr = io.ReadFull(c, b[:4]); ctxErr != nil {
|
||||
return
|
||||
}
|
||||
if b[0] != Version5 {
|
||||
return nil, errors.New("unexpected protocol version " + strconv.Itoa(int(b[0])))
|
||||
}
|
||||
if cmdErr := Reply(b[1]); cmdErr != StatusSucceeded {
|
||||
return nil, errors.New("unknown error " + cmdErr.String())
|
||||
}
|
||||
if b[2] != 0 {
|
||||
return nil, errors.New("non-zero reserved field")
|
||||
}
|
||||
l := 2
|
||||
var a Addr
|
||||
switch b[3] {
|
||||
case AddrTypeIPv4:
|
||||
l += net.IPv4len
|
||||
a.IP = make(net.IP, net.IPv4len)
|
||||
case AddrTypeIPv6:
|
||||
l += net.IPv6len
|
||||
a.IP = make(net.IP, net.IPv6len)
|
||||
case AddrTypeFQDN:
|
||||
if _, err := io.ReadFull(c, b[:1]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l += int(b[0])
|
||||
default:
|
||||
return nil, errors.New("unknown address type " + strconv.Itoa(int(b[3])))
|
||||
}
|
||||
if cap(b) < l {
|
||||
b = make([]byte, l)
|
||||
} else {
|
||||
b = b[:l]
|
||||
}
|
||||
if _, ctxErr = io.ReadFull(c, b); ctxErr != nil {
|
||||
return
|
||||
}
|
||||
if a.IP != nil {
|
||||
copy(a.IP, b)
|
||||
} else {
|
||||
a.Name = string(b[:len(b)-2])
|
||||
}
|
||||
a.Port = int(b[len(b)-2])<<8 | int(b[len(b)-1])
|
||||
return &a, nil
|
||||
}
|
||||
|
||||
func splitHostPort(address string) (string, int, error) {
|
||||
host, port, err := net.SplitHostPort(address)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
portnum, err := strconv.Atoi(port)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
if 1 > portnum || portnum > 0xffff {
|
||||
return "", 0, errors.New("port number out of range " + port)
|
||||
}
|
||||
return host, portnum, nil
|
||||
}
|
317
vendor/golang.org/x/net/internal/socks/socks.go
generated
vendored
Normal file
317
vendor/golang.org/x/net/internal/socks/socks.go
generated
vendored
Normal file
@ -0,0 +1,317 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package socks provides a SOCKS version 5 client implementation.
|
||||
//
|
||||
// SOCKS protocol version 5 is defined in RFC 1928.
|
||||
// Username/Password authentication for SOCKS version 5 is defined in
|
||||
// RFC 1929.
|
||||
package socks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// A Command represents a SOCKS command.
|
||||
type Command int
|
||||
|
||||
func (cmd Command) String() string {
|
||||
switch cmd {
|
||||
case CmdConnect:
|
||||
return "socks connect"
|
||||
case cmdBind:
|
||||
return "socks bind"
|
||||
default:
|
||||
return "socks " + strconv.Itoa(int(cmd))
|
||||
}
|
||||
}
|
||||
|
||||
// An AuthMethod represents a SOCKS authentication method.
|
||||
type AuthMethod int
|
||||
|
||||
// A Reply represents a SOCKS command reply code.
|
||||
type Reply int
|
||||
|
||||
func (code Reply) String() string {
|
||||
switch code {
|
||||
case StatusSucceeded:
|
||||
return "succeeded"
|
||||
case 0x01:
|
||||
return "general SOCKS server failure"
|
||||
case 0x02:
|
||||
return "connection not allowed by ruleset"
|
||||
case 0x03:
|
||||
return "network unreachable"
|
||||
case 0x04:
|
||||
return "host unreachable"
|
||||
case 0x05:
|
||||
return "connection refused"
|
||||
case 0x06:
|
||||
return "TTL expired"
|
||||
case 0x07:
|
||||
return "command not supported"
|
||||
case 0x08:
|
||||
return "address type not supported"
|
||||
default:
|
||||
return "unknown code: " + strconv.Itoa(int(code))
|
||||
}
|
||||
}
|
||||
|
||||
// Wire protocol constants.
|
||||
const (
|
||||
Version5 = 0x05
|
||||
|
||||
AddrTypeIPv4 = 0x01
|
||||
AddrTypeFQDN = 0x03
|
||||
AddrTypeIPv6 = 0x04
|
||||
|
||||
CmdConnect Command = 0x01 // establishes an active-open forward proxy connection
|
||||
cmdBind Command = 0x02 // establishes a passive-open forward proxy connection
|
||||
|
||||
AuthMethodNotRequired AuthMethod = 0x00 // no authentication required
|
||||
AuthMethodUsernamePassword AuthMethod = 0x02 // use username/password
|
||||
AuthMethodNoAcceptableMethods AuthMethod = 0xff // no acceptable authentication methods
|
||||
|
||||
StatusSucceeded Reply = 0x00
|
||||
)
|
||||
|
||||
// An Addr represents a SOCKS-specific address.
|
||||
// Either Name or IP is used exclusively.
|
||||
type Addr struct {
|
||||
Name string // fully-qualified domain name
|
||||
IP net.IP
|
||||
Port int
|
||||
}
|
||||
|
||||
func (a *Addr) Network() string { return "socks" }
|
||||
|
||||
func (a *Addr) String() string {
|
||||
if a == nil {
|
||||
return "<nil>"
|
||||
}
|
||||
port := strconv.Itoa(a.Port)
|
||||
if a.IP == nil {
|
||||
return net.JoinHostPort(a.Name, port)
|
||||
}
|
||||
return net.JoinHostPort(a.IP.String(), port)
|
||||
}
|
||||
|
||||
// A Conn represents a forward proxy connection.
|
||||
type Conn struct {
|
||||
net.Conn
|
||||
|
||||
boundAddr net.Addr
|
||||
}
|
||||
|
||||
// BoundAddr returns the address assigned by the proxy server for
|
||||
// connecting to the command target address from the proxy server.
|
||||
func (c *Conn) BoundAddr() net.Addr {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
return c.boundAddr
|
||||
}
|
||||
|
||||
// A Dialer holds SOCKS-specific options.
|
||||
type Dialer struct {
|
||||
cmd Command // either CmdConnect or cmdBind
|
||||
proxyNetwork string // network between a proxy server and a client
|
||||
proxyAddress string // proxy server address
|
||||
|
||||
// ProxyDial specifies the optional dial function for
|
||||
// establishing the transport connection.
|
||||
ProxyDial func(context.Context, string, string) (net.Conn, error)
|
||||
|
||||
// AuthMethods specifies the list of request authentication
|
||||
// methods.
|
||||
// If empty, SOCKS client requests only AuthMethodNotRequired.
|
||||
AuthMethods []AuthMethod
|
||||
|
||||
// Authenticate specifies the optional authentication
|
||||
// function. It must be non-nil when AuthMethods is not empty.
|
||||
// It must return an error when the authentication is failed.
|
||||
Authenticate func(context.Context, io.ReadWriter, AuthMethod) error
|
||||
}
|
||||
|
||||
// DialContext connects to the provided address on the provided
|
||||
// network.
|
||||
//
|
||||
// The returned error value may be a net.OpError. When the Op field of
|
||||
// net.OpError contains "socks", the Source field contains a proxy
|
||||
// server address and the Addr field contains a command target
|
||||
// address.
|
||||
//
|
||||
// See func Dial of the net package of standard library for a
|
||||
// description of the network and address parameters.
|
||||
func (d *Dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
if err := d.validateTarget(network, address); err != nil {
|
||||
proxy, dst, _ := d.pathAddrs(address)
|
||||
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
|
||||
}
|
||||
if ctx == nil {
|
||||
proxy, dst, _ := d.pathAddrs(address)
|
||||
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: errors.New("nil context")}
|
||||
}
|
||||
var err error
|
||||
var c net.Conn
|
||||
if d.ProxyDial != nil {
|
||||
c, err = d.ProxyDial(ctx, d.proxyNetwork, d.proxyAddress)
|
||||
} else {
|
||||
var dd net.Dialer
|
||||
c, err = dd.DialContext(ctx, d.proxyNetwork, d.proxyAddress)
|
||||
}
|
||||
if err != nil {
|
||||
proxy, dst, _ := d.pathAddrs(address)
|
||||
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
|
||||
}
|
||||
a, err := d.connect(ctx, c, address)
|
||||
if err != nil {
|
||||
c.Close()
|
||||
proxy, dst, _ := d.pathAddrs(address)
|
||||
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
|
||||
}
|
||||
return &Conn{Conn: c, boundAddr: a}, nil
|
||||
}
|
||||
|
||||
// DialWithConn initiates a connection from SOCKS server to the target
|
||||
// network and address using the connection c that is already
|
||||
// connected to the SOCKS server.
|
||||
//
|
||||
// It returns the connection's local address assigned by the SOCKS
|
||||
// server.
|
||||
func (d *Dialer) DialWithConn(ctx context.Context, c net.Conn, network, address string) (net.Addr, error) {
|
||||
if err := d.validateTarget(network, address); err != nil {
|
||||
proxy, dst, _ := d.pathAddrs(address)
|
||||
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
|
||||
}
|
||||
if ctx == nil {
|
||||
proxy, dst, _ := d.pathAddrs(address)
|
||||
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: errors.New("nil context")}
|
||||
}
|
||||
a, err := d.connect(ctx, c, address)
|
||||
if err != nil {
|
||||
proxy, dst, _ := d.pathAddrs(address)
|
||||
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
// Dial connects to the provided address on the provided network.
|
||||
//
|
||||
// Unlike DialContext, it returns a raw transport connection instead
|
||||
// of a forward proxy connection.
|
||||
//
|
||||
// Deprecated: Use DialContext or DialWithConn instead.
|
||||
func (d *Dialer) Dial(network, address string) (net.Conn, error) {
|
||||
if err := d.validateTarget(network, address); err != nil {
|
||||
proxy, dst, _ := d.pathAddrs(address)
|
||||
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
|
||||
}
|
||||
var err error
|
||||
var c net.Conn
|
||||
if d.ProxyDial != nil {
|
||||
c, err = d.ProxyDial(context.Background(), d.proxyNetwork, d.proxyAddress)
|
||||
} else {
|
||||
c, err = net.Dial(d.proxyNetwork, d.proxyAddress)
|
||||
}
|
||||
if err != nil {
|
||||
proxy, dst, _ := d.pathAddrs(address)
|
||||
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
|
||||
}
|
||||
if _, err := d.DialWithConn(context.Background(), c, network, address); err != nil {
|
||||
c.Close()
|
||||
return nil, err
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (d *Dialer) validateTarget(network, address string) error {
|
||||
switch network {
|
||||
case "tcp", "tcp6", "tcp4":
|
||||
default:
|
||||
return errors.New("network not implemented")
|
||||
}
|
||||
switch d.cmd {
|
||||
case CmdConnect, cmdBind:
|
||||
default:
|
||||
return errors.New("command not implemented")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Dialer) pathAddrs(address string) (proxy, dst net.Addr, err error) {
|
||||
for i, s := range []string{d.proxyAddress, address} {
|
||||
host, port, err := splitHostPort(s)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
a := &Addr{Port: port}
|
||||
a.IP = net.ParseIP(host)
|
||||
if a.IP == nil {
|
||||
a.Name = host
|
||||
}
|
||||
if i == 0 {
|
||||
proxy = a
|
||||
} else {
|
||||
dst = a
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// NewDialer returns a new Dialer that dials through the provided
|
||||
// proxy server's network and address.
|
||||
func NewDialer(network, address string) *Dialer {
|
||||
return &Dialer{proxyNetwork: network, proxyAddress: address, cmd: CmdConnect}
|
||||
}
|
||||
|
||||
const (
|
||||
authUsernamePasswordVersion = 0x01
|
||||
authStatusSucceeded = 0x00
|
||||
)
|
||||
|
||||
// UsernamePassword are the credentials for the username/password
|
||||
// authentication method.
|
||||
type UsernamePassword struct {
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
// Authenticate authenticates a pair of username and password with the
|
||||
// proxy server.
|
||||
func (up *UsernamePassword) Authenticate(ctx context.Context, rw io.ReadWriter, auth AuthMethod) error {
|
||||
switch auth {
|
||||
case AuthMethodNotRequired:
|
||||
return nil
|
||||
case AuthMethodUsernamePassword:
|
||||
if len(up.Username) == 0 || len(up.Username) > 255 || len(up.Password) == 0 || len(up.Password) > 255 {
|
||||
return errors.New("invalid username/password")
|
||||
}
|
||||
b := []byte{authUsernamePasswordVersion}
|
||||
b = append(b, byte(len(up.Username)))
|
||||
b = append(b, up.Username...)
|
||||
b = append(b, byte(len(up.Password)))
|
||||
b = append(b, up.Password...)
|
||||
// TODO(mikio): handle IO deadlines and cancelation if
|
||||
// necessary
|
||||
if _, err := rw.Write(b); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := io.ReadFull(rw, b[:2]); err != nil {
|
||||
return err
|
||||
}
|
||||
if b[0] != authUsernamePasswordVersion {
|
||||
return errors.New("invalid username/password version")
|
||||
}
|
||||
if b[1] != authStatusSucceeded {
|
||||
return errors.New("username/password authentication failed")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return errors.New("unsupported authentication method " + strconv.Itoa(int(auth)))
|
||||
}
|
54
vendor/golang.org/x/net/proxy/dial.go
generated
vendored
Normal file
54
vendor/golang.org/x/net/proxy/dial.go
generated
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
)
|
||||
|
||||
// A ContextDialer dials using a context.
|
||||
type ContextDialer interface {
|
||||
DialContext(ctx context.Context, network, address string) (net.Conn, error)
|
||||
}
|
||||
|
||||
// Dial works like DialContext on net.Dialer but using a dialer returned by FromEnvironment.
|
||||
//
|
||||
// The passed ctx is only used for returning the Conn, not the lifetime of the Conn.
|
||||
//
|
||||
// Custom dialers (registered via RegisterDialerType) that do not implement ContextDialer
|
||||
// can leak a goroutine for as long as it takes the underlying Dialer implementation to timeout.
|
||||
//
|
||||
// A Conn returned from a successful Dial after the context has been cancelled will be immediately closed.
|
||||
func Dial(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
d := FromEnvironment()
|
||||
if xd, ok := d.(ContextDialer); ok {
|
||||
return xd.DialContext(ctx, network, address)
|
||||
}
|
||||
return dialContext(ctx, d, network, address)
|
||||
}
|
||||
|
||||
// WARNING: this can leak a goroutine for as long as the underlying Dialer implementation takes to timeout
|
||||
// A Conn returned from a successful Dial after the context has been cancelled will be immediately closed.
|
||||
func dialContext(ctx context.Context, d Dialer, network, address string) (net.Conn, error) {
|
||||
var (
|
||||
conn net.Conn
|
||||
done = make(chan struct{}, 1)
|
||||
err error
|
||||
)
|
||||
go func() {
|
||||
conn, err = d.Dial(network, address)
|
||||
close(done)
|
||||
if conn != nil && ctx.Err() != nil {
|
||||
conn.Close()
|
||||
}
|
||||
}()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
err = ctx.Err()
|
||||
case <-done:
|
||||
}
|
||||
return conn, err
|
||||
}
|
31
vendor/golang.org/x/net/proxy/direct.go
generated
vendored
Normal file
31
vendor/golang.org/x/net/proxy/direct.go
generated
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
)
|
||||
|
||||
type direct struct{}
|
||||
|
||||
// Direct implements Dialer by making network connections directly using net.Dial or net.DialContext.
|
||||
var Direct = direct{}
|
||||
|
||||
var (
|
||||
_ Dialer = Direct
|
||||
_ ContextDialer = Direct
|
||||
)
|
||||
|
||||
// Dial directly invokes net.Dial with the supplied parameters.
|
||||
func (direct) Dial(network, addr string) (net.Conn, error) {
|
||||
return net.Dial(network, addr)
|
||||
}
|
||||
|
||||
// DialContext instantiates a net.Dialer and invokes its DialContext receiver with the supplied parameters.
|
||||
func (direct) DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
var d net.Dialer
|
||||
return d.DialContext(ctx, network, addr)
|
||||
}
|
155
vendor/golang.org/x/net/proxy/per_host.go
generated
vendored
Normal file
155
vendor/golang.org/x/net/proxy/per_host.go
generated
vendored
Normal file
@ -0,0 +1,155 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// A PerHost directs connections to a default Dialer unless the host name
|
||||
// requested matches one of a number of exceptions.
|
||||
type PerHost struct {
|
||||
def, bypass Dialer
|
||||
|
||||
bypassNetworks []*net.IPNet
|
||||
bypassIPs []net.IP
|
||||
bypassZones []string
|
||||
bypassHosts []string
|
||||
}
|
||||
|
||||
// NewPerHost returns a PerHost Dialer that directs connections to either
|
||||
// defaultDialer or bypass, depending on whether the connection matches one of
|
||||
// the configured rules.
|
||||
func NewPerHost(defaultDialer, bypass Dialer) *PerHost {
|
||||
return &PerHost{
|
||||
def: defaultDialer,
|
||||
bypass: bypass,
|
||||
}
|
||||
}
|
||||
|
||||
// Dial connects to the address addr on the given network through either
|
||||
// defaultDialer or bypass.
|
||||
func (p *PerHost) Dial(network, addr string) (c net.Conn, err error) {
|
||||
host, _, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return p.dialerForRequest(host).Dial(network, addr)
|
||||
}
|
||||
|
||||
// DialContext connects to the address addr on the given network through either
|
||||
// defaultDialer or bypass.
|
||||
func (p *PerHost) DialContext(ctx context.Context, network, addr string) (c net.Conn, err error) {
|
||||
host, _, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d := p.dialerForRequest(host)
|
||||
if x, ok := d.(ContextDialer); ok {
|
||||
return x.DialContext(ctx, network, addr)
|
||||
}
|
||||
return dialContext(ctx, d, network, addr)
|
||||
}
|
||||
|
||||
func (p *PerHost) dialerForRequest(host string) Dialer {
|
||||
if ip := net.ParseIP(host); ip != nil {
|
||||
for _, net := range p.bypassNetworks {
|
||||
if net.Contains(ip) {
|
||||
return p.bypass
|
||||
}
|
||||
}
|
||||
for _, bypassIP := range p.bypassIPs {
|
||||
if bypassIP.Equal(ip) {
|
||||
return p.bypass
|
||||
}
|
||||
}
|
||||
return p.def
|
||||
}
|
||||
|
||||
for _, zone := range p.bypassZones {
|
||||
if strings.HasSuffix(host, zone) {
|
||||
return p.bypass
|
||||
}
|
||||
if host == zone[1:] {
|
||||
// For a zone ".example.com", we match "example.com"
|
||||
// too.
|
||||
return p.bypass
|
||||
}
|
||||
}
|
||||
for _, bypassHost := range p.bypassHosts {
|
||||
if bypassHost == host {
|
||||
return p.bypass
|
||||
}
|
||||
}
|
||||
return p.def
|
||||
}
|
||||
|
||||
// AddFromString parses a string that contains comma-separated values
|
||||
// specifying hosts that should use the bypass proxy. Each value is either an
|
||||
// IP address, a CIDR range, a zone (*.example.com) or a host name
|
||||
// (localhost). A best effort is made to parse the string and errors are
|
||||
// ignored.
|
||||
func (p *PerHost) AddFromString(s string) {
|
||||
hosts := strings.Split(s, ",")
|
||||
for _, host := range hosts {
|
||||
host = strings.TrimSpace(host)
|
||||
if len(host) == 0 {
|
||||
continue
|
||||
}
|
||||
if strings.Contains(host, "/") {
|
||||
// We assume that it's a CIDR address like 127.0.0.0/8
|
||||
if _, net, err := net.ParseCIDR(host); err == nil {
|
||||
p.AddNetwork(net)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if ip := net.ParseIP(host); ip != nil {
|
||||
p.AddIP(ip)
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(host, "*.") {
|
||||
p.AddZone(host[1:])
|
||||
continue
|
||||
}
|
||||
p.AddHost(host)
|
||||
}
|
||||
}
|
||||
|
||||
// AddIP specifies an IP address that will use the bypass proxy. Note that
|
||||
// this will only take effect if a literal IP address is dialed. A connection
|
||||
// to a named host will never match an IP.
|
||||
func (p *PerHost) AddIP(ip net.IP) {
|
||||
p.bypassIPs = append(p.bypassIPs, ip)
|
||||
}
|
||||
|
||||
// AddNetwork specifies an IP range that will use the bypass proxy. Note that
|
||||
// this will only take effect if a literal IP address is dialed. A connection
|
||||
// to a named host will never match.
|
||||
func (p *PerHost) AddNetwork(net *net.IPNet) {
|
||||
p.bypassNetworks = append(p.bypassNetworks, net)
|
||||
}
|
||||
|
||||
// AddZone specifies a DNS suffix that will use the bypass proxy. A zone of
|
||||
// "example.com" matches "example.com" and all of its subdomains.
|
||||
func (p *PerHost) AddZone(zone string) {
|
||||
if strings.HasSuffix(zone, ".") {
|
||||
zone = zone[:len(zone)-1]
|
||||
}
|
||||
if !strings.HasPrefix(zone, ".") {
|
||||
zone = "." + zone
|
||||
}
|
||||
p.bypassZones = append(p.bypassZones, zone)
|
||||
}
|
||||
|
||||
// AddHost specifies a host name that will use the bypass proxy.
|
||||
func (p *PerHost) AddHost(host string) {
|
||||
if strings.HasSuffix(host, ".") {
|
||||
host = host[:len(host)-1]
|
||||
}
|
||||
p.bypassHosts = append(p.bypassHosts, host)
|
||||
}
|
149
vendor/golang.org/x/net/proxy/proxy.go
generated
vendored
Normal file
149
vendor/golang.org/x/net/proxy/proxy.go
generated
vendored
Normal file
@ -0,0 +1,149 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package proxy provides support for a variety of protocols to proxy network
|
||||
// data.
|
||||
package proxy // import "golang.org/x/net/proxy"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// A Dialer is a means to establish a connection.
|
||||
// Custom dialers should also implement ContextDialer.
|
||||
type Dialer interface {
|
||||
// Dial connects to the given address via the proxy.
|
||||
Dial(network, addr string) (c net.Conn, err error)
|
||||
}
|
||||
|
||||
// Auth contains authentication parameters that specific Dialers may require.
|
||||
type Auth struct {
|
||||
User, Password string
|
||||
}
|
||||
|
||||
// FromEnvironment returns the dialer specified by the proxy-related
|
||||
// variables in the environment and makes underlying connections
|
||||
// directly.
|
||||
func FromEnvironment() Dialer {
|
||||
return FromEnvironmentUsing(Direct)
|
||||
}
|
||||
|
||||
// FromEnvironmentUsing returns the dialer specify by the proxy-related
|
||||
// variables in the environment and makes underlying connections
|
||||
// using the provided forwarding Dialer (for instance, a *net.Dialer
|
||||
// with desired configuration).
|
||||
func FromEnvironmentUsing(forward Dialer) Dialer {
|
||||
allProxy := allProxyEnv.Get()
|
||||
if len(allProxy) == 0 {
|
||||
return forward
|
||||
}
|
||||
|
||||
proxyURL, err := url.Parse(allProxy)
|
||||
if err != nil {
|
||||
return forward
|
||||
}
|
||||
proxy, err := FromURL(proxyURL, forward)
|
||||
if err != nil {
|
||||
return forward
|
||||
}
|
||||
|
||||
noProxy := noProxyEnv.Get()
|
||||
if len(noProxy) == 0 {
|
||||
return proxy
|
||||
}
|
||||
|
||||
perHost := NewPerHost(proxy, forward)
|
||||
perHost.AddFromString(noProxy)
|
||||
return perHost
|
||||
}
|
||||
|
||||
// proxySchemes is a map from URL schemes to a function that creates a Dialer
|
||||
// from a URL with such a scheme.
|
||||
var proxySchemes map[string]func(*url.URL, Dialer) (Dialer, error)
|
||||
|
||||
// RegisterDialerType takes a URL scheme and a function to generate Dialers from
|
||||
// a URL with that scheme and a forwarding Dialer. Registered schemes are used
|
||||
// by FromURL.
|
||||
func RegisterDialerType(scheme string, f func(*url.URL, Dialer) (Dialer, error)) {
|
||||
if proxySchemes == nil {
|
||||
proxySchemes = make(map[string]func(*url.URL, Dialer) (Dialer, error))
|
||||
}
|
||||
proxySchemes[scheme] = f
|
||||
}
|
||||
|
||||
// FromURL returns a Dialer given a URL specification and an underlying
|
||||
// Dialer for it to make network requests.
|
||||
func FromURL(u *url.URL, forward Dialer) (Dialer, error) {
|
||||
var auth *Auth
|
||||
if u.User != nil {
|
||||
auth = new(Auth)
|
||||
auth.User = u.User.Username()
|
||||
if p, ok := u.User.Password(); ok {
|
||||
auth.Password = p
|
||||
}
|
||||
}
|
||||
|
||||
switch u.Scheme {
|
||||
case "socks5", "socks5h":
|
||||
addr := u.Hostname()
|
||||
port := u.Port()
|
||||
if port == "" {
|
||||
port = "1080"
|
||||
}
|
||||
return SOCKS5("tcp", net.JoinHostPort(addr, port), auth, forward)
|
||||
}
|
||||
|
||||
// If the scheme doesn't match any of the built-in schemes, see if it
|
||||
// was registered by another package.
|
||||
if proxySchemes != nil {
|
||||
if f, ok := proxySchemes[u.Scheme]; ok {
|
||||
return f(u, forward)
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errors.New("proxy: unknown scheme: " + u.Scheme)
|
||||
}
|
||||
|
||||
var (
|
||||
allProxyEnv = &envOnce{
|
||||
names: []string{"ALL_PROXY", "all_proxy"},
|
||||
}
|
||||
noProxyEnv = &envOnce{
|
||||
names: []string{"NO_PROXY", "no_proxy"},
|
||||
}
|
||||
)
|
||||
|
||||
// envOnce looks up an environment variable (optionally by multiple
|
||||
// names) once. It mitigates expensive lookups on some platforms
|
||||
// (e.g. Windows).
|
||||
// (Borrowed from net/http/transport.go)
|
||||
type envOnce struct {
|
||||
names []string
|
||||
once sync.Once
|
||||
val string
|
||||
}
|
||||
|
||||
func (e *envOnce) Get() string {
|
||||
e.once.Do(e.init)
|
||||
return e.val
|
||||
}
|
||||
|
||||
func (e *envOnce) init() {
|
||||
for _, n := range e.names {
|
||||
e.val = os.Getenv(n)
|
||||
if e.val != "" {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// reset is used by tests
|
||||
func (e *envOnce) reset() {
|
||||
e.once = sync.Once{}
|
||||
e.val = ""
|
||||
}
|
42
vendor/golang.org/x/net/proxy/socks5.go
generated
vendored
Normal file
42
vendor/golang.org/x/net/proxy/socks5.go
generated
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
|
||||
"golang.org/x/net/internal/socks"
|
||||
)
|
||||
|
||||
// SOCKS5 returns a Dialer that makes SOCKSv5 connections to the given
|
||||
// address with an optional username and password.
|
||||
// See RFC 1928 and RFC 1929.
|
||||
func SOCKS5(network, address string, auth *Auth, forward Dialer) (Dialer, error) {
|
||||
d := socks.NewDialer(network, address)
|
||||
if forward != nil {
|
||||
if f, ok := forward.(ContextDialer); ok {
|
||||
d.ProxyDial = func(ctx context.Context, network string, address string) (net.Conn, error) {
|
||||
return f.DialContext(ctx, network, address)
|
||||
}
|
||||
} else {
|
||||
d.ProxyDial = func(ctx context.Context, network string, address string) (net.Conn, error) {
|
||||
return dialContext(ctx, forward, network, address)
|
||||
}
|
||||
}
|
||||
}
|
||||
if auth != nil {
|
||||
up := socks.UsernamePassword{
|
||||
Username: auth.User,
|
||||
Password: auth.Password,
|
||||
}
|
||||
d.AuthMethods = []socks.AuthMethod{
|
||||
socks.AuthMethodNotRequired,
|
||||
socks.AuthMethodUsernamePassword,
|
||||
}
|
||||
d.Authenticate = up.Authenticate
|
||||
}
|
||||
return d, nil
|
||||
}
|
106
vendor/golang.org/x/net/websocket/client.go
generated
vendored
Normal file
106
vendor/golang.org/x/net/websocket/client.go
generated
vendored
Normal file
@ -0,0 +1,106 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// DialError is an error that occurs while dialling a websocket server.
|
||||
type DialError struct {
|
||||
*Config
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *DialError) Error() string {
|
||||
return "websocket.Dial " + e.Config.Location.String() + ": " + e.Err.Error()
|
||||
}
|
||||
|
||||
// NewConfig creates a new WebSocket config for client connection.
|
||||
func NewConfig(server, origin string) (config *Config, err error) {
|
||||
config = new(Config)
|
||||
config.Version = ProtocolVersionHybi13
|
||||
config.Location, err = url.ParseRequestURI(server)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
config.Origin, err = url.ParseRequestURI(origin)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
config.Header = http.Header(make(map[string][]string))
|
||||
return
|
||||
}
|
||||
|
||||
// NewClient creates a new WebSocket client connection over rwc.
|
||||
func NewClient(config *Config, rwc io.ReadWriteCloser) (ws *Conn, err error) {
|
||||
br := bufio.NewReader(rwc)
|
||||
bw := bufio.NewWriter(rwc)
|
||||
err = hybiClientHandshake(config, br, bw)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
buf := bufio.NewReadWriter(br, bw)
|
||||
ws = newHybiClientConn(config, buf, rwc)
|
||||
return
|
||||
}
|
||||
|
||||
// Dial opens a new client connection to a WebSocket.
|
||||
func Dial(url_, protocol, origin string) (ws *Conn, err error) {
|
||||
config, err := NewConfig(url_, origin)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if protocol != "" {
|
||||
config.Protocol = []string{protocol}
|
||||
}
|
||||
return DialConfig(config)
|
||||
}
|
||||
|
||||
var portMap = map[string]string{
|
||||
"ws": "80",
|
||||
"wss": "443",
|
||||
}
|
||||
|
||||
func parseAuthority(location *url.URL) string {
|
||||
if _, ok := portMap[location.Scheme]; ok {
|
||||
if _, _, err := net.SplitHostPort(location.Host); err != nil {
|
||||
return net.JoinHostPort(location.Host, portMap[location.Scheme])
|
||||
}
|
||||
}
|
||||
return location.Host
|
||||
}
|
||||
|
||||
// DialConfig opens a new client connection to a WebSocket with a config.
|
||||
func DialConfig(config *Config) (ws *Conn, err error) {
|
||||
var client net.Conn
|
||||
if config.Location == nil {
|
||||
return nil, &DialError{config, ErrBadWebSocketLocation}
|
||||
}
|
||||
if config.Origin == nil {
|
||||
return nil, &DialError{config, ErrBadWebSocketOrigin}
|
||||
}
|
||||
dialer := config.Dialer
|
||||
if dialer == nil {
|
||||
dialer = &net.Dialer{}
|
||||
}
|
||||
client, err = dialWithDialer(dialer, config)
|
||||
if err != nil {
|
||||
goto Error
|
||||
}
|
||||
ws, err = NewClient(config, client)
|
||||
if err != nil {
|
||||
client.Close()
|
||||
goto Error
|
||||
}
|
||||
return
|
||||
|
||||
Error:
|
||||
return nil, &DialError{config, err}
|
||||
}
|
24
vendor/golang.org/x/net/websocket/dial.go
generated
vendored
Normal file
24
vendor/golang.org/x/net/websocket/dial.go
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
)
|
||||
|
||||
func dialWithDialer(dialer *net.Dialer, config *Config) (conn net.Conn, err error) {
|
||||
switch config.Location.Scheme {
|
||||
case "ws":
|
||||
conn, err = dialer.Dial("tcp", parseAuthority(config.Location))
|
||||
|
||||
case "wss":
|
||||
conn, err = tls.DialWithDialer(dialer, "tcp", parseAuthority(config.Location), config.TlsConfig)
|
||||
|
||||
default:
|
||||
err = ErrBadScheme
|
||||
}
|
||||
return
|
||||
}
|
583
vendor/golang.org/x/net/websocket/hybi.go
generated
vendored
Normal file
583
vendor/golang.org/x/net/websocket/hybi.go
generated
vendored
Normal file
@ -0,0 +1,583 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package websocket
|
||||
|
||||
// This file implements a protocol of hybi draft.
|
||||
// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
websocketGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
|
||||
|
||||
closeStatusNormal = 1000
|
||||
closeStatusGoingAway = 1001
|
||||
closeStatusProtocolError = 1002
|
||||
closeStatusUnsupportedData = 1003
|
||||
closeStatusFrameTooLarge = 1004
|
||||
closeStatusNoStatusRcvd = 1005
|
||||
closeStatusAbnormalClosure = 1006
|
||||
closeStatusBadMessageData = 1007
|
||||
closeStatusPolicyViolation = 1008
|
||||
closeStatusTooBigData = 1009
|
||||
closeStatusExtensionMismatch = 1010
|
||||
|
||||
maxControlFramePayloadLength = 125
|
||||
)
|
||||
|
||||
var (
|
||||
ErrBadMaskingKey = &ProtocolError{"bad masking key"}
|
||||
ErrBadPongMessage = &ProtocolError{"bad pong message"}
|
||||
ErrBadClosingStatus = &ProtocolError{"bad closing status"}
|
||||
ErrUnsupportedExtensions = &ProtocolError{"unsupported extensions"}
|
||||
ErrNotImplemented = &ProtocolError{"not implemented"}
|
||||
|
||||
handshakeHeader = map[string]bool{
|
||||
"Host": true,
|
||||
"Upgrade": true,
|
||||
"Connection": true,
|
||||
"Sec-Websocket-Key": true,
|
||||
"Sec-Websocket-Origin": true,
|
||||
"Sec-Websocket-Version": true,
|
||||
"Sec-Websocket-Protocol": true,
|
||||
"Sec-Websocket-Accept": true,
|
||||
}
|
||||
)
|
||||
|
||||
// A hybiFrameHeader is a frame header as defined in hybi draft.
|
||||
type hybiFrameHeader struct {
|
||||
Fin bool
|
||||
Rsv [3]bool
|
||||
OpCode byte
|
||||
Length int64
|
||||
MaskingKey []byte
|
||||
|
||||
data *bytes.Buffer
|
||||
}
|
||||
|
||||
// A hybiFrameReader is a reader for hybi frame.
|
||||
type hybiFrameReader struct {
|
||||
reader io.Reader
|
||||
|
||||
header hybiFrameHeader
|
||||
pos int64
|
||||
length int
|
||||
}
|
||||
|
||||
func (frame *hybiFrameReader) Read(msg []byte) (n int, err error) {
|
||||
n, err = frame.reader.Read(msg)
|
||||
if frame.header.MaskingKey != nil {
|
||||
for i := 0; i < n; i++ {
|
||||
msg[i] = msg[i] ^ frame.header.MaskingKey[frame.pos%4]
|
||||
frame.pos++
|
||||
}
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (frame *hybiFrameReader) PayloadType() byte { return frame.header.OpCode }
|
||||
|
||||
func (frame *hybiFrameReader) HeaderReader() io.Reader {
|
||||
if frame.header.data == nil {
|
||||
return nil
|
||||
}
|
||||
if frame.header.data.Len() == 0 {
|
||||
return nil
|
||||
}
|
||||
return frame.header.data
|
||||
}
|
||||
|
||||
func (frame *hybiFrameReader) TrailerReader() io.Reader { return nil }
|
||||
|
||||
func (frame *hybiFrameReader) Len() (n int) { return frame.length }
|
||||
|
||||
// A hybiFrameReaderFactory creates new frame reader based on its frame type.
|
||||
type hybiFrameReaderFactory struct {
|
||||
*bufio.Reader
|
||||
}
|
||||
|
||||
// NewFrameReader reads a frame header from the connection, and creates new reader for the frame.
|
||||
// See Section 5.2 Base Framing protocol for detail.
|
||||
// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17#section-5.2
|
||||
func (buf hybiFrameReaderFactory) NewFrameReader() (frame frameReader, err error) {
|
||||
hybiFrame := new(hybiFrameReader)
|
||||
frame = hybiFrame
|
||||
var header []byte
|
||||
var b byte
|
||||
// First byte. FIN/RSV1/RSV2/RSV3/OpCode(4bits)
|
||||
b, err = buf.ReadByte()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
header = append(header, b)
|
||||
hybiFrame.header.Fin = ((header[0] >> 7) & 1) != 0
|
||||
for i := 0; i < 3; i++ {
|
||||
j := uint(6 - i)
|
||||
hybiFrame.header.Rsv[i] = ((header[0] >> j) & 1) != 0
|
||||
}
|
||||
hybiFrame.header.OpCode = header[0] & 0x0f
|
||||
|
||||
// Second byte. Mask/Payload len(7bits)
|
||||
b, err = buf.ReadByte()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
header = append(header, b)
|
||||
mask := (b & 0x80) != 0
|
||||
b &= 0x7f
|
||||
lengthFields := 0
|
||||
switch {
|
||||
case b <= 125: // Payload length 7bits.
|
||||
hybiFrame.header.Length = int64(b)
|
||||
case b == 126: // Payload length 7+16bits
|
||||
lengthFields = 2
|
||||
case b == 127: // Payload length 7+64bits
|
||||
lengthFields = 8
|
||||
}
|
||||
for i := 0; i < lengthFields; i++ {
|
||||
b, err = buf.ReadByte()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if lengthFields == 8 && i == 0 { // MSB must be zero when 7+64 bits
|
||||
b &= 0x7f
|
||||
}
|
||||
header = append(header, b)
|
||||
hybiFrame.header.Length = hybiFrame.header.Length*256 + int64(b)
|
||||
}
|
||||
if mask {
|
||||
// Masking key. 4 bytes.
|
||||
for i := 0; i < 4; i++ {
|
||||
b, err = buf.ReadByte()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
header = append(header, b)
|
||||
hybiFrame.header.MaskingKey = append(hybiFrame.header.MaskingKey, b)
|
||||
}
|
||||
}
|
||||
hybiFrame.reader = io.LimitReader(buf.Reader, hybiFrame.header.Length)
|
||||
hybiFrame.header.data = bytes.NewBuffer(header)
|
||||
hybiFrame.length = len(header) + int(hybiFrame.header.Length)
|
||||
return
|
||||
}
|
||||
|
||||
// A HybiFrameWriter is a writer for hybi frame.
|
||||
type hybiFrameWriter struct {
|
||||
writer *bufio.Writer
|
||||
|
||||
header *hybiFrameHeader
|
||||
}
|
||||
|
||||
func (frame *hybiFrameWriter) Write(msg []byte) (n int, err error) {
|
||||
var header []byte
|
||||
var b byte
|
||||
if frame.header.Fin {
|
||||
b |= 0x80
|
||||
}
|
||||
for i := 0; i < 3; i++ {
|
||||
if frame.header.Rsv[i] {
|
||||
j := uint(6 - i)
|
||||
b |= 1 << j
|
||||
}
|
||||
}
|
||||
b |= frame.header.OpCode
|
||||
header = append(header, b)
|
||||
if frame.header.MaskingKey != nil {
|
||||
b = 0x80
|
||||
} else {
|
||||
b = 0
|
||||
}
|
||||
lengthFields := 0
|
||||
length := len(msg)
|
||||
switch {
|
||||
case length <= 125:
|
||||
b |= byte(length)
|
||||
case length < 65536:
|
||||
b |= 126
|
||||
lengthFields = 2
|
||||
default:
|
||||
b |= 127
|
||||
lengthFields = 8
|
||||
}
|
||||
header = append(header, b)
|
||||
for i := 0; i < lengthFields; i++ {
|
||||
j := uint((lengthFields - i - 1) * 8)
|
||||
b = byte((length >> j) & 0xff)
|
||||
header = append(header, b)
|
||||
}
|
||||
if frame.header.MaskingKey != nil {
|
||||
if len(frame.header.MaskingKey) != 4 {
|
||||
return 0, ErrBadMaskingKey
|
||||
}
|
||||
header = append(header, frame.header.MaskingKey...)
|
||||
frame.writer.Write(header)
|
||||
data := make([]byte, length)
|
||||
for i := range data {
|
||||
data[i] = msg[i] ^ frame.header.MaskingKey[i%4]
|
||||
}
|
||||
frame.writer.Write(data)
|
||||
err = frame.writer.Flush()
|
||||
return length, err
|
||||
}
|
||||
frame.writer.Write(header)
|
||||
frame.writer.Write(msg)
|
||||
err = frame.writer.Flush()
|
||||
return length, err
|
||||
}
|
||||
|
||||
func (frame *hybiFrameWriter) Close() error { return nil }
|
||||
|
||||
type hybiFrameWriterFactory struct {
|
||||
*bufio.Writer
|
||||
needMaskingKey bool
|
||||
}
|
||||
|
||||
func (buf hybiFrameWriterFactory) NewFrameWriter(payloadType byte) (frame frameWriter, err error) {
|
||||
frameHeader := &hybiFrameHeader{Fin: true, OpCode: payloadType}
|
||||
if buf.needMaskingKey {
|
||||
frameHeader.MaskingKey, err = generateMaskingKey()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return &hybiFrameWriter{writer: buf.Writer, header: frameHeader}, nil
|
||||
}
|
||||
|
||||
type hybiFrameHandler struct {
|
||||
conn *Conn
|
||||
payloadType byte
|
||||
}
|
||||
|
||||
func (handler *hybiFrameHandler) HandleFrame(frame frameReader) (frameReader, error) {
|
||||
if handler.conn.IsServerConn() {
|
||||
// The client MUST mask all frames sent to the server.
|
||||
if frame.(*hybiFrameReader).header.MaskingKey == nil {
|
||||
handler.WriteClose(closeStatusProtocolError)
|
||||
return nil, io.EOF
|
||||
}
|
||||
} else {
|
||||
// The server MUST NOT mask all frames.
|
||||
if frame.(*hybiFrameReader).header.MaskingKey != nil {
|
||||
handler.WriteClose(closeStatusProtocolError)
|
||||
return nil, io.EOF
|
||||
}
|
||||
}
|
||||
if header := frame.HeaderReader(); header != nil {
|
||||
io.Copy(ioutil.Discard, header)
|
||||
}
|
||||
switch frame.PayloadType() {
|
||||
case ContinuationFrame:
|
||||
frame.(*hybiFrameReader).header.OpCode = handler.payloadType
|
||||
case TextFrame, BinaryFrame:
|
||||
handler.payloadType = frame.PayloadType()
|
||||
case CloseFrame:
|
||||
return nil, io.EOF
|
||||
case PingFrame, PongFrame:
|
||||
b := make([]byte, maxControlFramePayloadLength)
|
||||
n, err := io.ReadFull(frame, b)
|
||||
if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF {
|
||||
return nil, err
|
||||
}
|
||||
io.Copy(ioutil.Discard, frame)
|
||||
if frame.PayloadType() == PingFrame {
|
||||
if _, err := handler.WritePong(b[:n]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
return frame, nil
|
||||
}
|
||||
|
||||
func (handler *hybiFrameHandler) WriteClose(status int) (err error) {
|
||||
handler.conn.wio.Lock()
|
||||
defer handler.conn.wio.Unlock()
|
||||
w, err := handler.conn.frameWriterFactory.NewFrameWriter(CloseFrame)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
msg := make([]byte, 2)
|
||||
binary.BigEndian.PutUint16(msg, uint16(status))
|
||||
_, err = w.Write(msg)
|
||||
w.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
func (handler *hybiFrameHandler) WritePong(msg []byte) (n int, err error) {
|
||||
handler.conn.wio.Lock()
|
||||
defer handler.conn.wio.Unlock()
|
||||
w, err := handler.conn.frameWriterFactory.NewFrameWriter(PongFrame)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
n, err = w.Write(msg)
|
||||
w.Close()
|
||||
return n, err
|
||||
}
|
||||
|
||||
// newHybiConn creates a new WebSocket connection speaking hybi draft protocol.
|
||||
func newHybiConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn {
|
||||
if buf == nil {
|
||||
br := bufio.NewReader(rwc)
|
||||
bw := bufio.NewWriter(rwc)
|
||||
buf = bufio.NewReadWriter(br, bw)
|
||||
}
|
||||
ws := &Conn{config: config, request: request, buf: buf, rwc: rwc,
|
||||
frameReaderFactory: hybiFrameReaderFactory{buf.Reader},
|
||||
frameWriterFactory: hybiFrameWriterFactory{
|
||||
buf.Writer, request == nil},
|
||||
PayloadType: TextFrame,
|
||||
defaultCloseStatus: closeStatusNormal}
|
||||
ws.frameHandler = &hybiFrameHandler{conn: ws}
|
||||
return ws
|
||||
}
|
||||
|
||||
// generateMaskingKey generates a masking key for a frame.
|
||||
func generateMaskingKey() (maskingKey []byte, err error) {
|
||||
maskingKey = make([]byte, 4)
|
||||
if _, err = io.ReadFull(rand.Reader, maskingKey); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// generateNonce generates a nonce consisting of a randomly selected 16-byte
|
||||
// value that has been base64-encoded.
|
||||
func generateNonce() (nonce []byte) {
|
||||
key := make([]byte, 16)
|
||||
if _, err := io.ReadFull(rand.Reader, key); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
nonce = make([]byte, 24)
|
||||
base64.StdEncoding.Encode(nonce, key)
|
||||
return
|
||||
}
|
||||
|
||||
// removeZone removes IPv6 zone identifer from host.
|
||||
// E.g., "[fe80::1%en0]:8080" to "[fe80::1]:8080"
|
||||
func removeZone(host string) string {
|
||||
if !strings.HasPrefix(host, "[") {
|
||||
return host
|
||||
}
|
||||
i := strings.LastIndex(host, "]")
|
||||
if i < 0 {
|
||||
return host
|
||||
}
|
||||
j := strings.LastIndex(host[:i], "%")
|
||||
if j < 0 {
|
||||
return host
|
||||
}
|
||||
return host[:j] + host[i:]
|
||||
}
|
||||
|
||||
// getNonceAccept computes the base64-encoded SHA-1 of the concatenation of
|
||||
// the nonce ("Sec-WebSocket-Key" value) with the websocket GUID string.
|
||||
func getNonceAccept(nonce []byte) (expected []byte, err error) {
|
||||
h := sha1.New()
|
||||
if _, err = h.Write(nonce); err != nil {
|
||||
return
|
||||
}
|
||||
if _, err = h.Write([]byte(websocketGUID)); err != nil {
|
||||
return
|
||||
}
|
||||
expected = make([]byte, 28)
|
||||
base64.StdEncoding.Encode(expected, h.Sum(nil))
|
||||
return
|
||||
}
|
||||
|
||||
// Client handshake described in draft-ietf-hybi-thewebsocket-protocol-17
|
||||
func hybiClientHandshake(config *Config, br *bufio.Reader, bw *bufio.Writer) (err error) {
|
||||
bw.WriteString("GET " + config.Location.RequestURI() + " HTTP/1.1\r\n")
|
||||
|
||||
// According to RFC 6874, an HTTP client, proxy, or other
|
||||
// intermediary must remove any IPv6 zone identifier attached
|
||||
// to an outgoing URI.
|
||||
bw.WriteString("Host: " + removeZone(config.Location.Host) + "\r\n")
|
||||
bw.WriteString("Upgrade: websocket\r\n")
|
||||
bw.WriteString("Connection: Upgrade\r\n")
|
||||
nonce := generateNonce()
|
||||
if config.handshakeData != nil {
|
||||
nonce = []byte(config.handshakeData["key"])
|
||||
}
|
||||
bw.WriteString("Sec-WebSocket-Key: " + string(nonce) + "\r\n")
|
||||
bw.WriteString("Origin: " + strings.ToLower(config.Origin.String()) + "\r\n")
|
||||
|
||||
if config.Version != ProtocolVersionHybi13 {
|
||||
return ErrBadProtocolVersion
|
||||
}
|
||||
|
||||
bw.WriteString("Sec-WebSocket-Version: " + fmt.Sprintf("%d", config.Version) + "\r\n")
|
||||
if len(config.Protocol) > 0 {
|
||||
bw.WriteString("Sec-WebSocket-Protocol: " + strings.Join(config.Protocol, ", ") + "\r\n")
|
||||
}
|
||||
// TODO(ukai): send Sec-WebSocket-Extensions.
|
||||
err = config.Header.WriteSubset(bw, handshakeHeader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bw.WriteString("\r\n")
|
||||
if err = bw.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := http.ReadResponse(br, &http.Request{Method: "GET"})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.StatusCode != 101 {
|
||||
return ErrBadStatus
|
||||
}
|
||||
if strings.ToLower(resp.Header.Get("Upgrade")) != "websocket" ||
|
||||
strings.ToLower(resp.Header.Get("Connection")) != "upgrade" {
|
||||
return ErrBadUpgrade
|
||||
}
|
||||
expectedAccept, err := getNonceAccept(nonce)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.Header.Get("Sec-WebSocket-Accept") != string(expectedAccept) {
|
||||
return ErrChallengeResponse
|
||||
}
|
||||
if resp.Header.Get("Sec-WebSocket-Extensions") != "" {
|
||||
return ErrUnsupportedExtensions
|
||||
}
|
||||
offeredProtocol := resp.Header.Get("Sec-WebSocket-Protocol")
|
||||
if offeredProtocol != "" {
|
||||
protocolMatched := false
|
||||
for i := 0; i < len(config.Protocol); i++ {
|
||||
if config.Protocol[i] == offeredProtocol {
|
||||
protocolMatched = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !protocolMatched {
|
||||
return ErrBadWebSocketProtocol
|
||||
}
|
||||
config.Protocol = []string{offeredProtocol}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// newHybiClientConn creates a client WebSocket connection after handshake.
|
||||
func newHybiClientConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser) *Conn {
|
||||
return newHybiConn(config, buf, rwc, nil)
|
||||
}
|
||||
|
||||
// A HybiServerHandshaker performs a server handshake using hybi draft protocol.
|
||||
type hybiServerHandshaker struct {
|
||||
*Config
|
||||
accept []byte
|
||||
}
|
||||
|
||||
func (c *hybiServerHandshaker) ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err error) {
|
||||
c.Version = ProtocolVersionHybi13
|
||||
if req.Method != "GET" {
|
||||
return http.StatusMethodNotAllowed, ErrBadRequestMethod
|
||||
}
|
||||
// HTTP version can be safely ignored.
|
||||
|
||||
if strings.ToLower(req.Header.Get("Upgrade")) != "websocket" ||
|
||||
!strings.Contains(strings.ToLower(req.Header.Get("Connection")), "upgrade") {
|
||||
return http.StatusBadRequest, ErrNotWebSocket
|
||||
}
|
||||
|
||||
key := req.Header.Get("Sec-Websocket-Key")
|
||||
if key == "" {
|
||||
return http.StatusBadRequest, ErrChallengeResponse
|
||||
}
|
||||
version := req.Header.Get("Sec-Websocket-Version")
|
||||
switch version {
|
||||
case "13":
|
||||
c.Version = ProtocolVersionHybi13
|
||||
default:
|
||||
return http.StatusBadRequest, ErrBadWebSocketVersion
|
||||
}
|
||||
var scheme string
|
||||
if req.TLS != nil {
|
||||
scheme = "wss"
|
||||
} else {
|
||||
scheme = "ws"
|
||||
}
|
||||
c.Location, err = url.ParseRequestURI(scheme + "://" + req.Host + req.URL.RequestURI())
|
||||
if err != nil {
|
||||
return http.StatusBadRequest, err
|
||||
}
|
||||
protocol := strings.TrimSpace(req.Header.Get("Sec-Websocket-Protocol"))
|
||||
if protocol != "" {
|
||||
protocols := strings.Split(protocol, ",")
|
||||
for i := 0; i < len(protocols); i++ {
|
||||
c.Protocol = append(c.Protocol, strings.TrimSpace(protocols[i]))
|
||||
}
|
||||
}
|
||||
c.accept, err = getNonceAccept([]byte(key))
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
return http.StatusSwitchingProtocols, nil
|
||||
}
|
||||
|
||||
// Origin parses the Origin header in req.
|
||||
// If the Origin header is not set, it returns nil and nil.
|
||||
func Origin(config *Config, req *http.Request) (*url.URL, error) {
|
||||
var origin string
|
||||
switch config.Version {
|
||||
case ProtocolVersionHybi13:
|
||||
origin = req.Header.Get("Origin")
|
||||
}
|
||||
if origin == "" {
|
||||
return nil, nil
|
||||
}
|
||||
return url.ParseRequestURI(origin)
|
||||
}
|
||||
|
||||
func (c *hybiServerHandshaker) AcceptHandshake(buf *bufio.Writer) (err error) {
|
||||
if len(c.Protocol) > 0 {
|
||||
if len(c.Protocol) != 1 {
|
||||
// You need choose a Protocol in Handshake func in Server.
|
||||
return ErrBadWebSocketProtocol
|
||||
}
|
||||
}
|
||||
buf.WriteString("HTTP/1.1 101 Switching Protocols\r\n")
|
||||
buf.WriteString("Upgrade: websocket\r\n")
|
||||
buf.WriteString("Connection: Upgrade\r\n")
|
||||
buf.WriteString("Sec-WebSocket-Accept: " + string(c.accept) + "\r\n")
|
||||
if len(c.Protocol) > 0 {
|
||||
buf.WriteString("Sec-WebSocket-Protocol: " + c.Protocol[0] + "\r\n")
|
||||
}
|
||||
// TODO(ukai): send Sec-WebSocket-Extensions.
|
||||
if c.Header != nil {
|
||||
err := c.Header.WriteSubset(buf, handshakeHeader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
buf.WriteString("\r\n")
|
||||
return buf.Flush()
|
||||
}
|
||||
|
||||
func (c *hybiServerHandshaker) NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn {
|
||||
return newHybiServerConn(c.Config, buf, rwc, request)
|
||||
}
|
||||
|
||||
// newHybiServerConn returns a new WebSocket connection speaking hybi draft protocol.
|
||||
func newHybiServerConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn {
|
||||
return newHybiConn(config, buf, rwc, request)
|
||||
}
|
113
vendor/golang.org/x/net/websocket/server.go
generated
vendored
Normal file
113
vendor/golang.org/x/net/websocket/server.go
generated
vendored
Normal file
@ -0,0 +1,113 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func newServerConn(rwc io.ReadWriteCloser, buf *bufio.ReadWriter, req *http.Request, config *Config, handshake func(*Config, *http.Request) error) (conn *Conn, err error) {
|
||||
var hs serverHandshaker = &hybiServerHandshaker{Config: config}
|
||||
code, err := hs.ReadHandshake(buf.Reader, req)
|
||||
if err == ErrBadWebSocketVersion {
|
||||
fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code))
|
||||
fmt.Fprintf(buf, "Sec-WebSocket-Version: %s\r\n", SupportedProtocolVersion)
|
||||
buf.WriteString("\r\n")
|
||||
buf.WriteString(err.Error())
|
||||
buf.Flush()
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code))
|
||||
buf.WriteString("\r\n")
|
||||
buf.WriteString(err.Error())
|
||||
buf.Flush()
|
||||
return
|
||||
}
|
||||
if handshake != nil {
|
||||
err = handshake(config, req)
|
||||
if err != nil {
|
||||
code = http.StatusForbidden
|
||||
fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code))
|
||||
buf.WriteString("\r\n")
|
||||
buf.Flush()
|
||||
return
|
||||
}
|
||||
}
|
||||
err = hs.AcceptHandshake(buf.Writer)
|
||||
if err != nil {
|
||||
code = http.StatusBadRequest
|
||||
fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code))
|
||||
buf.WriteString("\r\n")
|
||||
buf.Flush()
|
||||
return
|
||||
}
|
||||
conn = hs.NewServerConn(buf, rwc, req)
|
||||
return
|
||||
}
|
||||
|
||||
// Server represents a server of a WebSocket.
|
||||
type Server struct {
|
||||
// Config is a WebSocket configuration for new WebSocket connection.
|
||||
Config
|
||||
|
||||
// Handshake is an optional function in WebSocket handshake.
|
||||
// For example, you can check, or don't check Origin header.
|
||||
// Another example, you can select config.Protocol.
|
||||
Handshake func(*Config, *http.Request) error
|
||||
|
||||
// Handler handles a WebSocket connection.
|
||||
Handler
|
||||
}
|
||||
|
||||
// ServeHTTP implements the http.Handler interface for a WebSocket
|
||||
func (s Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
s.serveWebSocket(w, req)
|
||||
}
|
||||
|
||||
func (s Server) serveWebSocket(w http.ResponseWriter, req *http.Request) {
|
||||
rwc, buf, err := w.(http.Hijacker).Hijack()
|
||||
if err != nil {
|
||||
panic("Hijack failed: " + err.Error())
|
||||
}
|
||||
// The server should abort the WebSocket connection if it finds
|
||||
// the client did not send a handshake that matches with protocol
|
||||
// specification.
|
||||
defer rwc.Close()
|
||||
conn, err := newServerConn(rwc, buf, req, &s.Config, s.Handshake)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if conn == nil {
|
||||
panic("unexpected nil conn")
|
||||
}
|
||||
s.Handler(conn)
|
||||
}
|
||||
|
||||
// Handler is a simple interface to a WebSocket browser client.
|
||||
// It checks if Origin header is valid URL by default.
|
||||
// You might want to verify websocket.Conn.Config().Origin in the func.
|
||||
// If you use Server instead of Handler, you could call websocket.Origin and
|
||||
// check the origin in your Handshake func. So, if you want to accept
|
||||
// non-browser clients, which do not send an Origin header, set a
|
||||
// Server.Handshake that does not check the origin.
|
||||
type Handler func(*Conn)
|
||||
|
||||
func checkOrigin(config *Config, req *http.Request) (err error) {
|
||||
config.Origin, err = Origin(config, req)
|
||||
if err == nil && config.Origin == nil {
|
||||
return fmt.Errorf("null origin")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// ServeHTTP implements the http.Handler interface for a WebSocket
|
||||
func (h Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
s := Server{Handler: h, Handshake: checkOrigin}
|
||||
s.serveWebSocket(w, req)
|
||||
}
|
451
vendor/golang.org/x/net/websocket/websocket.go
generated
vendored
Normal file
451
vendor/golang.org/x/net/websocket/websocket.go
generated
vendored
Normal file
@ -0,0 +1,451 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package websocket implements a client and server for the WebSocket protocol
|
||||
// as specified in RFC 6455.
|
||||
//
|
||||
// This package currently lacks some features found in alternative
|
||||
// and more actively maintained WebSocket packages:
|
||||
//
|
||||
// https://godoc.org/github.com/gorilla/websocket
|
||||
// https://godoc.org/nhooyr.io/websocket
|
||||
package websocket // import "golang.org/x/net/websocket"
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
ProtocolVersionHybi13 = 13
|
||||
ProtocolVersionHybi = ProtocolVersionHybi13
|
||||
SupportedProtocolVersion = "13"
|
||||
|
||||
ContinuationFrame = 0
|
||||
TextFrame = 1
|
||||
BinaryFrame = 2
|
||||
CloseFrame = 8
|
||||
PingFrame = 9
|
||||
PongFrame = 10
|
||||
UnknownFrame = 255
|
||||
|
||||
DefaultMaxPayloadBytes = 32 << 20 // 32MB
|
||||
)
|
||||
|
||||
// ProtocolError represents WebSocket protocol errors.
|
||||
type ProtocolError struct {
|
||||
ErrorString string
|
||||
}
|
||||
|
||||
func (err *ProtocolError) Error() string { return err.ErrorString }
|
||||
|
||||
var (
|
||||
ErrBadProtocolVersion = &ProtocolError{"bad protocol version"}
|
||||
ErrBadScheme = &ProtocolError{"bad scheme"}
|
||||
ErrBadStatus = &ProtocolError{"bad status"}
|
||||
ErrBadUpgrade = &ProtocolError{"missing or bad upgrade"}
|
||||
ErrBadWebSocketOrigin = &ProtocolError{"missing or bad WebSocket-Origin"}
|
||||
ErrBadWebSocketLocation = &ProtocolError{"missing or bad WebSocket-Location"}
|
||||
ErrBadWebSocketProtocol = &ProtocolError{"missing or bad WebSocket-Protocol"}
|
||||
ErrBadWebSocketVersion = &ProtocolError{"missing or bad WebSocket Version"}
|
||||
ErrChallengeResponse = &ProtocolError{"mismatch challenge/response"}
|
||||
ErrBadFrame = &ProtocolError{"bad frame"}
|
||||
ErrBadFrameBoundary = &ProtocolError{"not on frame boundary"}
|
||||
ErrNotWebSocket = &ProtocolError{"not websocket protocol"}
|
||||
ErrBadRequestMethod = &ProtocolError{"bad method"}
|
||||
ErrNotSupported = &ProtocolError{"not supported"}
|
||||
)
|
||||
|
||||
// ErrFrameTooLarge is returned by Codec's Receive method if payload size
|
||||
// exceeds limit set by Conn.MaxPayloadBytes
|
||||
var ErrFrameTooLarge = errors.New("websocket: frame payload size exceeds limit")
|
||||
|
||||
// Addr is an implementation of net.Addr for WebSocket.
|
||||
type Addr struct {
|
||||
*url.URL
|
||||
}
|
||||
|
||||
// Network returns the network type for a WebSocket, "websocket".
|
||||
func (addr *Addr) Network() string { return "websocket" }
|
||||
|
||||
// Config is a WebSocket configuration
|
||||
type Config struct {
|
||||
// A WebSocket server address.
|
||||
Location *url.URL
|
||||
|
||||
// A Websocket client origin.
|
||||
Origin *url.URL
|
||||
|
||||
// WebSocket subprotocols.
|
||||
Protocol []string
|
||||
|
||||
// WebSocket protocol version.
|
||||
Version int
|
||||
|
||||
// TLS config for secure WebSocket (wss).
|
||||
TlsConfig *tls.Config
|
||||
|
||||
// Additional header fields to be sent in WebSocket opening handshake.
|
||||
Header http.Header
|
||||
|
||||
// Dialer used when opening websocket connections.
|
||||
Dialer *net.Dialer
|
||||
|
||||
handshakeData map[string]string
|
||||
}
|
||||
|
||||
// serverHandshaker is an interface to handle WebSocket server side handshake.
|
||||
type serverHandshaker interface {
|
||||
// ReadHandshake reads handshake request message from client.
|
||||
// Returns http response code and error if any.
|
||||
ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err error)
|
||||
|
||||
// AcceptHandshake accepts the client handshake request and sends
|
||||
// handshake response back to client.
|
||||
AcceptHandshake(buf *bufio.Writer) (err error)
|
||||
|
||||
// NewServerConn creates a new WebSocket connection.
|
||||
NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) (conn *Conn)
|
||||
}
|
||||
|
||||
// frameReader is an interface to read a WebSocket frame.
|
||||
type frameReader interface {
|
||||
// Reader is to read payload of the frame.
|
||||
io.Reader
|
||||
|
||||
// PayloadType returns payload type.
|
||||
PayloadType() byte
|
||||
|
||||
// HeaderReader returns a reader to read header of the frame.
|
||||
HeaderReader() io.Reader
|
||||
|
||||
// TrailerReader returns a reader to read trailer of the frame.
|
||||
// If it returns nil, there is no trailer in the frame.
|
||||
TrailerReader() io.Reader
|
||||
|
||||
// Len returns total length of the frame, including header and trailer.
|
||||
Len() int
|
||||
}
|
||||
|
||||
// frameReaderFactory is an interface to creates new frame reader.
|
||||
type frameReaderFactory interface {
|
||||
NewFrameReader() (r frameReader, err error)
|
||||
}
|
||||
|
||||
// frameWriter is an interface to write a WebSocket frame.
|
||||
type frameWriter interface {
|
||||
// Writer is to write payload of the frame.
|
||||
io.WriteCloser
|
||||
}
|
||||
|
||||
// frameWriterFactory is an interface to create new frame writer.
|
||||
type frameWriterFactory interface {
|
||||
NewFrameWriter(payloadType byte) (w frameWriter, err error)
|
||||
}
|
||||
|
||||
type frameHandler interface {
|
||||
HandleFrame(frame frameReader) (r frameReader, err error)
|
||||
WriteClose(status int) (err error)
|
||||
}
|
||||
|
||||
// Conn represents a WebSocket connection.
|
||||
//
|
||||
// Multiple goroutines may invoke methods on a Conn simultaneously.
|
||||
type Conn struct {
|
||||
config *Config
|
||||
request *http.Request
|
||||
|
||||
buf *bufio.ReadWriter
|
||||
rwc io.ReadWriteCloser
|
||||
|
||||
rio sync.Mutex
|
||||
frameReaderFactory
|
||||
frameReader
|
||||
|
||||
wio sync.Mutex
|
||||
frameWriterFactory
|
||||
|
||||
frameHandler
|
||||
PayloadType byte
|
||||
defaultCloseStatus int
|
||||
|
||||
// MaxPayloadBytes limits the size of frame payload received over Conn
|
||||
// by Codec's Receive method. If zero, DefaultMaxPayloadBytes is used.
|
||||
MaxPayloadBytes int
|
||||
}
|
||||
|
||||
// Read implements the io.Reader interface:
|
||||
// it reads data of a frame from the WebSocket connection.
|
||||
// if msg is not large enough for the frame data, it fills the msg and next Read
|
||||
// will read the rest of the frame data.
|
||||
// it reads Text frame or Binary frame.
|
||||
func (ws *Conn) Read(msg []byte) (n int, err error) {
|
||||
ws.rio.Lock()
|
||||
defer ws.rio.Unlock()
|
||||
again:
|
||||
if ws.frameReader == nil {
|
||||
frame, err := ws.frameReaderFactory.NewFrameReader()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
ws.frameReader, err = ws.frameHandler.HandleFrame(frame)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if ws.frameReader == nil {
|
||||
goto again
|
||||
}
|
||||
}
|
||||
n, err = ws.frameReader.Read(msg)
|
||||
if err == io.EOF {
|
||||
if trailer := ws.frameReader.TrailerReader(); trailer != nil {
|
||||
io.Copy(ioutil.Discard, trailer)
|
||||
}
|
||||
ws.frameReader = nil
|
||||
goto again
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
// Write implements the io.Writer interface:
|
||||
// it writes data as a frame to the WebSocket connection.
|
||||
func (ws *Conn) Write(msg []byte) (n int, err error) {
|
||||
ws.wio.Lock()
|
||||
defer ws.wio.Unlock()
|
||||
w, err := ws.frameWriterFactory.NewFrameWriter(ws.PayloadType)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
n, err = w.Write(msg)
|
||||
w.Close()
|
||||
return n, err
|
||||
}
|
||||
|
||||
// Close implements the io.Closer interface.
|
||||
func (ws *Conn) Close() error {
|
||||
err := ws.frameHandler.WriteClose(ws.defaultCloseStatus)
|
||||
err1 := ws.rwc.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return err1
|
||||
}
|
||||
|
||||
// IsClientConn reports whether ws is a client-side connection.
|
||||
func (ws *Conn) IsClientConn() bool { return ws.request == nil }
|
||||
|
||||
// IsServerConn reports whether ws is a server-side connection.
|
||||
func (ws *Conn) IsServerConn() bool { return ws.request != nil }
|
||||
|
||||
// LocalAddr returns the WebSocket Origin for the connection for client, or
|
||||
// the WebSocket location for server.
|
||||
func (ws *Conn) LocalAddr() net.Addr {
|
||||
if ws.IsClientConn() {
|
||||
return &Addr{ws.config.Origin}
|
||||
}
|
||||
return &Addr{ws.config.Location}
|
||||
}
|
||||
|
||||
// RemoteAddr returns the WebSocket location for the connection for client, or
|
||||
// the Websocket Origin for server.
|
||||
func (ws *Conn) RemoteAddr() net.Addr {
|
||||
if ws.IsClientConn() {
|
||||
return &Addr{ws.config.Location}
|
||||
}
|
||||
return &Addr{ws.config.Origin}
|
||||
}
|
||||
|
||||
var errSetDeadline = errors.New("websocket: cannot set deadline: not using a net.Conn")
|
||||
|
||||
// SetDeadline sets the connection's network read & write deadlines.
|
||||
func (ws *Conn) SetDeadline(t time.Time) error {
|
||||
if conn, ok := ws.rwc.(net.Conn); ok {
|
||||
return conn.SetDeadline(t)
|
||||
}
|
||||
return errSetDeadline
|
||||
}
|
||||
|
||||
// SetReadDeadline sets the connection's network read deadline.
|
||||
func (ws *Conn) SetReadDeadline(t time.Time) error {
|
||||
if conn, ok := ws.rwc.(net.Conn); ok {
|
||||
return conn.SetReadDeadline(t)
|
||||
}
|
||||
return errSetDeadline
|
||||
}
|
||||
|
||||
// SetWriteDeadline sets the connection's network write deadline.
|
||||
func (ws *Conn) SetWriteDeadline(t time.Time) error {
|
||||
if conn, ok := ws.rwc.(net.Conn); ok {
|
||||
return conn.SetWriteDeadline(t)
|
||||
}
|
||||
return errSetDeadline
|
||||
}
|
||||
|
||||
// Config returns the WebSocket config.
|
||||
func (ws *Conn) Config() *Config { return ws.config }
|
||||
|
||||
// Request returns the http request upgraded to the WebSocket.
|
||||
// It is nil for client side.
|
||||
func (ws *Conn) Request() *http.Request { return ws.request }
|
||||
|
||||
// Codec represents a symmetric pair of functions that implement a codec.
|
||||
type Codec struct {
|
||||
Marshal func(v interface{}) (data []byte, payloadType byte, err error)
|
||||
Unmarshal func(data []byte, payloadType byte, v interface{}) (err error)
|
||||
}
|
||||
|
||||
// Send sends v marshaled by cd.Marshal as single frame to ws.
|
||||
func (cd Codec) Send(ws *Conn, v interface{}) (err error) {
|
||||
data, payloadType, err := cd.Marshal(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ws.wio.Lock()
|
||||
defer ws.wio.Unlock()
|
||||
w, err := ws.frameWriterFactory.NewFrameWriter(payloadType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(data)
|
||||
w.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
// Receive receives single frame from ws, unmarshaled by cd.Unmarshal and stores
|
||||
// in v. The whole frame payload is read to an in-memory buffer; max size of
|
||||
// payload is defined by ws.MaxPayloadBytes. If frame payload size exceeds
|
||||
// limit, ErrFrameTooLarge is returned; in this case frame is not read off wire
|
||||
// completely. The next call to Receive would read and discard leftover data of
|
||||
// previous oversized frame before processing next frame.
|
||||
func (cd Codec) Receive(ws *Conn, v interface{}) (err error) {
|
||||
ws.rio.Lock()
|
||||
defer ws.rio.Unlock()
|
||||
if ws.frameReader != nil {
|
||||
_, err = io.Copy(ioutil.Discard, ws.frameReader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ws.frameReader = nil
|
||||
}
|
||||
again:
|
||||
frame, err := ws.frameReaderFactory.NewFrameReader()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
frame, err = ws.frameHandler.HandleFrame(frame)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if frame == nil {
|
||||
goto again
|
||||
}
|
||||
maxPayloadBytes := ws.MaxPayloadBytes
|
||||
if maxPayloadBytes == 0 {
|
||||
maxPayloadBytes = DefaultMaxPayloadBytes
|
||||
}
|
||||
if hf, ok := frame.(*hybiFrameReader); ok && hf.header.Length > int64(maxPayloadBytes) {
|
||||
// payload size exceeds limit, no need to call Unmarshal
|
||||
//
|
||||
// set frameReader to current oversized frame so that
|
||||
// the next call to this function can drain leftover
|
||||
// data before processing the next frame
|
||||
ws.frameReader = frame
|
||||
return ErrFrameTooLarge
|
||||
}
|
||||
payloadType := frame.PayloadType()
|
||||
data, err := ioutil.ReadAll(frame)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return cd.Unmarshal(data, payloadType, v)
|
||||
}
|
||||
|
||||
func marshal(v interface{}) (msg []byte, payloadType byte, err error) {
|
||||
switch data := v.(type) {
|
||||
case string:
|
||||
return []byte(data), TextFrame, nil
|
||||
case []byte:
|
||||
return data, BinaryFrame, nil
|
||||
}
|
||||
return nil, UnknownFrame, ErrNotSupported
|
||||
}
|
||||
|
||||
func unmarshal(msg []byte, payloadType byte, v interface{}) (err error) {
|
||||
switch data := v.(type) {
|
||||
case *string:
|
||||
*data = string(msg)
|
||||
return nil
|
||||
case *[]byte:
|
||||
*data = msg
|
||||
return nil
|
||||
}
|
||||
return ErrNotSupported
|
||||
}
|
||||
|
||||
/*
|
||||
Message is a codec to send/receive text/binary data in a frame on WebSocket connection.
|
||||
To send/receive text frame, use string type.
|
||||
To send/receive binary frame, use []byte type.
|
||||
|
||||
Trivial usage:
|
||||
|
||||
import "websocket"
|
||||
|
||||
// receive text frame
|
||||
var message string
|
||||
websocket.Message.Receive(ws, &message)
|
||||
|
||||
// send text frame
|
||||
message = "hello"
|
||||
websocket.Message.Send(ws, message)
|
||||
|
||||
// receive binary frame
|
||||
var data []byte
|
||||
websocket.Message.Receive(ws, &data)
|
||||
|
||||
// send binary frame
|
||||
data = []byte{0, 1, 2}
|
||||
websocket.Message.Send(ws, data)
|
||||
|
||||
*/
|
||||
var Message = Codec{marshal, unmarshal}
|
||||
|
||||
func jsonMarshal(v interface{}) (msg []byte, payloadType byte, err error) {
|
||||
msg, err = json.Marshal(v)
|
||||
return msg, TextFrame, err
|
||||
}
|
||||
|
||||
func jsonUnmarshal(msg []byte, payloadType byte, v interface{}) (err error) {
|
||||
return json.Unmarshal(msg, v)
|
||||
}
|
||||
|
||||
/*
|
||||
JSON is a codec to send/receive JSON data in a frame from a WebSocket connection.
|
||||
|
||||
Trivial usage:
|
||||
|
||||
import "websocket"
|
||||
|
||||
type T struct {
|
||||
Msg string
|
||||
Count int
|
||||
}
|
||||
|
||||
// receive JSON type T
|
||||
var data T
|
||||
websocket.JSON.Receive(ws, &data)
|
||||
|
||||
// send JSON type T
|
||||
websocket.JSON.Send(ws, data)
|
||||
*/
|
||||
var JSON = Codec{jsonMarshal, jsonUnmarshal}
|
43
vendor/modules.txt
vendored
Normal file
43
vendor/modules.txt
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
# github.com/cyrilix/robocar-base v0.0.0-20191227154304-47d48c39b0a2
|
||||
github.com/cyrilix/robocar-base/cli
|
||||
github.com/cyrilix/robocar-base/mqttdevice
|
||||
github.com/cyrilix/robocar-base/service
|
||||
github.com/cyrilix/robocar-base/types
|
||||
# github.com/eclipse/paho.mqtt.golang v1.2.0
|
||||
github.com/eclipse/paho.mqtt.golang
|
||||
github.com/eclipse/paho.mqtt.golang/packets
|
||||
# golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933
|
||||
golang.org/x/net/internal/socks
|
||||
golang.org/x/net/proxy
|
||||
golang.org/x/net/websocket
|
||||
# periph.io/x/periph v3.6.2+incompatible
|
||||
periph.io/x/periph
|
||||
periph.io/x/periph/conn
|
||||
periph.io/x/periph/conn/gpio
|
||||
periph.io/x/periph/conn/gpio/gpioreg
|
||||
periph.io/x/periph/conn/gpio/gpiostream
|
||||
periph.io/x/periph/conn/i2c
|
||||
periph.io/x/periph/conn/i2c/i2creg
|
||||
periph.io/x/periph/conn/physic
|
||||
periph.io/x/periph/conn/pin
|
||||
periph.io/x/periph/conn/pin/pinreg
|
||||
periph.io/x/periph/conn/spi
|
||||
periph.io/x/periph/conn/spi/spireg
|
||||
periph.io/x/periph/experimental/devices/pca9685
|
||||
periph.io/x/periph/host
|
||||
periph.io/x/periph/host/allwinner
|
||||
periph.io/x/periph/host/am335x
|
||||
periph.io/x/periph/host/bcm283x
|
||||
periph.io/x/periph/host/beagle/black
|
||||
periph.io/x/periph/host/beagle/bone
|
||||
periph.io/x/periph/host/beagle/green
|
||||
periph.io/x/periph/host/chip
|
||||
periph.io/x/periph/host/cpu
|
||||
periph.io/x/periph/host/distro
|
||||
periph.io/x/periph/host/fs
|
||||
periph.io/x/periph/host/odroidc1
|
||||
periph.io/x/periph/host/pine64
|
||||
periph.io/x/periph/host/pmem
|
||||
periph.io/x/periph/host/rpi
|
||||
periph.io/x/periph/host/sysfs
|
||||
periph.io/x/periph/host/videocore
|
1
vendor/periph.io/x/periph/.gitignore
generated
vendored
Normal file
1
vendor/periph.io/x/periph/.gitignore
generated
vendored
Normal file
@ -0,0 +1 @@
|
||||
bin
|
254
vendor/periph.io/x/periph/.gohci.yml
generated
vendored
Normal file
254
vendor/periph.io/x/periph/.gohci.yml
generated
vendored
Normal file
@ -0,0 +1,254 @@
|
||||
# See https://github.com/periph/gohci
|
||||
version: 1
|
||||
workers:
|
||||
# BeagleBone Green Wireles by SeedStudio.
|
||||
# https://beagleboard.org/green-wireless
|
||||
- name: beaglebone-4373
|
||||
checks:
|
||||
- cmd:
|
||||
- go
|
||||
- install
|
||||
- -v
|
||||
- ./cmd/headers-list
|
||||
- ./cmd/i2c-list
|
||||
- ./cmd/periph-info
|
||||
- ./cmd/periph-smoketest
|
||||
- ./cmd/spi-list
|
||||
- cmd:
|
||||
- periph-info
|
||||
- cmd:
|
||||
- headers-list
|
||||
- -f
|
||||
- cmd:
|
||||
- i2c-list
|
||||
- cmd:
|
||||
- spi-list
|
||||
- cmd:
|
||||
- periph-smoketest
|
||||
- gpio
|
||||
- -pin1
|
||||
- P8_45
|
||||
- -pin2
|
||||
- P8_46
|
||||
# C.H.I.P. by Next Thing Co. The company closed its doors.
|
||||
- name: chip-a87d
|
||||
checks:
|
||||
- cmd:
|
||||
- go
|
||||
- install
|
||||
- -v
|
||||
- ./cmd/gpio-list
|
||||
- ./cmd/headers-list
|
||||
- ./cmd/i2c-list
|
||||
- ./cmd/periph-info
|
||||
- ./cmd/periph-smoketest
|
||||
- ./cmd/spi-list
|
||||
- cmd:
|
||||
- periph-info
|
||||
- cmd:
|
||||
- gpio-list
|
||||
- -f
|
||||
- cmd:
|
||||
- headers-list
|
||||
- -f
|
||||
- cmd:
|
||||
- i2c-list
|
||||
- cmd:
|
||||
- spi-list
|
||||
- cmd:
|
||||
- periph-smoketest
|
||||
- chip
|
||||
- cmd:
|
||||
- periph-smoketest
|
||||
- i2c-testboard
|
||||
- -bus
|
||||
- 1
|
||||
- cmd:
|
||||
- periph-smoketest
|
||||
- onewire-testboard
|
||||
- -i2cbus
|
||||
- 1
|
||||
- cmd:
|
||||
- periph-smoketest
|
||||
- sysfs-benchmark
|
||||
- -p
|
||||
- 1013
|
||||
- -short
|
||||
- cmd:
|
||||
- periph-smoketest
|
||||
- sysfs-benchmark
|
||||
- -p
|
||||
- 132
|
||||
- -short
|
||||
- cmd:
|
||||
- periph-smoketest
|
||||
- allwinner-benchmark
|
||||
- -p
|
||||
- 132
|
||||
- -short
|
||||
- cmd:
|
||||
- periph-smoketest
|
||||
- spi-testboard
|
||||
# Old MacBook Pro on 10.9.
|
||||
- name: mbp
|
||||
checks:
|
||||
- cmd:
|
||||
- go
|
||||
- test
|
||||
- -race
|
||||
- ./...
|
||||
- cmd:
|
||||
- go
|
||||
- install
|
||||
- -v
|
||||
- ./cmd/gpio-list
|
||||
- ./cmd/headers-list
|
||||
- ./cmd/i2c-list
|
||||
- ./cmd/periph-info
|
||||
- ./cmd/spi-list
|
||||
- cmd:
|
||||
- periph-info
|
||||
- cmd:
|
||||
- gpio-list
|
||||
- -f
|
||||
- cmd:
|
||||
- headers-list
|
||||
- -f
|
||||
- cmd:
|
||||
- i2c-list
|
||||
- cmd:
|
||||
- spi-list
|
||||
# ODROID-C1+ by HardKernel
|
||||
# https://www.hardkernel.com/shop/odroid-c1/
|
||||
- name: odroid-483d
|
||||
checks:
|
||||
- cmd:
|
||||
- go
|
||||
- test
|
||||
- -cover
|
||||
- -bench=.
|
||||
- -benchtime=1000ms
|
||||
- -benchmem
|
||||
- ./...
|
||||
- ./...
|
||||
- cmd:
|
||||
- go
|
||||
- install
|
||||
- -v
|
||||
- ./cmd/gpio-list
|
||||
- ./cmd/headers-list
|
||||
- ./cmd/i2c-list
|
||||
- ./cmd/periph-info
|
||||
- ./cmd/periph-smoketest
|
||||
- ./cmd/spi-list
|
||||
- cmd:
|
||||
- periph-info
|
||||
- cmd:
|
||||
- gpio-list
|
||||
- -f
|
||||
- cmd:
|
||||
- headers-list
|
||||
- -f
|
||||
- cmd:
|
||||
- i2c-list
|
||||
- cmd:
|
||||
- spi-list
|
||||
- cmd:
|
||||
- periph-smoketest
|
||||
- odroid-c1
|
||||
- cmd:
|
||||
- periph-smoketest
|
||||
- i2c-testboard
|
||||
- cmd:
|
||||
- periph-smoketest
|
||||
- onewire-testboard
|
||||
- cmd:
|
||||
- periph-smoketest
|
||||
- spi-testboard
|
||||
- cmd:
|
||||
- periph-smoketest
|
||||
- sysfs-benchmark
|
||||
- -p
|
||||
- 97
|
||||
- -short
|
||||
# Raspberry Pi 3
|
||||
- name: raspberrypi-2f34
|
||||
checks:
|
||||
- cmd:
|
||||
- go
|
||||
- install
|
||||
- -v
|
||||
- ./cmd/gpio-list
|
||||
- ./cmd/headers-list
|
||||
- ./cmd/i2c-list
|
||||
- ./cmd/periph-info
|
||||
- ./cmd/periph-smoketest
|
||||
- ./cmd/spi-list
|
||||
- cmd:
|
||||
- periph-info
|
||||
- cmd:
|
||||
- gpio-list
|
||||
- -f
|
||||
- cmd:
|
||||
- headers-list
|
||||
- -f
|
||||
- cmd:
|
||||
- i2c-list
|
||||
- cmd:
|
||||
- spi-list
|
||||
- cmd:
|
||||
- periph-smoketest
|
||||
- i2c-testboard
|
||||
- cmd:
|
||||
- periph-smoketest
|
||||
- onewire-testboard
|
||||
- -i2cbus
|
||||
- 1
|
||||
- cmd:
|
||||
- periph-smoketest
|
||||
- spi-testboard
|
||||
- cmd:
|
||||
- periph-smoketest
|
||||
- sysfs-benchmark
|
||||
- -p
|
||||
- 12
|
||||
- -short
|
||||
- cmd:
|
||||
- periph-smoketest
|
||||
- bcm283x-benchmark
|
||||
- -p
|
||||
- 12
|
||||
- -short
|
||||
- cmd:
|
||||
- periph-smoketest
|
||||
- gpio
|
||||
- -pin1
|
||||
- P1_15
|
||||
- -pin2
|
||||
- P1_16
|
||||
- cmd:
|
||||
- periph-smoketest
|
||||
- bcm283x
|
||||
- -quick
|
||||
# Laptop on Windows 10.
|
||||
- name: win10
|
||||
checks:
|
||||
- cmd:
|
||||
- go
|
||||
- test
|
||||
- -cover
|
||||
- -bench=.
|
||||
- -benchtime=1000ms
|
||||
- -benchmem
|
||||
- ./...
|
||||
- cmd:
|
||||
- go
|
||||
- test
|
||||
- -race
|
||||
- ./...
|
||||
- cmd:
|
||||
- go
|
||||
- vet
|
||||
- -all
|
||||
- -unsafeptr=false
|
||||
- ./...
|
53
vendor/periph.io/x/periph/.travis.yml
generated
vendored
Normal file
53
vendor/periph.io/x/periph/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
# Copyright 2019 The Periph Authors. All rights reserved.
|
||||
# Use of this source code is governed under the Apache License, Version 2.0
|
||||
# that can be found in the LICENSE file.
|
||||
|
||||
language: go
|
||||
sudo: false
|
||||
dist: xenial
|
||||
go_import_path: periph.io/x/periph
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- go: 1.12.x
|
||||
env: GO111MODULE=on
|
||||
cache:
|
||||
directories:
|
||||
# go1.10+ 'go test' cache on linux (macOS and Windows are # different).
|
||||
- $HOME/.cache/go-build
|
||||
# go1.11+ with GO111MODULE=on
|
||||
- $GOPATH/pkg/mod
|
||||
# Cache tools sources. Manually verified that both misspell and ineffassign
|
||||
# only depend on the stdlib.
|
||||
- $GOPATH/src/github\.com
|
||||
# For shadow.
|
||||
- $GOPATH/src/golang\.org
|
||||
# Dear future me: if you touch this line, don't forget to update the
|
||||
# conditions below!
|
||||
- go: 1.7.6
|
||||
|
||||
before_script:
|
||||
- echo $TRAVIS_GO_VERSION
|
||||
- go get -t -v periph.io/x/periph/...
|
||||
- if [[ $TRAVIS_GO_VERSION != 1.7.6 ]]; then go get -u -v github.com/client9/misspell/cmd/misspell github.com/gordonklaus/ineffassign golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow; fi
|
||||
|
||||
script:
|
||||
# Checks run on latest version.
|
||||
- if [[ $TRAVIS_GO_VERSION != 1.7.6 ]]; then echo 'Check Code is well formatted'; ! gofmt -s -d . | read; fi
|
||||
- if [[ $TRAVIS_GO_VERSION != 1.7.6 ]]; then echo 'Looking for external dependencies:'; go list -f '{{join .Imports "\n"}}' periph.io/x/periph/... | sort | uniq | grep -v ^periph.io/x/periph | xargs go list -f '{{if not .Standard}}- {{.ImportPath}}{{end}}'; fi
|
||||
- if [[ $TRAVIS_GO_VERSION != 1.7.6 ]]; then echo 'Erroring on external dependencies:'; ! go list -f '{{join .Imports "\n"}}' periph.io/x/periph/... | sort | uniq | grep -v ^periph.io/x/periph | xargs go list -f '{{if not .Standard}}Remove {{.ImportPath}}{{end}}' | grep -q Remove; fi
|
||||
- if [[ $TRAVIS_GO_VERSION != 1.7.6 ]]; then echo 'Erroring on /host depending on /devices:'; ! go list -f '{{.ImportPath}} depends on {{join .Imports ", "}}' periph.io/x/periph/host/... | sort | uniq | grep periph.io/x/periph/devices; fi
|
||||
- if [[ $TRAVIS_GO_VERSION != 1.7.6 ]]; then echo 'Erroring on /conn depending on /devices:'; ! go list -f '{{.ImportPath}} depends on {{join .Imports ", "}}' periph.io/x/periph/conn/... | sort | uniq | grep periph.io/x/periph/devices; fi
|
||||
- if [[ $TRAVIS_GO_VERSION != 1.7.6 ]]; then echo 'Erroring on /conn depending on /host:'; ! go list -f '{{.ImportPath}} depends on {{join .Imports ", "}}' periph.io/x/periph/conn/... | sort | uniq | grep periph.io/x/periph/host; fi
|
||||
- if [[ $TRAVIS_GO_VERSION != 1.7.6 ]]; then echo 'Erroring on misspelling'; ! misspell . | grep a; fi
|
||||
- if [[ $TRAVIS_GO_VERSION != 1.7.6 ]]; then ineffassign .; fi
|
||||
- if [[ $TRAVIS_GO_VERSION != 1.7.6 ]]; then ! go vet -vettool=$(which shadow) ./... |& grep -v '"err"' | grep -e '^[^#]'; fi
|
||||
- if [[ $TRAVIS_GO_VERSION != 1.7.6 ]]; then bash -c 'set -e; echo "" > coverage.txt; for d in $(go list ./...); do go test -covermode=count -coverprofile=p.out $d; if [ -f p.out ]; then cat p.out >> coverage.txt; rm p.out; fi; done'; fi
|
||||
- if [[ $TRAVIS_GO_VERSION != 1.7.6 ]]; then go test -race ./...; fi
|
||||
|
||||
# Checks run on older version.
|
||||
- if [[ $TRAVIS_GO_VERSION == 1.7.6 ]]; then go test ./...; fi
|
||||
- if [[ $TRAVIS_GO_VERSION == 1.7.6 ]]; then if find . -path ./.git -prune -o -type f -executable -print | grep -e . ; then echo 'Do not commit executables'; false; fi; fi
|
||||
|
||||
after_success:
|
||||
- if [[ $TRAVIS_GO_VERSION != 1.7.6 ]]; then bash <(curl -s https://codecov.io/bash); fi
|
15
vendor/periph.io/x/periph/AUTHORS
generated
vendored
Normal file
15
vendor/periph.io/x/periph/AUTHORS
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
# This is the list of The Periph Authors for copyright purposes.
|
||||
#
|
||||
# This does not necessarily list everyone who has contributed code, since in
|
||||
# some cases, their employer may be the copyright holder. To see the full list
|
||||
# of contributors, see the revision history in source control.
|
||||
Cássio Botaro <cassiobotaro@gmail.com>
|
||||
Fractal Industries, Inc
|
||||
Google Inc.
|
||||
Josh Gardiner
|
||||
Matt Aimonetti <mattaimonetti@gmail.com>
|
||||
Max Ekman <max@looplab.se>
|
||||
Rifiniti, Inc
|
||||
Stephan Sperber
|
||||
Thorsten von Eicken <tve@voneicken.com>
|
||||
|
4
vendor/periph.io/x/periph/CONTRIBUTING.md
generated
vendored
Normal file
4
vendor/periph.io/x/periph/CONTRIBUTING.md
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
# Contributing
|
||||
|
||||
Thanks for contributing to the project! Please look at [the periph contribution
|
||||
guidelines](https://periph.io/project/contributing/) first.
|
41
vendor/periph.io/x/periph/CONTRIBUTORS
generated
vendored
Normal file
41
vendor/periph.io/x/periph/CONTRIBUTORS
generated
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
# This is the official list of people who can contribute
|
||||
# (and typically have contributed) code to the periph repository.
|
||||
# The AUTHORS file lists the copyright holders; this file
|
||||
# lists people. For example, Google employees are listed here
|
||||
# but not in AUTHORS, because Google holds the copyright.
|
||||
#
|
||||
# Names should be added to this file only after verifying that
|
||||
# the individual or the individual's organization has agreed to
|
||||
# the appropriate Contributor License Agreement, found here:
|
||||
#
|
||||
# https://cla.developers.google.com/
|
||||
#
|
||||
# When adding J Random Contributor's name to this file,
|
||||
# either J's name or J's organization's name should be
|
||||
# added to the AUTHORS file, depending on whether the
|
||||
# individual or corporate CLA was used.
|
||||
|
||||
# Names should be added to this file like so:
|
||||
# Individual's name <submission email address>
|
||||
# Individual's name <submission email address> <email2> <emailN>
|
||||
#
|
||||
# An entry with multiple email addresses specifies that the
|
||||
# first address should be used in the submit logs and
|
||||
# that the other addresses should be recognized as the
|
||||
# same person when interacting with Gerrit.
|
||||
|
||||
# Please keep the list sorted.
|
||||
|
||||
Cássio Botaro <cassiobotaro@gmail.com>
|
||||
Eugene Dzhurynsky <jdevelop@gmail.com>
|
||||
Hidetoshi Shimokawa <smkwhdts@gmail.com>
|
||||
John Maguire <john.maguire@gmail.com>
|
||||
Josh Gardiner <josh@zool.com>
|
||||
Marc-Antoine Ruel <maruel@chromium.org> <maruel@gmail.com>
|
||||
Matt Aimonetti <mattaimonetti@gmail.com>
|
||||
Max Ekman <max@looplab.se>
|
||||
Matias Insaurralde <matias@insaurral.de>
|
||||
Seán C McCord <ulexus@gmail.com> <scm@cycoresys.com>
|
||||
Stephan Sperber <sperberstephan@googlemail.com>
|
||||
Thorsten von Eicken <tve@voneicken.com>
|
||||
|
202
vendor/periph.io/x/periph/LICENSE
generated
vendored
Normal file
202
vendor/periph.io/x/periph/LICENSE
generated
vendored
Normal file
@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
59
vendor/periph.io/x/periph/Makefile
generated
vendored
Normal file
59
vendor/periph.io/x/periph/Makefile
generated
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
# Copyright 2016 The Periph Authors. All rights reserved.
|
||||
# Use of this source code is governed under the Apache License, Version 2.0
|
||||
# that can be found in the LICENSE file.
|
||||
|
||||
# This Makefile captures common tasks for the periph library. The hope is that this Makefile can remain
|
||||
# simple and straightforward...
|
||||
|
||||
# *** This Makefile is a work in progress, please help impove it! ***
|
||||
|
||||
# not sure yet what all should do...
|
||||
all:
|
||||
@echo Available targets: test build
|
||||
|
||||
.PHONY: all test clean depend
|
||||
|
||||
# test runs the platform independent tests
|
||||
# (gofmt|grep is used to obtain a non-zero exit status if the formatting is off)
|
||||
test:
|
||||
go test ./...
|
||||
@if gofmt -l . | grep .go; then \
|
||||
echo "Repo contains improperly formatted go files; run gofmt on above files" && exit 1; \
|
||||
else echo "OK gofmt"; fi
|
||||
-go vet -unsafeptr=false ./...
|
||||
|
||||
# BUILD
|
||||
#
|
||||
# The build target cross compiles each program in cmd to a binary for each platform in the bin
|
||||
# directory. It is assumed that each command has a main.go file in its directory. Trying to keep all
|
||||
# this relatively simple and not descend into makefile hell...
|
||||
|
||||
# Get a list of all main.go in cmd subdirs
|
||||
# MAINS becomes: cmd/gpio-list/main.go cmd/periph-info/main.go ...
|
||||
MAINS := $(wildcard cmd/*/main.go)
|
||||
# Get a list of all the commands, i.e. names of dirs that contain a main.go
|
||||
# CMDS becomes: gpio-list periph-info ...
|
||||
CMDS := $(patsubst cmd/%/main.go,%,$(MAINS))
|
||||
# Get a list of binaries to build
|
||||
# BINS becomes: bin/gpio-list-arm bin/periph-info-arm ... bin/gpio-list-arm64 bin/periph-info-arm64 ...
|
||||
ARCHS := arm arm64 amd64 win64.exe
|
||||
BINS=$(foreach arch,$(ARCHS),$(foreach cmd,$(CMDS),bin/$(cmd)-$(arch)))
|
||||
|
||||
build: depend bin $(BINS)
|
||||
bin:
|
||||
mkdir bin
|
||||
|
||||
# Rules to build binaries for a command in cmd. The prereqs could be improved...
|
||||
bin/%-arm: cmd/%/*.go
|
||||
GOARCH=arm GOOS=linux go build -o $@ ./cmd/$*
|
||||
bin/%-arm64: cmd/%/*.go
|
||||
GOARCH=arm64 GOOS=linux go build -o $@ ./cmd/$*
|
||||
bin/%-amd64: cmd/%/*.go
|
||||
GOARCH=amd64 GOOS=linux go build -o $@ ./cmd/$*
|
||||
bin/%-win64.exe: cmd/%/*.go
|
||||
GOARCH=amd64 GOOS=windows go build -o $@ ./cmd/$*
|
||||
|
||||
# clean removes all compiled binaries
|
||||
clean:
|
||||
rm bin/*-*
|
||||
rmdir bin
|
59
vendor/periph.io/x/periph/README.md
generated
vendored
Normal file
59
vendor/periph.io/x/periph/README.md
generated
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
# periph - Peripherals I/O in Go
|
||||
|
||||
[](https://periph.io/)
|
||||
|
||||
Documentation is at https://periph.io
|
||||
|
||||
[](https://godoc.org/periph.io/x/periph)
|
||||
[](https://goreportcard.com/report/periph.io/x/periph)
|
||||
[](https://codecov.io/gh/google/periph)
|
||||
[](https://travis-ci.org/google/periph)
|
||||
|
||||
|
||||
Join us for a chat on
|
||||
[gophers.slack.com/messages/periph](https://gophers.slack.com/messages/periph),
|
||||
get an [invite here](https://invite.slack.golangbridge.org/).
|
||||
|
||||
|
||||
## Example
|
||||
|
||||
Blink a LED:
|
||||
|
||||
~~~go
|
||||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
"periph.io/x/periph/conn/gpio"
|
||||
"periph.io/x/periph/host"
|
||||
"periph.io/x/periph/host/rpi"
|
||||
)
|
||||
|
||||
func main() {
|
||||
host.Init()
|
||||
t := time.NewTicker(500 * time.Millisecond)
|
||||
for l := gpio.Low; ; l = !l {
|
||||
rpi.P1_33.Out(l)
|
||||
<-t.C
|
||||
}
|
||||
}
|
||||
~~~
|
||||
|
||||
Curious? Look at [supported devices](https://periph.io/device/) for more
|
||||
examples!
|
||||
|
||||
|
||||
## Authors
|
||||
|
||||
`periph` was initiated with ❤️️ and passion by [Marc-Antoine
|
||||
Ruel](https://github.com/maruel). The full list of contributors is in
|
||||
[AUTHORS](https://github.com/google/periph/blob/master/AUTHORS) and
|
||||
[CONTRIBUTORS](https://github.com/google/periph/blob/master/CONTRIBUTORS).
|
||||
|
||||
|
||||
## Disclaimer
|
||||
|
||||
This is not an official Google product (experimental or otherwise), it
|
||||
is just code that happens to be owned by Google.
|
||||
|
||||
This project is not affiliated with the Go project.
|
103
vendor/periph.io/x/periph/conn/conn.go
generated
vendored
Normal file
103
vendor/periph.io/x/periph/conn/conn.go
generated
vendored
Normal file
@ -0,0 +1,103 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package conn
|
||||
|
||||
import "strconv"
|
||||
|
||||
// Resource is a basic resource (like a gpio pin) or a device.
|
||||
type Resource interface {
|
||||
// String returns a human readable identifier representing this resource in a
|
||||
// descriptive way for the user. It is the same signature as fmt.Stringer.
|
||||
String() string
|
||||
// Halt stops the resource.
|
||||
//
|
||||
// Unlike a Conn, a Resource may not be closable, On the other hand, a
|
||||
// resource can be halted. What halting entails depends on the resource
|
||||
// device but it should stop motion, sensing loop, light emission or PWM
|
||||
// output and go back into an inert state.
|
||||
Halt() error
|
||||
}
|
||||
|
||||
// Duplex declares whether communication can happen simultaneously both ways.
|
||||
//
|
||||
// Some protocol can be either depending on configuration settings, like UART.
|
||||
type Duplex int
|
||||
|
||||
const (
|
||||
// DuplexUnknown is used when the duplex of a connection is yet to be known.
|
||||
//
|
||||
// Some protocol can be configured either as half-duplex or full-duplex and
|
||||
// the connection is not yet is a determinate state.
|
||||
DuplexUnknown Duplex = 0
|
||||
// Half means that communication can only occurs one way at a time.
|
||||
//
|
||||
// Examples include 1-wire and I²C.
|
||||
Half Duplex = 1
|
||||
// Full means that communication occurs simultaneously both ways in a
|
||||
// synchronized manner.
|
||||
//
|
||||
// Examples include SPI (except 3-wire variant).
|
||||
Full Duplex = 2
|
||||
)
|
||||
|
||||
const duplexName = "DuplexUnknownHalfFull"
|
||||
|
||||
var duplexIndex = [...]uint8{0, 13, 17, 21}
|
||||
|
||||
func (i Duplex) String() string {
|
||||
if i < 0 || i >= Duplex(len(duplexIndex)-1) {
|
||||
return "Duplex(" + strconv.Itoa(int(i)) + ")"
|
||||
}
|
||||
return duplexName[duplexIndex[i]:duplexIndex[i+1]]
|
||||
}
|
||||
|
||||
// Conn defines the interface for a connection on a point-to-point
|
||||
// communication channel.
|
||||
//
|
||||
// The connection can either be unidirectional (read-only, write-only) or
|
||||
// bidirectional (read-write). It can either be half-duplex or full duplex.
|
||||
//
|
||||
// This is the lowest common denominator for all point-to-point communication
|
||||
// channels.
|
||||
//
|
||||
// Implementation are expected but not required to also implement the following
|
||||
// interfaces:
|
||||
//
|
||||
// - fmt.Stringer which returns something meaningful to the user like "SPI0.1",
|
||||
// "I2C1.76", "COM6", etc.
|
||||
//
|
||||
// - io.Reader and io.Writer as a way to use io.Copy() for half duplex
|
||||
// operation.
|
||||
//
|
||||
// - io.Closer for the owner of the communication channel.
|
||||
type Conn interface {
|
||||
String() string
|
||||
// Tx does a single transaction.
|
||||
//
|
||||
// For full duplex protocols (generally SPI, UART), the two buffers must have
|
||||
// the same length as both reading and writing happen simultaneously.
|
||||
//
|
||||
// For half duplex protocols (I²C), there is no restriction as reading
|
||||
// happens after writing, and r can be nil.
|
||||
//
|
||||
// Query Limits.MaxTxSize() to know if there is a limit on the buffer size
|
||||
// per Tx() call.
|
||||
Tx(w, r []byte) error
|
||||
// Duplex returns the current duplex setting for this point-to-point
|
||||
// connection.
|
||||
//
|
||||
// It is expected to be either Half or Full unless the connection itself is
|
||||
// in an unknown state.
|
||||
Duplex() Duplex
|
||||
}
|
||||
|
||||
// Limits returns information about the connection's limits.
|
||||
type Limits interface {
|
||||
// MaxTxSize returns the maximum allowed data size to be sent as a single
|
||||
// I/O.
|
||||
//
|
||||
// Returns 0 if undefined.
|
||||
MaxTxSize() int
|
||||
}
|
63
vendor/periph.io/x/periph/conn/doc.go
generated
vendored
Normal file
63
vendor/periph.io/x/periph/conn/doc.go
generated
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// Package conn defines core interfaces for protocols and connections.
|
||||
//
|
||||
// This package and its subpackages describe the base interfaces to connect the
|
||||
// software with the real world. It doesn't contain any implementation but
|
||||
// includes registries to enable the application to discover the available
|
||||
// hardware.
|
||||
//
|
||||
// Concepts
|
||||
//
|
||||
// periph uses 3 layered concepts for interfacing:
|
||||
//
|
||||
// Bus → Port → Conn
|
||||
//
|
||||
// Not every subpackage expose all 3 concepts. In fact, most packages don't.
|
||||
// For example, SPI doesn't expose Bus as the OSes generally only expose the
|
||||
// Port, that is, a Chip Select (CS) line must be selected right upfront to get
|
||||
// an handle. For I²C, there's no Port to configure, so selecting a "slave"
|
||||
// address is sufficient to jump directly from a Bus to a Conn.
|
||||
//
|
||||
// periph doesn't have yet a concept of star-like communication network, like
|
||||
// an IP network.
|
||||
//
|
||||
// Bus
|
||||
//
|
||||
// A Bus is a multi-point communication channel where one "master" and multiple
|
||||
// "slaves" communicate together. In the case of periph, the Bus handle is
|
||||
// assumed to be the "master". The "master" generally initiates communications
|
||||
// and selects the "slave" to talk to.
|
||||
//
|
||||
// As the "master" selects a "slave" over a bus, a virtual Port is
|
||||
// automatically created.
|
||||
//
|
||||
// Examples include SPI, I²C and 1-wire. In each case, selecting a
|
||||
// communication line (Chip Select (CS) line for SPI, address for I²C or
|
||||
// 1-wire) converts the Bus into a Port.
|
||||
//
|
||||
// Port
|
||||
//
|
||||
// A port is a point-to-point communication channel that is yet to be
|
||||
// initialized. It cannot be used for communication until it is connected and
|
||||
// transformed into a Conn. Configuring a Port converts it into a Conn. Not all
|
||||
// Port need configuration.
|
||||
//
|
||||
// Conn
|
||||
//
|
||||
// A Conn is a fully configured half or full duplex communication channel that
|
||||
// is point-to-point, only between two devices. It is ready to use like any
|
||||
// readable and/or writable pipe.
|
||||
//
|
||||
// Subpackages
|
||||
//
|
||||
// Most connection-type specific subpackages include subpackages:
|
||||
//
|
||||
// → XXXreg: registry as that is populated by the host drivers and that can be
|
||||
// leveraged by applications.
|
||||
//
|
||||
// → XXXtest: fake implementation that can be leveraged when writing device
|
||||
// driver unit test.
|
||||
package conn
|
3
vendor/periph.io/x/periph/conn/duplex_string.go
generated
vendored
Normal file
3
vendor/periph.io/x/periph/conn/duplex_string.go
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
// Code generated by "stringer -type Duplex"; DO NOT EDIT
|
||||
|
||||
package conn
|
26
vendor/periph.io/x/periph/conn/gpio/func.go
generated
vendored
Normal file
26
vendor/periph.io/x/periph/conn/gpio/func.go
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
// Copyright 2018 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package gpio
|
||||
|
||||
import "periph.io/x/periph/conn/pin"
|
||||
|
||||
// Well known pin functionality.
|
||||
const (
|
||||
// Inputs
|
||||
IN pin.Func = "IN" // Input
|
||||
IN_HIGH pin.Func = "In/High" // Read high
|
||||
IN_LOW pin.Func = "In/Low" // Read low
|
||||
|
||||
// Outputs
|
||||
OUT pin.Func = "OUT" // Output, drive
|
||||
OUT_OC pin.Func = "OUT_OPEN" // Output, open collector/drain
|
||||
OUT_HIGH pin.Func = "Out/High" // Drive high
|
||||
OUT_LOW pin.Func = "Out/Low" // Drive low; open collector low
|
||||
|
||||
FLOAT pin.Func = "FLOAT" // Input float or Output open collector high
|
||||
|
||||
CLK pin.Func = "CLK" // Clock is a subset of a PWM, with a 50% duty cycle
|
||||
PWM pin.Func = "PWM" // Pulse Width Modulation, which is a clock with variable duty cycle
|
||||
)
|
329
vendor/periph.io/x/periph/conn/gpio/gpio.go
generated
vendored
Normal file
329
vendor/periph.io/x/periph/conn/gpio/gpio.go
generated
vendored
Normal file
@ -0,0 +1,329 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// Package gpio defines digital pins.
|
||||
//
|
||||
// All GPIO implementations are expected to implement PinIO but the device
|
||||
// driver may accept a more specific one like PinIn or PinOut.
|
||||
package gpio
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"periph.io/x/periph/conn/physic"
|
||||
"periph.io/x/periph/conn/pin"
|
||||
)
|
||||
|
||||
// Interfaces
|
||||
|
||||
// Level is the level of the pin: Low or High.
|
||||
type Level bool
|
||||
|
||||
const (
|
||||
// Low represents 0v.
|
||||
Low Level = false
|
||||
// High represents Vin, generally 3.3v or 5v.
|
||||
High Level = true
|
||||
)
|
||||
|
||||
func (l Level) String() string {
|
||||
if l == Low {
|
||||
return "Low"
|
||||
}
|
||||
return "High"
|
||||
}
|
||||
|
||||
// Pull specifies the internal pull-up or pull-down for a pin set as input.
|
||||
type Pull uint8
|
||||
|
||||
// Acceptable pull values.
|
||||
const (
|
||||
PullNoChange Pull = 0 // Do not change the previous pull resistor setting or an unknown value
|
||||
Float Pull = 1 // Let the input float
|
||||
PullDown Pull = 2 // Apply pull-down
|
||||
PullUp Pull = 3 // Apply pull-up
|
||||
)
|
||||
|
||||
const pullName = "PullNoChangeFloatPullDownPullUp"
|
||||
|
||||
var pullIndex = [...]uint8{0, 12, 17, 25, 31}
|
||||
|
||||
func (i Pull) String() string {
|
||||
if i >= Pull(len(pullIndex)-1) {
|
||||
return "Pull(" + strconv.Itoa(int(i)) + ")"
|
||||
}
|
||||
return pullName[pullIndex[i]:pullIndex[i+1]]
|
||||
}
|
||||
|
||||
// Edge specifies if an input pin should have edge detection enabled.
|
||||
//
|
||||
// Only enable it when needed, since this causes system interrupts.
|
||||
type Edge int
|
||||
|
||||
// Acceptable edge detection values.
|
||||
const (
|
||||
NoEdge Edge = 0
|
||||
RisingEdge Edge = 1
|
||||
FallingEdge Edge = 2
|
||||
BothEdges Edge = 3
|
||||
)
|
||||
|
||||
const edgeName = "NoEdgeRisingEdgeFallingEdgeBothEdges"
|
||||
|
||||
var edgeIndex = [...]uint8{0, 6, 16, 27, 36}
|
||||
|
||||
func (i Edge) String() string {
|
||||
if i >= Edge(len(edgeIndex)-1) {
|
||||
return "Edge(" + strconv.Itoa(int(i)) + ")"
|
||||
}
|
||||
return edgeName[edgeIndex[i]:edgeIndex[i+1]]
|
||||
}
|
||||
|
||||
const (
|
||||
// DutyMax is a duty cycle of 100%.
|
||||
DutyMax Duty = 1 << 24
|
||||
// DutyHalf is a 50% duty PWM, which boils down to a normal clock.
|
||||
DutyHalf Duty = DutyMax / 2
|
||||
)
|
||||
|
||||
// Duty is the duty cycle for a PWM.
|
||||
//
|
||||
// Valid values are between 0 and DutyMax.
|
||||
type Duty int32
|
||||
|
||||
func (d Duty) String() string {
|
||||
// TODO(maruel): Implement one fractional number.
|
||||
return strconv.Itoa(int((d+50)/(DutyMax/100))) + "%"
|
||||
}
|
||||
|
||||
// Valid returns true if the Duty cycle value is valid.
|
||||
func (d Duty) Valid() bool {
|
||||
return d >= 0 && d <= DutyMax
|
||||
}
|
||||
|
||||
// ParseDuty parses a string and converts it to a Duty value.
|
||||
func ParseDuty(s string) (Duty, error) {
|
||||
percent := strings.HasSuffix(s, "%")
|
||||
if percent {
|
||||
s = s[:len(s)-1]
|
||||
}
|
||||
i64, err := strconv.ParseInt(s, 10, 32)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i := Duty(i64)
|
||||
if percent {
|
||||
// TODO(maruel): Add support for fractional number.
|
||||
if i < 0 {
|
||||
return 0, errors.New("duty must be >= 0%")
|
||||
}
|
||||
if i > 100 {
|
||||
return 0, errors.New("duty must be <= 100%")
|
||||
}
|
||||
return ((i * DutyMax) + 49) / 100, nil
|
||||
}
|
||||
if i < 0 {
|
||||
return 0, errors.New("duty must be >= 0")
|
||||
}
|
||||
if i > DutyMax {
|
||||
return 0, errors.New("duty must be <= " + strconv.Itoa(int(DutyMax)))
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
// PinIn is an input GPIO pin.
|
||||
//
|
||||
// It may optionally support internal pull resistor and edge based triggering.
|
||||
//
|
||||
// A button is semantically a PinIn. So if you are looking to read from a
|
||||
// button, PinIn is the interface you are looking for.
|
||||
type PinIn interface {
|
||||
pin.Pin
|
||||
// In setups a pin as an input.
|
||||
//
|
||||
// If WaitForEdge() is planned to be called, make sure to use one of the Edge
|
||||
// value. Otherwise, use NoEdge to not generated unneeded hardware interrupts.
|
||||
//
|
||||
// Calling In() will try to empty the accumulated edges but it cannot be 100%
|
||||
// reliable due to the OS (linux) and its driver. It is possible that on a
|
||||
// gpio that is as input, doing a quick Out(), In() may return an edge that
|
||||
// occurred before the Out() call.
|
||||
In(pull Pull, edge Edge) error
|
||||
// Read return the current pin level.
|
||||
//
|
||||
// Behavior is undefined if In() wasn't used before.
|
||||
//
|
||||
// In some rare case, it is possible that Read() fails silently. This happens
|
||||
// if another process on the host messes up with the pin after In() was
|
||||
// called. In this case, call In() again.
|
||||
Read() Level
|
||||
// WaitForEdge() waits for the next edge or immediately return if an edge
|
||||
// occurred since the last call.
|
||||
//
|
||||
// Only waits for the kind of edge as specified in a previous In() call.
|
||||
// Behavior is undefined if In() with a value other than NoEdge wasn't called
|
||||
// before.
|
||||
//
|
||||
// Returns true if an edge was detected during or before this call. Return
|
||||
// false if the timeout occurred or In() was called while waiting, causing the
|
||||
// function to exit.
|
||||
//
|
||||
// Multiple edges may or may not accumulate between two calls to
|
||||
// WaitForEdge(). The behavior in this case is undefined and is OS driver
|
||||
// specific.
|
||||
//
|
||||
// It is not required to call Read() to reset the edge detection.
|
||||
//
|
||||
// Specify -1 to effectively disable timeout.
|
||||
WaitForEdge(timeout time.Duration) bool
|
||||
// Pull returns the internal pull resistor if the pin is set as input pin.
|
||||
//
|
||||
// Returns PullNoChange if the value cannot be read.
|
||||
Pull() Pull
|
||||
// DefaultPull returns the pull that is initialized on CPU/device reset. This
|
||||
// is useful to determine if the pin is acceptable for operation with
|
||||
// certain devices.
|
||||
DefaultPull() Pull
|
||||
}
|
||||
|
||||
// PinOut is an output GPIO pin.
|
||||
//
|
||||
// A LED, a buzzer, a servo, are semantically a PinOut. So if you are looking
|
||||
// to control these, PinOut is the interface you are looking for.
|
||||
type PinOut interface {
|
||||
pin.Pin
|
||||
// Out sets a pin as output if it wasn't already and sets the initial value.
|
||||
//
|
||||
// After the initial call to ensure that the pin has been set as output, it
|
||||
// is generally safe to ignore the error returned.
|
||||
//
|
||||
// Out() tries to empty the accumulated edges detected if the gpio was
|
||||
// previously set as input but this is not 100% guaranteed due to the OS.
|
||||
Out(l Level) error
|
||||
// PWM sets the PWM output on supported pins, if the pin has hardware PWM
|
||||
// support.
|
||||
//
|
||||
// To use as a general purpose clock, set duty to DutyHalf. Some pins may
|
||||
// only support DutyHalf and no other value.
|
||||
//
|
||||
// Using 0 as frequency will use the optimal value as supported/preferred by
|
||||
// the pin.
|
||||
//
|
||||
// To use as a servo, see https://en.wikipedia.org/wiki/Servo_control as an
|
||||
// explanation how to calculate duty.
|
||||
PWM(duty Duty, f physic.Frequency) error
|
||||
}
|
||||
|
||||
// PinIO is a GPIO pin that supports both input and output. It matches both
|
||||
// interfaces PinIn and PinOut.
|
||||
//
|
||||
// A GPIO pin implementing PinIO may fail at either input or output or both.
|
||||
type PinIO interface {
|
||||
pin.Pin
|
||||
// PinIn
|
||||
In(pull Pull, edge Edge) error
|
||||
Read() Level
|
||||
WaitForEdge(timeout time.Duration) bool
|
||||
Pull() Pull
|
||||
DefaultPull() Pull
|
||||
// PinOut
|
||||
Out(l Level) error
|
||||
PWM(duty Duty, f physic.Frequency) error
|
||||
}
|
||||
|
||||
// INVALID implements PinIO and fails on all access.
|
||||
var INVALID PinIO
|
||||
|
||||
// RealPin is implemented by aliased pin and allows the retrieval of the real
|
||||
// pin underlying an alias.
|
||||
//
|
||||
// Aliases are created by RegisterAlias. Aliases permits presenting a user
|
||||
// friendly GPIO pin name while representing the underlying real pin.
|
||||
//
|
||||
// The purpose of the RealPin is to be able to cleanly test whether an arbitrary
|
||||
// gpio.PinIO returned by ByName is an alias for another pin, and resolve it.
|
||||
type RealPin interface {
|
||||
Real() PinIO // Real returns the real pin behind an Alias
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
// errInvalidPin is returned when trying to use INVALID.
|
||||
var errInvalidPin = errors.New("gpio: invalid pin")
|
||||
|
||||
func init() {
|
||||
INVALID = invalidPin{}
|
||||
}
|
||||
|
||||
// invalidPin implements PinIO for compatibility but fails on all access.
|
||||
type invalidPin struct {
|
||||
}
|
||||
|
||||
func (invalidPin) String() string {
|
||||
return "INVALID"
|
||||
}
|
||||
|
||||
func (invalidPin) Halt() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (invalidPin) Number() int {
|
||||
return -1
|
||||
}
|
||||
|
||||
func (invalidPin) Name() string {
|
||||
return "INVALID"
|
||||
}
|
||||
|
||||
func (invalidPin) Function() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (invalidPin) Func() pin.Func {
|
||||
return pin.FuncNone
|
||||
}
|
||||
|
||||
func (invalidPin) SupportedFuncs() []pin.Func {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (invalidPin) SetFunc(f pin.Func) error {
|
||||
return errInvalidPin
|
||||
}
|
||||
|
||||
func (invalidPin) In(Pull, Edge) error {
|
||||
return errInvalidPin
|
||||
}
|
||||
|
||||
func (invalidPin) Read() Level {
|
||||
return Low
|
||||
}
|
||||
|
||||
func (invalidPin) WaitForEdge(timeout time.Duration) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (invalidPin) Pull() Pull {
|
||||
return PullNoChange
|
||||
}
|
||||
|
||||
func (invalidPin) DefaultPull() Pull {
|
||||
return PullNoChange
|
||||
}
|
||||
|
||||
func (invalidPin) Out(Level) error {
|
||||
return errInvalidPin
|
||||
}
|
||||
|
||||
func (invalidPin) PWM(Duty, physic.Frequency) error {
|
||||
return errInvalidPin
|
||||
}
|
||||
|
||||
var _ PinIn = INVALID
|
||||
var _ PinOut = INVALID
|
||||
var _ PinIO = INVALID
|
||||
var _ pin.PinFunc = &invalidPin{}
|
213
vendor/periph.io/x/periph/conn/gpio/gpioreg/gpioreg.go
generated
vendored
Normal file
213
vendor/periph.io/x/periph/conn/gpio/gpioreg/gpioreg.go
generated
vendored
Normal file
@ -0,0 +1,213 @@
|
||||
// Copyright 2017 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// Package gpioreg defines a registry for the known digital pins.
|
||||
package gpioreg
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"periph.io/x/periph/conn/gpio"
|
||||
)
|
||||
|
||||
// ByName returns a GPIO pin from its name, gpio number or one of its aliases.
|
||||
//
|
||||
// For example on a Raspberry Pi, the following values will return the same
|
||||
// GPIO: the gpio as a number "2", the chipset name "GPIO2", the board pin
|
||||
// position "P1_3", it's function name "I2C1_SDA".
|
||||
//
|
||||
// Returns nil if the gpio pin is not present.
|
||||
func ByName(name string) gpio.PinIO {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
if p, ok := byName[name]; ok {
|
||||
return p
|
||||
}
|
||||
if dest, ok := byAlias[name]; ok {
|
||||
if p := getByNameDeep(dest); p != nil {
|
||||
// Wraps the destination in an alias, so the name makes sense to the user.
|
||||
// The main drawback is that casting into other gpio interfaces like
|
||||
// gpio.PinPWM requires going through gpio.RealPin first.
|
||||
return &pinAlias{p, name}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// All returns all the GPIO pins available on this host.
|
||||
//
|
||||
// The list is guaranteed to be in order of name using 'natural sorting'.
|
||||
//
|
||||
// This list excludes aliases.
|
||||
//
|
||||
// This list excludes non-GPIO pins like GROUND, V3_3, etc, since they are not
|
||||
// GPIO.
|
||||
func All() []gpio.PinIO {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
out := make([]gpio.PinIO, 0, len(byName))
|
||||
for _, p := range byName {
|
||||
out = insertPinByName(out, p)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Aliases returns all pin aliases.
|
||||
//
|
||||
// The list is guaranteed to be in order of aliase name.
|
||||
func Aliases() []gpio.PinIO {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
out := make([]gpio.PinIO, 0, len(byAlias))
|
||||
for name, dest := range byAlias {
|
||||
// Skip aliases that were not resolved.
|
||||
if p := getByNameDeep(dest); p != nil {
|
||||
out = insertPinByName(out, &pinAlias{p, name})
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Register registers a GPIO pin.
|
||||
//
|
||||
// Registering the same pin number or name twice is an error.
|
||||
//
|
||||
// The pin registered cannot implement the interface RealPin.
|
||||
func Register(p gpio.PinIO) error {
|
||||
name := p.Name()
|
||||
if len(name) == 0 {
|
||||
return errors.New("gpioreg: can't register a pin with no name")
|
||||
}
|
||||
if r, ok := p.(gpio.RealPin); ok {
|
||||
return errors.New("gpioreg: can't register pin " + strconv.Quote(name) + ", it is already an alias to " + strconv.Quote(r.Real().String()))
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
if orig, ok := byName[name]; ok {
|
||||
return errors.New("gpioreg: can't register pin " + strconv.Quote(name) + " twice; already registered as " + strconv.Quote(orig.String()))
|
||||
}
|
||||
if dest, ok := byAlias[name]; ok {
|
||||
return errors.New("gpioreg: can't register pin " + strconv.Quote(name) + "; an alias already exist to: " + strconv.Quote(dest))
|
||||
}
|
||||
byName[name] = p
|
||||
return nil
|
||||
}
|
||||
|
||||
// RegisterAlias registers an alias for a GPIO pin.
|
||||
//
|
||||
// It is possible to register an alias for a pin that itself has not been
|
||||
// registered yet. It is valid to register an alias to another alias. It is
|
||||
// valid to register the same alias multiple times, overriding the previous
|
||||
// alias.
|
||||
func RegisterAlias(alias string, dest string) error {
|
||||
if len(alias) == 0 {
|
||||
return errors.New("gpioreg: can't register an alias with no name")
|
||||
}
|
||||
if len(dest) == 0 {
|
||||
return errors.New("gpioreg: can't register alias " + strconv.Quote(alias) + " with no dest")
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
if _, ok := byName[alias]; ok {
|
||||
return errors.New("gpioreg: can't register alias " + strconv.Quote(alias) + " for a pin that exists")
|
||||
}
|
||||
byAlias[alias] = dest
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unregister removes a previously registered GPIO pin or alias from the GPIO
|
||||
// pin registry.
|
||||
//
|
||||
// This can happen when a GPIO pin is exposed via an USB device and the device
|
||||
// is unplugged, or when a generic OS provided pin is superseded by a CPU
|
||||
// specific implementation.
|
||||
func Unregister(name string) error {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
if _, ok := byName[name]; ok {
|
||||
delete(byName, name)
|
||||
return nil
|
||||
}
|
||||
if _, ok := byAlias[name]; ok {
|
||||
delete(byAlias, name)
|
||||
return nil
|
||||
}
|
||||
return errors.New("gpioreg: can't unregister unknown pin name " + strconv.Quote(name))
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
var (
|
||||
mu sync.Mutex
|
||||
byName = map[string]gpio.PinIO{}
|
||||
byAlias = map[string]string{}
|
||||
)
|
||||
|
||||
// pinAlias implements an alias for a PinIO.
|
||||
//
|
||||
// pinAlias implements the RealPin interface, which allows querying for the
|
||||
// real pin under the alias.
|
||||
type pinAlias struct {
|
||||
gpio.PinIO
|
||||
name string
|
||||
}
|
||||
|
||||
// String returns the alias name along the real pin's Name() in parenthesis, if
|
||||
// known, else the real pin's number.
|
||||
func (a *pinAlias) String() string {
|
||||
return a.name + "(" + a.PinIO.Name() + ")"
|
||||
}
|
||||
|
||||
// Name returns the pinAlias's name.
|
||||
func (a *pinAlias) Name() string {
|
||||
return a.name
|
||||
}
|
||||
|
||||
// Real returns the real pin behind the alias
|
||||
func (a *pinAlias) Real() gpio.PinIO {
|
||||
return a.PinIO
|
||||
}
|
||||
|
||||
// getByNameDeep recursively resolves the aliases to get the pin.
|
||||
func getByNameDeep(name string) gpio.PinIO {
|
||||
if p, ok := byName[name]; ok {
|
||||
return p
|
||||
}
|
||||
if dest, ok := byAlias[name]; ok {
|
||||
if p := getByNameDeep(dest); p != nil {
|
||||
// Return the deep pin directly, bypassing the aliases.
|
||||
return p
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// insertPinByName inserts pin p into list l while keeping l ordered by name.
|
||||
func insertPinByName(l []gpio.PinIO, p gpio.PinIO) []gpio.PinIO {
|
||||
n := p.Name()
|
||||
i := search(len(l), func(i int) bool { return lessNatural(n, l[i].Name()) })
|
||||
l = append(l, nil)
|
||||
copy(l[i+1:], l[i:])
|
||||
l[i] = p
|
||||
return l
|
||||
}
|
||||
|
||||
// search implements the same algorithm as sort.Search().
|
||||
//
|
||||
// It was extracted to to not depend on sort, which depends on reflect.
|
||||
func search(n int, f func(int) bool) int {
|
||||
lo := 0
|
||||
for hi := n; lo < hi; {
|
||||
if i := int(uint(lo+hi) >> 1); !f(i) {
|
||||
lo = i + 1
|
||||
} else {
|
||||
hi = i
|
||||
}
|
||||
}
|
||||
return lo
|
||||
}
|
76
vendor/periph.io/x/periph/conn/gpio/gpioreg/natsort.go
generated
vendored
Normal file
76
vendor/periph.io/x/periph/conn/gpio/gpioreg/natsort.go
generated
vendored
Normal file
@ -0,0 +1,76 @@
|
||||
// Copyright 2018 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package gpioreg
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// lessNatural does a 'natural' comparison on the two strings.
|
||||
//
|
||||
// It is extracted from https://github.com/maruel/natural.
|
||||
func lessNatural(a, b string) bool {
|
||||
for {
|
||||
if a == b {
|
||||
return false
|
||||
}
|
||||
if p := commonPrefix(a, b); p != 0 {
|
||||
a = a[p:]
|
||||
b = b[p:]
|
||||
}
|
||||
if ia := digits(a); ia > 0 {
|
||||
if ib := digits(b); ib > 0 {
|
||||
// Both sides have digits.
|
||||
an, aerr := strconv.ParseUint(a[:ia], 10, 64)
|
||||
bn, berr := strconv.ParseUint(b[:ib], 10, 64)
|
||||
if aerr == nil && berr == nil {
|
||||
if an != bn {
|
||||
return an < bn
|
||||
}
|
||||
// Semantically the same digits, e.g. "00" == "0", "01" == "1". In
|
||||
// this case, only continue processing if there's trailing data on
|
||||
// both sides, otherwise do lexical comparison.
|
||||
if ia != len(a) && ib != len(b) {
|
||||
a = a[ia:]
|
||||
b = b[ib:]
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return a < b
|
||||
}
|
||||
}
|
||||
|
||||
// commonPrefix returns the common prefix except for digits.
|
||||
func commonPrefix(a, b string) int {
|
||||
m := len(a)
|
||||
if n := len(b); n < m {
|
||||
m = n
|
||||
}
|
||||
if m == 0 {
|
||||
return 0
|
||||
}
|
||||
_ = a[m-1]
|
||||
_ = b[m-1]
|
||||
for i := 0; i < m; i++ {
|
||||
ca := a[i]
|
||||
cb := b[i]
|
||||
if (ca >= '0' && ca <= '9') || (cb >= '0' && cb <= '9') || ca != cb {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func digits(s string) int {
|
||||
for i := 0; i < len(s); i++ {
|
||||
c := s[i]
|
||||
if c < '0' || c > '9' {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return len(s)
|
||||
}
|
220
vendor/periph.io/x/periph/conn/gpio/gpiostream/gpiostream.go
generated
vendored
Normal file
220
vendor/periph.io/x/periph/conn/gpio/gpiostream/gpiostream.go
generated
vendored
Normal file
@ -0,0 +1,220 @@
|
||||
// Copyright 2017 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// Package gpiostream defines digital streams.
|
||||
//
|
||||
// Warning
|
||||
//
|
||||
// This package is still in flux as development is on-going.
|
||||
package gpiostream
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"periph.io/x/periph/conn/gpio"
|
||||
"periph.io/x/periph/conn/physic"
|
||||
"periph.io/x/periph/conn/pin"
|
||||
)
|
||||
|
||||
// Stream is the interface to define a generic stream.
|
||||
type Stream interface {
|
||||
// Frequency is the minimum data rate at which the binary stream is usable.
|
||||
//
|
||||
// For example, a bit stream may have a 10kHz data rate.
|
||||
Frequency() physic.Frequency
|
||||
// Duration of the binary stream. For infinitely looping streams, it is the
|
||||
// duration of the non-looping part.
|
||||
Duration() time.Duration
|
||||
}
|
||||
|
||||
// BitStream is a stream of bits to be written or read.
|
||||
type BitStream struct {
|
||||
// Bits is a densely packed bitstream.
|
||||
//
|
||||
// The stream is required to be a multiple of 8 samples.
|
||||
Bits []byte
|
||||
// Freq is the rate at each the bit (not byte) stream should be processed.
|
||||
Freq physic.Frequency
|
||||
// LSBF when true means than Bits is in LSB-first. When false, the data is
|
||||
// MSB-first.
|
||||
//
|
||||
// With MSBF, the first bit processed is the most significant one (0x80). For
|
||||
// example, I²C, I2S PCM and SPI use MSB-first at the word level. This
|
||||
// requires to pack words correctly.
|
||||
//
|
||||
// With LSBF, the first bit processed is the least significant one (0x01).
|
||||
// For example, Ethernet uses LSB-first at the byte level and MSB-first at
|
||||
// the word level.
|
||||
LSBF bool
|
||||
}
|
||||
|
||||
// Frequency implements Stream.
|
||||
func (b *BitStream) Frequency() physic.Frequency {
|
||||
return b.Freq
|
||||
}
|
||||
|
||||
// Duration implements Stream.
|
||||
func (b *BitStream) Duration() time.Duration {
|
||||
if b.Freq == 0 {
|
||||
return 0
|
||||
}
|
||||
return b.Freq.Period() * time.Duration(len(b.Bits)*8)
|
||||
}
|
||||
|
||||
// GoString implements fmt.GoStringer.
|
||||
func (b *BitStream) GoString() string {
|
||||
return fmt.Sprintf("&gpiostream.BitStream{Bits: %x, Freq:%s, LSBF:%t}", b.Bits, b.Freq, b.LSBF)
|
||||
}
|
||||
|
||||
// EdgeStream is a stream of edges to be written.
|
||||
//
|
||||
// This struct is more efficient than BitStream for short repetitive pulses,
|
||||
// like controlling a servo. A PWM can be created by specifying a slice of
|
||||
// twice the same resolution and make it looping via a Program.
|
||||
type EdgeStream struct {
|
||||
// Edges is the list of Level change. It is assumed that the signal starts
|
||||
// with gpio.High. Use a duration of 0 for Edges[0] to start with a Low
|
||||
// instead of the default High.
|
||||
//
|
||||
// The value is a multiple of Res. Use a 0 value to 'extend' a continuous
|
||||
// signal that lasts more than "2^16-1*Res" duration by skipping a pulse.
|
||||
Edges []uint16
|
||||
// Res is the minimum resolution at which the edges should be
|
||||
// rasterized.
|
||||
//
|
||||
// The lower the value, the more memory shall be used when rasterized.
|
||||
Freq physic.Frequency
|
||||
}
|
||||
|
||||
// Frequency implements Stream.
|
||||
func (e *EdgeStream) Frequency() physic.Frequency {
|
||||
return e.Freq
|
||||
}
|
||||
|
||||
// Duration implements Stream.
|
||||
func (e *EdgeStream) Duration() time.Duration {
|
||||
if e.Freq == 0 {
|
||||
return 0
|
||||
}
|
||||
t := 0
|
||||
for _, edge := range e.Edges {
|
||||
t += int(edge)
|
||||
}
|
||||
return e.Freq.Period() * time.Duration(t)
|
||||
}
|
||||
|
||||
// Program is a loop of streams.
|
||||
//
|
||||
// This is itself a stream, it can be used to reduce memory usage when repeated
|
||||
// patterns are used.
|
||||
type Program struct {
|
||||
Parts []Stream // Each part must be a BitStream, EdgeStream or Program
|
||||
Loops int // Set to -1 to create an infinite loop
|
||||
}
|
||||
|
||||
// Frequency implements Stream.
|
||||
func (p *Program) Frequency() physic.Frequency {
|
||||
if p.Loops == 0 {
|
||||
return 0
|
||||
}
|
||||
var buf [16]physic.Frequency
|
||||
freqs := buf[:0]
|
||||
for _, part := range p.Parts {
|
||||
if f := part.Frequency(); f != 0 {
|
||||
freqs = insertFreq(freqs, f)
|
||||
}
|
||||
}
|
||||
if len(freqs) == 0 {
|
||||
return 0
|
||||
}
|
||||
f := freqs[0]
|
||||
for i := 1; i < len(freqs); i++ {
|
||||
if r := freqs[i]; r*2 < f {
|
||||
break
|
||||
}
|
||||
// Take in account Nyquist rate. https://wikipedia.org/wiki/Nyquist_rate
|
||||
f *= 2
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
// Duration implements Stream.
|
||||
func (p *Program) Duration() time.Duration {
|
||||
if p.Loops == 0 {
|
||||
return 0
|
||||
}
|
||||
var d time.Duration
|
||||
for _, s := range p.Parts {
|
||||
d += s.Duration()
|
||||
}
|
||||
if p.Loops > 1 {
|
||||
d *= time.Duration(p.Loops)
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
// PinIn allows to read a bit stream from a pin.
|
||||
//
|
||||
// Caveat
|
||||
//
|
||||
// This interface doesn't enable sampling multiple pins in a
|
||||
// synchronized way or reading in a continuous uninterrupted way. As such, it
|
||||
// should be considered experimental.
|
||||
type PinIn interface {
|
||||
pin.Pin
|
||||
// StreamIn reads for the pin at the specified resolution to fill the
|
||||
// provided buffer.
|
||||
//
|
||||
// May only support a subset of the structs implementing Stream.
|
||||
StreamIn(p gpio.Pull, b Stream) error
|
||||
}
|
||||
|
||||
// PinOut allows to stream to a pin.
|
||||
//
|
||||
// The Stream may be a Program, a BitStream or an EdgeStream. If it is a
|
||||
// Program that is an infinite loop, a separate goroutine can be used to cancel
|
||||
// the program. In this case StreamOut() returns without an error.
|
||||
//
|
||||
// Caveat
|
||||
//
|
||||
// This interface doesn't enable streaming to multiple pins in a
|
||||
// synchronized way or reading in a continuous uninterrupted way. As such, it
|
||||
// should be considered experimental.
|
||||
type PinOut interface {
|
||||
pin.Pin
|
||||
StreamOut(s Stream) error
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
// insertFreq inserts in reverse order, highest frequency first.
|
||||
func insertFreq(l []physic.Frequency, f physic.Frequency) []physic.Frequency {
|
||||
i := search(len(l), func(i int) bool { return l[i] < f })
|
||||
l = append(l, 0)
|
||||
copy(l[i+1:], l[i:])
|
||||
l[i] = f
|
||||
return l
|
||||
}
|
||||
|
||||
// search implements the same algorithm as sort.Search().
|
||||
//
|
||||
// It was extracted to to not depend on sort, which depends on reflect.
|
||||
func search(n int, f func(int) bool) int {
|
||||
lo := 0
|
||||
for hi := n; lo < hi; {
|
||||
if i := int(uint(lo+hi) >> 1); !f(i) {
|
||||
lo = i + 1
|
||||
} else {
|
||||
hi = i
|
||||
}
|
||||
}
|
||||
return lo
|
||||
}
|
||||
|
||||
var _ Stream = &BitStream{}
|
||||
var _ Stream = &EdgeStream{}
|
||||
var _ Stream = &Program{}
|
13
vendor/periph.io/x/periph/conn/i2c/func.go
generated
vendored
Normal file
13
vendor/periph.io/x/periph/conn/i2c/func.go
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
// Copyright 2018 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package i2c
|
||||
|
||||
import "periph.io/x/periph/conn/pin"
|
||||
|
||||
// Well known pin functionality.
|
||||
const (
|
||||
SCL pin.Func = "I2C_SCL" // Clock
|
||||
SDA pin.Func = "I2C_SDA" // Data
|
||||
)
|
135
vendor/periph.io/x/periph/conn/i2c/i2c.go
generated
vendored
Normal file
135
vendor/periph.io/x/periph/conn/i2c/i2c.go
generated
vendored
Normal file
@ -0,0 +1,135 @@
|
||||
// Copyright 2016 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// Package i2c defines the API to communicate with devices over the I²C
|
||||
// protocol.
|
||||
//
|
||||
// As described in https://periph.io/x/periph/conn#hdr-Concepts, periph.io uses
|
||||
// the concepts of Bus, Port and Conn.
|
||||
//
|
||||
// In the package i2c, 'Port' is not exposed, since once you know the I²C
|
||||
// device address, there's no unconfigured Port to configure.
|
||||
//
|
||||
// Instead, the package includes the adapter 'Dev' to directly convert an I²C
|
||||
// bus 'i2c.Bus' into a connection 'conn.Conn' by only specifying the device
|
||||
// I²C address.
|
||||
//
|
||||
// See https://en.wikipedia.org/wiki/I%C2%B2C for more information.
|
||||
package i2c
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"strconv"
|
||||
|
||||
"periph.io/x/periph/conn"
|
||||
"periph.io/x/periph/conn/gpio"
|
||||
"periph.io/x/periph/conn/physic"
|
||||
)
|
||||
|
||||
// Bus defines the interface a concrete I²C driver must implement.
|
||||
//
|
||||
// This interface is consummed by a device driver for a device sitting on a bus.
|
||||
//
|
||||
// This interface doesn't implement conn.Conn since a device address must be
|
||||
// specified. Use i2cdev.Dev as an adapter to get a conn.Conn compatible
|
||||
// object.
|
||||
type Bus interface {
|
||||
String() string
|
||||
// Tx does a transaction at the specified device address.
|
||||
//
|
||||
// Write is done first, then read. One of 'w' or 'r' can be omitted for a
|
||||
// unidirectional operation.
|
||||
Tx(addr uint16, w, r []byte) error
|
||||
// SetSpeed changes the bus speed, if supported.
|
||||
//
|
||||
// On linux due to the way the I²C sysfs driver is exposed in userland,
|
||||
// calling this function will likely affect *all* I²C buses on the host.
|
||||
SetSpeed(f physic.Frequency) error
|
||||
}
|
||||
|
||||
// BusCloser is an I²C bus that can be closed.
|
||||
//
|
||||
// This interface is meant to be handled by the application and not the device
|
||||
// driver. A device driver doesn't "own" a bus, hence it must operate on a Bus,
|
||||
// not a BusCloser.
|
||||
type BusCloser interface {
|
||||
io.Closer
|
||||
Bus
|
||||
}
|
||||
|
||||
// Pins defines the pins that an I²C bus interconnect is using on the host.
|
||||
//
|
||||
// It is expected that a implementer of Bus also implement Pins but this is not
|
||||
// a requirement.
|
||||
type Pins interface {
|
||||
// SCL returns the CLK (clock) pin.
|
||||
SCL() gpio.PinIO
|
||||
// SDA returns the DATA pin.
|
||||
SDA() gpio.PinIO
|
||||
}
|
||||
|
||||
// Dev is a device on a I²C bus.
|
||||
//
|
||||
// It implements conn.Conn.
|
||||
//
|
||||
// It saves from repeatedly specifying the device address.
|
||||
type Dev struct {
|
||||
Bus Bus
|
||||
Addr uint16
|
||||
}
|
||||
|
||||
func (d *Dev) String() string {
|
||||
s := "<nil>"
|
||||
if d.Bus != nil {
|
||||
s = d.Bus.String()
|
||||
}
|
||||
return s + "(" + strconv.Itoa(int(d.Addr)) + ")"
|
||||
}
|
||||
|
||||
// Tx does a transaction by adding the device's address to each command.
|
||||
//
|
||||
// It's a wrapper for Bus.Tx().
|
||||
func (d *Dev) Tx(w, r []byte) error {
|
||||
return d.Bus.Tx(d.Addr, w, r)
|
||||
}
|
||||
|
||||
// Write writes to the I²C bus without reading, implementing io.Writer.
|
||||
//
|
||||
// It's a wrapper for Tx()
|
||||
func (d *Dev) Write(b []byte) (int, error) {
|
||||
if err := d.Tx(b, nil); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
// Duplex always return conn.Half for I²C.
|
||||
func (d *Dev) Duplex() conn.Duplex {
|
||||
return conn.Half
|
||||
}
|
||||
|
||||
// Addr is an I²C slave address.
|
||||
type Addr uint16
|
||||
|
||||
// Set sets the Addr to a value represented by the string s. Values maybe in
|
||||
// decimal or hexadecimal form. Set implements the flag.Value interface.
|
||||
func (a *Addr) Set(s string) error {
|
||||
// Allow for only maximum of 10 bits for i2c addresses.
|
||||
u, err := strconv.ParseUint(s, 0, 10)
|
||||
if err != nil {
|
||||
return errI2CSetError
|
||||
}
|
||||
*a = Addr(u)
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns an i2c.Addr as a string formated in hexadecimal.
|
||||
func (a Addr) String() string {
|
||||
return "0x" + strconv.FormatInt(int64(a), 16)
|
||||
}
|
||||
|
||||
var errI2CSetError = errors.New("invalid i2c address")
|
||||
|
||||
var _ conn.Conn = &Dev{}
|
252
vendor/periph.io/x/periph/conn/i2c/i2creg/i2creg.go
generated
vendored
Normal file
252
vendor/periph.io/x/periph/conn/i2c/i2creg/i2creg.go
generated
vendored
Normal file
@ -0,0 +1,252 @@
|
||||
// Copyright 2017 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// Package i2creg defines I²C bus registry to list buses present on the host.
|
||||
package i2creg
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"periph.io/x/periph/conn/i2c"
|
||||
)
|
||||
|
||||
// Opener opens an handle to a bus.
|
||||
//
|
||||
// It is provided by the actual bus driver.
|
||||
type Opener func() (i2c.BusCloser, error)
|
||||
|
||||
// Ref references an I²C bus.
|
||||
//
|
||||
// It is returned by All() to enumerate all registered buses.
|
||||
type Ref struct {
|
||||
// Name of the bus.
|
||||
//
|
||||
// It must not be a sole number. It must be unique across the host.
|
||||
Name string
|
||||
// Aliases are the alternative names that can be used to reference this bus.
|
||||
Aliases []string
|
||||
// Number of the bus or -1 if the bus doesn't have any "native" number.
|
||||
//
|
||||
// Buses provided by the CPU normally have a 0 based number. Buses provided
|
||||
// via an addon (like over USB) generally are not numbered.
|
||||
Number int
|
||||
// Open is the factory to open an handle to this I²C bus.
|
||||
Open Opener
|
||||
}
|
||||
|
||||
// Open opens an I²C bus by its name, an alias or its number and returns an
|
||||
// handle to it.
|
||||
//
|
||||
// Specify the empty string "" to get the first available bus. This is the
|
||||
// recommended default value unless an application knows the exact bus to use.
|
||||
//
|
||||
// Each bus can register multiple aliases, each leading to the same bus handle.
|
||||
//
|
||||
// "Bus number" is a generic concept that is highly dependent on the platform
|
||||
// and OS. On some platform, the first bus may have the number 0, 1 or higher.
|
||||
// Bus numbers are not necessarily continuous and may not start at 0. It was
|
||||
// observed that the bus number as reported by the OS may change across OS
|
||||
// revisions.
|
||||
//
|
||||
// When the I²C bus is provided by an off board plug and play bus like USB via
|
||||
// a FT232H USB device, there can be no associated number.
|
||||
func Open(name string) (i2c.BusCloser, error) {
|
||||
var r *Ref
|
||||
var err error
|
||||
func() {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
if len(byName) == 0 {
|
||||
err = errors.New("i2creg: no bus found; did you forget to call Init()?")
|
||||
return
|
||||
}
|
||||
if len(name) == 0 {
|
||||
r = getDefault()
|
||||
return
|
||||
}
|
||||
// Try by name, by alias, by number.
|
||||
if r = byName[name]; r == nil {
|
||||
if r = byAlias[name]; r == nil {
|
||||
if i, err2 := strconv.Atoi(name); err2 == nil {
|
||||
r = byNumber[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if r == nil {
|
||||
return nil, errors.New("i2creg: can't open unknown bus: " + strconv.Quote(name))
|
||||
}
|
||||
return r.Open()
|
||||
}
|
||||
|
||||
// All returns a copy of all the registered references to all know I²C buses
|
||||
// available on this host.
|
||||
//
|
||||
// The list is sorted by the bus name.
|
||||
func All() []*Ref {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
out := make([]*Ref, 0, len(byName))
|
||||
for _, v := range byName {
|
||||
r := &Ref{Name: v.Name, Aliases: make([]string, len(v.Aliases)), Number: v.Number, Open: v.Open}
|
||||
copy(r.Aliases, v.Aliases)
|
||||
out = insertRef(out, r)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Register registers an I²C bus.
|
||||
//
|
||||
// Registering the same bus name twice is an error, e.g. o.Name(). o.Number()
|
||||
// can be -1 to signify that the bus doesn't have an inherent "bus number". A
|
||||
// good example is a bus provided over a FT232H device connected on an USB bus.
|
||||
// In this case, the bus name should be created from the serial number of the
|
||||
// device for unique identification.
|
||||
func Register(name string, aliases []string, number int, o Opener) error {
|
||||
if len(name) == 0 {
|
||||
return errors.New("i2creg: can't register a bus with no name")
|
||||
}
|
||||
if o == nil {
|
||||
return errors.New("i2creg: can't register bus " + strconv.Quote(name) + " with nil Opener")
|
||||
}
|
||||
if number < -1 {
|
||||
return errors.New("i2creg: can't register bus " + strconv.Quote(name) + " with invalid bus number " + strconv.Itoa(number))
|
||||
}
|
||||
if _, err := strconv.Atoi(name); err == nil {
|
||||
return errors.New("i2creg: can't register bus " + strconv.Quote(name) + " with name being only a number")
|
||||
}
|
||||
if strings.Contains(name, ":") {
|
||||
return errors.New("i2creg: can't register bus " + strconv.Quote(name) + " with name containing ':'")
|
||||
}
|
||||
for _, alias := range aliases {
|
||||
if len(alias) == 0 {
|
||||
return errors.New("i2creg: can't register bus " + strconv.Quote(name) + " with an empty alias")
|
||||
}
|
||||
if name == alias {
|
||||
return errors.New("i2creg: can't register bus " + strconv.Quote(name) + " with an alias the same as the bus name")
|
||||
}
|
||||
if _, err := strconv.Atoi(alias); err == nil {
|
||||
return errors.New("i2creg: can't register bus " + strconv.Quote(name) + " with an alias that is a number: " + strconv.Quote(alias))
|
||||
}
|
||||
if strings.Contains(alias, ":") {
|
||||
return errors.New("i2creg: can't register bus " + strconv.Quote(name) + " with an alias containing ':': " + strconv.Quote(alias))
|
||||
}
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
if _, ok := byName[name]; ok {
|
||||
return errors.New("i2creg: can't register bus " + strconv.Quote(name) + " twice")
|
||||
}
|
||||
if _, ok := byAlias[name]; ok {
|
||||
return errors.New("i2creg: can't register bus " + strconv.Quote(name) + " twice; it is already an alias")
|
||||
}
|
||||
if number != -1 {
|
||||
if _, ok := byNumber[number]; ok {
|
||||
return errors.New("i2creg: can't register bus " + strconv.Quote(name) + "; bus number " + strconv.Itoa(number) + " is already registered")
|
||||
}
|
||||
}
|
||||
for _, alias := range aliases {
|
||||
if _, ok := byName[alias]; ok {
|
||||
return errors.New("i2creg: can't register bus " + strconv.Quote(name) + " twice; alias " + strconv.Quote(alias) + " is already a bus")
|
||||
}
|
||||
if _, ok := byAlias[alias]; ok {
|
||||
return errors.New("i2creg: can't register bus " + strconv.Quote(name) + " twice; alias " + strconv.Quote(alias) + " is already an alias")
|
||||
}
|
||||
}
|
||||
|
||||
r := &Ref{Name: name, Aliases: make([]string, len(aliases)), Number: number, Open: o}
|
||||
copy(r.Aliases, aliases)
|
||||
byName[name] = r
|
||||
if number != -1 {
|
||||
byNumber[number] = r
|
||||
}
|
||||
for _, alias := range aliases {
|
||||
byAlias[alias] = r
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unregister removes a previously registered I²C bus.
|
||||
//
|
||||
// This can happen when an I²C bus is exposed via an USB device and the device
|
||||
// is unplugged.
|
||||
func Unregister(name string) error {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
r := byName[name]
|
||||
if r == nil {
|
||||
return errors.New("i2creg: can't unregister unknown bus name " + strconv.Quote(name))
|
||||
}
|
||||
delete(byName, name)
|
||||
delete(byNumber, r.Number)
|
||||
for _, alias := range r.Aliases {
|
||||
delete(byAlias, alias)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
var (
|
||||
mu sync.Mutex
|
||||
byName = map[string]*Ref{}
|
||||
// Caches
|
||||
byNumber = map[int]*Ref{}
|
||||
byAlias = map[string]*Ref{}
|
||||
)
|
||||
|
||||
// getDefault returns the Ref that should be used as the default bus.
|
||||
func getDefault() *Ref {
|
||||
var o *Ref
|
||||
if len(byNumber) == 0 {
|
||||
// Fallback to use byName using a lexical sort.
|
||||
name := ""
|
||||
for n, o2 := range byName {
|
||||
if len(name) == 0 || n < name {
|
||||
o = o2
|
||||
name = n
|
||||
}
|
||||
}
|
||||
return o
|
||||
}
|
||||
number := int((^uint(0)) >> 1)
|
||||
for n, o2 := range byNumber {
|
||||
if number > n {
|
||||
number = n
|
||||
o = o2
|
||||
}
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
func insertRef(l []*Ref, r *Ref) []*Ref {
|
||||
n := r.Name
|
||||
i := search(len(l), func(i int) bool { return l[i].Name > n })
|
||||
l = append(l, nil)
|
||||
copy(l[i+1:], l[i:])
|
||||
l[i] = r
|
||||
return l
|
||||
}
|
||||
|
||||
// search implements the same algorithm as sort.Search().
|
||||
//
|
||||
// It was extracted to to not depend on sort, which depends on reflect.
|
||||
func search(n int, f func(int) bool) int {
|
||||
lo := 0
|
||||
for hi := n; lo < hi; {
|
||||
if i := int(uint(lo+hi) >> 1); !f(i) {
|
||||
lo = i + 1
|
||||
} else {
|
||||
hi = i
|
||||
}
|
||||
}
|
||||
return lo
|
||||
}
|
21
vendor/periph.io/x/periph/conn/physic/doc.go
generated
vendored
Normal file
21
vendor/periph.io/x/periph/conn/physic/doc.go
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
// Copyright 2018 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
// Package physic declares types for physical input, outputs and measurement
|
||||
// units.
|
||||
//
|
||||
// This includes temperature, humidity, pressure, tension, current, etc.
|
||||
//
|
||||
// SI units
|
||||
//
|
||||
// The supported S.I. units is a subset of the official ones.
|
||||
// T tera 10¹² 1000000000000
|
||||
// G giga 10⁹ 1000000000
|
||||
// M mega 10⁶ 1000000
|
||||
// k kilo 10³ 1000
|
||||
// m milli 10⁻³ 0.001
|
||||
// µ,u micro 10⁻⁶ 0.000001
|
||||
// n nano 10⁻⁹ 0.000000001
|
||||
// p pico 10⁻¹² 0.000000000001
|
||||
package physic
|
42
vendor/periph.io/x/periph/conn/physic/physic.go
generated
vendored
Normal file
42
vendor/periph.io/x/periph/conn/physic/physic.go
generated
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
// Copyright 2018 The Periph Authors. All rights reserved.
|
||||
// Use of this source code is governed under the Apache License, Version 2.0
|
||||
// that can be found in the LICENSE file.
|
||||
|
||||
package physic
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"periph.io/x/periph/conn"
|
||||
)
|
||||
|
||||
// Env represents measurements from an environmental sensor.
|
||||
type Env struct {
|
||||
Temperature Temperature
|
||||
Pressure Pressure
|
||||
Humidity RelativeHumidity
|
||||
}
|
||||
|
||||
// SenseEnv represents an environmental sensor.
|
||||
type SenseEnv interface {
|
||||
conn.Resource
|
||||
|
||||
// Sense returns the value read from the sensor. Unsupported metrics are not
|
||||
// modified.
|
||||
Sense(env *Env) error
|
||||
// SenseContinuous initiates a continuous sensing at the specified interval.
|
||||
//
|
||||
// It is important to call Halt() once done with the sensing, which will turn
|
||||
// the device off and will close the channel.
|
||||
SenseContinuous(interval time.Duration) (<-chan Env, error)
|
||||
// Precision returns this sensor's precision.
|
||||
//
|
||||
// The env values are set to the number of bits that are significant for each
|
||||
// items that this sensor can measure.
|
||||
//
|
||||
// Precision is not accuracy. The sensor may have absolute and relative
|
||||
// errors in its measurement, that are likely well above the reported
|
||||
// precision. Accuracy may be improved on some sensor by using oversampling,
|
||||
// or doing oversampling in software. Refer to its datasheet if available.
|
||||
Precision(env *Env)
|
||||
}
|
2254
vendor/periph.io/x/periph/conn/physic/units.go
generated
vendored
Normal file
2254
vendor/periph.io/x/periph/conn/physic/units.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user