final use of c libepics
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user