Skip to content

Commit

Permalink
feat: implement building block for encoding
Browse files Browse the repository at this point in the history
  • Loading branch information
muktihari committed Nov 26, 2023
1 parent 3f31fb2 commit 0896fd7
Show file tree
Hide file tree
Showing 15 changed files with 551 additions and 99 deletions.
6 changes: 0 additions & 6 deletions src/wasm/activity-service/activity/fit/creator.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
package fit

import (
"strconv"

"github.com/muktihari/fit/kit/datetime"
"github.com/muktihari/fit/kit/typeconv"
"github.com/muktihari/fit/profile/typedef"
"github.com/muktihari/fit/profile/untyped/fieldnum"
"github.com/muktihari/fit/proto"
"github.com/muktihari/openactivity-fit/activity"
Expand All @@ -23,14 +19,12 @@ func NewCreator(mesg proto.Message) activity.Creator {
continue
}
m.Manufacturer = &manufacturer
m.Name = activity.FormatTitle(typeconv.ToUint16[typedef.Manufacturer](field.Value).String())
case fieldnum.FileIdProduct:
product, ok := field.Value.(uint16)
if !ok {
continue
}
m.Product = &product
m.Name += " (" + strconv.FormatUint(uint64(product), 10) + ")"
case fieldnum.FileIdTimeCreated:
m.TimeCreated = datetime.ToTime(field.Value)
}
Expand Down
31 changes: 31 additions & 0 deletions src/wasm/activity-service/activity/fit/manufacturer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package fit

type Manufacturer struct {
ID uint16
Name string
Products []ManufacturerProduct
}

func (m *Manufacturer) ToMap() map[string]any {
products := make([]any, len(m.Products))
for i := range m.Products {
products[i] = m.Products[i].ToMap()
}
return map[string]any{
"id": uint16(m.ID),
"name": m.Name,
"products": products,
}
}

type ManufacturerProduct struct {
ID uint16
Name string
}

func (p *ManufacturerProduct) ToMap() map[string]any {
return map[string]any{
"id": p.ID,
"name": p.Name,
}
}
38 changes: 35 additions & 3 deletions src/wasm/activity-service/activity/fit/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"
"io"
"strconv"

