208 lines
3.9 KiB
Go
208 lines
3.9 KiB
Go
package cl104
|
|
|
|
import (
|
|
"context"
|
|
"datart/config"
|
|
"datart/log"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/gorilla/websocket"
|
|
)
|
|
|
|
type info struct {
|
|
IOA int `json:"ioa"`
|
|
Val float64 `json:"val"`
|
|
Q int `json:"q"`
|
|
MS int64 `json:"ms"`
|
|
}
|
|
|
|
type Msg struct {
|
|
TI int `json:"ti"`
|
|
COT int `json:"cot"`
|
|
PN int `json:"pn"`
|
|
CA int `json:"ca"`
|
|
Infos []*info `json:"infos"`
|
|
}
|
|
|
|
const UpConnLimit int64 = 10
|
|
|
|
type StatusCh struct {
|
|
Status byte
|
|
UpChan chan []byte
|
|
}
|
|
|
|
var broadcastCh = make(chan []byte, 1024)
|
|
var UpChans []*StatusCh = make([]*StatusCh, UpConnLimit)
|
|
var UpChansMu sync.RWMutex
|
|
|
|
var cl2Chan = map[string]chan []byte{}
|
|
var CA2Chan = map[int]chan []byte{}
|
|
|
|
func init() {
|
|
conf := config.Conf().CL104ConfAll()
|
|
|
|
for _, addr2CAs := range conf {
|
|
for addr, cas := range addr2CAs {
|
|
clChan := make(chan []byte, 16)
|
|
cl2Chan[addr] = clChan
|
|
for _, ca := range cas {
|
|
CA2Chan[ca] = clChan
|
|
}
|
|
}
|
|
}
|
|
|
|
for i := 0; i < len(UpChans); i++ {
|
|
UpChans[i] = &StatusCh{
|
|
Status: 0,
|
|
UpChan: make(chan []byte, 16),
|
|
}
|
|
}
|
|
|
|
go func() {
|
|
for data := range broadcastCh {
|
|
d := append([]byte(nil), data...)
|
|
UpChansMu.RLock()
|
|
for i := 0; i < len(UpChans); i++ {
|
|
if UpChans[i].Status == 1 {
|
|
select {
|
|
case UpChans[i].UpChan <- d:
|
|
default:
|
|
}
|
|
}
|
|
}
|
|
UpChansMu.RUnlock()
|
|
}
|
|
}()
|
|
}
|
|
|
|
func ConnectCLs(ctx context.Context) {
|
|
for cl, ch := range cl2Chan {
|
|
go func(ctx context.Context, cl string, ch chan []byte) {
|
|
connectingCL(ctx, cl, ch)
|
|
}(ctx, cl, ch)
|
|
}
|
|
}
|
|
|
|
func connectingCL(ctx context.Context, cl string, ch chan []byte) {
|
|
for {
|
|
time.Sleep(time.Second * 5)
|
|
subctx, cancel := context.WithCancel(ctx)
|
|
newConnectCL(subctx, cancel, cl, ch)
|
|
}
|
|
}
|
|
|
|
func newConnectCL(ctx context.Context, cancel context.CancelFunc, cl string, ch chan []byte) {
|
|
c, _, err := websocket.DefaultDialer.DialContext(ctx, "ws://"+cl+"/api/104", nil)
|
|
if err != nil {
|
|
log.Error("client dial:", err)
|
|
return
|
|
}
|
|
defer c.Close()
|
|
|
|
go monitoringCLRead(ctx, cancel, c)
|
|
go monitoringCLWrite(ctx, cancel, c, ch)
|
|
|
|
<-ctx.Done()
|
|
}
|
|
|
|
func monitoringCLRead(ctx context.Context, cancel context.CancelFunc, c *websocket.Conn) {
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
default:
|
|
mt, rm, err := c.ReadMessage()
|
|
if err != nil {
|
|
if websocket.IsCloseError(err, websocket.CloseNormalClosure) {
|
|
cancel()
|
|
return
|
|
}
|
|
if ce, ok := err.(*websocket.CloseError); ok {
|
|
log.Info("client closed with code:", ce.Code, "text:", ce.Text)
|
|
cancel()
|
|
return
|
|
}
|
|
log.Error("server read:", err)
|
|
cancel()
|
|
return
|
|
}
|
|
|
|
switch mt {
|
|
case websocket.TextMessage:
|
|
select {
|
|
case broadcastCh <- rm:
|
|
case <-time.After(time.Second * 5):
|
|
log.Error("drop cl msg:", string(rm))
|
|
}
|
|
default:
|
|
log.Error("invalid msg type:", mt)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func monitoringCLWrite(ctx context.Context, cancel context.CancelFunc, c *websocket.Conn, ch chan []byte) {
|
|
var err error
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
case <-time.After(time.Second * 54):
|
|
err = c.WriteControl(websocket.PingMessage, nil, time.Now().Add(time.Second*10))
|
|
if err != nil {
|
|
cancel()
|
|
return
|
|
}
|
|
case msg := <-ch:
|
|
err = c.WriteMessage(websocket.TextMessage, msg)
|
|
if err != nil {
|
|
cancel()
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func AcquireUpSlot() (int, bool) {
|
|
UpChansMu.Lock()
|
|
defer UpChansMu.Unlock()
|
|
for i := 0; i < len(UpChans); i++ {
|
|
if UpChans[i].Status == 0 {
|
|
UpChans[i].Status = 1
|
|
return i, true
|
|
}
|
|
}
|
|
return -1, false
|
|
}
|
|
|
|
func ReleaseUpSlot(idx int) {
|
|
if idx < 0 || idx >= len(UpChans) {
|
|
return
|
|
}
|
|
|
|
UpChansMu.Lock()
|
|
old := UpChans[idx].UpChan
|
|
UpChans[idx].UpChan = make(chan []byte, 16)
|
|
UpChans[idx].Status = 0
|
|
UpChansMu.Unlock()
|
|
|
|
cleanupDone := make(chan struct{})
|
|
go func() {
|
|
defer close(cleanupDone)
|
|
for {
|
|
select {
|
|
case <-old:
|
|
continue
|
|
default:
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
|
|
select {
|
|
case <-cleanupDone:
|
|
case <-time.After(100 * time.Millisecond):
|
|
}
|
|
}
|