Implemented epics read/write

This commit is contained in:
Martino Ferrari
2026-04-27 12:42:10 +02:00
parent b76b7f0ba8
commit 1bda25454b
32 changed files with 1553 additions and 281 deletions
+36 -11
View File
@@ -86,23 +86,47 @@ func (b *Broker) Source(name string) (datasource.DataSource, bool) {
// If this is the first subscriber for the signal, the upstream DS subscription
// is started. Subsequent calls for the same signal share the existing one.
// The returned func must be called when the client no longer needs the signal.
//
// The broker mutex is NOT held while the upstream ds.Subscribe call is in
// progress — some data sources (e.g. EPICS) block until the channel connects
// or times out. Releasing the lock allows other goroutines to proceed.
func (b *Broker) Subscribe(ref SignalRef, ch chan<- Update) (func(), error) {
b.mu.Lock()
defer b.mu.Unlock()
sub, exists := b.subs[ref]
if !exists {
ds, found := b.sources[ref.DS]
if !found {
return nil, fmt.Errorf("unknown data source %q", ref.DS)
}
if exists {
// Fast path: already subscribed upstream; just add this client.
sub.mu.Lock()
sub.clients[ch] = struct{}{}
sub.mu.Unlock()
b.mu.Unlock()
return func() { b.unsubscribe(ref, ch) }, nil
}
rawCh := make(chan datasource.Value, 32)
dsCancel, err := ds.Subscribe(b.ctx, ref.Name, rawCh)
if err != nil {
return nil, fmt.Errorf("subscribe %s/%s: %w", ref.DS, ref.Name, err)
}
// Slow path: need to start an upstream subscription.
// Release the lock before the potentially-blocking ds.Subscribe call.
ds, found := b.sources[ref.DS]
b.mu.Unlock()
if !found {
return nil, fmt.Errorf("unknown data source %q", ref.DS)
}
rawCh := make(chan datasource.Value, 32)
dsCancel, err := ds.Subscribe(b.ctx, ref.Name, rawCh)
if err != nil {
return nil, fmt.Errorf("subscribe %s/%s: %w", ref.DS, ref.Name, err)
}
// Re-acquire the lock to register the new subscription.
// Another goroutine might have registered the same ref while the lock
// was released; if so, use that subscription and discard ours.
b.mu.Lock()
if existing, ok := b.subs[ref]; ok {
// Race: someone else subscribed first. Discard duplicate upstream.
dsCancel()
sub = existing
} else {
sub = &signalSub{
clients: make(map[chan<- Update]struct{}),
done: make(chan struct{}),
@@ -116,6 +140,7 @@ func (b *Broker) Subscribe(ref SignalRef, ch chan<- Update) (func(), error) {
sub.mu.Lock()
sub.clients[ch] = struct{}{}
sub.mu.Unlock()
b.mu.Unlock()
return func() { b.unsubscribe(ref, ch) }, nil
}