Wworking on improving the tool
This commit is contained in:
+71
-10
@@ -7,6 +7,7 @@ import (
|
||||
"io"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/uopi/uopi/internal/broker"
|
||||
@@ -21,12 +22,20 @@ type Handler struct {
|
||||
synthetic *synthetic.Synthetic // nil if not enabled
|
||||
store *storage.Store
|
||||
channelFinderURL string // empty if not configured
|
||||
archiverURL string // empty if not configured
|
||||
log *slog.Logger
|
||||
}
|
||||
|
||||
// New creates an API Handler. synth may be nil if the synthetic DS is disabled.
|
||||
func New(b *broker.Broker, synth *synthetic.Synthetic, store *storage.Store, channelFinderURL string, log *slog.Logger) *Handler {
|
||||
return &Handler{broker: b, synthetic: synth, store: store, channelFinderURL: channelFinderURL, log: log}
|
||||
func New(b *broker.Broker, synth *synthetic.Synthetic, store *storage.Store, channelFinderURL, archiverURL string, log *slog.Logger) *Handler {
|
||||
return &Handler{
|
||||
broker: b,
|
||||
synthetic: synth,
|
||||
store: store,
|
||||
channelFinderURL: channelFinderURL,
|
||||
archiverURL: archiverURL,
|
||||
log: log,
|
||||
}
|
||||
}
|
||||
|
||||
// Register mounts all API routes onto mux under the given prefix.
|
||||
@@ -36,6 +45,7 @@ func (h *Handler) Register(mux *http.ServeMux, prefix string) {
|
||||
mux.HandleFunc("GET "+prefix+"/signals", h.listSignals)
|
||||
mux.HandleFunc("GET "+prefix+"/signals/search", h.searchSignals)
|
||||
mux.HandleFunc("GET "+prefix+"/channel-finder", h.channelFinder)
|
||||
mux.HandleFunc("GET "+prefix+"/archiver/search", h.archiverSearch)
|
||||
mux.HandleFunc("GET "+prefix+"/interfaces", h.listInterfaces)
|
||||
mux.HandleFunc("POST "+prefix+"/interfaces", h.createInterface)
|
||||
mux.HandleFunc("GET "+prefix+"/interfaces/{id}", h.getInterface)
|
||||
@@ -72,14 +82,16 @@ func (h *Handler) listDataSources(w http.ResponseWriter, r *http.Request) {
|
||||
// ── /signals ──────────────────────────────────────────────────────────────────
|
||||
|
||||
type signalInfo struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Unit string `json:"unit,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
DisplayLow float64 `json:"displayLow"`
|
||||
DisplayHigh float64 `json:"displayHigh"`
|
||||
Writable bool `json:"writable"`
|
||||
EnumStrings []string `json:"enumStrings,omitempty"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Unit string `json:"unit,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
DisplayLow float64 `json:"displayLow"`
|
||||
DisplayHigh float64 `json:"displayHigh"`
|
||||
Writable bool `json:"writable"`
|
||||
EnumStrings []string `json:"enumStrings,omitempty"`
|
||||
Properties map[string]string `json:"properties,omitempty"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
}
|
||||
|
||||
func (h *Handler) listSignals(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -160,6 +172,12 @@ func (h *Handler) channelFinder(w http.ResponseWriter, r *http.Request) {
|
||||
cfURL := strings.TrimRight(h.channelFinderURL, "/") + "/resources/channels"
|
||||
if q != "" {
|
||||
cfURL += "?~name=*" + q + "*"
|
||||
} else if r.URL.RawQuery != "" && r.URL.RawQuery != "q=" {
|
||||
cfURL += "?" + r.URL.RawQuery
|
||||
} else {
|
||||
// Empty query check: return empty results to signal CFS is configured.
|
||||
jsonOK(w, []struct{}{})
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := http.Get(cfURL) //nolint:noctx
|
||||
@@ -187,6 +205,47 @@ func (h *Handler) channelFinder(w http.ResponseWriter, r *http.Request) {
|
||||
jsonOK(w, names)
|
||||
}
|
||||
|
||||
// ── /archiver/search ──────────────────────────────────────────────────────────
|
||||
|
||||
// archiverSearch proxies a search query to the EPICS Archive Appliance
|
||||
// management API when configured. Returns 501 otherwise.
|
||||
func (h *Handler) archiverSearch(w http.ResponseWriter, r *http.Request) {
|
||||
if h.archiverURL == "" {
|
||||
jsonError(w, http.StatusNotImplemented, "Archiver not configured (set archive_url in config)")
|
||||
return
|
||||
}
|
||||
|
||||
pattern := r.URL.Query().Get("q")
|
||||
if pattern == "" {
|
||||
pattern = "*"
|
||||
}
|
||||
// AA expects glob patterns. If no glob characters, wrap in stars.
|
||||
if !strings.ContainsAny(pattern, "*?[]") {
|
||||
pattern = "*" + pattern + "*"
|
||||
}
|
||||
|
||||
searchURL := strings.TrimRight(h.archiverURL, "/") + "/mgmt/bpl/getAllPVs?pv=" + url.QueryEscape(pattern)
|
||||
resp, err := http.Get(searchURL) //nolint:noctx
|
||||
if err != nil {
|
||||
jsonError(w, http.StatusBadGateway, "Archiver request failed: "+err.Error())
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
jsonError(w, http.StatusBadGateway, "Archiver returned status "+resp.Status)
|
||||
return
|
||||
}
|
||||
|
||||
var pvs []string
|
||||
if err := json.NewDecoder(resp.Body).Decode(&pvs); err != nil {
|
||||
jsonError(w, http.StatusBadGateway, "Archiver response parse error: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
jsonOK(w, pvs)
|
||||
}
|
||||
|
||||
// ── /groups ───────────────────────────────────────────────────────────────────
|
||||
|
||||
// getGroups returns the workspace group tree as a JSON array.
|
||||
@@ -416,6 +475,8 @@ func metaToSignalInfo(m datasource.Metadata) signalInfo {
|
||||
DisplayHigh: m.DisplayHigh,
|
||||
Writable: m.Writable,
|
||||
EnumStrings: m.EnumStrings,
|
||||
Properties: m.Properties,
|
||||
Tags: m.Tags,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user