more tinkering.
This commit is contained in:
@@ -1,14 +1,18 @@
|
||||
package lucifer3
|
||||
|
||||
import (
|
||||
"log"
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type ServiceKey struct{}
|
||||
|
||||
type Event interface {
|
||||
EventName() string
|
||||
EventDescription() string
|
||||
}
|
||||
|
||||
type Command interface {
|
||||
CommandDescription() string
|
||||
}
|
||||
|
||||
type EventBus struct {
|
||||
@@ -18,7 +22,7 @@ type EventBus struct {
|
||||
}
|
||||
|
||||
// JoinCallback joins the event bus for a moment.
|
||||
func (b *EventBus) JoinCallback(cb func(event Event, sender ServiceID) bool) {
|
||||
func (b *EventBus) JoinCallback(cb func(event Event) bool) {
|
||||
b.Join(newCallbackService(cb))
|
||||
}
|
||||
|
||||
@@ -30,7 +34,7 @@ func (b *EventBus) Join(service Service) {
|
||||
b.mu.Lock()
|
||||
listener := &serviceListener{
|
||||
bus: b,
|
||||
queue: make([]queuedEvent, 0, 16),
|
||||
queue: make([]serviceMessage, 0, 16),
|
||||
service: service,
|
||||
}
|
||||
|
||||
@@ -40,7 +44,17 @@ func (b *EventBus) Join(service Service) {
|
||||
b.mu.Unlock()
|
||||
}
|
||||
|
||||
func (b *EventBus) Send(event Event, sender ServiceID, recipient *ServiceID) {
|
||||
func (b *EventBus) RunCommand(command Command) {
|
||||
fmt.Println("[COMMAND]", command.CommandDescription())
|
||||
b.send(serviceMessage{command: command})
|
||||
}
|
||||
|
||||
func (b *EventBus) RunEvent(event Event) {
|
||||
fmt.Println("[EVENT]", event.EventDescription())
|
||||
b.send(serviceMessage{event: event})
|
||||
}
|
||||
|
||||
func (b *EventBus) send(message serviceMessage) {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
|
||||
@@ -50,15 +64,9 @@ func (b *EventBus) Send(event Event, sender ServiceID, recipient *ServiceID) {
|
||||
deleteList = append(deleteList, i-len(deleteList))
|
||||
continue
|
||||
}
|
||||
if recipient != nil && *recipient != listener.service.ServiceID() {
|
||||
continue
|
||||
}
|
||||
|
||||
listener.mu.Lock()
|
||||
listener.queue = append(listener.queue, queuedEvent{
|
||||
event: event,
|
||||
sender: sender,
|
||||
})
|
||||
listener.queue = append(listener.queue, message)
|
||||
listener.mu.Unlock()
|
||||
}
|
||||
|
||||
@@ -66,12 +74,6 @@ func (b *EventBus) Send(event Event, sender ServiceID, recipient *ServiceID) {
|
||||
b.listeners = append(b.listeners[:i], b.listeners[i+1:]...)
|
||||
}
|
||||
|
||||
if recipient != nil {
|
||||
log.Printf("%s (-> %s): %s", &sender, recipient, event.EventName())
|
||||
} else {
|
||||
log.Printf("%s: %s", &sender, event.EventName())
|
||||
}
|
||||
|
||||
if b.signal != nil {
|
||||
close(b.signal)
|
||||
b.signal = nil
|
||||
@@ -92,12 +94,18 @@ func (b *EventBus) signalCh() <-chan struct{} {
|
||||
type serviceListener struct {
|
||||
mu sync.Mutex
|
||||
bus *EventBus
|
||||
queue []queuedEvent
|
||||
queue []serviceMessage
|
||||
service Service
|
||||
}
|
||||
|
||||
func (l *serviceListener) run(signal <-chan struct{}) {
|
||||
queue := make([]queuedEvent, 0, 16)
|
||||
// Detect command support
|
||||
var activeService ActiveService
|
||||
if as, ok := l.service.(ActiveService); ok {
|
||||
activeService = as
|
||||
}
|
||||
|
||||
queue := make([]serviceMessage, 0, 16)
|
||||
|
||||
for {
|
||||
// Listen for the signal, but stop here if the service has marked
|
||||
@@ -126,13 +134,19 @@ func (l *serviceListener) run(signal <-chan struct{}) {
|
||||
return
|
||||
}
|
||||
|
||||
l.service.Handle(l.bus, message.event, message.sender)
|
||||
if message.event != nil {
|
||||
l.service.HandleEvent(l.bus, message.event)
|
||||
}
|
||||
|
||||
if message.command != nil && activeService != nil {
|
||||
activeService.HandleCommand(l.bus, message.command)
|
||||
}
|
||||
}
|
||||
queue = queue[:0]
|
||||
}
|
||||
}
|
||||
|
||||
type queuedEvent struct {
|
||||
type serviceMessage struct {
|
||||
event Event
|
||||
sender ServiceID
|
||||
command Command
|
||||
}
|
||||
|
||||
+45
-44
@@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
lucifer3 "git.aiterp.net/lucifer3/server"
|
||||
"git.aiterp.net/lucifer3/server/commands"
|
||||
"git.aiterp.net/lucifer3/server/device"
|
||||
"git.aiterp.net/lucifer3/server/events"
|
||||
"git.aiterp.net/lucifer3/server/internal/color"
|
||||
@@ -13,65 +14,65 @@ import (
|
||||
func main() {
|
||||
bus := lucifer3.EventBus{}
|
||||
|
||||
bus.JoinCallback(func(event lucifer3.Event, sender lucifer3.ServiceID) bool {
|
||||
bus.JoinCallback(func(event lucifer3.Event) bool {
|
||||
switch event := event.(type) {
|
||||
case events.HubConnected:
|
||||
log.Println("Callback got connect event")
|
||||
case events.DeviceAssignment:
|
||||
log.Println("Callback can see", len(event.IDs), "devices are being assigned")
|
||||
case events.DeviceRegister:
|
||||
log.Println("Callback should not see that", event.InternalID, "has got a new ID")
|
||||
case events.DeviceDesiredStateChange:
|
||||
log.Println("Callback saw", event.ID, "got a new state.")
|
||||
case events.Connected:
|
||||
log.Println("Callback got connect event for", event.Prefix)
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
c := color.RGB{Red: 1, Green: 0.7, Blue: 0.25}.ToXY()
|
||||
|
||||
bus.Send(events.HubConnected{}, lucifer3.ServiceID{Kind: lucifer3.SKDeviceHub, ID: 1}, nil)
|
||||
bus.Send(events.DeviceAssignment{
|
||||
IDs: []int64{1, 2, 3, 4, 5, 6, 7, 8, 9},
|
||||
Version: 1,
|
||||
State: &device.State{
|
||||
Power: gentools.Ptr(true),
|
||||
Temperature: nil,
|
||||
Intensity: nil,
|
||||
Color: &color.Color{XY: &c},
|
||||
},
|
||||
}, lucifer3.ServiceID{Kind: lucifer3.SKSingleton, Name: "DeviceManager"}, nil)
|
||||
bus.Send(events.DeviceAssignment{
|
||||
IDs: []int64{44, 45, 46, 47, 48, 49, 50, 51, 52, 53},
|
||||
Version: 2,
|
||||
Effect: gentools.Ptr[int64](1044),
|
||||
}, lucifer3.ServiceID{Kind: lucifer3.SKSingleton, Name: "DeviceManager"}, nil)
|
||||
bus.RunEvent(events.Connected{Prefix: "nanoleaf:10.80.1.11"})
|
||||
|
||||
time.Sleep(time.Second / 2)
|
||||
|
||||
bus.Send(events.DeviceHardwareStateChange{
|
||||
InternalID: "c68b310c-7fd6-47a4-ae11-d60d701769da",
|
||||
DeviceFlags: device.SFlagPower | device.SFlagIntensity,
|
||||
ColorFlags: 0,
|
||||
for _, id := range []string{"e28c", "67db", "f744", "d057", "73c1"} {
|
||||
bus.RunEvent(events.HardwareState{
|
||||
DeviceID: "nanoleaf:10.80.1.11:" + id,
|
||||
SupportFlags: device.SFlagPower | device.SFlagColor | device.SFlagIntensity,
|
||||
ColorFlags: device.CFlagRGB,
|
||||
State: device.State{},
|
||||
})
|
||||
}
|
||||
|
||||
bus.RunCommand(commands.ReplaceScene{
|
||||
IDs: []string{"nanoleaf:10.80.1.11:e28c", "nanoleaf:10.80.1.11:67db", "nanoleaf:10.80.1.11:f744", "nanoleaf:10.80.1.11:d057", "nanoleaf:10.80.1.11:73c1"},
|
||||
SceneID: 7,
|
||||
})
|
||||
for _, id := range []string{"nanoleaf:10.80.1.11:e28c", "nanoleaf:10.80.1.11:67db", "nanoleaf:10.80.1.11:f744", "nanoleaf:10.80.1.11:d057", "nanoleaf:10.80.1.11:73c1"} {
|
||||
bus.RunCommand(commands.SetState{
|
||||
ID: id,
|
||||
State: device.State{
|
||||
Power: gentools.Ptr(true),
|
||||
Intensity: gentools.Ptr(0.75),
|
||||
Color: gentools.Ptr(color.MustParse("xy:0.3209,0.1542")),
|
||||
},
|
||||
}, lucifer3.ServiceID{Kind: lucifer3.SKDeviceHub, ID: 1}, nil)
|
||||
})
|
||||
}
|
||||
|
||||
time.Sleep(time.Second / 2)
|
||||
|
||||
bus.RunEvent(events.HardwareState{
|
||||
DeviceID: "nanoleaf:10.80.1.11:40e5",
|
||||
SupportFlags: device.SFlagPower | device.SFlagColor | device.SFlagIntensity,
|
||||
ColorFlags: device.CFlagRGB,
|
||||
State: device.State{},
|
||||
})
|
||||
bus.RunCommand(commands.ReplaceScene{
|
||||
IDs: []string{"nanoleaf:10.80.1.11:40e5", "nanoleaf:10.80.1.11:e28c", "nanoleaf:10.80.1.11:67db", "nanoleaf:10.80.1.11:f744", "nanoleaf:10.80.1.11:d057", "nanoleaf:10.80.1.11:73c1"},
|
||||
SceneID: 7,
|
||||
})
|
||||
bus.RunCommand(commands.SetState{
|
||||
ID: "nanoleaf:10.80.1.11:40e5",
|
||||
State: device.State{
|
||||
Power: gentools.Ptr(true),
|
||||
Intensity: gentools.Ptr(0.75),
|
||||
Color: gentools.Ptr(color.MustParse("xy:0.3209,0.1542")),
|
||||
},
|
||||
})
|
||||
|
||||
time.Sleep(time.Second / 4)
|
||||
|
||||
bus.Send(events.DeviceRegister{
|
||||
InternalID: "c68b310c-7fd6-47a4-ae11-d60d701769da",
|
||||
ID: 10,
|
||||
}, lucifer3.ServiceID{Kind: lucifer3.SKSingleton, Name: "DeviceManager"}, &lucifer3.ServiceID{Kind: lucifer3.SKDeviceHub, ID: 1})
|
||||
bus.Send(events.DeviceDesiredStateChange{
|
||||
ID: 10,
|
||||
NewState: device.State{
|
||||
Power: gentools.Ptr(true),
|
||||
Intensity: gentools.Ptr(0.75),
|
||||
},
|
||||
}, lucifer3.ServiceID{Kind: lucifer3.SKSingleton, Name: "DeviceManager"}, nil)
|
||||
|
||||
time.Sleep(time.Second * 4)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
lucifer3 "git.aiterp.net/lucifer3/server"
|
||||
"git.aiterp.net/lucifer3/server/internal/formattools"
|
||||
)
|
||||
|
||||
type Assign struct {
|
||||
IDs []string `json:"ids"`
|
||||
Effect lucifer3.Effect `json:"effect"`
|
||||
}
|
||||
|
||||
func (c Assign) CommandDescription() string {
|
||||
return fmt.Sprintf("Assign(%v, %s)", formattools.CompactIDList(c.IDs), c.Effect.EffectDescription())
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.aiterp.net/lucifer3/server/internal/formattools"
|
||||
)
|
||||
|
||||
type ReplaceScene struct {
|
||||
IDs []string `json:"ids"`
|
||||
SceneID int64 `json:"sceneId"`
|
||||
}
|
||||
|
||||
func (c ReplaceScene) CommandDescription() string {
|
||||
return fmt.Sprintf("ReplaceScene(%v, %d)", formattools.CompactIDList(c.IDs), c.SceneID)
|
||||
}
|
||||
|
||||
type ClearScene struct {
|
||||
IDs []string `json:"ids"`
|
||||
}
|
||||
|
||||
func (c ClearScene) CommandDescription() string {
|
||||
return fmt.Sprintf("ClearScene(%v)", formattools.CompactIDList(c.IDs))
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.aiterp.net/lucifer3/server/device"
|
||||
)
|
||||
|
||||
type SetState struct {
|
||||
ID string
|
||||
State device.State
|
||||
}
|
||||
|
||||
func (c SetState) CommandDescription() string {
|
||||
return fmt.Sprintf("SetState(%s, %s)", c.ID, c.State)
|
||||
}
|
||||
+24
-9
@@ -1,17 +1,12 @@
|
||||
package device
|
||||
|
||||
import "math/bits"
|
||||
import (
|
||||
"git.aiterp.net/lucifer3/server/internal/gentools"
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
type SupportFlags uint32
|
||||
|
||||
func (f SupportFlags) HasAny(d SupportFlags) bool {
|
||||
return (f & d) != 0
|
||||
}
|
||||
|
||||
func (f SupportFlags) HasAll(d SupportFlags) bool {
|
||||
return bits.OnesCount32(uint32(f&d)) == bits.OnesCount32(uint32(d))
|
||||
}
|
||||
|
||||
const (
|
||||
SFlagPower SupportFlags = 1 << iota
|
||||
SFlagIntensity
|
||||
@@ -23,6 +18,20 @@ const (
|
||||
SFlagSensorPresence
|
||||
)
|
||||
|
||||
var supportFlags = []SupportFlags{SFlagPower, SFlagIntensity, SFlagColor, SFlagTemperature, SFlagSensorButtons, SFlagSensorTemperature, SFlagSensorLightLevel, SFlagSensorPresence}
|
||||
|
||||
func (f SupportFlags) String() string {
|
||||
return gentools.FlagString(f, supportFlags, []string{"P", "I", "C", "T", "SB", "ST", "SLL", "SP"})
|
||||
}
|
||||
|
||||
func (f SupportFlags) HasAny(d SupportFlags) bool {
|
||||
return (f & d) != 0
|
||||
}
|
||||
|
||||
func (f SupportFlags) HasAll(d SupportFlags) bool {
|
||||
return bits.OnesCount32(uint32(f&d)) == bits.OnesCount32(uint32(d))
|
||||
}
|
||||
|
||||
// ColorFlag is primarily to detect warm-white lights, as XY/RGB/HS/HSK can convert without trouble.
|
||||
type ColorFlag uint32
|
||||
|
||||
@@ -34,6 +43,12 @@ const (
|
||||
CFlagKelvin
|
||||
)
|
||||
|
||||
var colorFlags = []ColorFlag{CFlagXY, CFlagRGB, CFlagHS, CFlagHSK, CFlagKelvin}
|
||||
|
||||
func (f ColorFlag) String() string {
|
||||
return gentools.FlagString(f, colorFlags, []string{"XY", "RGB", "HS", "HSK", "K"})
|
||||
}
|
||||
|
||||
func (f ColorFlag) IsColor() bool {
|
||||
return f.HasAny(CFlagXY | CFlagRGB | CFlagHS | CFlagHSK)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package effects
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.aiterp.net/lucifer3/server/device"
|
||||
"git.aiterp.net/lucifer3/server/internal/color"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Manual struct {
|
||||
Power *bool `json:"power,omitempty"`
|
||||
Color *color.Color `json:"color,omitempty"`
|
||||
Intensity *float64 `json:"intensity,omitempty"`
|
||||
Temperature *float64 `json:"temperature,omitempty"`
|
||||
}
|
||||
|
||||
func (e *Manual) EffectDescription() string {
|
||||
return fmt.Sprintf("Manual%s", e.State(0, 0, 0).String())
|
||||
}
|
||||
|
||||
func (e *Manual) Frequency() time.Duration {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (e *Manual) State(int, int, int) device.State {
|
||||
return device.State{
|
||||
Power: e.Power,
|
||||
Temperature: e.Temperature,
|
||||
Intensity: e.Intensity,
|
||||
Color: e.Color,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package events
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Connected struct {
|
||||
Prefix string `json:"prefix"`
|
||||
}
|
||||
|
||||
func (e Connected) EventDescription() string {
|
||||
return fmt.Sprintf("Connect(prefix:%s)", e.Prefix)
|
||||
}
|
||||
|
||||
type Disconnected struct {
|
||||
Prefix string `json:"prefix"`
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
|
||||
func (e Disconnected) EventDescription() string {
|
||||
return fmt.Sprintf("Disconnected(prefix:%s, reason:%s)", e.Prefix, e.Reason)
|
||||
}
|
||||
+10
-71
@@ -3,86 +3,25 @@ package events
|
||||
import (
|
||||
"fmt"
|
||||
"git.aiterp.net/lucifer3/server/device"
|
||||
"git.aiterp.net/lucifer3/server/internal/gentools"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type DeviceHardwareStateChange struct {
|
||||
ID int64 `json:"id,omitempty"`
|
||||
InternalID string `json:"internalId"`
|
||||
DeviceFlags device.SupportFlags `json:"deviceFlags"`
|
||||
type HardwareState struct {
|
||||
DeviceID string `json:"internalId"`
|
||||
SupportFlags device.SupportFlags `json:"deviceFlags"`
|
||||
ColorFlags device.ColorFlag `json:"colorFlags"`
|
||||
State device.State `json:"state"`
|
||||
}
|
||||
|
||||
func (d DeviceHardwareStateChange) EventName() string {
|
||||
idStr := ""
|
||||
if d.ID != 0 {
|
||||
idStr = fmt.Sprintf("id:%d, ", d.ID)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("DeviceHardwareStateChange(%siid:%s, dflags:%d, cflags:%d, state:%s)",
|
||||
idStr, d.InternalID, d.DeviceFlags, d.ColorFlags, d.State,
|
||||
func (d HardwareState) EventDescription() string {
|
||||
return fmt.Sprintf("HardwareState(id:%s, sflags:%s, cflags:%s, state:%s)",
|
||||
d.DeviceID, d.SupportFlags, d.ColorFlags, d.State,
|
||||
)
|
||||
}
|
||||
|
||||
// DeviceRegister must be sent directly to the device hub that took it.
|
||||
type DeviceRegister struct {
|
||||
ID int64 `json:"id"`
|
||||
InternalID string `json:"internalId"`
|
||||
type Unreachable struct {
|
||||
DeviceID string `json:"deviceId"`
|
||||
}
|
||||
|
||||
func (d DeviceRegister) EventName() string {
|
||||
return fmt.Sprintf("DeviceRegister(id:%d, iid:%s)", d.ID, d.InternalID)
|
||||
}
|
||||
|
||||
type DeviceDesiredStateChange struct {
|
||||
ID int64 `json:"id"`
|
||||
NewState device.State `json:"newState"`
|
||||
}
|
||||
|
||||
func (d DeviceDesiredStateChange) EventName() string {
|
||||
return fmt.Sprintf("DeviceDesiredStateChange(id:%d, state:%s)",
|
||||
d.ID, d.NewState,
|
||||
)
|
||||
}
|
||||
|
||||
type DeviceAssignment struct {
|
||||
IDs []int64 `json:"ids"`
|
||||
Version int64 `json:"version"` // DeviceManager sets the version.
|
||||
State *device.State `json:"state"` // DeviceManager sets the state in a follow-up event. Others still need to see it to kick the device.
|
||||
Effect *int64 `json:"effect"` // An effect will pick this up. A scene may defer to own effects
|
||||
Scene *int64 `json:"scene"` // A scene will take it and handle it. Might move it out a level so scenes are just filters that create assignments
|
||||
}
|
||||
|
||||
func (d DeviceAssignment) EventName() string {
|
||||
s := "(!!no change!!)"
|
||||
switch {
|
||||
case d.State != nil:
|
||||
s = fmt.Sprintf("state:%s", *d.State)
|
||||
case d.Effect != nil:
|
||||
s = fmt.Sprintf("effect:%d", *d.Effect)
|
||||
case d.Scene != nil:
|
||||
s = fmt.Sprintf("scene:%d", *d.Scene)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("DeviceAssignment(ids:%s, version:%d, %s)",
|
||||
strings.Join(gentools.FmtSprintArray(d.IDs), ","),
|
||||
d.Version,
|
||||
s,
|
||||
)
|
||||
}
|
||||
|
||||
// DeviceRestore attempts to restore devices to a previous version. It may not be
|
||||
// successful.
|
||||
type DeviceRestore struct {
|
||||
IDs []int64 `json:"ids"`
|
||||
Version int64 `json:"version"`
|
||||
}
|
||||
|
||||
func (d DeviceRestore) EventName() string {
|
||||
return fmt.Sprintf("DeviceRestore(ids:%s, version:%d)",
|
||||
strings.Join(gentools.FmtSprintArray(d.IDs), ","),
|
||||
d.Version,
|
||||
)
|
||||
func (d Unreachable) EventDescription() string {
|
||||
return fmt.Sprintf("Unreachable(id:%s)", d.DeviceID)
|
||||
}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
package events
|
||||
|
||||
import "fmt"
|
||||
|
||||
type HubConnected struct{}
|
||||
|
||||
func (c HubConnected) EventName() string {
|
||||
return "HubConnected"
|
||||
}
|
||||
|
||||
type HubDisconnected struct {
|
||||
Reason string
|
||||
}
|
||||
|
||||
func (d HubDisconnected) EventName() string {
|
||||
return fmt.Sprintf("HubDisconnected(reason:%s)", d.Reason)
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package lucifer3
|
||||
|
||||
import (
|
||||
"git.aiterp.net/lucifer3/server/device"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Effect interface {
|
||||
State(index, round, random int) device.State
|
||||
Frequency() time.Duration
|
||||
EffectDescription() string
|
||||
}
|
||||
@@ -206,6 +206,15 @@ func (col *Color) colorful() colorful.Color {
|
||||
}
|
||||
}
|
||||
|
||||
func MustParse(raw string) Color {
|
||||
col, err := Parse(raw)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return col
|
||||
}
|
||||
|
||||
func Parse(raw string) (col Color, err error) {
|
||||
if raw == "" {
|
||||
return
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package formattools
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func CompactIDList(ids []string) []string {
|
||||
sort.Strings(ids)
|
||||
|
||||
currentGroup := ""
|
||||
currentIDs := make([]string, 0, len(ids))
|
||||
groups := make([]string, 0, 2)
|
||||
for _, id := range ids {
|
||||
if id == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
split := strings.SplitN(id, ":", 3)
|
||||
if len(split) < 3 {
|
||||
continue
|
||||
}
|
||||
|
||||
group := id[:len(split[0])+len(split[1])+1]
|
||||
|
||||
if group != currentGroup {
|
||||
if len(currentIDs) > 0 {
|
||||
if len(currentIDs) == 1 {
|
||||
groups = append(groups, fmt.Sprintf("%s:%s", currentGroup, currentIDs[0]))
|
||||
} else {
|
||||
groups = append(groups, fmt.Sprintf("%s:{%s}", currentGroup, strings.Join(currentIDs, ",")))
|
||||
}
|
||||
currentIDs = currentIDs[:0]
|
||||
}
|
||||
currentGroup = group
|
||||
}
|
||||
|
||||
currentIDs = append(currentIDs, split[2])
|
||||
}
|
||||
|
||||
if len(currentIDs) > 0 {
|
||||
if len(currentIDs) == 1 {
|
||||
groups = append(groups, fmt.Sprintf("%s:%s", currentGroup, currentIDs[0]))
|
||||
} else {
|
||||
groups = append(groups, fmt.Sprintf("%s:{%s}", currentGroup, strings.Join(currentIDs, ",")))
|
||||
}
|
||||
}
|
||||
|
||||
return groups
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
package gentools
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func FmtSprintArray[T any](arr []T) []string {
|
||||
res := make([]string, len(arr))
|
||||
@@ -10,3 +13,7 @@ func FmtSprintArray[T any](arr []T) []string {
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func FmtSprintArrayJoin[T any](arr []T, sep string) string {
|
||||
return strings.Join(FmtSprintArray(arr), sep)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package gentools
|
||||
|
||||
import "strings"
|
||||
|
||||
func FlagString[T ~uint32](value T, flags []T, names []string) string {
|
||||
if value == 0 {
|
||||
return "(none)"
|
||||
}
|
||||
|
||||
parts := make([]string, 0, len(flags))
|
||||
for i, flag := range flags {
|
||||
if value&flag == flag {
|
||||
parts = append(parts, names[i])
|
||||
}
|
||||
}
|
||||
|
||||
return strings.Join(parts, "|")
|
||||
}
|
||||
+7
-68
@@ -1,40 +1,19 @@
|
||||
package lucifer3
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
type Service interface {
|
||||
Active() bool
|
||||
ServiceID() ServiceID
|
||||
Handle(bus *EventBus, event Event, sender ServiceID)
|
||||
}
|
||||
|
||||
type ServiceID struct {
|
||||
Kind ServiceKind
|
||||
ID int64
|
||||
Name string
|
||||
}
|
||||
|
||||
func (s *ServiceID) String() string {
|
||||
if s == nil {
|
||||
return "BROADCAST"
|
||||
}
|
||||
if s.Kind == SKNotService {
|
||||
return "EXTERNAL"
|
||||
}
|
||||
|
||||
if s.Name != "" {
|
||||
return s.Name
|
||||
} else {
|
||||
return fmt.Sprintf("%s[%d]", s.Kind.String(), s.ID)
|
||||
HandleEvent(bus *EventBus, event Event)
|
||||
}
|
||||
|
||||
type ActiveService interface {
|
||||
HandleCommand(bus *EventBus, command Command)
|
||||
}
|
||||
|
||||
type ServiceCommon struct {
|
||||
serviceID ServiceID
|
||||
inactive uint32
|
||||
}
|
||||
|
||||
@@ -46,60 +25,20 @@ func (s *ServiceCommon) markInactive() {
|
||||
atomic.StoreUint32(&s.inactive, 1)
|
||||
}
|
||||
|
||||
func (s *ServiceCommon) ServiceID() ServiceID {
|
||||
return s.serviceID
|
||||
}
|
||||
|
||||
type ServiceKind int
|
||||
|
||||
func (s ServiceKind) String() string {
|
||||
switch s {
|
||||
case SKSingleton:
|
||||
return "Singleton"
|
||||
case SKStorage:
|
||||
return "Storage"
|
||||
case SKDeviceHub:
|
||||
return "DeviceHub"
|
||||
case SKEffect:
|
||||
return "Effect"
|
||||
case SKCallback:
|
||||
return "Callback"
|
||||
case SKNotService:
|
||||
return "NotService"
|
||||
default:
|
||||
return "???"
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
SKNotService ServiceKind = iota
|
||||
SKSingleton
|
||||
SKStorage
|
||||
SKDeviceHub
|
||||
SKEffect
|
||||
SKCallback
|
||||
)
|
||||
|
||||
type callbackService struct {
|
||||
ServiceCommon
|
||||
cb func(event Event, sender ServiceID) bool
|
||||
cb func(event Event) bool
|
||||
}
|
||||
|
||||
func (c *callbackService) Handle(_ *EventBus, event Event, sender ServiceID) {
|
||||
if !c.cb(event, sender) {
|
||||
func (c *callbackService) HandleEvent(_ *EventBus, event Event) {
|
||||
if !c.cb(event) {
|
||||
c.markInactive()
|
||||
}
|
||||
}
|
||||
|
||||
var nextCallback int64 = 0
|
||||
|
||||
func newCallbackService(cb func(event Event, sender ServiceID) bool) Service {
|
||||
func newCallbackService(cb func(event Event) bool) Service {
|
||||
return &callbackService{
|
||||
ServiceCommon: ServiceCommon{
|
||||
serviceID: ServiceID{
|
||||
Kind: SKCallback,
|
||||
ID: atomic.AddInt64(&nextCallback, 1),
|
||||
},
|
||||
inactive: 0,
|
||||
},
|
||||
cb: cb,
|
||||
|
||||
Reference in New Issue
Block a user