Merge branch 'trunk' of git.dynatron.me:amigan/stillbox into trunk

This commit is contained in:
Daniel 2024-08-08 19:25:24 -04:00
commit 1d4eac2a89
16 changed files with 688 additions and 153 deletions

135
pkg/calls/filter.go Normal file
View file

@ -0,0 +1,135 @@
package calls
import (
"context"
"dynatron.me/x/stillbox/pkg/gordio/database"
"dynatron.me/x/stillbox/pkg/pb"
)
type Talkgroup struct {
System uint32
Talkgroup uint32
}
func (c *Call) TalkgroupTuple() Talkgroup {
return Talkgroup{System: uint32(c.System), Talkgroup: uint32(c.Talkgroup)}
}
func (t Talkgroup) Pack() int64 {
// P25 system IDs are 12 bits, so we can fit them in a signed 8 byte int (int64, pg INT8)
return int64((int64(t.System) << 32) | int64(t.Talkgroup))
}
type TalkgroupFilter struct {
Talkgroups []Talkgroup `json:"talkgroups,omitempty"`
TalkgroupsNot []Talkgroup `json:"talkgroupsNot,omitempty"`
TalkgroupTagsAll []string `json:"talkgroupTagsAll,omitempty"`
TalkgroupTagsAny []string `json:"talkgroupTagsAny,omitempty"`
TalkgroupTagsNot []string `json:"talkgroupTagsNot,omitempty"`
talkgroups map[Talkgroup]bool
}
func TalkgroupFilterFromPB(ctx context.Context, p *pb.Filter) (*TalkgroupFilter, error) {
tgf := &TalkgroupFilter{
TalkgroupTagsAll: p.TalkgroupTagsAll,
TalkgroupTagsAny: p.TalkgroupTagsAny,
TalkgroupTagsNot: p.TalkgroupTagsNot,
}
if l := len(p.Talkgroups); l > 0 {
tgf.Talkgroups = make([]Talkgroup, l)
for i, t := range p.Talkgroups {
tgf.Talkgroups[i] = Talkgroup{
System: uint32(t.System),
Talkgroup: uint32(t.Talkgroup),
}
}
}
if l := len(p.TalkgroupsNot); l > 0 {
tgf.TalkgroupsNot = make([]Talkgroup, l)
for i, t := range p.TalkgroupsNot {
tgf.TalkgroupsNot[i] = Talkgroup{
System: uint32(t.System),
Talkgroup: uint32(t.Talkgroup),
}
}
}
return tgf, tgf.compile(ctx)
}
func PackedTGs(tg []Talkgroup) []int64 {
s := make([]int64, len(tg))
for i, v := range tg {
s[i] = v.Pack()
}
return s
}
func (f *TalkgroupFilter) hasTags() bool {
return len(f.TalkgroupTagsAny) > 0 || len(f.TalkgroupTagsAll) > 0 || len(f.TalkgroupTagsNot) > 0
}
func (f *TalkgroupFilter) GetFinalTalkgroups() map[Talkgroup]bool {
return f.talkgroups
}
func (f *TalkgroupFilter) compile(ctx context.Context) error {
f.talkgroups = make(map[Talkgroup]bool)
for _, tg := range f.Talkgroups {
f.talkgroups[tg] = true
}
if f.hasTags() { // don't bother with DB if no tags
db := database.FromCtx(ctx)
tagTGs, err := db.GetTalkgroupIDsByTags(ctx, f.TalkgroupTagsAny, f.TalkgroupTagsAll, f.TalkgroupTagsNot)
if err != nil {
return err
}
for _, tg := range tagTGs {
f.talkgroups[Talkgroup{System: uint32(tg.SystemID), Talkgroup: uint32(tg.Tgid)}] = true
}
}
for _, tg := range f.TalkgroupsNot {
f.talkgroups[tg] = false
}
return nil
}
func (f *TalkgroupFilter) Test(ctx context.Context, call *Call) bool {
if f == nil { // no filter means all calls
return true
}
if f.talkgroups == nil {
err := f.compile(ctx)
if err != nil {
panic(err)
}
}
tg := call.TalkgroupTuple()
tgRes, have := f.talkgroups[tg]
if have {
return tgRes
}
for _, patch := range call.Patches {
tg.Talkgroup = uint32(patch)
tgRes, have := f.talkgroups[tg]
if have {
return tgRes
}
}
return false
}

View file

