itd/socket.go

498 lines
9.4 KiB
Go
Raw Normal View History

2021-08-21 08:19:49 +00:00
/*
* itd uses bluetooth low energy to communicate with InfiniTime devices
* Copyright (C) 2021 Arsen Musayelyan
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main
import (
2022-04-23 00:12:30 +00:00
"errors"
"fmt"
2021-11-23 06:04:09 +00:00
"io"
2021-08-21 08:19:49 +00:00
"net"
"os"
"path/filepath"
"time"
"github.com/rs/zerolog/log"
"go.arsenm.dev/infinitime"
"go.arsenm.dev/infinitime/blefs"
2022-04-23 00:12:30 +00:00
"go.arsenm.dev/itd/api"
"go.arsenm.dev/lrpc/codec"
"go.arsenm.dev/lrpc/server"
2022-04-23 00:12:30 +00:00
)
2022-04-23 01:43:13 +00:00
// This type signifies an unneeded value.
2022-04-23 00:12:30 +00:00
// A struct{} is used as it takes no space in memory.
2022-04-23 01:43:13 +00:00
// This exists for readability purposes
2022-04-23 00:12:30 +00:00
type none = struct{}
var (
ErrDFUInvalidFile = errors.New("provided file is invalid for given upgrade type")
ErrDFUNotEnoughFiles = errors.New("not enough files provided for given upgrade type")
ErrDFUInvalidUpgType = errors.New("invalid upgrade type")
2021-08-21 08:19:49 +00:00
)
type DoneMap map[string]chan struct{}
func (dm DoneMap) Exists(key string) bool {
_, ok := dm[key]
return ok
}
func (dm DoneMap) Done(key string) {
ch := dm[key]
ch <- struct{}{}
}
func (dm DoneMap) Create(key string) {
dm[key] = make(chan struct{}, 1)
}
func (dm DoneMap) Remove(key string) {
close(dm[key])
delete(dm, key)
}
var done = DoneMap{}
2021-08-21 08:19:49 +00:00
func startSocket(dev *infinitime.Device) error {
// Make socket directory if non-existant
2022-02-21 19:20:02 +00:00
err := os.MkdirAll(filepath.Dir(k.String("socket.path")), 0755)
2021-08-21 08:19:49 +00:00
if err != nil {
return err
}
// Remove old socket if it exists
2022-02-21 19:20:02 +00:00
err = os.RemoveAll(k.String("socket.path"))
2021-08-21 08:19:49 +00:00
if err != nil {
return err
}
// Listen on socket path
2022-02-21 19:20:02 +00:00
ln, err := net.Listen("unix", k.String("socket.path"))
2021-08-21 08:19:49 +00:00
if err != nil {
return err
}
fs, err := dev.FS()
if err != nil {
log.Warn().Err(err).Msg("Error getting BLE filesystem")
}
srv := server.New()
2021-08-21 08:19:49 +00:00
2022-04-23 00:12:30 +00:00
itdAPI := &ITD{
dev: dev,
}
err = srv.Register(itdAPI)
2022-04-23 01:43:13 +00:00
if err != nil {
return err
}
2022-04-23 00:12:30 +00:00
fsAPI := &FS{
dev: dev,
fs: fs,
}
err = srv.Register(fsAPI)
2022-04-23 01:43:13 +00:00
if err != nil {
return err
}
2022-04-23 00:12:30 +00:00
2022-05-01 18:41:16 +00:00
go srv.Serve(ln, codec.Default)
2022-04-23 00:12:30 +00:00
2021-08-21 08:19:49 +00:00
// Log socket start
2022-02-21 19:20:02 +00:00
log.Info().Str("path", k.String("socket.path")).Msg("Started control socket")
2021-08-21 08:19:49 +00:00
return nil
}
2022-04-23 00:12:30 +00:00
type ITD struct {
dev *infinitime.Device
}
2021-08-21 08:19:49 +00:00
func (i *ITD) HeartRate(_ *server.Context) (uint8, error) {
return i.dev.HeartRate()
2022-04-23 00:12:30 +00:00
}
func (i *ITD) WatchHeartRate(ctx *server.Context) error {
ch, err := ctx.MakeChannel()
if err != nil {
return err
2022-04-23 18:29:16 +00:00
}
2021-08-21 08:19:49 +00:00
2022-04-23 00:12:30 +00:00
heartRateCh, cancel, err := i.dev.WatchHeartRate()
if err != nil {
return err
}
2022-04-23 00:12:30 +00:00
go func() {
// For every heart rate value
for {
2022-04-23 00:12:30 +00:00
select {
case <-ctx.Done():
fmt.Println("ctx done")
2022-04-23 00:12:30 +00:00
// Stop notifications if done signal received
cancel()
return
case heartRate := <-heartRateCh:
ch <- heartRate
2021-08-21 08:19:49 +00:00
}
2022-04-23 00:12:30 +00:00
}
}()
return nil
}
func (i *ITD) BatteryLevel(_ *server.Context) (uint8, error) {
return i.dev.BatteryLevel()
2022-04-23 00:12:30 +00:00
}
func (i *ITD) WatchBatteryLevel(ctx *server.Context) error {
ch, err := ctx.MakeChannel()
if err != nil {
return err
2022-04-23 18:29:16 +00:00
}
2022-04-23 00:12:30 +00:00
battLevelCh, cancel, err := i.dev.WatchBatteryLevel()
if err != nil {
return err
}
2021-08-21 08:19:49 +00:00
2022-04-23 00:12:30 +00:00
go func() {
// For every heart rate value
for {
2022-04-23 00:12:30 +00:00
select {
case <-ctx.Done():
2022-04-23 00:12:30 +00:00
// Stop notifications if done signal received
cancel()
return
case battLevel := <-battLevelCh:
ch <- battLevel
2021-08-21 08:19:49 +00:00
}
2022-04-23 00:12:30 +00:00
}
}()
return nil
}
func (i *ITD) Motion(_ *server.Context) (infinitime.MotionValues, error) {
return i.dev.Motion()
2022-04-23 00:12:30 +00:00
}
func (i *ITD) WatchMotion(ctx *server.Context) error {
ch, err := ctx.MakeChannel()
if err != nil {
return err
2022-04-23 18:29:16 +00:00
}
2022-04-23 00:12:30 +00:00
motionValsCh, cancel, err := i.dev.WatchMotion()
if err != nil {
return err
}
go func() {
// For every heart rate value
for {
2022-04-23 00:12:30 +00:00
select {
case <-ctx.Done():
2022-04-23 00:12:30 +00:00
// Stop notifications if done signal received
cancel()
return
case motionVals := <-motionValsCh:
ch <- motionVals
2022-04-23 00:12:30 +00:00
}
}
}()
return nil
}
func (i *ITD) StepCount(_ *server.Context) (uint32, error) {
return i.dev.StepCount()
2022-04-23 00:12:30 +00:00
}
func (i *ITD) WatchStepCount(ctx *server.Context) error {
ch, err := ctx.MakeChannel()
if err != nil {
return err
2022-04-23 18:29:16 +00:00
}
2022-04-23 00:12:30 +00:00
stepCountCh, cancel, err := i.dev.WatchStepCount()
if err != nil {
return err
}
go func() {
// For every heart rate value
for {
2022-04-23 00:12:30 +00:00
select {
case <-ctx.Done():
2022-04-23 00:12:30 +00:00
// Stop notifications if done signal received
cancel()
return
case stepCount := <-stepCountCh:
ch <- stepCount
2021-08-21 08:19:49 +00:00
}
2022-04-23 00:12:30 +00:00
}
}()
2021-08-21 08:19:49 +00:00
2022-04-23 00:12:30 +00:00
return nil
}
func (i *ITD) Version(_ *server.Context) (string, error) {
return i.dev.Version()
2022-04-23 00:12:30 +00:00
}
2021-08-21 08:19:49 +00:00
func (i *ITD) Address(_ *server.Context) string {
return i.dev.Address()
2022-04-23 00:12:30 +00:00
}
func (i *ITD) Notify(_ *server.Context, data api.NotifyData) error {
2022-04-23 00:12:30 +00:00
return i.dev.Notify(data.Title, data.Body)
}
func (i *ITD) SetTime(_ *server.Context, t *time.Time) error {
return i.dev.SetTime(*t)
2022-04-23 00:12:30 +00:00
}
func (i *ITD) WeatherUpdate(_ *server.Context) {
2022-04-23 00:12:30 +00:00
sendWeatherCh <- struct{}{}
}
func (i *ITD) FirmwareUpgrade(ctx *server.Context, reqData api.FwUpgradeData) error {
2022-04-23 00:12:30 +00:00
i.dev.DFU.Reset()
switch reqData.Type {
case api.UpgradeTypeArchive:
// If less than one file, return error
if len(reqData.Files) < 1 {
return ErrDFUNotEnoughFiles
}
// If file is not zip archive, return error
if filepath.Ext(reqData.Files[0]) != ".zip" {
return ErrDFUInvalidFile
}
// Load DFU archive
err := i.dev.DFU.LoadArchive(reqData.Files[0])
if err != nil {
return err
}
case api.UpgradeTypeFiles:
// If less than two files, return error
if len(reqData.Files) < 2 {
return ErrDFUNotEnoughFiles
}
// If first file is not init packet, return error
if filepath.Ext(reqData.Files[0]) != ".dat" {
return ErrDFUInvalidFile
}
// If second file is not firmware image, return error
if filepath.Ext(reqData.Files[1]) != ".bin" {
return ErrDFUInvalidFile
}
// Load individual DFU files
err := i.dev.DFU.LoadFiles(reqData.Files[0], reqData.Files[1])
if err != nil {
return err
}
default:
return ErrDFUInvalidUpgType
}
ch, err := ctx.MakeChannel()
if err != nil {
return err
2022-04-23 18:29:16 +00:00
}
2022-04-23 00:12:30 +00:00
go func() {
// For every progress event
for event := range i.dev.DFU.Progress() {
ch <- event
}
firmwareUpdating = false
// Send zero object to signal completion
close(ch)
}()
2022-04-23 00:12:30 +00:00
// Set firmwareUpdating
firmwareUpdating = true
go func() {
// Start DFU
err := i.dev.DFU.Start()
if err != nil {
2022-04-23 01:43:13 +00:00
log.Error().Err(err).Msg("Error while upgrading firmware")
2021-08-21 08:19:49 +00:00
firmwareUpdating = false
2022-04-23 00:12:30 +00:00
return
}
}()
2021-12-13 01:08:48 +00:00
2022-04-23 00:12:30 +00:00
return nil
}
2021-12-13 01:08:48 +00:00
2022-04-23 00:12:30 +00:00
type FS struct {
dev *infinitime.Device
fs *blefs.FS
}
2021-12-13 01:08:48 +00:00
func (fs *FS) Remove(_ *server.Context, paths []string) error {
2022-04-23 00:12:30 +00:00
fs.updateFS()
for _, path := range paths {
err := fs.fs.Remove(path)
if err != nil {
return err
}
}
return nil
}
2021-12-12 06:11:01 +00:00
func (fs *FS) Rename(_ *server.Context, paths [2]string) error {
2022-04-23 00:12:30 +00:00
fs.updateFS()
return fs.fs.Rename(paths[0], paths[1])
}
2021-12-12 06:11:01 +00:00
func (fs *FS) Mkdir(_ *server.Context, paths []string) error {
2022-04-23 00:12:30 +00:00
fs.updateFS()
for _, path := range paths {
err := fs.fs.Mkdir(path)
if err != nil {
return err
}
}
return nil
}
2021-12-12 06:11:01 +00:00
func (fs *FS) ReadDir(_ *server.Context, dir string) ([]api.FileInfo, error) {
2022-04-23 00:12:30 +00:00
fs.updateFS()
2021-12-12 06:11:01 +00:00
2022-04-23 00:12:30 +00:00
entries, err := fs.fs.ReadDir(dir)
if err != nil {
return nil, err
2022-04-23 00:12:30 +00:00
}
var fileInfo []api.FileInfo
for _, entry := range entries {
info, err := entry.Info()
if err != nil {
return nil, err
2022-04-23 00:12:30 +00:00
}
fileInfo = append(fileInfo, api.FileInfo{
Name: info.Name(),
Size: info.Size(),
IsDir: info.IsDir(),
})
}
return fileInfo, nil
2022-04-23 00:12:30 +00:00
}
func (fs *FS) Upload(ctx *server.Context, paths [2]string) error {
2022-04-23 00:12:30 +00:00
fs.updateFS()
localFile, err := os.Open(paths[1])
if err != nil {
return err
}
localInfo, err := localFile.Stat()
if err != nil {
return err
}
remoteFile, err := fs.fs.Create(paths[0], uint32(localInfo.Size()))
if err != nil {
return err
}
ch, err := ctx.MakeChannel()
if err != nil {
return err
}
go func() {
// For every progress event
for sent := range remoteFile.Progress() {
ch <- api.FSTransferProgress{
Total: remoteFile.Size(),
Sent: sent,
2022-04-23 18:29:16 +00:00
}
}
2022-04-23 00:12:30 +00:00
// Send zero object to signal completion
close(ch)
}()
2022-04-23 00:12:30 +00:00
go func() {
io.Copy(remoteFile, localFile)
localFile.Close()
remoteFile.Close()
}()
return nil
2021-08-21 08:19:49 +00:00
}
func (fs *FS) Download(ctx *server.Context, paths [2]string) error {
2022-04-23 00:12:30 +00:00
fs.updateFS()
2022-04-23 00:12:30 +00:00
localFile, err := os.Create(paths[0])
if err != nil {
return err
}
2022-04-23 00:12:30 +00:00
remoteFile, err := fs.fs.Open(paths[1])
2021-08-21 08:19:49 +00:00
if err != nil {
2022-04-23 00:12:30 +00:00
return err
2021-08-21 08:19:49 +00:00
}
ch, err := ctx.MakeChannel()
if err != nil {
return err
}
go func() {
// For every progress event
for sent := range remoteFile.Progress() {
ch <- api.FSTransferProgress{
Total: remoteFile.Size(),
Sent: sent,
2022-04-23 00:12:30 +00:00
}
}
// Send zero object to signal completion
close(ch)
localFile.Close()
remoteFile.Close()
}()
2022-04-23 00:12:30 +00:00
go io.Copy(localFile, remoteFile)
return nil
}
func (fs *FS) updateFS() {
if fs.fs == nil || updateFS {
// Get new FS
newFS, err := fs.dev.FS()
if err != nil {
log.Warn().Err(err).Msg("Error updating BLE filesystem")
} else {
// Set FS pointer to new FS
fs.fs = newFS
// Reset updateFS
updateFS = false
}
}
2021-08-21 08:19:49 +00:00
}