初始化siyue
This commit is contained in:
commit
c977643f79
|
|
@ -0,0 +1,5 @@
|
|||
cok-ble
|
||||
.vscode
|
||||
*.sum
|
||||
*.yml
|
||||
*.yaml
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
.PHONY: arm
|
||||
arm:
|
||||
go mod tidy
|
||||
GOOS=linux GOARCH=arm GOARM=7 go build -o april .
|
||||
|
||||
.PHONY: windows
|
||||
windows:
|
||||
go mod tidy
|
||||
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o april.exe .
|
||||
|
||||
.PHONY: macos
|
||||
macos:
|
||||
go mod tidy
|
||||
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build .
|
||||
|
||||
.PHONY: release
|
||||
release: arm
|
||||
ssh pi@192.168.0.27 "sudo systemctl stop april.service"
|
||||
scp ./april pi@192.168.0.27:~/
|
||||
ssh pi@192.168.0.27 "sudo systemctl start april.service"
|
||||
|
||||
.PHONY: nodejs
|
||||
nodejs:
|
||||
npm run build
|
||||
|
||||
.PHONY: front
|
||||
front:
|
||||
scp -r public/dist/* pi@192.168.0.21:~/demo/
|
||||
|
|
@ -0,0 +1 @@
|
|||
@url=localhost:8080/cok-ble
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package gin
|
||||
|
||||
//GIN 配置
|
||||
type Config struct {
|
||||
RootPath string
|
||||
Addr string
|
||||
Port int
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package gin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func Service(conf *Config) {
|
||||
if conf == nil {
|
||||
conf = &Config{
|
||||
RootPath: "/",
|
||||
Addr: "0.0.0.0",
|
||||
Port: 80,
|
||||
}
|
||||
}
|
||||
|
||||
router := gin.New()
|
||||
routerSetup(router, &conf.RootPath)
|
||||
s := &http.Server{
|
||||
Addr: fmt.Sprintf("%s:%d", conf.Addr, conf.Port),
|
||||
Handler: router,
|
||||
ReadTimeout: 10 * time.Second,
|
||||
WriteTimeout: 10 * time.Second,
|
||||
MaxHeaderBytes: 1 << 20,
|
||||
}
|
||||
logrus.Printf("start service on %s", fmt.Sprintf("%s:%d", conf.Addr, conf.Port))
|
||||
logrus.Fatal(s.ListenAndServe())
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package gin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
//路由配置
|
||||
func routerSetup(router *gin.Engine, rootpath *string) {
|
||||
router.Use(gin.Recovery())
|
||||
router.GET(`/health/check`)
|
||||
|
||||
r := router.Group(fmt.Sprintf("/%s", *rootpath))
|
||||
{
|
||||
r.POST(`/register`)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
module myschools.me/wyh/ble-april
|
||||
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/gin-gonic/gin v1.7.7
|
||||
github.com/jonboulle/clockwork v0.2.2 // indirect
|
||||
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible
|
||||
github.com/lestrrat-go/strftime v1.0.5 // indirect
|
||||
github.com/paypal/gatt v0.0.0-20151011220935-4ae819d591cf
|
||||
github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5
|
||||
github.com/shirou/gopsutil v2.21.11+incompatible
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/spf13/viper v1.10.1
|
||||
github.com/tklauser/go-sysconf v0.3.10 // indirect
|
||||
github.com/vmihailenco/msgpack v3.3.3+incompatible
|
||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||
)
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
rotatelogs "github.com/lestrrat-go/file-rotatelogs"
|
||||
"github.com/rifflock/lfshook"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func init() {
|
||||
//日志初始化
|
||||
// logrus.SetOutput(os.Stdout)
|
||||
logrus.SetOutput(ioutil.Discard)
|
||||
logrus.SetFormatter(&logrus.TextFormatter{})
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
logrus.AddHook(newLfsHook(7))
|
||||
}
|
||||
|
||||
func newLfsHook(maxRemainCnt uint) logrus.Hook {
|
||||
//检查与创建日志文件夹
|
||||
_, err := os.Stat("logs")
|
||||
if os.IsNotExist(err) {
|
||||
os.Mkdir("logs", 0755)
|
||||
}
|
||||
|
||||
logName := fmt.Sprintf(`logs/%s`, APPNAME)
|
||||
writer, err := rotatelogs.New(
|
||||
logName+"%Y%m%d.log",
|
||||
rotatelogs.WithLinkName(logName),
|
||||
rotatelogs.WithRotationTime(24*time.Hour),
|
||||
rotatelogs.WithRotationCount(maxRemainCnt),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
panic("config local file system for logger error: " + err.Error())
|
||||
}
|
||||
|
||||
lfsHook := lfshook.NewHook(lfshook.WriterMap{
|
||||
logrus.DebugLevel: writer,
|
||||
logrus.InfoLevel: writer,
|
||||
logrus.WarnLevel: writer,
|
||||
logrus.ErrorLevel: writer,
|
||||
logrus.FatalLevel: writer,
|
||||
logrus.PanicLevel: writer,
|
||||
}, &logrus.TextFormatter{})
|
||||
|
||||
return lfsHook
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/viper"
|
||||
"myschools.me/wyh/ble-april/gin"
|
||||
"myschools.me/wyh/ble-april/service"
|
||||
)
|
||||
|
||||
const (
|
||||
APPNAME = "ble-april"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cf := flag.String("config", "april.yml", "file of config")
|
||||
flag.Parse()
|
||||
viper.SetConfigFile(*cf)
|
||||
if err := viper.ReadInConfig(); err != nil {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"func": "main",
|
||||
}).Fatalf("%s", err.Error())
|
||||
}
|
||||
|
||||
//ble服务
|
||||
service.BleService()
|
||||
go service.BleMessagePush()
|
||||
go service.BleCacheClear()
|
||||
go service.Bletokeninit()
|
||||
go service.BleHeartBeat()
|
||||
|
||||
go gin.Service(&gin.Config{
|
||||
RootPath: APPNAME,
|
||||
Addr: viper.GetString("api.host"),
|
||||
Port: viper.GetInt("api.port"),
|
||||
})
|
||||
|
||||
select {}
|
||||
}
|
||||
|
|
@ -0,0 +1,337 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/paypal/gatt"
|
||||
"github.com/paypal/gatt/examples/option"
|
||||
"github.com/shirou/gopsutil/cpu"
|
||||
"github.com/shirou/gopsutil/disk"
|
||||
"github.com/shirou/gopsutil/mem"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/vmihailenco/msgpack"
|
||||
)
|
||||
|
||||
var m map[string]*IBeancon
|
||||
var chn chan *IBeancon
|
||||
var token string
|
||||
var expiretime time.Time
|
||||
|
||||
func init() {
|
||||
m = make(map[string]*IBeancon, 500)
|
||||
chn = make(chan *IBeancon, 500)
|
||||
}
|
||||
|
||||
type Bletoken struct {
|
||||
AppKey string
|
||||
AppSecret string
|
||||
}
|
||||
|
||||
type BleResponse struct {
|
||||
Code int
|
||||
Msg string
|
||||
Data struct {
|
||||
Token string
|
||||
Expire int64
|
||||
}
|
||||
}
|
||||
|
||||
func Bletokeninit() {
|
||||
for {
|
||||
if time.Now().Before(expiretime) {
|
||||
time.Sleep(5 * time.Minute)
|
||||
continue
|
||||
}
|
||||
|
||||
bb, err := json.Marshal(&Bletoken{
|
||||
AppKey: viper.GetString("api.appkey"),
|
||||
AppSecret: viper.GetString("api.appsecret"),
|
||||
})
|
||||
if err != nil {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"func": "Bletokeninit",
|
||||
}).Warnf("%s", err.Error())
|
||||
continue
|
||||
}
|
||||
reader := strings.NewReader(string(bb))
|
||||
//生成要访问的url
|
||||
url := fmt.Sprintf("%s/oauth", viper.GetString("srv.host"))
|
||||
resp, err := http.Post(url, "application/json;charset=UTF-8", reader)
|
||||
if err != nil {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"func": "BleMessagePush",
|
||||
}).Warnf("http.post: %s", err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
logrus.Println("ioutil.ReadAll:", err.Error())
|
||||
continue
|
||||
}
|
||||
logrus.Println("body", string(body))
|
||||
// 解析返回值,正确时添加入内存
|
||||
retbody := &BleResponse{}
|
||||
err = json.Unmarshal(body, retbody)
|
||||
if err != nil {
|
||||
logrus.Println("err: ", err)
|
||||
}
|
||||
token = retbody.Data.Token
|
||||
expire := retbody.Data.Expire
|
||||
expiretime = time.Now().Add(time.Duration(time.Duration(expire) * time.Second))
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//蓝牙接收服务
|
||||
func BleService() {
|
||||
go func() {
|
||||
dev, err := gatt.NewDevice(option.DefaultClientOptions...)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to open device, err:%s\n", err)
|
||||
}
|
||||
dev.Handle(
|
||||
gatt.PeripheralDiscovered(onPerhipheralDiscovered),
|
||||
)
|
||||
dev.Init(onStateChanged)
|
||||
}()
|
||||
}
|
||||
|
||||
func onStateChanged(device gatt.Device, s gatt.State) {
|
||||
switch s {
|
||||
case gatt.StatePoweredOn:
|
||||
device.Scan([]gatt.UUID{}, true)
|
||||
return
|
||||
default:
|
||||
device.StopScanning()
|
||||
}
|
||||
}
|
||||
|
||||
func onPerhipheralDiscovered(p gatt.Peripheral, a *gatt.Advertisement, rssi int) {
|
||||
//小于设定阈值不处理
|
||||
if rssi < viper.GetInt("option.rssi") {
|
||||
return
|
||||
}
|
||||
// 实例化蓝牙数据
|
||||
b, err := NewiBeacon(a.ManufacturerData, p.ID())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if beacon := m[b.DeviceID]; beacon != nil {
|
||||
beacon.Updated = b.HappenTime.Unix()
|
||||
m[b.DeviceID] = beacon
|
||||
return
|
||||
}
|
||||
|
||||
// 更新持续时长和更新时间
|
||||
chn <- b
|
||||
}
|
||||
|
||||
// 取出数据进行处理
|
||||
func BleMessagePush() {
|
||||
|
||||
for {
|
||||
if b, ok := <-chn; ok {
|
||||
// // 上传服务器
|
||||
bb, err := json.Marshal(b)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
reader := strings.NewReader(string(bb))
|
||||
// 发送请求
|
||||
url := fmt.Sprintf("%s/receive", viper.GetString("srv.host"))
|
||||
|
||||
client := &http.Client{}
|
||||
//生成要访问的url
|
||||
|
||||
//提交请求
|
||||
reqest, err := http.NewRequest("POST", url, reader)
|
||||
//增加header选项
|
||||
reqest.Header.Set("token", token)
|
||||
reqest.Header.Set("Content-Type", "application/json;charset=UTF-8")
|
||||
if err != nil {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"func": "BleMessagePush",
|
||||
}).Warnf("%s", err.Error())
|
||||
continue
|
||||
}
|
||||
//处理返回结果
|
||||
resp, err := client.Do(reqest)
|
||||
if err != nil {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"func": "BleMessagePush",
|
||||
}).Warnf("%s", err.Error())
|
||||
continue
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
logrus.Println("ioutil.ReadAll:", err.Error())
|
||||
continue
|
||||
}
|
||||
logrus.Println("body", string(body))
|
||||
// 解析返回值,正确时添加入内存
|
||||
i := make(map[string]interface{}, 0)
|
||||
err = json.Unmarshal(body, &i)
|
||||
if err != nil {
|
||||
logrus.Println("err: ", err.Error())
|
||||
continue
|
||||
}
|
||||
if i["msg"] == "ok" {
|
||||
m[b.DeviceID] = b
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
type IBeancon struct {
|
||||
DeviceID string
|
||||
HappenTime time.Time
|
||||
Updated int64 `json:"-"`
|
||||
}
|
||||
|
||||
type BleHeart struct {
|
||||
Cpu float64
|
||||
Mem float64
|
||||
Disk float64
|
||||
}
|
||||
|
||||
func BleHeartBeat() {
|
||||
|
||||
for {
|
||||
time.Sleep(1 * time.Minute)
|
||||
// 判断60内有无通讯
|
||||
if len(m) != 0 {
|
||||
continue
|
||||
}
|
||||
percent, _ := cpu.Percent(time.Second, false)
|
||||
memInfo, _ := mem.VirtualMemory()
|
||||
parts, _ := disk.Partitions(true)
|
||||
diskInfo, _ := disk.Usage(parts[0].Mountpoint)
|
||||
|
||||
heart := &BleHeart{
|
||||
Cpu: percent[0],
|
||||
Mem: memInfo.UsedPercent,
|
||||
Disk: diskInfo.UsedPercent,
|
||||
}
|
||||
ht, err := json.Marshal(heart)
|
||||
if err != nil {
|
||||
logrus.Println("json.Marshal: ", err.Error())
|
||||
continue
|
||||
}
|
||||
reader := strings.NewReader(string(ht))
|
||||
// 发送请求
|
||||
url := fmt.Sprintf("%s/heart", viper.GetString("srv.host"))
|
||||
|
||||
client := &http.Client{}
|
||||
//生成要访问的url
|
||||
|
||||
//提交请求
|
||||
reqest, err := http.NewRequest("POST", url, reader)
|
||||
//增加header选项
|
||||
reqest.Header.Set("token", token)
|
||||
reqest.Header.Set("Content-Type", "application/json;charset=UTF-8")
|
||||
if err != nil {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"func": "BleMessagePush",
|
||||
}).Warnf("%s", err.Error())
|
||||
continue
|
||||
}
|
||||
//处理返回结果
|
||||
resp, err := client.Do(reqest)
|
||||
if err != nil {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"func": "BleMessagePush",
|
||||
}).Warnf("%s", err.Error())
|
||||
continue
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
logrus.Println("ioutil.ReadAll:", err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
logrus.Println("heart", string(body))
|
||||
// 解析返回值,正确时添加入内存
|
||||
i := make(map[string]interface{}, 0)
|
||||
err = json.Unmarshal(body, &i)
|
||||
if err != nil {
|
||||
logrus.Println("err: ", err.Error())
|
||||
continue
|
||||
}
|
||||
if i["msg"] != "ok" {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"func": "BleHeartBeat",
|
||||
}).Warnf("%s", errors.New("Push Heart Fail"))
|
||||
continue
|
||||
}
|
||||
// 没有通讯发送请求,cpu,内存,磁盘占用率
|
||||
}
|
||||
}
|
||||
|
||||
type BeaconAprilBag struct {
|
||||
VFirmware string `json:"v-firmware"`
|
||||
MID uint `json:"mid"`
|
||||
Time int64 `json:"time"`
|
||||
IP string `json:"ip"`
|
||||
Mac string `json:"mac"`
|
||||
Devices []byte `json:"devices"`
|
||||
}
|
||||
|
||||
type BeaconApril struct {
|
||||
AdvType string
|
||||
Mac string
|
||||
Rssi int
|
||||
AdvertisementData string
|
||||
}
|
||||
|
||||
func NewiBeacon(data []byte, mac string) (*IBeancon, error) {
|
||||
out1 := &BeaconAprilBag{}
|
||||
if err := msgpack.Unmarshal(data, &out1); err != nil {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"func": "NewiBeacon",
|
||||
}).Warnf("%s", err.Error())
|
||||
return nil, err
|
||||
}
|
||||
// 解析out1.device
|
||||
// bytes.Split()
|
||||
|
||||
if len(data) < 25 || binary.BigEndian.Uint32(data) != 0x4c000215 {
|
||||
return nil, errors.New("not an iBeacon")
|
||||
}
|
||||
beacon := new(IBeancon)
|
||||
beacon.DeviceID = mac
|
||||
beacon.HappenTime = time.Now()
|
||||
beacon.Updated = time.Now().Unix()
|
||||
return beacon, nil
|
||||
}
|
||||
|
||||
// 清除缓存
|
||||
func BleCacheClear() {
|
||||
t := viper.GetInt64("option.interval")
|
||||
if t < 30 {
|
||||
t = 30
|
||||
}
|
||||
for {
|
||||
now := time.Now().Unix()
|
||||
for k, v := range m {
|
||||
if now-v.Updated > t {
|
||||
delete(m, k)
|
||||
}
|
||||
}
|
||||
|
||||
time.Sleep(time.Duration(t/5) * time.Second)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func httpPost(uri string, body interface{}) (*[]byte, error) {
|
||||
bb, err := json.Marshal(body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reader := strings.NewReader(string(bb))
|
||||
|
||||
url := fmt.Sprintf("%s%s", viper.GetString("srv.host"), uri)
|
||||
resp, err := http.Post(url, "application/json;charset=UTF-8", reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
respbody, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &respbody, nil
|
||||
}
|
||||
|
||||
func httpGet(uri string) (*[]byte, error) {
|
||||
url := fmt.Sprintf("%s%s", viper.GetString("srv.host"), uri)
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
respbody, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &respbody, nil
|
||||
}
|
||||
Loading…
Reference in New Issue