up
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
// Package udp manages user-defined UDP integrations: inbound listeners
|
||||
// (WSJT-X, JTDX, MSHV log events; JTAlert ADIF; N1MM XML; DXHunter call)
|
||||
// and outbound emitters (db_updated → notifies Cloudlog/N1MM when HamLog
|
||||
// just logged a QSO).
|
||||
//
|
||||
// One Server per connection row, started/stopped by the Manager when the
|
||||
// user enables/disables or edits the row. Multicast support lets multiple
|
||||
// apps share the same port without bind conflicts — essential since
|
||||
// WSJT-X uses 2237 and several tools already listen there.
|
||||
package udp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Direction is "inbound" (we listen) or "outbound" (we emit).
|
||||
type Direction string
|
||||
|
||||
const (
|
||||
Inbound Direction = "inbound"
|
||||
Outbound Direction = "outbound"
|
||||
)
|
||||
|
||||
// ServiceType selects the parser/emitter for a connection.
|
||||
type ServiceType string
|
||||
|
||||
const (
|
||||
ServiceWSJT ServiceType = "wsjt" // WSJT-X / JTDX / MSHV binary
|
||||
ServiceADIF ServiceType = "adif" // text ADIF over UDP
|
||||
ServiceN1MM ServiceType = "n1mm" // N1MM Logger+ XML
|
||||
ServiceRemoteCall ServiceType = "remote_call" // plain text callsign
|
||||
ServiceDBUpdated ServiceType = "db_updated" // outbound ADIF of local QSO
|
||||
)
|
||||
|
||||
// Config is one user-defined UDP connection.
|
||||
type Config struct {
|
||||
ID int64 `json:"id"`
|
||||
Direction Direction `json:"direction"`
|
||||
Name string `json:"name"`
|
||||
Port int `json:"port"`
|
||||
ServiceType ServiceType `json:"service_type"`
|
||||
Multicast bool `json:"multicast"`
|
||||
MulticastGroup string `json:"multicast_group"`
|
||||
DestinationIP string `json:"destination_ip"` // outbound only
|
||||
Enabled bool `json:"enabled"`
|
||||
SortOrder int `json:"sort_order"`
|
||||
}
|
||||
|
||||
// Repo is the persistence layer for UDP integration rows.
|
||||
type Repo struct{ db *sql.DB }
|
||||
|
||||
func NewRepo(db *sql.DB) *Repo { return &Repo{db: db} }
|
||||
|
||||
func (r *Repo) List(ctx context.Context) ([]Config, error) {
|
||||
rows, err := r.db.QueryContext(ctx, `
|
||||
SELECT id, direction, name, port, service_type,
|
||||
multicast, multicast_group, destination_ip,
|
||||
enabled, sort_order
|
||||
FROM integrations_udp
|
||||
ORDER BY direction, sort_order, id`)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list udp: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
var out []Config
|
||||
for rows.Next() {
|
||||
var c Config
|
||||
var mc, en int
|
||||
if err := rows.Scan(&c.ID, &c.Direction, &c.Name, &c.Port, &c.ServiceType,
|
||||
&mc, &c.MulticastGroup, &c.DestinationIP, &en, &c.SortOrder); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.Multicast = mc != 0
|
||||
c.Enabled = en != 0
|
||||
out = append(out, c)
|
||||
}
|
||||
return out, rows.Err()
|
||||
}
|
||||
|
||||
func (r *Repo) Save(ctx context.Context, c *Config) error {
|
||||
if c.Direction != Inbound && c.Direction != Outbound {
|
||||
return fmt.Errorf("invalid direction %q", c.Direction)
|
||||
}
|
||||
if c.Name == "" {
|
||||
return fmt.Errorf("name required")
|
||||
}
|
||||
mc, en := 0, 0
|
||||
if c.Multicast {
|
||||
mc = 1
|
||||
}
|
||||
if c.Enabled {
|
||||
en = 1
|
||||
}
|
||||
if c.ID == 0 {
|
||||
res, err := r.db.ExecContext(ctx, `
|
||||
INSERT INTO integrations_udp(direction, name, port, service_type,
|
||||
multicast, multicast_group, destination_ip, enabled, sort_order)
|
||||
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
c.Direction, c.Name, c.Port, c.ServiceType,
|
||||
mc, c.MulticastGroup, c.DestinationIP, en, c.SortOrder)
|
||||
if err != nil {
|
||||
return fmt.Errorf("insert udp: %w", err)
|
||||
}
|
||||
id, _ := res.LastInsertId()
|
||||
c.ID = id
|
||||
return nil
|
||||
}
|
||||
_, err := r.db.ExecContext(ctx, `
|
||||
UPDATE integrations_udp SET
|
||||
direction = ?, name = ?, port = ?, service_type = ?,
|
||||
multicast = ?, multicast_group = ?, destination_ip = ?,
|
||||
enabled = ?, sort_order = ?,
|
||||
updated_at = strftime('%Y-%m-%dT%H:%M:%fZ','now')
|
||||
WHERE id = ?`,
|
||||
c.Direction, c.Name, c.Port, c.ServiceType,
|
||||
mc, c.MulticastGroup, c.DestinationIP, en, c.SortOrder, c.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("update udp: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Repo) Delete(ctx context.Context, id int64) error {
|
||||
_, err := r.db.ExecContext(ctx, `DELETE FROM integrations_udp WHERE id = ?`, id)
|
||||
return err
|
||||
}
|
||||
Reference in New Issue
Block a user