"github.com/muktihari/fit/decoder"
"github.com/muktihari/openactivity-fit/activity"
Expand All @@ -13,11 +14,15 @@ import (
var _ activity.Service = &service{}

type service struct {
preprocessor *preprocessor.Preprocessor
preprocessor *preprocessor.Preprocessor
manufacturers map[uint16]Manufacturer
}

func NewService(preproc *preprocessor.Preprocessor) activity.Service {
return &service{preprocessor: preproc}
func NewService(preproc *preprocessor.Preprocessor, manufacturers map[uint16]Manufacturer) activity.Service {
return &service{
preprocessor: preproc,
manufacturers: manufacturers,
}
}

func (s *service) Decode(ctx context.Context, r io.Reader) ([]activity.Activity, error) {
Expand Down Expand Up @@ -60,6 +65,11 @@ func (s *service) convertListenerResultToActivity(result *ListenerResult) *activ

s.sanitize(result)

creator := result.Creator
if creator.Manufacturer != nil && creator.Product != nil {
creator.Name = s.creatorName(*creator.Manufacturer, *creator.Product)
}

act := &activity.Activity{
Creator: *result.Creator,
Timezone: result.Timezone,
Expand Down Expand Up @@ -223,3 +233,25 @@ func (s *service) finalizeSession(ses *activity.Session) {
sesFromLaps := activity.NewSessionFromLaps(ses.Laps, ses.Sport)
activity.CombineSession(ses, sesFromLaps)
}

func (s *service) creatorName(manufacturerID, productID uint16) string {
manufacturer, ok := s.manufacturers[manufacturerID]
if !ok {
return activity.Unknown
}

var productName string
for i := range manufacturer.Products {
product := manufacturer.Products[i]
if product.ID == productID {
productName = product.Name
break
}
}

if productName == "" {
productName = "(" + strconv.FormatUint(uint64(productID), 10) + ")"
}

return manufacturer.Name + " " + productName
}
4 changes: 2 additions & 2 deletions src/wasm/activity-service/activity/fit/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func NewSession(mesg proto.Message) *activity.Session {
if !ok || sport == basetype.EnumInvalid {
continue
}
ses.Sport = activity.FormatTitle(typedef.Sport(sport).String())
ses.Sport = kit.FormatTitle(typedef.Sport(sport).String())
case fieldnum.SessionTotalMovingTime:
totalMovingTime, ok := field.Value.(uint32)
if !ok || totalMovingTime == basetype.Uint32Invalid {
Expand Down Expand Up @@ -149,7 +149,7 @@ func NewSession(mesg proto.Message) *activity.Session {
}
}

if ses.Sport == activity.FormatTitle(typedef.SportAll.String()) {
if ses.Sport == kit.FormatTitle(typedef.SportAll.String()) {
ses.Sport = activity.SportGeneric
}

Expand Down
3 changes: 2 additions & 1 deletion src/wasm/activity-service/activity/gpx/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/muktihari/openactivity-fit/activity"
"github.com/muktihari/openactivity-fit/activity/gpx/schema"
"github.com/muktihari/openactivity-fit/kit"
"github.com/muktihari/openactivity-fit/preprocessor"
)

Expand Down Expand Up @@ -60,7 +61,7 @@ func (s *service) Decode(ctx context.Context, r io.Reader) ([]activity.Activity,
for i := range gpx.Tracks { // Sessions
trk := gpx.Tracks[i]

sport := activity.FormatTitle(trk.Type)
sport := kit.FormatTitle(trk.Type)
if sport == "" || sport == "Other" {
sport = activity.SportGeneric
}
Expand Down
2 changes: 1 addition & 1 deletion src/wasm/activity-service/activity/tcx/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func (s *service) Decode(ctx context.Context, r io.Reader) ([]activity.Activity,
continue
}

sport := activity.FormatTitle(a.Activity.Sport)
sport := kit.FormatTitle(a.Activity.Sport)
if sport == "" || sport == "Other" {
sport = activity.SportGeneric
}
Expand Down
17 changes: 0 additions & 17 deletions src/wasm/activity-service/activity/util.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
package activity

import (
"strings"
"time"
"unicode"

"golang.org/x/text/cases"
"golang.org/x/text/language"
)

const (
Expand Down Expand Up @@ -48,18 +43,6 @@ func HasPace(sport string) bool {
}
}

// FormatTitle returns init capital for every word. "snow boarding", "snow_boarding", "SNOW_boardinG" -> "Show Boarding".
func FormatTitle(s string) string {
s = strings.Map(func(r rune) rune {
if !unicode.IsLetter(r) && !unicode.IsDigit(r) {
return ' '
}
return r
}, s)
s = cases.Title(language.English).String(s)
return s
}

func isBelong(timestamp, startTime, endTime time.Time) bool {
if timestamp.Equal(startTime) {
return true
Expand Down
21 changes: 20 additions & 1 deletion src/wasm/activity-service/kit/kit.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
package kit

import "golang.org/x/exp/constraints"
import (
"strings"
"unicode"

"golang.org/x/exp/constraints"
"golang.org/x/text/cases"
"golang.org/x/text/language"
)

// Ptr returns pointer of v
func Ptr[T any](v T) *T { return &v }
Expand All @@ -23,3 +30,15 @@ func PickNonZeroValuePtr[T constraints.Integer | constraints.Float](x, y *T) *T
}
return x
}

// FormatTitle returns init capital for every word. "snow boarding", "snow_boarding", "SNOW_boardinG" -> "Show Boarding".
func FormatTitle(s string) string {
s = strings.Map(func(r rune) rune {
if !unicode.IsLetter(r) && !unicode.IsDigit(r) {
return ' '
}
return r
}, s)
s = cases.Title(language.English).String(s)
return s
}
86 changes: 81 additions & 5 deletions src/wasm/activity-service/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,100 @@ package main
import (
"bytes"
"context"
_ "embed"
"encoding/json"
"fmt"
"io"
"strconv"
"syscall/js"

"github.com/muktihari/fit/profile/typedef"
"github.com/muktihari/openactivity-fit/activity/fit"
"github.com/muktihari/openactivity-fit/activity/gpx"
"github.com/muktihari/openactivity-fit/activity/tcx"
"github.com/muktihari/openactivity-fit/kit"
"github.com/muktihari/openactivity-fit/preprocessor"
"github.com/muktihari/openactivity-fit/service"
"github.com/muktihari/openactivity-fit/service/result"
"golang.org/x/exp/slices"
)

//go:embed manufacturers.json
var manufacturerJson []byte

func main() {
manufacturers, err := makeManufacturers()
if err != nil {
fmt.Printf("could not make manufactures mapping: %v\n", err)
}

preproc := preprocessor.New()

fs := fit.NewService(preproc)
fs := fit.NewService(preproc, manufacturers)
gs := gpx.NewService(preproc)
ts := tcx.NewService(preproc)

s := service.New(fs, gs, ts)
s := service.New(fs, gs, ts, manufacturers)

js.Global().Set("decode", Decode(s))
js.Global().Set("decode", createDecodeFunc(s))
js.Global().Set("encode", createEncodeFunc(s))
js.Global().Set("manufacturerList", createManufacturerListFunc(s))

fmt.Println("WebAssembly: Activity Service Instantiated")
select {} // never exit
}

func Decode(s service.Service) js.Func {
func makeManufacturers() (manufacturers map[uint16]fit.Manufacturer, err error) {
manufacturers = make(map[uint16]fit.Manufacturer)

var source map[string]fit.Manufacturer
if err = json.Unmarshal(manufacturerJson, &source); err != nil {
return
}

manufacturerIDs := typedef.ListManufacturer()
garminProductIDs := typedef.ListGarminProduct()

for i := range manufacturerIDs {
manufacturerID := manufacturerIDs[i]
manufacturer := fit.Manufacturer{
ID: uint16(manufacturerID),
Name: kit.FormatTitle(manufacturerID.String()),
}

if manufacturer.ID == uint16(typedef.ManufacturerGarmin) {
for j := range garminProductIDs {
product := fit.ManufacturerProduct{
ID: uint16(garminProductIDs[j]),
Name: kit.FormatTitle(garminProductIDs[j].String()),
}
manufacturer.Products = append(manufacturer.Products, product)
}
}

if m, ok := source[strconv.FormatUint(uint64(manufacturer.ID), 10)]; ok {
manufacturer.Name = m.Name
manufacturer.Products = m.Products
}

slices.SortFunc(manufacturer.Products, func(a, b fit.ManufacturerProduct) int {
if a.Name < b.Name {
return -1
}
return 1
})

manufacturers[manufacturer.ID] = manufacturer
}

return
}

func createDecodeFunc(s service.Service) js.Func {
return js.FuncOf(func(this js.Value, args []js.Value) any {
input := args[0] // input is an Array<Uint8Array>
if input.Length() == 0 {
return service.Result{Err: fmt.Errorf("no input is passed")}.ToMap()
return result.Decode{Err: fmt.Errorf("no input is passed")}.ToMap()
}

rs := make([]io.Reader, input.Length())
Expand All @@ -49,3 +112,16 @@ func Decode(s service.Service) js.Func {
return result.ToMap()
})
}

func createEncodeFunc(s service.Service) js.Func {
return js.FuncOf(func(this js.Value, args []js.Value) any {
result := s.Encode(context.Background(), nil)
return result.ToMap()
})
}

func createManufacturerListFunc(s service.Service) js.Func {
return js.FuncOf(func(this js.Value, args []js.Value) any {
return s.ManufacturerList().ToMap()
})
}
Loading

0 comments on commit 0896fd7

Please sign in to comment.