final use of c libepics

This commit is contained in:
Martino Ferrari
2026-04-27 12:56:00 +02:00
parent 1bda25454b
commit 47e481a461
3 changed files with 143 additions and 9 deletions
+72 -8
View File
@@ -444,41 +444,105 @@ func (e *EPICS) ListSignals(_ context.Context) ([]datasource.Metadata, error) {
}
// Write puts a new value onto a CA channel.
func (e *EPICS) Write(_ context.Context, signal string, value any) error {
// If the signal is not currently subscribed (e.g. a button in oneshot mode),
// a temporary CA channel is created, used for the put, then torn down.
func (e *EPICS) Write(ctx context.Context, signal string, value any) error {
e.attachCAContext()
// Resolve the chid: use an existing subscribed channel if available,
// otherwise create a temporary one (mirrors GetMetadata's approach).
e.mu.Lock()
caCh, ok := e.channels[signal]
e.mu.Unlock()
if !ok {
return datasource.ErrNotFound
var chid C.chid
tempChannel := false
if ok {
chid = caCh.chid
} else {
// Create a temporary channel for the put.
handle := nextHandle()
entry := &connEntry{connCh: make(chan struct{}, 1)}
handleMu.Lock()
connTable[handle] = entry
handleMu.Unlock()
pvName := C.CString(signal)
defer C.free(unsafe.Pointer(pvName))
status := C.caCreateChannel(pvName, C.uintptr_t(handle), &chid)
if status != C.ECA_NORMAL {
handleMu.Lock()
delete(connTable, handle)
handleMu.Unlock()
return fmt.Errorf("epics: ca_create_channel(%q) failed: status %d", signal, int(status))
}
C.ca_flush_io()
select {
case <-entry.connCh:
case <-time.After(5 * time.Second):
C.ca_clear_channel(chid)
handleMu.Lock()
delete(connTable, handle)
handleMu.Unlock()
return fmt.Errorf("epics: timeout connecting to %q for write", signal)
case <-ctx.Done():
C.ca_clear_channel(chid)
handleMu.Lock()
delete(connTable, handle)
handleMu.Unlock()
return ctx.Err()
}
handleMu.Lock()
delete(connTable, handle)
handleMu.Unlock()
tempChannel = true
}
var status C.int
switch v := value.(type) {
case float64:
cv := C.double(v)
status = C.caPut(C.DBR_DOUBLE, caCh.chid, unsafe.Pointer(&cv))
status = C.caPut(C.DBR_DOUBLE, chid, unsafe.Pointer(&cv))
case int64:
cv := C.long(v)
status = C.caPut(C.DBR_LONG, caCh.chid, unsafe.Pointer(&cv))
status = C.caPut(C.DBR_LONG, chid, unsafe.Pointer(&cv))
case string:
cs := C.CString(v)
defer C.free(unsafe.Pointer(cs))
status = C.caPut(C.DBR_STRING, caCh.chid, unsafe.Pointer(cs))
status = C.caPut(C.DBR_STRING, chid, unsafe.Pointer(cs))
case bool:
var iv C.short
if v {
iv = 1
}
status = C.caPut(C.DBR_SHORT, caCh.chid, unsafe.Pointer(&iv))
status = C.caPut(C.DBR_SHORT, chid, unsafe.Pointer(&iv))
default:
if tempChannel {
C.ca_clear_channel(chid)
C.ca_flush_io()
}
return fmt.Errorf("epics: unsupported value type %T for Write", value)
}
if status != C.ECA_NORMAL {
if tempChannel {
C.ca_clear_channel(chid)
C.ca_flush_io()
}
return fmt.Errorf("epics: ca_put(%q) failed: status %d", signal, int(status))
}
C.ca_flush_io()
if tempChannel {
C.ca_pend_io(3.0) // flush + wait for put completion before tearing down
C.ca_clear_channel(chid)
C.ca_flush_io()
} else {
C.ca_flush_io()
}
return nil
}