Replace --broadcast flag with per-universe target config

- Remove --broadcast CLI flag
- Add [[target]] config section for per-universe destination addresses
- Each ArtNet output universe can have its own target IP (broadcast or unicast)
- ArtPoll discovery broadcasts to all unique target addresses
- Falls back to configured target when no nodes discovered for universe

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Ian Gulliver
2025-12-22 18:06:33 -08:00
parent b0e9ecdee7
commit 86403f1ff8
5 changed files with 162 additions and 68 deletions

View File

@@ -11,9 +11,41 @@ import (
// Config represents the application configuration
type Config struct {
Targets []Target `toml:"target"`
Mappings []Mapping `toml:"mapping"`
}
// Target represents a target address for an output universe
type Target struct {
Universe TargetUniverse `toml:"universe"`
Address string `toml:"address"`
}
// TargetUniverse is a universe that can be parsed from string or int
type TargetUniverse struct {
artnet.Universe
}
func (t *TargetUniverse) UnmarshalTOML(data interface{}) error {
switch v := data.(type) {
case string:
u, err := parseUniverse(v)
if err != nil {
return err
}
t.Universe = u
return nil
case int64:
t.Universe = artnet.Universe(v)
return nil
case float64:
t.Universe = artnet.Universe(int64(v))
return nil
default:
return fmt.Errorf("unsupported universe type: %T", data)
}
}
// Protocol specifies the output protocol
type Protocol string
@@ -211,6 +243,13 @@ func Load(path string) (*Config, error) {
return nil, fmt.Errorf("failed to load config: %w", err)
}
// Validate targets
for i, t := range cfg.Targets {
if t.Address == "" {
return nil, fmt.Errorf("target %d: address is required", i)
}
}
for i := range cfg.Mappings {
m := &cfg.Mappings[i]
if m.From.ChannelStart < 1 || m.From.ChannelStart > 512 {
@@ -286,3 +325,12 @@ func (c *Config) SACNSourceUniverses() []uint16 {
}
return result
}
// TargetMap returns a map of universe to target address
func (c *Config) TargetMap() map[artnet.Universe]string {
result := make(map[artnet.Universe]string)
for _, t := range c.Targets {
result[t.Universe.Universe] = t.Address
}
return result
}