Added virtual arena that responds to signals on a channel, cleaned up log messages, and fixed update() hierarchy

graph-rework
noah metz 2023-06-01 13:11:32 -06:00
parent 26a2a63d83
commit 990b93757f
6 changed files with 287 additions and 94 deletions

@ -6,17 +6,19 @@ import (
"errors" "errors"
"reflect" "reflect"
"sort" "sort"
"sync"
) )
// Update the events listeners, and notify the parent to do the same // Update the events listeners, and notify the parent to do the same
func (event * BaseEvent) Update() error { func (event * BaseEvent) Update(reason string) error {
err := event.UpdateListeners() log.Printf("UPDATE BaseEvent %s: %s", event.Name(), reason)
err := event.UpdateListeners(reason)
if err != nil { if err != nil {
return err return err
} }
if event.parent != nil{ if event.parent != nil{
return event.parent.Update() return event.parent.Update("update parent")
} }
return nil return nil
} }
@ -79,8 +81,19 @@ type BaseEvent struct {
abort chan string abort chan string
} }
func (queue * EventQueue) Abort() error {
for _, event := range(queue.children) {
event.Abort()
}
for _, c := range(queue.resource_aborts) {
c <- "event abort"
}
queue.signal <- "abort"
return nil
}
func (event * BaseEvent) Abort() error { func (event * BaseEvent) Abort() error {
for _, event := range(event.Children()) { for _, event := range(event.children) {
event.Abort() event.Abort()
} }
event.signal <- "abort" event.signal <- "abort"
@ -94,21 +107,28 @@ func (event * BaseEvent) Signal(action string) error {
func (event * BaseEvent) LockResources() error { func (event * BaseEvent) LockResources() error {
locked_resources := []Resource{} locked_resources := []Resource{}
lock_err := false var lock_err error = nil
for _, resource := range(event.RequiredResources()) { for _, resource := range(event.RequiredResources()) {
err := resource.Lock(event) err := resource.Lock(event)
if err != nil { if err != nil {
lock_err = true lock_err = err
break
} }
locked_resources = append(locked_resources, resource) locked_resources = append(locked_resources, resource)
} }
if lock_err == true { if lock_err != nil {
for _, resource := range(locked_resources) { for _, resource := range(locked_resources) {
resource.Unlock(event) resource.Unlock(event)
} }
return errors.New("failed to lock required resources") return lock_err
} else {
for _, resource := range(locked_resources) {
log.Printf("NOTIFYING %s that it's locked", resource.Name())
resource.NotifyLocked()
}
} }
return nil return nil
} }
@ -118,6 +138,7 @@ func (event * BaseEvent) Finish() error {
if err != nil { if err != nil {
panic(err) panic(err)
} }
resource.Update("unlocking after event finish")
} }
return event.DoneResource().Unlock(event) return event.DoneResource().Unlock(event)
} }
@ -131,6 +152,7 @@ func (event * BaseEvent) Run() error {
var err error = nil var err error = nil
for next_action != "" { for next_action != "" {
// Check if the edge exists // Check if the edge exists
cur_action := next_action
action, exists := event.actions[next_action] action, exists := event.actions[next_action]
if exists == false { if exists == false {
error_str := fmt.Sprintf("%s is not a valid action", next_action) error_str := fmt.Sprintf("%s is not a valid action", next_action)
@ -154,7 +176,8 @@ func (event * BaseEvent) Run() error {
} }
// Update the event after running the edge // Update the event after running the edge
event.Update() update_str := fmt.Sprintf("ACTION %s: NEXT %s", cur_action, next_action)
event.Update(update_str)
} }
err = event.DoneResource().Unlock(event) err = event.DoneResource().Unlock(event)
@ -168,6 +191,8 @@ func (event * BaseEvent) Run() error {
// On start, it attempts to start it's children from the highest 'priority' // On start, it attempts to start it's children from the highest 'priority'
type EventQueue struct { type EventQueue struct {
BaseEvent BaseEvent
resource_aborts map[string]chan string
resource_lock sync.Mutex
} }
func NewBaseEvent(name string, description string, required_resources []Resource) (BaseEvent) { func NewBaseEvent(name string, description string, required_resources []Resource) (BaseEvent) {
@ -177,7 +202,7 @@ func NewBaseEvent(name string, description string, required_resources []Resource
name: name, name: name,
description: description, description: description,
id: randid(), id: randid(),
listeners: []chan error{}, listeners: []chan string{},
}, },
parent: nil, parent: nil,
children: []Event{}, children: []Event{},
@ -209,18 +234,17 @@ func NewEvent(name string, description string, required_resources []Resource) (*
func NewEventQueue(name string, description string, required_resources []Resource) (* EventQueue) { func NewEventQueue(name string, description string, required_resources []Resource) (* EventQueue) {
queue := &EventQueue{ queue := &EventQueue{
BaseEvent: NewBaseEvent(name, description, []Resource{}), BaseEvent: NewBaseEvent(name, description, []Resource{}),
resource_aborts: map[string]chan string{},
} }
// Need to lock it with th BaseEvent since Unlock is implemented on the BaseEvent // Need to lock it with th BaseEvent since Unlock is implemented on the BaseEvent
queue.LockDone() queue.LockDone()
queue.actions["start"] = func() (string, error) { queue.actions["start"] = func() (string, error) {
log.Printf("Starting Event Queue")
return "queue_event", nil return "queue_event", nil
} }
queue.actions["queue_event"] = func() (string, error) { queue.actions["queue_event"] = func() (string, error) {
log.Printf("Queueing events")
// Copy the events to sort the list // Copy the events to sort the list
copied_events := make([]Event, len(queue.Children())) copied_events := make([]Event, len(queue.Children()))
copy(copied_events, queue.Children()) copy(copied_events, queue.Children())
@ -233,6 +257,43 @@ func NewEventQueue(name string, description string, required_resources []Resourc
wait := false wait := false
for _, event := range(copied_events) { for _, event := range(copied_events) {
// Update the resource_chans
for _, resource := range(event.RequiredResources()) {
queue.resource_lock.Lock()
_, exists := queue.resource_aborts[resource.ID()]
if exists == false {
log.Printf("RESOURCE_LISTENER_START: %s", resource.Name())
abort := make(chan string, 1)
queue.resource_aborts[resource.ID()] = abort
go func(queue *EventQueue, resource Resource, abort chan string) {
log.Printf("RESOURCE_LISTENER_GOROUTINE: %s", resource.Name())
resource_chan := resource.UpdateChannel()
for true {
select {
case <- abort:
queue.resource_lock.Lock()
delete(queue.resource_aborts, resource.ID())
queue.resource_lock.Unlock()
log.Printf("RESORCE_LISTENER_ABORT: %s", resource.Name())
break
case msg, ok := <- resource_chan:
if ok == false {
queue.resource_lock.Lock()
delete(queue.resource_aborts, resource.ID())
queue.resource_lock.Unlock()
log.Printf("RESOURCE_LISTENER_CLOSED: %s : %s", resource.Name(), msg)
break
}
log.Printf("RESOURCE_LISTENER_UPDATED: %s : %s", resource.Name(), msg)
queue.signal <- "resource_update"
}
}
log.Printf("RESOURCE_LISTENER_DYING: %s", resource.Name())
}(queue, resource, abort)
}
queue.resource_lock.Unlock()
}
info := queue.ChildInfo(event).(*EventQueueInfo) info := queue.ChildInfo(event).(*EventQueueInfo)
if info.state == "queued" { if info.state == "queued" {
wait = true wait = true
@ -240,11 +301,15 @@ func NewEventQueue(name string, description string, required_resources []Resourc
err := event.LockResources() err := event.LockResources()
// start in new goroutine // start in new goroutine
if err != nil { if err != nil {
} else { } else {
info.state = "running" info.state = "running"
log.Printf("EVENT_START: %s", event.Name())
go func(event Event, info * EventQueueInfo, queue Event) { go func(event Event, info * EventQueueInfo, queue Event) {
event.Run() log.Printf("EVENT_GOROUTINE: %s", event.Name())
err := event.Run()
if err != nil {
log.Printf("EVENT_ERROR: %s", err)
}
info.state = "done" info.state = "done"
event.Finish() event.Finish()
queue.Signal("event_done") queue.Signal("event_done")
@ -263,17 +328,14 @@ func NewEventQueue(name string, description string, required_resources []Resourc
} }
queue.actions["event_done"] = func() (string, error) { queue.actions["event_done"] = func() (string, error) {
log.Printf("event_done")
return "queue_event", nil return "queue_event", nil
} }
queue.actions["resource_available"] = func() (string, error) { queue.actions["resource_update"] = func() (string, error) {
log.Printf("resources_available")
return "queue_event", nil return "queue_event", nil
} }
queue.actions["event_added"] = func() (string, error) { queue.actions["event_added"] = func() (string, error) {
log.Printf("event_added")
return "queue_event", nil return "queue_event", nil
} }
@ -347,7 +409,7 @@ func (event * BaseEvent) addChild(child Event, info EventInfo) error {
event.children = append(event.children, child) event.children = append(event.children, child)
event.child_info[child] = info event.child_info[child] = info
event.Update() event.Update("child added")
return nil return nil
} }

@ -2,6 +2,7 @@ package main
import ( import (
"errors" "errors"
"log"
"sync" "sync"
"github.com/google/uuid" "github.com/google/uuid"
) )
@ -17,9 +18,9 @@ type GraphNode interface {
Name() string Name() string
Description() string Description() string
ID() string ID() string
UpdateListeners() error UpdateListeners(info string) error
UpdateChannel() chan error UpdateChannel() chan string
Update() error Update(reason string) error
} }
// BaseNode is the most basic implementation of the GraphNode interface // BaseNode is the most basic implementation of the GraphNode interface
@ -28,7 +29,7 @@ type BaseNode struct {
name string name string
description string description string
id string id string
listeners []chan error listeners []chan string
listeners_lock sync.Mutex listeners_lock sync.Mutex
} }
@ -45,8 +46,9 @@ func (node * BaseNode) ID() string {
} }
// Create a new listener channel for the node, add it to the nodes listener list, and return the new channel // Create a new listener channel for the node, add it to the nodes listener list, and return the new channel
func (node * BaseNode) UpdateChannel() chan error { const listener_buffer = 10
new_listener := make(chan error, 1) func (node * BaseNode) UpdateChannel() chan string{
new_listener := make(chan string, listener_buffer)
node.listeners_lock.Lock() node.listeners_lock.Lock()
node.listeners = append(node.listeners, new_listener) node.listeners = append(node.listeners, new_listener)
node.listeners_lock.Unlock() node.listeners_lock.Unlock()
@ -54,7 +56,7 @@ func (node * BaseNode) UpdateChannel() chan error {
} }
// Send the update to listener channels // Send the update to listener channels
func (node * BaseNode) UpdateListeners() error { func (node * BaseNode) UpdateListeners(info string) error {
closed_listeners := []int{} closed_listeners := []int{}
listeners_closed := false listeners_closed := false
@ -63,7 +65,7 @@ func (node * BaseNode) UpdateListeners() error {
node.listeners_lock.Lock() node.listeners_lock.Lock()
for i, listener := range node.listeners { for i, listener := range node.listeners {
select { select {
case listener <- nil: case listener <- info:
default: default:
close(listener) close(listener)
closed_listeners = append(closed_listeners, i) closed_listeners = append(closed_listeners, i)
@ -74,7 +76,7 @@ func (node * BaseNode) UpdateListeners() error {
// If any listeners have been closed, loop over the listeners // If any listeners have been closed, loop over the listeners
// Add listeners to the "remaining" list if i insn't in closed_listeners // Add listeners to the "remaining" list if i insn't in closed_listeners
if listeners_closed == true { if listeners_closed == true {
remaining_listeners := []chan error{} remaining_listeners := []chan string{}
for i, listener := range node.listeners { for i, listener := range node.listeners {
listener_closed := false listener_closed := false
for _, index := range closed_listeners { for _, index := range closed_listeners {
@ -96,6 +98,7 @@ func (node * BaseNode) UpdateListeners() error {
} }
// Basic implementation must be overwritten to do anything useful // Basic implementation must be overwritten to do anything useful
func (node * BaseNode) Update() error { func (node * BaseNode) Update(reason string) error {
log.Printf("UPDATE: BaseNode %s: %s", node.Name(), reason)
return errors.New("Cannot Update a BaseNode") return errors.New("Cannot Update a BaseNode")
} }

@ -2,6 +2,7 @@ package main
import ( import (
"log" "log"
"math/rand"
) )
func fake_team(org string, id string, names []string) (*Team, []*Member) { func fake_team(org string, id string, names []string) (*Team, []*Member) {
@ -57,10 +58,14 @@ func fake_data() * EventManager {
resources = append(resources, m12[0]) resources = append(resources, m12[0])
alliances := []*Alliance{} alliances := []*Alliance{}
for i, team := range teams[:len(teams)-1] { for i, team := range(teams){
for _, team2 := range teams[i+1:] { for true {
alliance := NewAlliance(team, team2) idx := rand.Intn(len(teams))
if idx != i {
alliance := NewAlliance(team, teams[idx])
alliances = append(alliances, alliance) alliances = append(alliances, alliance)
break
}
} }
} }
@ -81,14 +86,19 @@ func fake_data() * EventManager {
resources = append(resources, alliance) resources = append(resources, alliance)
} }
root_event := NewEventQueue("root_event", "", []Resource{}) root_event := NewEventQueue("root_event", "", []Resource{})
event_manager := NewEventManager(root_event, resources) event_manager := NewEventManager(root_event, resources)
arena_idx := 0 arena_idx := 0
for i, alliance := range alliances[:len(alliances)-1] { for i := 0; i < len(alliances)*5; i++ {
for _, alliance2 := range alliances[i+1:] { alliance := alliances[i % len(alliances)]
for true {
idx := rand.Intn(len(alliances))
if idx != i {
alliance2 := alliances[idx]
if alliance.Children()[0] == alliance2.Children()[0] || alliance.Children()[0] == alliance2.Children()[1] || alliance.Children()[1] == alliance2.Children()[0] || alliance.Children()[1] == alliance2.Children()[1] {
} else {
match := NewMatch(alliance, alliance2, arenas[arena_idx]) match := NewMatch(alliance, alliance2, arenas[arena_idx])
log.Printf("Adding %s", match.Name())
err := event_manager.AddEvent(root_event, match, NewEventQueueInfo(i)) err := event_manager.AddEvent(root_event, match, NewEventQueueInfo(i))
if err != nil { if err != nil {
log.Printf("Error adding %s: %s", match.Name(), err) log.Printf("Error adding %s: %s", match.Name(), err)
@ -98,9 +108,10 @@ func fake_data() * EventManager {
arena_idx = 0 arena_idx = 0
} }
} }
break
}
}
} }
return event_manager return event_manager
} }

@ -36,8 +36,31 @@ func NewEventManager(root_event Event, dag_nodes []Resource) * EventManager {
return manager; return manager;
} }
// Connect to all resources(in a thread to handle reconnections), and start the first event
func (manager * EventManager) Run() error { func (manager * EventManager) Run() error {
return manager.root_event.Run() aborts := []chan error{}
for _, resource := range(manager.dag_nodes) {
abort := make(chan error, 1)
abort_used := resource.Connect(abort)
if abort_used == true {
aborts = append(aborts, abort)
}
}
abort := make(chan error, 1)
go func(abort chan error, aborts []chan error) {
<- abort
for _, c := range(aborts) {
c <- nil
}
}(abort, aborts)
err := manager.root_event.Run()
abort <- nil
if err != nil {
return err
}
return nil
} }
func (manager * EventManager) FindResource(id string) Resource { func (manager * EventManager) FindResource(id string) Resource {

@ -4,39 +4,42 @@ import (
"fmt" "fmt"
"errors" "errors"
"sync" "sync"
"log"
) )
// Resources propagate update up to multiple parents, and not downwards // Resources propagate update up to multiple parents, and not downwards
// (subscriber to team won't get update to alliance, but subscriber to alliance will get update to team) // (subscriber to team won't get update to alliance, but subscriber to alliance will get update to team)
func (resource * BaseResource) Update() error { func (resource * BaseResource) Update(reason string) error {
err := resource.UpdateListeners() log.Printf("UPDATE BaseResource %s: %s", resource.Name(), reason)
err := resource.UpdateListeners(reason)
if err != nil { if err != nil {
return err return err
} }
for _, parent := range resource.Parents() { for _, parent := range resource.Parents() {
err := parent.Update() err := parent.Update("update parents")
if err != nil { if err != nil {
return err return err
} }
} }
if resource.lock_holder != nil {
resource.lock_holder.Update()
}
return nil return nil
} }
// Resource is the interface that DAG nodes are made from // Resource is the interface that DAG nodes are made from
// A resource needs to be able to represent logical entities and connections to physical entities.
// A resource lock could be aborted at any time if this connection is broken, if that happens the event locking it must be aborted
// The device connection should be maintained as much as possible(requiring some reconnection behaviour in the background)
type Resource interface { type Resource interface {
GraphNode GraphNode
AddParent(parent Resource) error AddParent(parent Resource) error
Children() []Resource Children() []Resource
Parents() []Resource Parents() []Resource
Lock(event Event) error Lock(event Event) error
NotifyLocked() error
Unlock(event Event) error Unlock(event Event) error
Owner() Event Owner() Event
Connect(abort chan error) bool
} }
// BaseResource is the most basic resource that can exist in the DAG // BaseResource is the most basic resource that can exist in the DAG
@ -49,19 +52,43 @@ type BaseResource struct {
state_lock sync.Mutex state_lock sync.Mutex
} }
func (resource * BaseResource) Connect(abort chan error) bool {
return false
}
func (resource * BaseResource) Owner() Event { func (resource * BaseResource) Owner() Event {
return resource.lock_holder return resource.lock_holder
} }
func (resource * BaseResource) NotifyLocked() error {
err := resource.Update("finalize_lock")
if err != nil {
return err
}
for _, child := range(resource.children) {
err = child.NotifyLocked()
if err != nil {
return err
}
}
return nil
}
// Grab the state mutex and check the state, if unlocked continue to hold the mutex while doing the same for children // Grab the state mutex and check the state, if unlocked continue to hold the mutex while doing the same for children
// When the bottom of a tree is reached(no more children) go back up and set the lock state // When the bottom of a tree is reached(no more children) go back up and set the lock state
func (resource * BaseResource) Lock(event Event) error { func (resource * BaseResource) Lock(event Event) error {
return resource.lock(event)
}
func (resource * BaseResource) lock(event Event) error {
var err error = nil var err error = nil
locked := false
resource.state_lock.Lock() resource.state_lock.Lock()
if resource.lock_holder != nil { if resource.lock_holder != nil {
err = errors.New("Resource already locked") err_str := fmt.Sprintf("Resource already locked: %s", resource.Name())
err = errors.New(err_str)
} else { } else {
all_children_locked := true all_children_locked := true
for _, child := range resource.Children() { for _, child := range resource.Children() {
@ -73,15 +100,10 @@ func (resource * BaseResource) Lock(event Event) error {
} }
if all_children_locked == true { if all_children_locked == true {
resource.lock_holder = event resource.lock_holder = event
locked = true
} }
} }
resource.state_lock.Unlock() resource.state_lock.Unlock()
if locked == true {
resource.Update()
}
return err return err
} }
@ -89,7 +111,7 @@ func (resource * BaseResource) Lock(event Event) error {
// If the child isn't locked by the unlocker // If the child isn't locked by the unlocker
func (resource * BaseResource) Unlock(event Event) error { func (resource * BaseResource) Unlock(event Event) error {
var err error = nil var err error = nil
unlocked := false //unlocked := false
resource.state_lock.Lock() resource.state_lock.Lock()
if resource.lock_holder == nil { if resource.lock_holder == nil {
@ -107,14 +129,14 @@ func (resource * BaseResource) Unlock(event Event) error {
} }
if all_children_unlocked == true{ if all_children_unlocked == true{
resource.lock_holder = nil resource.lock_holder = nil
unlocked = true //unlocked = true
} }
} }
resource.state_lock.Unlock() resource.state_lock.Unlock()
if unlocked == true { /*if unlocked == true {
resource.Update() resource.Update("unlocking resource")
} }*/
return err return err
} }
@ -154,7 +176,7 @@ func NewResource(name string, description string, children []Resource) * BaseRes
name: name, name: name,
description: description, description: description,
id: randid(), id: randid(),
listeners: []chan error{}, listeners: []chan string{},
}, },
parents: []Resource{}, parents: []Resource{},
children: children, children: children,

124
vex.go

@ -4,29 +4,9 @@ import (
"fmt" "fmt"
"log" "log"
"time" "time"
"errors"
) )
type Arena struct {
BaseResource
}
func NewVirtualArena(name string) * Arena {
arena := &Arena{
BaseResource: BaseResource{
BaseNode: BaseNode{
name: name,
description: "A virtual vex arena",
id: randid(),
listeners: []chan error{},
},
parents: []Resource{},
children: []Resource{},
},
}
return arena
}
type Member struct { type Member struct {
BaseResource BaseResource
} }
@ -38,7 +18,7 @@ func NewMember(name string) * Member {
name: name, name: name,
description: "A Team Member", description: "A Team Member",
id: randid(), id: randid(),
listeners: []chan error{}, listeners: []chan string{},
}, },
parents: []Resource{}, parents: []Resource{},
children: []Resource{}, children: []Resource{},
@ -72,7 +52,7 @@ func NewTeam(org string, team string, members []*Member) * Team {
name: name, name: name,
description: description, description: description,
id: randid(), id: randid(),
listeners: []chan error{}, listeners: []chan string{},
}, },
parents: []Resource{}, parents: []Resource{},
children: make([]Resource, len(members)), children: make([]Resource, len(members)),
@ -100,7 +80,7 @@ func NewAlliance(team0 * Team, team1 * Team) * Alliance {
name: name, name: name,
description: description, description: description,
id: randid(), id: randid(),
listeners: []chan error{}, listeners: []chan string{},
}, },
parents: []Resource{}, parents: []Resource{},
children: []Resource{team0, team1}, children: []Resource{team0, team1},
@ -116,10 +96,99 @@ type Match struct {
control_start time.Time control_start time.Time
} }
type Arena struct {
BaseResource
connected bool
}
func NewVirtualArena(name string) * Arena {
arena := &Arena{
BaseResource: BaseResource{
BaseNode: BaseNode{
name: name,
description: "A virtual vex arena",
id: randid(),
listeners: []chan string{},
},
parents: []Resource{},
children: []Resource{},
},
connected: false,
}
return arena
}
func (arena * Arena) Lock(event Event) error {
if arena.connected == false {
log.Printf("ARENA NOT CONNECTED: %s", arena.Name())
error_str := fmt.Sprintf("%s is not connected, cannot lock", arena.Name())
return errors.New(error_str)
}
return arena.lock(event)
}
func (arena * Arena) Connect(abort chan error) bool {
log.Printf("Connecting %s", arena.Name())
go func(arena * Arena, abort chan error) {
update_channel := arena.UpdateChannel()
owner := arena.Owner()
var owner_channel chan string = nil
if owner != nil {
owner_channel = owner.UpdateChannel()
}
arena.connected = true
update_str := fmt.Sprintf("VIRTUAL_ARENA connected: %s", arena.Name())
arena.Update(update_str)
log.Printf("VIRTUAL_ARENA goroutine starting: %s", arena.Name())
for true {
select {
case <- abort:
log.Printf("Virtual arena %s aborting", arena.Name())
break
case update, ok := <- update_channel:
if !ok {
panic("own update_channel closed")
}
log.Printf("%s update: %s", arena.Name(), update)
new_owner := arena.Owner()
if new_owner != owner {
log.Printf("NEW_OWNER for %s", arena.Name())
if new_owner != nil {
log.Printf("new: %s", new_owner.Name())
} else {
log.Printf("new: nil")
}
if owner != nil {
log.Printf("old: %s", owner.Name())
} else {
log.Printf("old: nil")
}
owner = new_owner
if owner != nil {
owner_channel = owner.UpdateChannel()
} else {
owner_channel = nil
}
}
case update, ok := <- owner_channel:
if !ok {
panic("owner update channel closed")
}
log.Printf("%s owner update: %s", arena.Name(), update)
log.Printf("owner: %s", owner.Name())
}
}
}(arena, abort)
return true
}
const start_slack = 3000 * time.Millisecond const start_slack = 3000 * time.Millisecond
func NewMatch(alliance0 * Alliance, alliance1 * Alliance, arena * Arena) * Match { func NewMatch(alliance0 * Alliance, alliance1 * Alliance, arena * Arena) * Match {
name := fmt.Sprintf("Match: %s vs. %s", alliance0.Name(), alliance1.Name() ) name := fmt.Sprintf("Match: %s vs. %s on %s", alliance0.Name(), alliance1.Name(), arena.Name())
description := "A vex match" description := "A vex match"
match := &Match{ match := &Match{
@ -131,13 +200,15 @@ func NewMatch(alliance0 * Alliance, alliance1 * Alliance, arena * Arena) * Match
match.LockDone() match.LockDone()
match.actions["start"] = func() (string, error) { match.actions["start"] = func() (string, error) {
log.Printf("Starting match") log.Printf("Starting match %s", match.Name())
log.Printf("%s", match.RequiredResources()[2].Owner().Name())
match.control = "none" match.control = "none"
match.state = "scheduled" match.state = "scheduled"
return "wait", nil return "wait", nil
} }
match.actions["queue_autonomous"] = func() (string, error) { match.actions["queue_autonomous"] = func() (string, error) {
log.Printf("queue_autonomous")
match.control = "none" match.control = "none"
match.state = "autonomous_queued" match.state = "autonomous_queued"
match.control_start = time.Now().Add(start_slack) match.control_start = time.Now().Add(start_slack)
@ -145,6 +216,7 @@ func NewMatch(alliance0 * Alliance, alliance1 * Alliance, arena * Arena) * Match
} }
match.actions["start_autonomous"] = func() (string, error) { match.actions["start_autonomous"] = func() (string, error) {
log.Printf("start_autonomous")
match.control = "autonomous" match.control = "autonomous"
match.state = "autonomous_running" match.state = "autonomous_running"
return "wait", nil return "wait", nil