@ -13,6 +13,8 @@ import (
"github.com/jackc/pgx/v5/pgxpool" "github.com/jackc/pgx/v5/pgxpool"
) )
// This file will eventually turn into a postgres driver.
// DB is a database handle. // DB is a database handle.
type DB struct { type DB struct {
*pgxpool.Pool *pgxpool.Pool

View file

@ -68,8 +68,9 @@ type System struct {
} }
type Talkgroup struct { type Talkgroup struct {
SystemID int `json:"system_id"` ID int64 `json:"id"`
Tgid int `json:"tgid"` SystemID int32 `json:"system_id"`
Tgid int32 `json:"tgid"`
Name *string `json:"name"` Name *string `json:"name"`
TgGroup *string `json:"tg_group"` TgGroup *string `json:"tg_group"`
Frequency *int32 `json:"frequency"` Frequency *int32 `json:"frequency"`

View file

@ -13,12 +13,14 @@ import (
type Querier interface { type Querier interface {
AddCall(ctx context.Context, arg AddCallParams) (uuid.UUID, error) AddCall(ctx context.Context, arg AddCallParams) (uuid.UUID, error)
BulkSetTalkgroupTags(ctx context.Context, iD int64, tags []string) error
CreateAPIKey(ctx context.Context, owner int, expires pgtype.Timestamp, disabled *bool) (ApiKey, error) CreateAPIKey(ctx context.Context, owner int, expires pgtype.Timestamp, disabled *bool) (ApiKey, error)
CreateUser(ctx context.Context, arg CreateUserParams) (User, error) CreateUser(ctx context.Context, arg CreateUserParams) (User, error)
DeleteAPIKey(ctx context.Context, apiKey string) error DeleteAPIKey(ctx context.Context, apiKey string) error
DeleteUser(ctx context.Context, username string) error DeleteUser(ctx context.Context, username string) error
GetAPIKey(ctx context.Context, apiKey string) (ApiKey, error) GetAPIKey(ctx context.Context, apiKey string) (ApiKey, error)
GetTalkgroupTags(ctx context.Context, systemID int, tgid int) ([]string, error) GetTalkgroupIDsByTags(ctx context.Context, anytags []string, alltags []string, nottags []string) ([]GetTalkgroupIDsByTagsRow, error)
GetTalkgroupTags(ctx context.Context, sys int, tg int) ([]string, error)
GetTalkgroupsWithAllTags(ctx context.Context, tags []string) ([]Talkgroup, error) GetTalkgroupsWithAllTags(ctx context.Context, tags []string) ([]Talkgroup, error)
GetTalkgroupsWithAnyTags(ctx context.Context, tags []string) ([]Talkgroup, error) GetTalkgroupsWithAnyTags(ctx context.Context, tags []string) ([]Talkgroup, error)
GetUserByID(ctx context.Context, id int32) (User, error) GetUserByID(ctx context.Context, id int32) (User, error)
@ -26,7 +28,7 @@ type Querier interface {
GetUserByUsername(ctx context.Context, username string) (User, error) GetUserByUsername(ctx context.Context, username string) (User, error)
GetUsers(ctx context.Context) ([]User, error) GetUsers(ctx context.Context) ([]User, error)
SetCallTranscript(ctx context.Context, iD uuid.UUID, transcript *string) error SetCallTranscript(ctx context.Context, iD uuid.UUID, transcript *string) error
SetTalkgroupTags(ctx context.Context, tags []string, tgid int) error SetTalkgroupTags(ctx context.Context, sys int, tg int, tags []string) error
UpdatePassword(ctx context.Context, username string, password string) error UpdatePassword(ctx context.Context, username string, password string) error
} }

View file

@ -9,20 +9,62 @@ import (
"context" "context"
) )
const getTalkgroupTags = `-- name: GetTalkgroupTags :one const bulkSetTalkgroupTags = `-- name: BulkSetTalkgroupTags :exec
SELECT tags FROM talkgroups UPDATE talkgroups SET tags = $2
WHERE system_id = $1 AND tgid = $2 WHERE id = ANY($1)
` `
func (q *Queries) GetTalkgroupTags(ctx context.Context, systemID int, tgid int) ([]string, error) { func (q *Queries) BulkSetTalkgroupTags(ctx context.Context, iD int64, tags []string) error {
row := q.db.QueryRow(ctx, getTalkgroupTags, systemID, tgid) _, err := q.db.Exec(ctx, bulkSetTalkgroupTags, iD, tags)
return err
}
const getTalkgroupIDsByTags = `-- name: GetTalkgroupIDsByTags :many
SELECT system_id, tgid FROM talkgroups
WHERE (tags @> ARRAY[$1])
AND (tags && ARRAY[$2])
AND NOT (tags @> ARRAY[$3])
`
type GetTalkgroupIDsByTagsRow struct {
SystemID int32 `json:"system_id"`
Tgid int32 `json:"tgid"`
}
func (q *Queries) GetTalkgroupIDsByTags(ctx context.Context, anytags []string, alltags []string, nottags []string) ([]GetTalkgroupIDsByTagsRow, error) {
rows, err := q.db.Query(ctx, getTalkgroupIDsByTags, anytags, alltags, nottags)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetTalkgroupIDsByTagsRow
for rows.Next() {
var i GetTalkgroupIDsByTagsRow
if err := rows.Scan(&i.SystemID, &i.Tgid); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getTalkgroupTags = `-- name: GetTalkgroupTags :one
SELECT tags FROM talkgroups
WHERE id = systg2id($1, $2)
`
func (q *Queries) GetTalkgroupTags(ctx context.Context, sys int, tg int) ([]string, error) {
row := q.db.QueryRow(ctx, getTalkgroupTags, sys, tg)
var tags []string var tags []string
err := row.Scan(&tags) err := row.Scan(&tags)
return tags, err return tags, err
} }
const getTalkgroupsWithAllTags = `-- name: GetTalkgroupsWithAllTags :many const getTalkgroupsWithAllTags = `-- name: GetTalkgroupsWithAllTags :many
SELECT system_id, tgid, name, tg_group, frequency, metadata, tags FROM talkgroups SELECT id, system_id, tgid, name, tg_group, frequency, metadata, tags FROM talkgroups
WHERE tags && ARRAY[$1] WHERE tags && ARRAY[$1]
` `
@ -36,6 +78,7 @@ func (q *Queries) GetTalkgroupsWithAllTags(ctx context.Context, tags []string) (
for rows.Next() { for rows.Next() {
var i Talkgroup var i Talkgroup
if err := rows.Scan( if err := rows.Scan(
&i.ID,
&i.SystemID, &i.SystemID,
&i.Tgid, &i.Tgid,
&i.Name, &i.Name,
@ -55,7 +98,7 @@ func (q *Queries) GetTalkgroupsWithAllTags(ctx context.Context, tags []string) (
} }
const getTalkgroupsWithAnyTags = `-- name: GetTalkgroupsWithAnyTags :many const getTalkgroupsWithAnyTags = `-- name: GetTalkgroupsWithAnyTags :many
SELECT system_id, tgid, name, tg_group, frequency, metadata, tags FROM talkgroups SELECT id, system_id, tgid, name, tg_group, frequency, metadata, tags FROM talkgroups
WHERE tags @> ARRAY[$1] WHERE tags @> ARRAY[$1]
` `
@ -69,6 +112,7 @@ func (q *Queries) GetTalkgroupsWithAnyTags(ctx context.Context, tags []string) (
for rows.Next() { for rows.Next() {
var i Talkgroup var i Talkgroup
if err := rows.Scan( if err := rows.Scan(
&i.ID,
&i.SystemID, &i.SystemID,
&i.Tgid, &i.Tgid,
&i.Name, &i.Name,
@ -88,11 +132,11 @@ func (q *Queries) GetTalkgroupsWithAnyTags(ctx context.Context, tags []string) (
} }
const setTalkgroupTags = `-- name: SetTalkgroupTags :exec const setTalkgroupTags = `-- name: SetTalkgroupTags :exec
UPDATE talkgroups SET tags = $1 UPDATE talkgroups SET tags = $3
WHERE system_id = $1 AND tgid = $2 WHERE id = systg2id($1, $2)
` `
func (q *Queries) SetTalkgroupTags(ctx context.Context, tags []string, tgid int) error { func (q *Queries) SetTalkgroupTags(ctx context.Context, sys int, tg int, tags []string) error {
_, err := q.db.Exec(ctx, setTalkgroupTags, tags, tgid) _, err := q.db.Exec(ctx, setTalkgroupTags, sys, tg, tags)
return err return err
} }

View file

@ -1,10 +1,11 @@
package nexus package nexus
import ( import (
"context"
"io" "io"
"sync" "sync"
"dynatron.me/x/stillbox/pkg/live" "dynatron.me/x/stillbox/pkg/calls"
"dynatron.me/x/stillbox/pkg/pb" "dynatron.me/x/stillbox/pkg/pb"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
@ -16,8 +17,8 @@ type Client interface {
Connection Connection
HandleCommand(*pb.Command) HandleCommand(context.Context, *pb.Command)
HandleMessage([]byte) HandleMessage(context.Context, []byte)
} }
type client struct { type client struct {
@ -25,7 +26,9 @@ type client struct {
Connection Connection
live live.Listener liveState pb.LiveState
filter *calls.TalkgroupFilter
nexus *Nexus nexus *Nexus
} }
@ -45,7 +48,7 @@ func (n *Nexus) NewClient(conn Connection) Client {
return sess return sess
} }
func (c *client) HandleMessage(mesgBytes []byte) { func (c *client) HandleMessage(ctx context.Context, mesgBytes []byte) {
var msg pb.Command var msg pb.Command
err := proto.Unmarshal(mesgBytes, &msg) err := proto.Unmarshal(mesgBytes, &msg)
if err != nil { if err != nil {
@ -53,5 +56,5 @@ func (c *client) HandleMessage(mesgBytes []byte) {
return return
} }
c.HandleCommand(&msg) c.HandleCommand(ctx, &msg)
} }

View file

@ -1,25 +1,51 @@
package nexus package nexus
import ( import (
"context"
"dynatron.me/x/stillbox/pkg/calls"
"dynatron.me/x/stillbox/pkg/pb" "dynatron.me/x/stillbox/pkg/pb"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
) )
func (c *client) HandleCommand(cmd *pb.Command) { func (c *client) HandleCommand(ctx context.Context, cmd *pb.Command) {
switch cc := cmd.Command.(type) { switch cc := cmd.Command.(type) {
case *pb.Command_LiveCommand: case *pb.Command_LiveCommand:
c.Live(cc.LiveCommand) c.Live(ctx, cc.LiveCommand)
case *pb.Command_SearchCommand: case *pb.Command_SearchCommand:
default: default:
log.Error().Msgf("unknown command %T", cmd) log.Error().Msgf("unknown command %T", cmd)
} }
} }
func (c *client) Live(cmd *pb.Live) { func (c *client) SendError(err error) {
switch cmd.State { e := &pb.Message{
case pb.LiveState_LS_STOPPED: ToClientMessage: &pb.Message_Error{
case pb.LiveState_LS_LIVE: Error: &pb.Error{
case pb.LiveState_LS_PAUSED: Error: err.Error(),
},
},
}
c.Send(e)
}
func (c *client) Live(ctx context.Context, cmd *pb.Live) {
c.Lock()
defer c.Unlock()
if cmd.State != nil {
c.liveState = *cmd.State
}
if cmd.Filter != nil {
filter, err := calls.TalkgroupFilterFromPB(ctx, cmd.Filter)
if err != nil {
log.Error().Err(err).Msg("filter create failed")
c.SendError(err)
return
}
c.filter = filter
} }
} }

View file

@ -1,6 +1,7 @@
package nexus package nexus
import ( import (
"context"
"sync" "sync"
"dynatron.me/x/stillbox/pkg/calls" "dynatron.me/x/stillbox/pkg/calls"
@ -34,7 +35,7 @@ func New() *Nexus {
return n return n
} }
func (n *Nexus) Go(done <-chan struct{}) { func (n *Nexus) Go(ctx context.Context) {
for { for {
select { select {
case call, ok := <-n.callCh: case call, ok := <-n.callCh:
@ -42,8 +43,8 @@ func (n *Nexus) Go(done <-chan struct{}) {
return return
} }
n.broadcastCallToClients(call) n.broadcastCallToClients(ctx, call)
case <-done: case <-ctx.Done():
return return
} }
} }
@ -53,14 +54,18 @@ func (n *Nexus) BroadcastCall(call *calls.Call) {
n.callCh <- call n.callCh <- call
} }
func (n *Nexus) broadcastCallToClients(call *calls.Call) { func (n *Nexus) broadcastCallToClients(ctx context.Context, call *calls.Call) {
message := &pb.Message{ message := &pb.Message{
ToClientMessage: &pb.Message_Call{Call: call.ToPB()}, ToClientMessage: &pb.Message_Call{Call: call.ToPB()},
} }
n.Lock() n.Lock()
defer n.Unlock() defer n.Unlock()
for cl, _ := range n.clients { for cl := range n.clients {
if !cl.filter.Test(ctx, call) {
continue
}
if cl.Send(message) { if cl.Send(message) {
// we already hold the lock, and the channel is closed anyway // we already hold the lock, and the channel is closed anyway
delete(n.clients, cl) delete(n.clients, cl)

View file

@ -1,6 +1,7 @@
package nexus package nexus
import ( import (
"context"
"io" "io"
"net/http" "net/http"
"time" "time"
@ -81,11 +82,11 @@ func (wm *wsManager) serveWS(w http.ResponseWriter, r *http.Request) {
cli := wm.NewClient(wsc) cli := wm.NewClient(wsc)
wm.Register(cli) wm.Register(cli)
go wsc.readPump(wm, cli) go wsc.readPump(r.Context(), wm, cli)
go wsc.writePump() go wsc.writePump()
} }
func (conn *wsConn) readPump(reg Registry, c Client) { func (conn *wsConn) readPump(ctx context.Context, reg Registry, c Client) {
defer func() { defer func() {
reg.Unregister(c) reg.Unregister(c)
conn.Close() conn.Close()
@ -110,7 +111,7 @@ func (conn *wsConn) readPump(reg Registry, c Client) {
break break
} }
go c.HandleMessage(message) go c.HandleMessage(ctx, message)
} }
} }

View file

@ -1,6 +1,7 @@
package server package server
import ( import (
"context"
"net/http" "net/http"
"dynatron.me/x/stillbox/pkg/gordio/auth" "dynatron.me/x/stillbox/pkg/gordio/auth"
@ -54,12 +55,11 @@ func New(cfg *config.Config) (*Server, error) {
func (s *Server) Go() error { func (s *Server) Go() error {
defer s.db.Close() defer s.db.Close()
done := make(chan struct{})
defer func() { ctx, cancel := context.WithCancel(database.CtxWithDB(context.Background(), s.db))
close(done) defer cancel()
}()
go s.nex.Go(done) go s.nex.Go(ctx)
http.ListenAndServe(s.conf.Listen, s.r) http.ListenAndServe(s.conf.Listen, s.r)
return nil return nil

View file

@ -10,8 +10,8 @@ import (
"time" "time"
"dynatron.me/x/stillbox/internal/common" "dynatron.me/x/stillbox/internal/common"
"dynatron.me/x/stillbox/pkg/gordio/auth"
"dynatron.me/x/stillbox/pkg/calls" "dynatron.me/x/stillbox/pkg/calls"
"dynatron.me/x/stillbox/pkg/gordio/auth"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
) )

View file

@ -1,13 +0,0 @@
package live
import (
"dynatron.me/x/stillbox/pkg/pb"
)
type Listener struct {
pb.Live
}
func (l *Listener) IsLive() bool {
return l.State == pb.LiveState_LS_LIVE
}

View file

@ -80,6 +80,7 @@ type Message struct {
// *Message_Call // *Message_Call
// *Message_Notification // *Message_Notification
// *Message_Popup // *Message_Popup
// *Message_Error
ToClientMessage isMessage_ToClientMessage `protobuf_oneof:"toClient_message"` ToClientMessage isMessage_ToClientMessage `protobuf_oneof:"toClient_message"`
} }
@ -143,6 +144,13 @@ func (x *Message) GetPopup() *UserPopup {
return nil return nil
} }
func (x *Message) GetError() *Error {
if x, ok := x.GetToClientMessage().(*Message_Error); ok {
return x.Error
}
return nil
}
type isMessage_ToClientMessage interface { type isMessage_ToClientMessage interface {
isMessage_ToClientMessage() isMessage_ToClientMessage()
} }
@ -159,12 +167,18 @@ type Message_Popup struct {
Popup *UserPopup `protobuf:"bytes,3,opt,name=popup,proto3,oneof"` Popup *UserPopup `protobuf:"bytes,3,opt,name=popup,proto3,oneof"`
} }
type Message_Error struct {
Error *Error `protobuf:"bytes,4,opt,name=error,proto3,oneof"`
}
func (*Message_Call) isMessage_ToClientMessage() {} func (*Message_Call) isMessage_ToClientMessage() {}
func (*Message_Notification) isMessage_ToClientMessage() {} func (*Message_Notification) isMessage_ToClientMessage() {}
func (*Message_Popup) isMessage_ToClientMessage() {} func (*Message_Popup) isMessage_ToClientMessage() {}
func (*Message_Error) isMessage_ToClientMessage() {}
type Call struct { type Call struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@ -339,6 +353,53 @@ func (x *UserPopup) GetMsg() string {
return "" return ""
} }
type Error struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Error string `protobuf:"bytes,1,opt,name=error,proto3" json:"error,omitempty"`
}
func (x *Error) Reset() {
*x = Error{}
if protoimpl.UnsafeEnabled {
mi := &file_stillbox_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Error) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Error) ProtoMessage() {}
func (x *Error) ProtoReflect() protoreflect.Message {
mi := &file_stillbox_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Error.ProtoReflect.Descriptor instead.
func (*Error) Descriptor() ([]byte, []int) {
return file_stillbox_proto_rawDescGZIP(), []int{3}
}
func (x *Error) GetError() string {
if x != nil {
return x.Error
}
return ""
}
type Notification struct { type Notification struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@ -352,7 +413,7 @@ type Notification struct {
func (x *Notification) Reset() { func (x *Notification) Reset() {
*x = Notification{} *x = Notification{}
if protoimpl.UnsafeEnabled { if protoimpl.UnsafeEnabled {
mi := &file_stillbox_proto_msgTypes[3] mi := &file_stillbox_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -365,7 +426,7 @@ func (x *Notification) String() string {
func (*Notification) ProtoMessage() {} func (*Notification) ProtoMessage() {}
func (x *Notification) ProtoReflect() protoreflect.Message { func (x *Notification) ProtoReflect() protoreflect.Message {
mi := &file_stillbox_proto_msgTypes[3] mi := &file_stillbox_proto_msgTypes[4]
if protoimpl.UnsafeEnabled && x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -378,7 +439,7 @@ func (x *Notification) ProtoReflect() protoreflect.Message {
// Deprecated: Use Notification.ProtoReflect.Descriptor instead. // Deprecated: Use Notification.ProtoReflect.Descriptor instead.
func (*Notification) Descriptor() ([]byte, []int) { func (*Notification) Descriptor() ([]byte, []int) {
return file_stillbox_proto_rawDescGZIP(), []int{3} return file_stillbox_proto_rawDescGZIP(), []int{4}
} }
func (x *Notification) GetDateTime() *timestamppb.Timestamp { func (x *Notification) GetDateTime() *timestamppb.Timestamp {
@ -417,7 +478,7 @@ type Command struct {
func (x *Command) Reset() { func (x *Command) Reset() {
*x = Command{} *x = Command{}
if protoimpl.UnsafeEnabled { if protoimpl.UnsafeEnabled {
mi := &file_stillbox_proto_msgTypes[4] mi := &file_stillbox_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -430,7 +491,7 @@ func (x *Command) String() string {
func (*Command) ProtoMessage() {} func (*Command) ProtoMessage() {}
func (x *Command) ProtoReflect() protoreflect.Message { func (x *Command) ProtoReflect() protoreflect.Message {
mi := &file_stillbox_proto_msgTypes[4] mi := &file_stillbox_proto_msgTypes[5]
if protoimpl.UnsafeEnabled && x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -443,7 +504,7 @@ func (x *Command) ProtoReflect() protoreflect.Message {
// Deprecated: Use Command.ProtoReflect.Descriptor instead. // Deprecated: Use Command.ProtoReflect.Descriptor instead.
func (*Command) Descriptor() ([]byte, []int) { func (*Command) Descriptor() ([]byte, []int) {
return file_stillbox_proto_rawDescGZIP(), []int{4} return file_stillbox_proto_rawDescGZIP(), []int{5}
} }
func (m *Command) GetCommand() isCommand_Command { func (m *Command) GetCommand() isCommand_Command {
@ -488,13 +549,14 @@ type Live struct {
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
State LiveState `protobuf:"varint,1,opt,name=state,proto3,enum=stillbox.LiveState" json:"state,omitempty"` State *LiveState `protobuf:"varint,1,opt,name=state,proto3,enum=stillbox.LiveState,oneof" json:"state,omitempty"`
Filter *Filter `protobuf:"bytes,2,opt,name=filter,proto3,oneof" json:"filter,omitempty"`
} }
func (x *Live) Reset() { func (x *Live) Reset() {
*x = Live{} *x = Live{}
if protoimpl.UnsafeEnabled { if protoimpl.UnsafeEnabled {
mi := &file_stillbox_proto_msgTypes[5] mi := &file_stillbox_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -507,7 +569,7 @@ func (x *Live) String() string {
func (*Live) ProtoMessage() {} func (*Live) ProtoMessage() {}
func (x *Live) ProtoReflect() protoreflect.Message { func (x *Live) ProtoReflect() protoreflect.Message {
mi := &file_stillbox_proto_msgTypes[5] mi := &file_stillbox_proto_msgTypes[6]
if protoimpl.UnsafeEnabled && x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -520,16 +582,157 @@ func (x *Live) ProtoReflect() protoreflect.Message {
// Deprecated: Use Live.ProtoReflect.Descriptor instead. // Deprecated: Use Live.ProtoReflect.Descriptor instead.
func (*Live) Descriptor() ([]byte, []int) { func (*Live) Descriptor() ([]byte, []int) {
return file_stillbox_proto_rawDescGZIP(), []int{5} return file_stillbox_proto_rawDescGZIP(), []int{6}
} }
func (x *Live) GetState() LiveState { func (x *Live) GetState() LiveState {
if x != nil { if x != nil && x.State != nil {
return x.State return *x.State
} }
return LiveState_LS_STOPPED return LiveState_LS_STOPPED
} }
func (x *Live) GetFilter() *Filter {
if x != nil {
return x.Filter
}
return nil
}
type Talkgroup struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
System int32 `protobuf:"varint,1,opt,name=system,proto3" json:"system,omitempty"`
Talkgroup int32 `protobuf:"varint,2,opt,name=talkgroup,proto3" json:"talkgroup,omitempty"`
}
func (x *Talkgroup) Reset() {
*x = Talkgroup{}
if protoimpl.UnsafeEnabled {
mi := &file_stillbox_proto_msgTypes[7]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Talkgroup) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Talkgroup) ProtoMessage() {}
func (x *Talkgroup) ProtoReflect() protoreflect.Message {
mi := &file_stillbox_proto_msgTypes[7]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Talkgroup.ProtoReflect.Descriptor instead.
func (*Talkgroup) Descriptor() ([]byte, []int) {
return file_stillbox_proto_rawDescGZIP(), []int{7}
}
func (x *Talkgroup) GetSystem() int32 {
if x != nil {
return x.System
}
return 0
}
func (x *Talkgroup) GetTalkgroup() int32 {
if x != nil {
return x.Talkgroup
}
return 0
}
type Filter struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Talkgroups []*Talkgroup `protobuf:"bytes,1,rep,name=talkgroups,proto3" json:"talkgroups,omitempty"`
TalkgroupsNot []*Talkgroup `protobuf:"bytes,2,rep,name=talkgroups_not,json=talkgroupsNot,proto3" json:"talkgroups_not,omitempty"`
TalkgroupTagsAll []string `protobuf:"bytes,3,rep,name=talkgroup_tags_all,json=talkgroupTagsAll,proto3" json:"talkgroup_tags_all,omitempty"`
TalkgroupTagsAny []string `protobuf:"bytes,4,rep,name=talkgroup_tags_any,json=talkgroupTagsAny,proto3" json:"talkgroup_tags_any,omitempty"`
TalkgroupTagsNot []string `protobuf:"bytes,5,rep,name=talkgroup_tags_not,json=talkgroupTagsNot,proto3" json:"talkgroup_tags_not,omitempty"`
}
func (x *Filter) Reset() {
*x = Filter{}
if protoimpl.UnsafeEnabled {
mi := &file_stillbox_proto_msgTypes[8]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Filter) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Filter) ProtoMessage() {}
func (x *Filter) ProtoReflect() protoreflect.Message {
mi := &file_stillbox_proto_msgTypes[8]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Filter.ProtoReflect.Descriptor instead.
func (*Filter) Descriptor() ([]byte, []int) {
return file_stillbox_proto_rawDescGZIP(), []int{8}
}
func (x *Filter) GetTalkgroups() []*Talkgroup {
if x != nil {
return x.Talkgroups
}
return nil
}
func (x *Filter) GetTalkgroupsNot() []*Talkgroup {
if x != nil {
return x.TalkgroupsNot
}
return nil
}
func (x *Filter) GetTalkgroupTagsAll() []string {
if x != nil {
return x.TalkgroupTagsAll
}
return nil
}
func (x *Filter) GetTalkgroupTagsAny() []string {
if x != nil {
return x.TalkgroupTagsAny
}
return nil
}
func (x *Filter) GetTalkgroupTagsNot() []string {
if x != nil {
return x.TalkgroupTagsNot
}
return nil
}
type Search struct { type Search struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
@ -539,7 +742,7 @@ type Search struct {
func (x *Search) Reset() { func (x *Search) Reset() {
*x = Search{} *x = Search{}
if protoimpl.UnsafeEnabled { if protoimpl.UnsafeEnabled {
mi := &file_stillbox_proto_msgTypes[6] mi := &file_stillbox_proto_msgTypes[9]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi) ms.StoreMessageInfo(mi)
} }
@ -552,7 +755,7 @@ func (x *Search) String() string {
func (*Search) ProtoMessage() {} func (*Search) ProtoMessage() {}
func (x *Search) ProtoReflect() protoreflect.Message { func (x *Search) ProtoReflect() protoreflect.Message {
mi := &file_stillbox_proto_msgTypes[6] mi := &file_stillbox_proto_msgTypes[9]
if protoimpl.UnsafeEnabled && x != nil { if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil { if ms.LoadMessageInfo() == nil {
@ -565,7 +768,7 @@ func (x *Search) ProtoReflect() protoreflect.Message {
// Deprecated: Use Search.ProtoReflect.Descriptor instead. // Deprecated: Use Search.ProtoReflect.Descriptor instead.
func (*Search) Descriptor() ([]byte, []int) { func (*Search) Descriptor() ([]byte, []int) {
return file_stillbox_proto_rawDescGZIP(), []int{6} return file_stillbox_proto_rawDescGZIP(), []int{9}
} }
var File_stillbox_proto protoreflect.FileDescriptor var File_stillbox_proto protoreflect.FileDescriptor
@ -574,7 +777,7 @@ var file_stillbox_proto_rawDesc = []byte{
0x0a, 0x0e, 0x73, 0x74, 0x69, 0x6c, 0x6c, 0x62, 0x6f, 0x78, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x0a, 0x0e, 0x73, 0x74, 0x69, 0x6c, 0x6c, 0x62, 0x6f, 0x78, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x12, 0x08, 0x73, 0x74, 0x69, 0x6c, 0x6c, 0x62, 0x6f, 0x78, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x12, 0x08, 0x73, 0x74, 0x69, 0x6c, 0x6c, 0x62, 0x6f, 0x78, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67,
0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65,
0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xae, 0x01, 0x0a, 0x07, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xd7, 0x01, 0x0a, 0x07,
0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x24, 0x0a, 0x04, 0x63, 0x61, 0x6c, 0x6c, 0x18, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x24, 0x0a, 0x04, 0x63, 0x61, 0x6c, 0x6c, 0x18,
0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x73, 0x74, 0x69, 0x6c, 0x6c, 0x62, 0x6f, 0x78, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x73, 0x74, 0x69, 0x6c, 0x6c, 0x62, 0x6f, 0x78,
0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x48, 0x00, 0x52, 0x04, 0x63, 0x61, 0x6c, 0x6c, 0x12, 0x3c, 0x0a, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x48, 0x00, 0x52, 0x04, 0x63, 0x61, 0x6c, 0x6c, 0x12, 0x3c, 0x0a,
@ -584,56 +787,85 @@ var file_stillbox_proto_rawDesc = []byte{
0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2b, 0x0a, 0x05, 0x70, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2b, 0x0a, 0x05, 0x70,
0x6f, 0x70, 0x75, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x73, 0x74, 0x69, 0x6f, 0x70, 0x75, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x73, 0x74, 0x69,
0x6c, 0x6c, 0x62, 0x6f, 0x78, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x50, 0x6f, 0x70, 0x75, 0x70, 0x48, 0x6c, 0x6c, 0x62, 0x6f, 0x78, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x50, 0x6f, 0x70, 0x75, 0x70, 0x48,
0x00, 0x52, 0x05, 0x70, 0x6f, 0x70, 0x75, 0x70, 0x42, 0x12, 0x0a, 0x10, 0x74, 0x6f, 0x43, 0x6c, 0x00, 0x52, 0x05, 0x70, 0x6f, 0x70, 0x75, 0x70, 0x12, 0x27, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f,
0x69, 0x65, 0x6e, 0x74, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0xd2, 0x02, 0x0a, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x73, 0x74, 0x69, 0x6c, 0x6c, 0x62,
0x04, 0x43, 0x61, 0x6c, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x4e, 0x61, 0x6f, 0x78, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f,
0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x4e, 0x72, 0x42, 0x12, 0x0a, 0x10, 0x74, 0x6f, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x6d, 0x65,
0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x54, 0x79, 0x70, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0xd2, 0x02, 0x0a, 0x04, 0x43, 0x61, 0x6c, 0x6c, 0x12, 0x1c,
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x54, 0x79, 0x70, 0x0a, 0x09, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
0x65, 0x12, 0x36, 0x0a, 0x08, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x09, 0x52, 0x09, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09,
0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x54, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x54, 0x79, 0x70, 0x65, 0x12, 0x36, 0x0a, 0x08, 0x64, 0x61,
0x08, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x79, 0x73, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67,
0x74, 0x65, 0x6d, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54,
0x6d, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x61, 0x6c, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x05, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x08, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69,
0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x74, 0x61, 0x6c, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x18, 0x04, 0x20, 0x01,
0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x28, 0x05, 0x52, 0x06, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x61,
0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x66, 0x72, 0x65, 0x71, 0x75, 0x6c, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x74,
0x65, 0x6e, 0x63, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x66, 0x72, 0x65, 0x71, 0x61, 0x6c, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72,
0x75, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x66, 0x72, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
0x63, 0x69, 0x65, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x03, 0x52, 0x0b, 0x66, 0x72, 0x65, 0x71, 0x12, 0x1c, 0x0a, 0x09, 0x66, 0x72, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x07, 0x20,
0x75, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x74, 0x63, 0x68, 0x01, 0x28, 0x03, 0x52, 0x09, 0x66, 0x72, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x20,
0x65, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x05, 0x52, 0x07, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x0a, 0x0b, 0x66, 0x72, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x18, 0x08, 0x20,
0x73, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x03, 0x28, 0x03, 0x52, 0x0b, 0x66, 0x72, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73,
0x28, 0x05, 0x52, 0x07, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28,
0x75, 0x64, 0x69, 0x6f, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x61, 0x75, 0x64, 0x69, 0x05, 0x52, 0x07, 0x70, 0x61, 0x74, 0x63, 0x68, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x6f,
0x6f, 0x22, 0x1d, 0x0a, 0x09, 0x55, 0x73, 0x65, 0x72, 0x50, 0x6f, 0x70, 0x75, 0x70, 0x12, 0x10, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x05, 0x52, 0x07, 0x73, 0x6f, 0x75,
0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x72, 0x63, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x18, 0x0b, 0x20,
0x22, 0x76, 0x0a, 0x0c, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x61, 0x75, 0x64, 0x69, 0x6f, 0x22, 0x1d, 0x0a, 0x09, 0x55, 0x73,
0x12, 0x36, 0x0a, 0x08, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x65, 0x72, 0x50, 0x6f, 0x70, 0x75, 0x70, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x01,
0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x22, 0x1d, 0x0a, 0x05, 0x45, 0x72, 0x72,
0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x08, 0x6f, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28,
0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x76, 0x0a, 0x0c, 0x4e, 0x6f, 0x74, 0x69,
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x63, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x36, 0x0a, 0x08, 0x64, 0x61, 0x74, 0x65,
0x74, 0x69, 0x6f, 0x6e, 0x55, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f,
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x72, 0x6c, 0x22, 0x82, 0x01, 0x0a, 0x07, 0x43, 0x6f, 0x6d, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d,
0x6d, 0x61, 0x6e, 0x64, 0x12, 0x32, 0x0a, 0x0b, 0x6c, 0x69, 0x76, 0x65, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x08, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65,
0x61, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x73, 0x74, 0x69, 0x6c, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d,
0x6c, 0x62, 0x6f, 0x78, 0x2e, 0x4c, 0x69, 0x76, 0x65, 0x48, 0x00, 0x52, 0x0b, 0x6c, 0x69, 0x76, 0x73, 0x67, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x72, 0x6c, 0x18,
0x65, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x38, 0x0a, 0x0d, 0x73, 0x65, 0x61, 0x72, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x72, 0x6c,
0x63, 0x68, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x82, 0x01, 0x0a, 0x07, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x32, 0x0a, 0x0b,
0x10, 0x2e, 0x73, 0x74, 0x69, 0x6c, 0x6c, 0x62, 0x6f, 0x78, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x6c, 0x69, 0x76, 0x65, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
0x68, 0x48, 0x00, 0x52, 0x0d, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x0b, 0x32, 0x0e, 0x2e, 0x73, 0x74, 0x69, 0x6c, 0x6c, 0x62, 0x6f, 0x78, 0x2e, 0x4c, 0x69, 0x76,
0x6e, 0x64, 0x42, 0x09, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x22, 0x31, 0x0a, 0x65, 0x48, 0x00, 0x52, 0x0b, 0x6c, 0x69, 0x76, 0x65, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64,
0x04, 0x4c, 0x69, 0x76, 0x65, 0x12, 0x29, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x12, 0x38, 0x0a, 0x0d, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e,
0x20, 0x01, 0x28, 0x0e, 0x32, 0x13, 0x2e, 0x73, 0x74, 0x69, 0x6c, 0x6c, 0x62, 0x6f, 0x78, 0x2e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x73, 0x74, 0x69, 0x6c, 0x6c, 0x62,
0x4c, 0x69, 0x76, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x6f, 0x78, 0x2e, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x48, 0x00, 0x52, 0x0d, 0x73, 0x65, 0x61,
0x22, 0x08, 0x0a, 0x06, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2a, 0x37, 0x0a, 0x09, 0x4c, 0x69, 0x72, 0x63, 0x68, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x42, 0x09, 0x0a, 0x07, 0x63, 0x6f,
0x76, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0e, 0x0a, 0x0a, 0x4c, 0x53, 0x5f, 0x53, 0x54, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x22, 0x7a, 0x0a, 0x04, 0x4c, 0x69, 0x76, 0x65, 0x12, 0x2e, 0x0a,
0x4f, 0x50, 0x50, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x4c, 0x53, 0x5f, 0x4c, 0x49, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x13, 0x2e, 0x73,
0x56, 0x45, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x4c, 0x53, 0x5f, 0x50, 0x41, 0x55, 0x53, 0x45, 0x74, 0x69, 0x6c, 0x6c, 0x62, 0x6f, 0x78, 0x2e, 0x4c, 0x69, 0x76, 0x65, 0x53, 0x74, 0x61, 0x74,
0x44, 0x10, 0x02, 0x42, 0x06, 0x5a, 0x04, 0x2e, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x65, 0x48, 0x00, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x88, 0x01, 0x01, 0x12, 0x2d, 0x0a,
0x74, 0x6f, 0x33, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e,
0x73, 0x74, 0x69, 0x6c, 0x6c, 0x62, 0x6f, 0x78, 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x48,
0x01, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x88, 0x01, 0x01, 0x42, 0x08, 0x0a, 0x06,
0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65,
0x72, 0x22, 0x41, 0x0a, 0x09, 0x54, 0x61, 0x6c, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x16,
0x0a, 0x06, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06,
0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x61, 0x6c, 0x6b, 0x67, 0x72,
0x6f, 0x75, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x74, 0x61, 0x6c, 0x6b, 0x67,
0x72, 0x6f, 0x75, 0x70, 0x22, 0x83, 0x02, 0x0a, 0x06, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12,
0x33, 0x0a, 0x0a, 0x74, 0x61, 0x6c, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x01, 0x20,
0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x73, 0x74, 0x69, 0x6c, 0x6c, 0x62, 0x6f, 0x78, 0x2e, 0x54,
0x61, 0x6c, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x52, 0x0a, 0x74, 0x61, 0x6c, 0x6b, 0x67, 0x72,
0x6f, 0x75, 0x70, 0x73, 0x12, 0x3a, 0x0a, 0x0e, 0x74, 0x61, 0x6c, 0x6b, 0x67, 0x72, 0x6f, 0x75,
0x70, 0x73, 0x5f, 0x6e, 0x6f, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x73,
0x74, 0x69, 0x6c, 0x6c, 0x62, 0x6f, 0x78, 0x2e, 0x54, 0x61, 0x6c, 0x6b, 0x67, 0x72, 0x6f, 0x75,
0x70, 0x52, 0x0d, 0x74, 0x61, 0x6c, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x4e, 0x6f, 0x74,
0x12, 0x2c, 0x0a, 0x12, 0x74, 0x61, 0x6c, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x74, 0x61,
0x67, 0x73, 0x5f, 0x61, 0x6c, 0x6c, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x74, 0x61,
0x6c, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x54, 0x61, 0x67, 0x73, 0x41, 0x6c, 0x6c, 0x12, 0x2c,
0x0a, 0x12, 0x74, 0x61, 0x6c, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x74, 0x61, 0x67, 0x73,
0x5f, 0x61, 0x6e, 0x79, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x74, 0x61, 0x6c, 0x6b,
0x67, 0x72, 0x6f, 0x75, 0x70, 0x54, 0x61, 0x67, 0x73, 0x41, 0x6e, 0x79, 0x12, 0x2c, 0x0a, 0x12,
0x74, 0x61, 0x6c, 0x6b, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x5f, 0x74, 0x61, 0x67, 0x73, 0x5f, 0x6e,
0x6f, 0x74, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x74, 0x61, 0x6c, 0x6b, 0x67, 0x72,
0x6f, 0x75, 0x70, 0x54, 0x61, 0x67, 0x73, 0x4e, 0x6f, 0x74, 0x22, 0x08, 0x0a, 0x06, 0x53, 0x65,
0x61, 0x72, 0x63, 0x68, 0x2a, 0x37, 0x0a, 0x09, 0x4c, 0x69, 0x76, 0x65, 0x53, 0x74, 0x61, 0x74,
0x65, 0x12, 0x0e, 0x0a, 0x0a, 0x4c, 0x53, 0x5f, 0x53, 0x54, 0x4f, 0x50, 0x50, 0x45, 0x44, 0x10,
0x00, 0x12, 0x0b, 0x0a, 0x07, 0x4c, 0x53, 0x5f, 0x4c, 0x49, 0x56, 0x45, 0x10, 0x01, 0x12, 0x0d,
0x0a, 0x09, 0x4c, 0x53, 0x5f, 0x50, 0x41, 0x55, 0x53, 0x45, 0x44, 0x10, 0x02, 0x42, 0x06, 0x5a,
0x04, 0x2e, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
} }
var ( var (
@ -649,32 +881,39 @@ func file_stillbox_proto_rawDescGZIP() []byte {
} }
var file_stillbox_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_stillbox_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_stillbox_proto_msgTypes = make([]protoimpl.MessageInfo, 7) var file_stillbox_proto_msgTypes = make([]protoimpl.MessageInfo, 10)
var file_stillbox_proto_goTypes = []any{ var file_stillbox_proto_goTypes = []any{
(LiveState)(0), // 0: stillbox.LiveState (LiveState)(0), // 0: stillbox.LiveState
(*Message)(nil), // 1: stillbox.Message (*Message)(nil), // 1: stillbox.Message
(*Call)(nil), // 2: stillbox.Call (*Call)(nil), // 2: stillbox.Call
(*UserPopup)(nil), // 3: stillbox.UserPopup (*UserPopup)(nil), // 3: stillbox.UserPopup
(*Notification)(nil), // 4: stillbox.Notification (*Error)(nil), // 4: stillbox.Error
(*Command)(nil), // 5: stillbox.Command (*Notification)(nil), // 5: stillbox.Notification
(*Live)(nil), // 6: stillbox.Live (*Command)(nil), // 6: stillbox.Command
(*Search)(nil), // 7: stillbox.Search (*Live)(nil), // 7: stillbox.Live
(*timestamppb.Timestamp)(nil), // 8: google.protobuf.Timestamp (*Talkgroup)(nil), // 8: stillbox.Talkgroup
(*Filter)(nil), // 9: stillbox.Filter
(*Search)(nil), // 10: stillbox.Search
(*timestamppb.Timestamp)(nil), // 11: google.protobuf.Timestamp
} }
var file_stillbox_proto_depIdxs = []int32{ var file_stillbox_proto_depIdxs = []int32{
2, // 0: stillbox.Message.call:type_name -> stillbox.Call 2, // 0: stillbox.Message.call:type_name -> stillbox.Call
4, // 1: stillbox.Message.notification:type_name -> stillbox.Notification 5, // 1: stillbox.Message.notification:type_name -> stillbox.Notification
3, // 2: stillbox.Message.popup:type_name -> stillbox.UserPopup 3, // 2: stillbox.Message.popup:type_name -> stillbox.UserPopup
8, // 3: stillbox.Call.dateTime:type_name -> google.protobuf.Timestamp 4, // 3: stillbox.Message.error:type_name -> stillbox.Error
8, // 4: stillbox.Notification.dateTime:type_name -> google.protobuf.Timestamp 11, // 4: stillbox.Call.dateTime:type_name -> google.protobuf.Timestamp
6, // 5: stillbox.Command.liveCommand:type_name -> stillbox.Live 11, // 5: stillbox.Notification.dateTime:type_name -> google.protobuf.Timestamp
7, // 6: stillbox.Command.searchCommand:type_name -> stillbox.Search 7, // 6: stillbox.Command.liveCommand:type_name -> stillbox.Live
0, // 7: stillbox.Live.state:type_name -> stillbox.LiveState 10, // 7: stillbox.Command.searchCommand:type_name -> stillbox.Search
8, // [8:8] is the sub-list for method output_type 0, // 8: stillbox.Live.state:type_name -> stillbox.LiveState
8, // [8:8] is the sub-list for method input_type 9, // 9: stillbox.Live.filter:type_name -> stillbox.Filter
8, // [8:8] is the sub-list for extension type_name 8, // 10: stillbox.Filter.talkgroups:type_name -> stillbox.Talkgroup
8, // [8:8] is the sub-list for extension extendee 8, // 11: stillbox.Filter.talkgroups_not:type_name -> stillbox.Talkgroup
0, // [0:8] is the sub-list for field type_name 12, // [12:12] is the sub-list for method output_type
12, // [12:12] is the sub-list for method input_type
12, // [12:12] is the sub-list for extension type_name
12, // [12:12] is the sub-list for extension extendee
0, // [0:12] is the sub-list for field type_name
} }
func init() { file_stillbox_proto_init() } func init() { file_stillbox_proto_init() }
@ -720,7 +959,7 @@ func file_stillbox_proto_init() {
} }
} }
file_stillbox_proto_msgTypes[3].Exporter = func(v any, i int) any { file_stillbox_proto_msgTypes[3].Exporter = func(v any, i int) any {
switch v := v.(*Notification); i { switch v := v.(*Error); i {
case 0: case 0:
return &v.state return &v.state
case 1: case 1:
@ -732,7 +971,7 @@ func file_stillbox_proto_init() {
} }
} }
file_stillbox_proto_msgTypes[4].Exporter = func(v any, i int) any { file_stillbox_proto_msgTypes[4].Exporter = func(v any, i int) any {
switch v := v.(*Command); i { switch v := v.(*Notification); i {
case 0: case 0:
return &v.state return &v.state
case 1: case 1:
@ -744,7 +983,7 @@ func file_stillbox_proto_init() {
} }
} }
file_stillbox_proto_msgTypes[5].Exporter = func(v any, i int) any { file_stillbox_proto_msgTypes[5].Exporter = func(v any, i int) any {
switch v := v.(*Live); i { switch v := v.(*Command); i {
case 0: case 0:
return &v.state return &v.state
case 1: case 1:
@ -756,6 +995,42 @@ func file_stillbox_proto_init() {
} }
} }
file_stillbox_proto_msgTypes[6].Exporter = func(v any, i int) any { file_stillbox_proto_msgTypes[6].Exporter = func(v any, i int) any {
switch v := v.(*Live); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_stillbox_proto_msgTypes[7].Exporter = func(v any, i int) any {
switch v := v.(*Talkgroup); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_stillbox_proto_msgTypes[8].Exporter = func(v any, i int) any {
switch v := v.(*Filter); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_stillbox_proto_msgTypes[9].Exporter = func(v any, i int) any {
switch v := v.(*Search); i { switch v := v.(*Search); i {
case 0: case 0:
return &v.state return &v.state
@ -772,18 +1047,20 @@ func file_stillbox_proto_init() {
(*Message_Call)(nil), (*Message_Call)(nil),
(*Message_Notification)(nil), (*Message_Notification)(nil),
(*Message_Popup)(nil), (*Message_Popup)(nil),
(*Message_Error)(nil),
} }
file_stillbox_proto_msgTypes[4].OneofWrappers = []any{ file_stillbox_proto_msgTypes[5].OneofWrappers = []any{
(*Command_LiveCommand)(nil), (*Command_LiveCommand)(nil),
(*Command_SearchCommand)(nil), (*Command_SearchCommand)(nil),
} }
file_stillbox_proto_msgTypes[6].OneofWrappers = []any{}
type x struct{} type x struct{}
out := protoimpl.TypeBuilder{ out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{ File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(), GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_stillbox_proto_rawDesc, RawDescriptor: file_stillbox_proto_rawDesc,
NumEnums: 1, NumEnums: 1,
NumMessages: 7, NumMessages: 10,
NumExtensions: 0, NumExtensions: 0,
NumServices: 0, NumServices: 0,
}, },

View file

@ -9,6 +9,7 @@ message Message {
Call call = 1; Call call = 1;
Notification notification = 2; Notification notification = 2;
UserPopup popup = 3; UserPopup popup = 3;
Error error = 4;
} }
} }
@ -30,6 +31,10 @@ message UserPopup {
string msg = 1; string msg = 1;
} }
message Error {
string error = 1;
}
message Notification { message Notification {
google.protobuf.Timestamp dateTime = 1; google.protobuf.Timestamp dateTime = 1;
string msg = 2; string msg = 2;
@ -50,7 +55,21 @@ enum LiveState {
} }
message Live { message Live {
LiveState state = 1; optional LiveState state = 1;
optional Filter filter = 2;
}
message Talkgroup {
int32 system = 1;
int32 talkgroup = 2;
}
message Filter {
repeated Talkgroup talkgroups = 1;
repeated Talkgroup talkgroups_not = 2;
repeated string talkgroup_tags_all = 3;
repeated string talkgroup_tags_any = 4;
repeated string talkgroup_tags_not = 5;
} }
message Search { message Search {

View file

@ -23,19 +23,42 @@ CREATE TABLE IF NOT EXISTS systems(
name TEXT NOT NULL name TEXT NOT NULL
); );
CREATE OR REPLACE FUNCTION systg2id(_sys INTEGER, _tg INTEGER) RETURNS INT8 LANGUAGE plpgsql AS
$$
BEGIN
RETURN ((_sys::BIGINT << 32) | _tg);
END
$$;
CREATE OR REPLACE FUNCTION tgfromid(_id INT8) RETURNS INTEGER LANGUAGE plpgsql AS
$$
BEGIN
RETURN (_id & x'ffffffff'::BIGINT);
END
$$;
CREATE OR REPLACE FUNCTION sysfromid(_id INT8) RETURNS INTEGER LANGUAGE plpgsql AS
$$
BEGIN
RETURN (_id >> 32);
END
$$;
CREATE TABLE IF NOT EXISTS talkgroups( CREATE TABLE IF NOT EXISTS talkgroups(
system_id INTEGER REFERENCES systems(id) NOT NULL, id INT8 PRIMARY KEY,
tgid INTEGER, system_id INT4 REFERENCES systems(id) NOT NULL GENERATED ALWAYS AS (id >> 32) STORED,
tgid INT4 NOT NULL GENERATED ALWAYS AS (id & x'ffffffff'::BIGINT) STORED,
name TEXT, name TEXT,
tg_group TEXT, tg_group TEXT,
frequency INTEGER, frequency INTEGER,
metadata JSONB, metadata JSONB,
tags TEXT[] NOT NULL DEFAULT '{}', tags TEXT[] NOT NULL DEFAULT '{}'
PRIMARY KEY (system_id, tgid)
); );
CREATE INDEX IF NOT EXISTS talkgroup_id_tags ON talkgroups USING GIN (tags); CREATE INDEX IF NOT EXISTS talkgroup_id_tags ON talkgroups USING GIN (tags);
CREATE TABLE IF NOT EXISTS talkgroups_learned( CREATE TABLE IF NOT EXISTS talkgroups_learned(
id SERIAL PRIMARY KEY, id SERIAL PRIMARY KEY,
system_id INTEGER REFERENCES systems(id) NOT NULL, system_id INTEGER REFERENCES systems(id) NOT NULL,

View file

@ -6,10 +6,20 @@ WHERE tags @> ARRAY[$1];
SELECT * FROM talkgroups SELECT * FROM talkgroups
WHERE tags && ARRAY[$1]; WHERE tags && ARRAY[$1];
-- name: GetTalkgroupIDsByTags :many
SELECT system_id, tgid FROM talkgroups
WHERE (tags @> ARRAY[sqlc.arg(anyTags)])
AND (tags && ARRAY[sqlc.arg(allTags)])
AND NOT (tags @> ARRAY[sqlc.arg(notTags)]);
-- name: GetTalkgroupTags :one -- name: GetTalkgroupTags :one
SELECT tags FROM talkgroups SELECT tags FROM talkgroups
WHERE system_id = $1 AND tgid = $2; WHERE id = systg2id($1, $2);
-- name: SetTalkgroupTags :exec -- name: SetTalkgroupTags :exec
UPDATE talkgroups SET tags = $1 UPDATE talkgroups SET tags = $3
WHERE system_id = $1 AND tgid = $2; WHERE id = systg2id($1, $2);
-- name: BulkSetTalkgroupTags :exec
UPDATE talkgroups SET tags = $2
WHERE id = ANY($1);