waveterm/pkg/eventbus/eventbus.go
Mike Sawka 01b5d71709
new wshrpc mechanism (#112)
lots of changes. new wshrpc implementation. unify websocket, web,
blockcontroller, domain sockets, and terminal inputs to all use the new
rpc system.

lots of moving files around to deal with circular dependencies

use new wshrpc as a client in wsh cmd
2024-07-17 15:24:43 -07:00

142 lines
3.2 KiB
Go

// Copyright 2024, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
package eventbus
import (
"encoding/json"
"fmt"
"log"
"os"
"sync"
"time"
"github.com/wavetermdev/thenextwave/pkg/waveobj"
)
const (
WSEvent_WaveObjUpdate = "waveobj:update"
WSEvent_BlockFile = "blockfile"
WSEvent_Config = "config"
WSEvent_BlockControllerStatus = "blockcontroller:status"
WSEvent_LayoutAction = "layoutaction"
WSEvent_ElectronNewWindow = "electron:newwindow"
WSEvent_Rpc = "rpc"
)
type WSEventType struct {
EventType string `json:"eventtype"`
ORef string `json:"oref,omitempty"`
Data any `json:"data"`
}
const (
FileOp_Append = "append"
FileOp_Truncate = "truncate"
)
type WSFileEventData struct {
ZoneId string `json:"zoneid"`
FileName string `json:"filename"`
FileOp string `json:"fileop"`
Data64 string `json:"data64"`
}
type WindowWatchData struct {
WindowWSCh chan any
WaveWindowId string
WatchedORefs map[waveobj.ORef]bool
}
const (
WSLayoutActionType_Insert = "insert"
WSLayoutActionType_Remove = "delete"
)
type WSLayoutActionData struct {
TabId string `json:"tabid"`
ActionType string `json:"actiontype"`
BlockId string `json:"blockid"`
}
var globalLock = &sync.Mutex{}
var wsMap = make(map[string]*WindowWatchData) // websocketid => WindowWatchData
func RegisterWSChannel(connId string, windowId string, ch chan any) {
globalLock.Lock()
defer globalLock.Unlock()
wsMap[connId] = &WindowWatchData{
WindowWSCh: ch,
WaveWindowId: windowId,
WatchedORefs: make(map[waveobj.ORef]bool),
}
}
func UnregisterWSChannel(connId string) {
globalLock.Lock()
defer globalLock.Unlock()
delete(wsMap, connId)
}
func getWindowWatchesForWindowId(windowId string) []*WindowWatchData {
globalLock.Lock()
defer globalLock.Unlock()
var watches []*WindowWatchData
for _, wdata := range wsMap {
if wdata.WaveWindowId == windowId {
watches = append(watches, wdata)
}
}
return watches
}
// TODO fix busy wait -- but we need to wait until a new window connects back with a websocket
// returns true if the window is connected
func BusyWaitForWindowId(windowId string, timeout time.Duration) bool {
endTime := time.Now().Add(timeout)
for {
if len(getWindowWatchesForWindowId(windowId)) > 0 {
return true
}
if time.Now().After(endTime) {
return false
}
time.Sleep(20 * time.Millisecond)
}
}
func getAllWatches() []*WindowWatchData {
globalLock.Lock()
defer globalLock.Unlock()
watches := make([]*WindowWatchData, 0, len(wsMap))
for _, wdata := range wsMap {
watches = append(watches, wdata)
}
return watches
}
func SendEventToWindow(windowId string, event WSEventType) {
wwdArr := getWindowWatchesForWindowId(windowId)
for _, wdata := range wwdArr {
wdata.WindowWSCh <- event
}
}
func SendEvent(event WSEventType) {
wwdArr := getAllWatches()
for _, wdata := range wwdArr {
wdata.WindowWSCh <- event
}
}
func SendEventToElectron(event WSEventType) {
barr, err := json.Marshal(event)
if err != nil {
log.Printf("cannot marshal electron message: %v\n", err)
return
}
// send to electron
log.Printf("sending event to electron: %q\n", event.EventType)
fmt.Fprintf(os.Stderr, "\nWAVESRV-EVENT:%s\n", string(barr))
}