Phase 2/4 done, working on phase 3/5
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
//go:build epics
|
||||
|
||||
package epics
|
||||
|
||||
/*
|
||||
#include "ca_wrapper.h"
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/uopi/uopi/internal/datasource"
|
||||
)
|
||||
|
||||
// goCAMonitorCallback is called from the C shim caMonitorCallbackShim on every
|
||||
// CA monitor event. It runs on CA's internal callback thread, so it must be
|
||||
// short and must not block. In particular it must not allocate heap memory
|
||||
// that the Go GC would need to track across a CGo boundary.
|
||||
//
|
||||
//export goCAMonitorCallback
|
||||
func goCAMonitorCallback(handle C.uintptr_t, dbrType C.int, count C.long,
|
||||
dbr unsafe.Pointer, severity C.int, epicsTimeSecs C.double) {
|
||||
|
||||
h := uintptr(handle)
|
||||
|
||||
// Compute the Go timestamp. EPICS timestamps are seconds since the EPICS
|
||||
// epoch (1 Jan 1990 UTC).
|
||||
const epicsEpochOffset = 631152000 // Unix seconds of 1990-01-01 00:00:00 UTC
|
||||
secs := float64(epicsTimeSecs)
|
||||
fullSecs := int64(secs)
|
||||
nsec := int64((secs - float64(fullSecs)) * 1e9)
|
||||
ts := time.Unix(fullSecs+epicsEpochOffset, nsec)
|
||||
|
||||
// Map CA alarm severity to datasource quality.
|
||||
var q datasource.Quality
|
||||
switch int(severity) {
|
||||
case 0: // NO_ALARM
|
||||
q = datasource.QualityGood
|
||||
case 1, 2: // MINOR_ALARM, MAJOR_ALARM
|
||||
q = datasource.QualityUncertain
|
||||
default: // INVALID_ALARM (3)
|
||||
q = datasource.QualityBad
|
||||
}
|
||||
|
||||
// Extract the value from the DBR buffer.
|
||||
var data any
|
||||
switch int(dbrType) {
|
||||
case int(C.DBR_TIME_DOUBLE):
|
||||
p := (*C.struct_dbr_time_double)(dbr)
|
||||
data = float64(p.value)
|
||||
|
||||
case int(C.DBR_TIME_FLOAT):
|
||||
p := (*C.struct_dbr_time_float)(dbr)
|
||||
data = float64(p.value)
|
||||
|
||||
case int(C.DBR_TIME_LONG):
|
||||
p := (*C.struct_dbr_time_long)(dbr)
|
||||
data = int64(p.value)
|
||||
|
||||
case int(C.DBR_TIME_SHORT):
|
||||
p := (*C.struct_dbr_time_short)(dbr)
|
||||
data = int64(p.value)
|
||||
|
||||
case int(C.DBR_TIME_ENUM):
|
||||
p := (*C.struct_dbr_time_enum)(dbr)
|
||||
data = int64(p.value)
|
||||
|
||||
case int(C.DBR_TIME_STRING):
|
||||
p := (*C.struct_dbr_time_string)(dbr)
|
||||
// dbr_string is a fixed [MAX_STRING_SIZE]byte array
|
||||
data = C.GoString((*C.char)(unsafe.Pointer(&p.value[0])))
|
||||
|
||||
default:
|
||||
// Unknown type; mark as bad quality and return early.
|
||||
q = datasource.QualityBad
|
||||
data = float64(0)
|
||||
}
|
||||
|
||||
v := datasource.Value{Timestamp: ts, Data: data, Quality: q}
|
||||
|
||||
// Look up the subscription channel under the global handle table lock and
|
||||
// perform a non-blocking send so we never stall the CA callback thread.
|
||||
handleMu.Lock()
|
||||
ch, ok := handleTable[h]
|
||||
handleMu.Unlock()
|
||||
|
||||
if ok {
|
||||
select {
|
||||
case ch <- v:
|
||||
default:
|
||||
// Subscriber is not reading fast enough; drop the update rather than
|
||||
// block the CA callback thread.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// goCAConnectionCallback is called from the C shim caConnectionCallbackShim
|
||||
// whenever a channel connects or disconnects.
|
||||
//
|
||||
//export goCAConnectionCallback
|
||||
func goCAConnectionCallback(handle C.uintptr_t, connected C.int) {
|
||||
h := uintptr(handle)
|
||||
|
||||
handleMu.Lock()
|
||||
entry, ok := connTable[h]
|
||||
handleMu.Unlock()
|
||||
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if int(connected) == 1 {
|
||||
// Signal the one-shot "connected" channel if it has not been closed yet.
|
||||
select {
|
||||
case entry.connCh <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
} else {
|
||||
// Channel disconnected; push a QualityBad sentinel to the subscriber.
|
||||
handleMu.Lock()
|
||||
ch, chOk := handleTable[h]
|
||||
handleMu.Unlock()
|
||||
|
||||
if chOk {
|
||||
v := datasource.Value{
|
||||
Timestamp: time.Now(),
|
||||
Data: float64(0),
|
||||
Quality: datasource.QualityBad,
|
||||
}
|
||||
select {
|
||||
case ch <- v:
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user