mirror of
https://github.com/Team254/cheesy-arena-lite.git
synced 2026-03-09 21:56:50 -04:00
Poll access point for network configuration and reflect it in the match play view.
This commit is contained in:
@@ -45,7 +45,7 @@ const (
|
||||
type Arena struct {
|
||||
Database *model.Database
|
||||
EventSettings *model.EventSettings
|
||||
accessPoint *network.AccessPoint
|
||||
accessPoint network.AccessPoint
|
||||
networkSwitch *network.Switch
|
||||
Plc plc.Plc
|
||||
TbaClient *partner.TbaClient
|
||||
@@ -143,8 +143,8 @@ func (arena *Arena) LoadSettings() error {
|
||||
arena.EventSettings = settings
|
||||
|
||||
// Initialize the components that depend on settings.
|
||||
arena.accessPoint = network.NewAccessPoint(settings.ApAddress, settings.ApUsername, settings.ApPassword,
|
||||
settings.ApTeamChannel, settings.ApAdminChannel, settings.ApAdminWpaKey)
|
||||
arena.accessPoint.SetSettings(settings.ApAddress, settings.ApUsername, settings.ApPassword,
|
||||
settings.ApTeamChannel, settings.ApAdminChannel, settings.ApAdminWpaKey, settings.NetworkSecurityEnabled)
|
||||
arena.networkSwitch = network.NewSwitch(settings.SwitchAddress, settings.SwitchPassword)
|
||||
arena.Plc.SetAddress(settings.PlcAddress)
|
||||
arena.TbaClient = partner.NewTbaClient(settings.TbaEventCode, settings.TbaSecretId, settings.TbaSecret)
|
||||
@@ -559,6 +559,7 @@ func (arena *Arena) Run() {
|
||||
// Start other loops in goroutines.
|
||||
go arena.listenForDriverStations()
|
||||
go arena.listenForDsUdpPackets()
|
||||
go arena.accessPoint.Run()
|
||||
go arena.Plc.Run()
|
||||
|
||||
for {
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/Team254/cheesy-arena/game"
|
||||
"github.com/Team254/cheesy-arena/led"
|
||||
"github.com/Team254/cheesy-arena/model"
|
||||
"github.com/Team254/cheesy-arena/network"
|
||||
"github.com/Team254/cheesy-arena/vaultled"
|
||||
"github.com/Team254/cheesy-arena/websocket"
|
||||
"strconv"
|
||||
@@ -89,15 +90,22 @@ func (arena *Arena) generateAllianceStationDisplayModeMessage() interface{} {
|
||||
}
|
||||
|
||||
func (arena *Arena) generateArenaStatusMessage() interface{} {
|
||||
// Convert AP team wifi network status array to a map by station for ease of client use.
|
||||
teamWifiStatuses := make(map[string]network.TeamWifiStatus)
|
||||
for i, station := range []string{"R1", "R2", "R3", "B1", "B2", "B3"} {
|
||||
teamWifiStatuses[station] = arena.accessPoint.TeamWifiStatuses[i]
|
||||
}
|
||||
|
||||
return &struct {
|
||||
AllianceStations map[string]*AllianceStation
|
||||
TeamWifiStatuses map[string]network.TeamWifiStatus
|
||||
MatchState
|
||||
CanStartMatch bool
|
||||
PlcIsHealthy bool
|
||||
FieldEstop bool
|
||||
GameSpecificData string
|
||||
}{arena.AllianceStations, arena.MatchState, arena.checkCanStartMatch() == nil, arena.Plc.IsHealthy,
|
||||
arena.Plc.GetFieldEstop(), arena.CurrentMatch.GameSpecificData}
|
||||
}{arena.AllianceStations, teamWifiStatuses, arena.MatchState, arena.checkCanStartMatch() == nil,
|
||||
arena.Plc.IsHealthy, arena.Plc.GetFieldEstop(), arena.CurrentMatch.GameSpecificData}
|
||||
}
|
||||
|
||||
func (arena *Arena) generateAudienceDisplayModeMessage() interface{} {
|
||||
|
||||
@@ -9,52 +9,71 @@ import (
|
||||
"fmt"
|
||||
"github.com/Team254/cheesy-arena/model"
|
||||
"golang.org/x/crypto/ssh"
|
||||
"os"
|
||||
"log"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const accessPointSshPort = 22
|
||||
const accessPointConnectTimeoutSec = 1
|
||||
const accessPointCommandTimeoutSec = 3
|
||||
const (
|
||||
accessPointSshPort = 22
|
||||
accessPointConnectTimeoutSec = 1
|
||||
accessPointPollPeriodSec = 3
|
||||
)
|
||||
|
||||
type AccessPoint struct {
|
||||
address string
|
||||
port int
|
||||
username string
|
||||
password string
|
||||
teamChannel int
|
||||
adminChannel int
|
||||
adminWpaKey string
|
||||
mutex sync.Mutex
|
||||
address string
|
||||
username string
|
||||
password string
|
||||
teamChannel int
|
||||
adminChannel int
|
||||
adminWpaKey string
|
||||
networkSecurityEnabled bool
|
||||
mutex sync.Mutex
|
||||
TeamWifiStatuses [6]TeamWifiStatus
|
||||
}
|
||||
|
||||
func NewAccessPoint(address, username, password string, teamChannel, adminChannel int,
|
||||
adminWpaKey string) *AccessPoint {
|
||||
return &AccessPoint{address: address, port: accessPointSshPort, username: username, password: password,
|
||||
teamChannel: teamChannel, adminChannel: adminChannel, adminWpaKey: adminWpaKey}
|
||||
type TeamWifiStatus struct {
|
||||
TeamId int
|
||||
RadioLinked bool
|
||||
}
|
||||
|
||||
func (ap *AccessPoint) SetSettings(address, username, password string, teamChannel, adminChannel int,
|
||||
adminWpaKey string, networkSecurityEnabled bool) {
|
||||
ap.address = address
|
||||
ap.username = username
|
||||
ap.password = password
|
||||
ap.teamChannel = teamChannel
|
||||
ap.adminChannel = adminChannel
|
||||
ap.adminWpaKey = adminWpaKey
|
||||
ap.networkSecurityEnabled = networkSecurityEnabled
|
||||
}
|
||||
|
||||
// Loops indefinitely to read status from the access point.
|
||||
func (ap *AccessPoint) Run() {
|
||||
for {
|
||||
if ap.networkSecurityEnabled {
|
||||
ap.updateTeamWifiStatuses()
|
||||
}
|
||||
|
||||
time.Sleep(time.Second * accessPointPollPeriodSec)
|
||||
}
|
||||
}
|
||||
|
||||
// Sets up wireless networks for the given set of teams.
|
||||
func (ap *AccessPoint) ConfigureTeamWifi(red1, red2, red3, blue1, blue2, blue3 *model.Team) error {
|
||||
// Make sure multiple configurations aren't being set at the same time.
|
||||
ap.mutex.Lock()
|
||||
defer ap.mutex.Unlock()
|
||||
|
||||
config, err := ap.generateAccessPointConfig(red1, red2, red3, blue1, blue2, blue3)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
command := fmt.Sprintf("uci batch <<ENDCONFIG && wifi radio0\n%s\nENDCONFIG\n", config)
|
||||
return ap.runCommand(command)
|
||||
_, err = ap.runCommand(command)
|
||||
return err
|
||||
}
|
||||
|
||||
func (ap *AccessPoint) ConfigureAdminWifi() error {
|
||||
// Make sure multiple configurations aren't being set at the same time.
|
||||
ap.mutex.Lock()
|
||||
defer ap.mutex.Unlock()
|
||||
|
||||
disabled := 0
|
||||
if ap.adminChannel == 0 {
|
||||
disabled = 1
|
||||
@@ -67,40 +86,35 @@ func (ap *AccessPoint) ConfigureAdminWifi() error {
|
||||
"commit wireless",
|
||||
}
|
||||
command := fmt.Sprintf("uci batch <<ENDCONFIG && wifi\n%s\nENDCONFIG\n", strings.Join(commands, "\n"))
|
||||
return ap.runCommand(command)
|
||||
_, err := ap.runCommand(command)
|
||||
return err
|
||||
}
|
||||
|
||||
// Logs into the access point via SSH and runs the given shell command.
|
||||
func (ap *AccessPoint) runCommand(command string) error {
|
||||
func (ap *AccessPoint) runCommand(command string) (string, error) {
|
||||
// Make sure multiple commands aren't being run at the same time.
|
||||
ap.mutex.Lock()
|
||||
defer ap.mutex.Unlock()
|
||||
|
||||
// Open an SSH connection to the AP.
|
||||
config := &ssh.ClientConfig{User: ap.username,
|
||||
Auth: []ssh.AuthMethod{ssh.Password(ap.password)},
|
||||
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
||||
Timeout: accessPointConnectTimeoutSec * time.Second}
|
||||
|
||||
conn, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", ap.address, ap.port), config)
|
||||
conn, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", ap.address, accessPointSshPort), config)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
session, err := conn.NewSession()
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
defer session.Close()
|
||||
defer conn.Close()
|
||||
session.Stdout = os.Stdout
|
||||
|
||||
// Run the command with a timeout. An error will be returned if the exit status is non-zero.
|
||||
commandChan := make(chan error, 1)
|
||||
go func() {
|
||||
commandChan <- session.Run(command)
|
||||
}()
|
||||
select {
|
||||
case err = <-commandChan:
|
||||
return err
|
||||
case <-time.After(accessPointCommandTimeoutSec * time.Second):
|
||||
return fmt.Errorf("WiFi SSH command timed out after %d seconds", accessPointCommandTimeoutSec)
|
||||
}
|
||||
outputBytes, err := session.Output(command)
|
||||
return string(outputBytes), err
|
||||
}
|
||||
|
||||
func (ap *AccessPoint) generateAccessPointConfig(red1, red2, red3, blue1, blue2, blue3 *model.Team) (string, error) {
|
||||
@@ -134,9 +148,9 @@ func (ap *AccessPoint) generateAccessPointConfig(red1, red2, red3, blue1, blue2,
|
||||
// Verifies the validity of the given team's WPA key and adds a network for it to the list to be configured.
|
||||
func addTeamConfigCommands(position int, team *model.Team, commands *[]string) error {
|
||||
if team == nil {
|
||||
*commands = append(*commands, fmt.Sprintf("set wireless.@wifi-iface[%d].disabled='1'", position),
|
||||
fmt.Sprintf("set wireless.@wifi-iface[%d].ssid=''", position),
|
||||
fmt.Sprintf("set wireless.@wifi-iface[%d].key=''", position))
|
||||
*commands = append(*commands, fmt.Sprintf("set wireless.@wifi-iface[%d].disabled='0'", position),
|
||||
fmt.Sprintf("set wireless.@wifi-iface[%d].ssid='no-team-%d'", position, position),
|
||||
fmt.Sprintf("set wireless.@wifi-iface[%d].key='no-team-%d'", position, position))
|
||||
} else {
|
||||
if len(team.WpaKey) < 8 || len(team.WpaKey) > 63 {
|
||||
return fmt.Errorf("Invalid WPA key '%s' configured for team %d.", team.WpaKey, team.Id)
|
||||
@@ -149,3 +163,39 @@ func addTeamConfigCommands(position int, team *model.Team, commands *[]string) e
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Fetches the current wifi network status from the access point and updates the status structure.
|
||||
func (ap *AccessPoint) updateTeamWifiStatuses() {
|
||||
output, err := ap.runCommand("iwinfo")
|
||||
if err != nil {
|
||||
log.Printf("Error getting wifi info from AP: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := decodeWifiInfo(output, ap.TeamWifiStatuses[:]); err != nil {
|
||||
log.Println(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Parses the given output from the "iwinfo" command on the AP and updates the given status structure with the result.
|
||||
func decodeWifiInfo(wifiInfo string, statuses []TeamWifiStatus) error {
|
||||
ssidRe := regexp.MustCompile("ESSID: \"([-\\w ]*)\"")
|
||||
ssids := ssidRe.FindAllStringSubmatch(wifiInfo, -1)
|
||||
linkQualityRe := regexp.MustCompile("Link Quality: ([-\\w ]+)/([-\\w ]+)")
|
||||
linkQualities := linkQualityRe.FindAllStringSubmatch(wifiInfo, -1)
|
||||
|
||||
// There should be at least six networks present -- one for each team on the 5GHz radio, plus one on the 2.4GHz
|
||||
// radio if the admin network is enabled.
|
||||
if len(ssids) < 6 || len(linkQualities) < 6 {
|
||||
return fmt.Errorf("Could not parse wifi info; expected 6 team networks, got %d.", len(ssids))
|
||||
}
|
||||
|
||||
for i := range statuses {
|
||||
ssid := ssids[i][1]
|
||||
statuses[i].TeamId, _ = strconv.Atoi(ssid) // Any non-numeric SSIDs will be represented by a zero.
|
||||
linkQualityNumerator := linkQualities[i][1]
|
||||
statuses[i].RadioLinked = linkQualityNumerator != "unknown"
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -4,8 +4,10 @@
|
||||
package network
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/Team254/cheesy-arena/model"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"io/ioutil"
|
||||
"regexp"
|
||||
"testing"
|
||||
)
|
||||
@@ -18,20 +20,20 @@ func TestConfigureAccessPoint(t *testing.T) {
|
||||
wpaKeyRe := regexp.MustCompile("key='([-\\w ]*)'")
|
||||
ap := AccessPoint{teamChannel: 1234, adminChannel: 4321, adminWpaKey: "blorpy"}
|
||||
|
||||
// Should disable all team SSIDs if there are no teams.
|
||||
// Should put dummy values for all team SSIDs if there are no teams.
|
||||
config, _ := ap.generateAccessPointConfig(nil, nil, nil, nil, nil, nil)
|
||||
disableds := disabledRe.FindAllStringSubmatch(config, -1)
|
||||
ssids := ssidRe.FindAllStringSubmatch(config, -1)
|
||||
wpaKeys := wpaKeyRe.FindAllStringSubmatch(config, -1)
|
||||
if assert.Equal(t, 6, len(disableds)) && assert.Equal(t, 6, len(ssids)) && assert.Equal(t, 6, len(wpaKeys)) {
|
||||
for i := 0; i < 6; i++ {
|
||||
assert.Equal(t, "1", disableds[i][1])
|
||||
assert.Equal(t, "", ssids[i][1])
|
||||
assert.Equal(t, "", wpaKeys[i][1])
|
||||
assert.Equal(t, "0", disableds[i][1])
|
||||
assert.Equal(t, fmt.Sprintf("no-team-%d", i+1), ssids[i][1])
|
||||
assert.Equal(t, fmt.Sprintf("no-team-%d", i+1), wpaKeys[i][1])
|
||||
}
|
||||
}
|
||||
|
||||
// Should configure two SSIDs for two teams and disable the rest.
|
||||
// Should configure two SSIDs for two teams and put dummy values for the rest.
|
||||
config, _ = ap.generateAccessPointConfig(&model.Team{Id: 254, WpaKey: "aaaaaaaa"}, nil, nil, nil, nil,
|
||||
&model.Team{Id: 1114, WpaKey: "bbbbbbbb"})
|
||||
disableds = disabledRe.FindAllStringSubmatch(config, -1)
|
||||
@@ -42,9 +44,9 @@ func TestConfigureAccessPoint(t *testing.T) {
|
||||
assert.Equal(t, "254", ssids[0][1])
|
||||
assert.Equal(t, "aaaaaaaa", wpaKeys[0][1])
|
||||
for i := 1; i < 5; i++ {
|
||||
assert.Equal(t, "1", disableds[i][1])
|
||||
assert.Equal(t, "", ssids[i][1])
|
||||
assert.Equal(t, "", wpaKeys[i][1])
|
||||
assert.Equal(t, "0", disableds[i][1])
|
||||
assert.Equal(t, fmt.Sprintf("no-team-%d", i+1), ssids[i][1])
|
||||
assert.Equal(t, fmt.Sprintf("no-team-%d", i+1), wpaKeys[i][1])
|
||||
}
|
||||
assert.Equal(t, "0", disableds[5][1])
|
||||
assert.Equal(t, "1114", ssids[5][1])
|
||||
@@ -83,3 +85,55 @@ func TestConfigureAccessPoint(t *testing.T) {
|
||||
assert.Contains(t, err.Error(), "Invalid WPA key")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeWifiInfo(t *testing.T) {
|
||||
var statuses [6]TeamWifiStatus
|
||||
|
||||
// Test with zero team networks configured.
|
||||
output, err := ioutil.ReadFile("testdata/iwinfo_0_teams.txt")
|
||||
if assert.Nil(t, err) {
|
||||
assert.Nil(t, decodeWifiInfo(string(output), statuses[:]))
|
||||
assertTeamWifiStatus(t, 0, false, statuses[0])
|
||||
assertTeamWifiStatus(t, 0, false, statuses[1])
|
||||
assertTeamWifiStatus(t, 0, false, statuses[2])
|
||||
assertTeamWifiStatus(t, 0, false, statuses[3])
|
||||
assertTeamWifiStatus(t, 0, false, statuses[4])
|
||||
assertTeamWifiStatus(t, 0, false, statuses[5])
|
||||
}
|
||||
|
||||
// Test with two team networks configured.
|
||||
output, err = ioutil.ReadFile("testdata/iwinfo_2_teams.txt")
|
||||
if assert.Nil(t, err) {
|
||||
assert.Nil(t, decodeWifiInfo(string(output), statuses[:]))
|
||||
assertTeamWifiStatus(t, 0, false, statuses[0])
|
||||
assertTeamWifiStatus(t, 2471, true, statuses[1])
|
||||
assertTeamWifiStatus(t, 0, false, statuses[2])
|
||||
assertTeamWifiStatus(t, 254, false, statuses[3])
|
||||
assertTeamWifiStatus(t, 0, false, statuses[4])
|
||||
assertTeamWifiStatus(t, 0, false, statuses[5])
|
||||
}
|
||||
|
||||
// Test with six team networks configured.
|
||||
output, err = ioutil.ReadFile("testdata/iwinfo_6_teams.txt")
|
||||
if assert.Nil(t, err) {
|
||||
assert.Nil(t, decodeWifiInfo(string(output), statuses[:]))
|
||||
assertTeamWifiStatus(t, 254, false, statuses[0])
|
||||
assertTeamWifiStatus(t, 1678, false, statuses[1])
|
||||
assertTeamWifiStatus(t, 2910, true, statuses[2])
|
||||
assertTeamWifiStatus(t, 604, false, statuses[3])
|
||||
assertTeamWifiStatus(t, 8, false, statuses[4])
|
||||
assertTeamWifiStatus(t, 2471, true, statuses[5])
|
||||
}
|
||||
|
||||
// Test with invalid input.
|
||||
assert.NotNil(t, decodeWifiInfo("", statuses[:]))
|
||||
output, err = ioutil.ReadFile("testdata/iwinfo_invalid.txt")
|
||||
if assert.Nil(t, err) {
|
||||
assert.NotNil(t, decodeWifiInfo(string(output), statuses[:]))
|
||||
}
|
||||
}
|
||||
|
||||
func assertTeamWifiStatus(t *testing.T, expectedTeamId int, expectedRadioLinked bool, status TeamWifiStatus) {
|
||||
assert.Equal(t, expectedTeamId, status.TeamId)
|
||||
assert.Equal(t, expectedRadioLinked, status.RadioLinked)
|
||||
}
|
||||
|
||||
91
network/testdata/iwinfo_0_teams.txt
vendored
Normal file
91
network/testdata/iwinfo_0_teams.txt
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
wlan0 ESSID: "no-team-1"
|
||||
Access Point: 00:25:9C:14:65:8A
|
||||
Mode: Master Channel: 161 (5.805 GHz)
|
||||
Tx-Power: 23 dBm Link Quality: unknown/70
|
||||
Signal: unknown Noise: -94 dBm
|
||||
Bit Rate: unknown
|
||||
Encryption: WPA2 PSK (CCMP)
|
||||
Type: nl80211 HW Mode(s): 802.11nac
|
||||
Hardware: 11AB:2A55 11AB:0000 [Marvell 88W8864]
|
||||
TX power offset: none
|
||||
Frequency offset: none
|
||||
Supports VAPs: yes PHY name: phy0
|
||||
|
||||
wlan0-1 ESSID: "no-team-2"
|
||||
Access Point: 02:25:9C:14:65:8A
|
||||
Mode: Master Channel: 161 (5.805 GHz)
|
||||
Tx-Power: 23 dBm Link Quality: unknown/70
|
||||
Signal: unknown Noise: -94 dBm
|
||||
Bit Rate: unknown
|
||||
Encryption: WPA2 PSK (CCMP)
|
||||
Type: nl80211 HW Mode(s): 802.11nac
|
||||
Hardware: 11AB:2A55 11AB:0000 [Marvell 88W8864]
|
||||
TX power offset: none
|
||||
Frequency offset: none
|
||||
Supports VAPs: yes PHY name: phy0
|
||||
|
||||
wlan0-2 ESSID: "no-team-3"
|
||||
Access Point: 06:25:9C:14:65:8A
|
||||
Mode: Master Channel: 161 (5.805 GHz)
|
||||
Tx-Power: 23 dBm Link Quality: unknown/70
|
||||
Signal: unknown Noise: -94 dBm
|
||||
Bit Rate: unknown
|
||||
Encryption: WPA2 PSK (CCMP)
|
||||
Type: nl80211 HW Mode(s): 802.11nac
|
||||
Hardware: 11AB:2A55 11AB:0000 [Marvell 88W8864]
|
||||
TX power offset: none
|
||||
Frequency offset: none
|
||||
Supports VAPs: yes PHY name: phy0
|
||||
|
||||
wlan0-3 ESSID: "no-team-4"
|
||||
Access Point: 0A:25:9C:14:65:8A
|
||||
Mode: Master Channel: 161 (5.805 GHz)
|
||||
Tx-Power: 23 dBm Link Quality: unknown/70
|
||||
Signal: unknown Noise: -94 dBm
|
||||
Bit Rate: unknown
|
||||
Encryption: WPA2 PSK (CCMP)
|
||||
Type: nl80211 HW Mode(s): 802.11nac
|
||||
Hardware: 11AB:2A55 11AB:0000 [Marvell 88W8864]
|
||||
TX power offset: none
|
||||
Frequency offset: none
|
||||
Supports VAPs: yes PHY name: phy0
|
||||
|
||||
wlan0-4 ESSID: "no-team-5"
|
||||
Access Point: 0E:25:9C:14:65:8A
|
||||
Mode: Master Channel: 161 (5.805 GHz)
|
||||
Tx-Power: 23 dBm Link Quality: unknown/70
|
||||
Signal: unknown Noise: -94 dBm
|
||||
Bit Rate: unknown
|
||||
Encryption: WPA2 PSK (CCMP)
|
||||
Type: nl80211 HW Mode(s): 802.11nac
|
||||
Hardware: 11AB:2A55 11AB:0000 [Marvell 88W8864]
|
||||
TX power offset: none
|
||||
Frequency offset: none
|
||||
Supports VAPs: yes PHY name: phy0
|
||||
|
||||
wlan0-5 ESSID: "no-team-6"
|
||||
Access Point: 12:25:9C:14:65:8A
|
||||
Mode: Master Channel: 161 (5.805 GHz)
|
||||
Tx-Power: 23 dBm Link Quality: unknown/70
|
||||
Signal: unknown Noise: -94 dBm
|
||||
Bit Rate: unknown
|
||||
Encryption: WPA2 PSK (CCMP)
|
||||
Type: nl80211 HW Mode(s): 802.11nac
|
||||
Hardware: 11AB:2A55 11AB:0000 [Marvell 88W8864]
|
||||
TX power offset: none
|
||||
Frequency offset: none
|
||||
Supports VAPs: yes PHY name: phy0
|
||||
|
||||
wlan1 ESSID: unknown
|
||||
Access Point: 00:00:00:00:00:00
|
||||
Mode: Client Channel: unknown (unknown)
|
||||
Tx-Power: unknown Link Quality: unknown/70
|
||||
Signal: unknown Noise: unknown
|
||||
Bit Rate: unknown
|
||||
Encryption: unknown
|
||||
Type: nl80211 HW Mode(s): 802.11bgn
|
||||
Hardware: 11AB:2A55 11AB:0000 [Marvell 88W8864]
|
||||
TX power offset: none
|
||||
Frequency offset: none
|
||||
Supports VAPs: yes PHY name: phy1
|
||||
|
||||
91
network/testdata/iwinfo_2_teams.txt
vendored
Normal file
91
network/testdata/iwinfo_2_teams.txt
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
wlan0 ESSID: "no-team-1"
|
||||
Access Point: 00:25:9C:14:65:8A
|
||||
Mode: Master Channel: 161 (5.805 GHz)
|
||||
Tx-Power: 23 dBm Link Quality: unknown/70
|
||||
Signal: unknown Noise: -94 dBm
|
||||
Bit Rate: unknown
|
||||
Encryption: WPA2 PSK (CCMP)
|
||||
Type: nl80211 HW Mode(s): 802.11nac
|
||||
Hardware: 11AB:2A55 11AB:0000 [Marvell 88W8864]
|
||||
TX power offset: none
|
||||
Frequency offset: none
|
||||
Supports VAPs: yes PHY name: phy0
|
||||
|
||||
wlan0-1 ESSID: "2471"
|
||||
Access Point: 02:25:9C:14:65:8A
|
||||
Mode: Master Channel: 161 (5.805 GHz)
|
||||
Tx-Power: 23 dBm Link Quality: 2/70
|
||||
Signal: unknown Noise: -94 dBm
|
||||
Bit Rate: unknown
|
||||
Encryption: WPA2 PSK (CCMP)
|
||||
Type: nl80211 HW Mode(s): 802.11nac
|
||||
Hardware: 11AB:2A55 11AB:0000 [Marvell 88W8864]
|
||||
TX power offset: none
|
||||
Frequency offset: none
|
||||
Supports VAPs: yes PHY name: phy0
|
||||
|
||||
wlan0-2 ESSID: "no-team-3"
|
||||
Access Point: 06:25:9C:14:65:8A
|
||||
Mode: Master Channel: 161 (5.805 GHz)
|
||||
Tx-Power: 23 dBm Link Quality: unknown/70
|
||||
Signal: unknown Noise: -94 dBm
|
||||
Bit Rate: unknown
|
||||
Encryption: WPA2 PSK (CCMP)
|
||||
Type: nl80211 HW Mode(s): 802.11nac
|
||||
Hardware: 11AB:2A55 11AB:0000 [Marvell 88W8864]
|
||||
TX power offset: none
|
||||
Frequency offset: none
|
||||
Supports VAPs: yes PHY name: phy0
|
||||
|
||||
wlan0-3 ESSID: "254"
|
||||
Access Point: 0A:25:9C:14:65:8A
|
||||
Mode: Master Channel: 161 (5.805 GHz)
|
||||
Tx-Power: 23 dBm Link Quality: unknown/70
|
||||
Signal: unknown Noise: -94 dBm
|
||||
Bit Rate: unknown
|
||||
Encryption: WPA2 PSK (CCMP)
|
||||
Type: nl80211 HW Mode(s): 802.11nac
|
||||
Hardware: 11AB:2A55 11AB:0000 [Marvell 88W8864]
|
||||
TX power offset: none
|
||||
Frequency offset: none
|
||||
Supports VAPs: yes PHY name: phy0
|
||||
|
||||
wlan0-4 ESSID: "no-team-5"
|
||||
Access Point: 0E:25:9C:14:65:8A
|
||||
Mode: Master Channel: 161 (5.805 GHz)
|
||||
Tx-Power: 23 dBm Link Quality: unknown/70
|
||||
Signal: unknown Noise: -94 dBm
|
||||
Bit Rate: unknown
|
||||
Encryption: WPA2 PSK (CCMP)
|
||||
Type: nl80211 HW Mode(s): 802.11nac
|
||||
Hardware: 11AB:2A55 11AB:0000 [Marvell 88W8864]
|
||||
TX power offset: none
|
||||
Frequency offset: none
|
||||
Supports VAPs: yes PHY name: phy0
|
||||
|
||||
wlan0-5 ESSID: "no-team-6"
|
||||
Access Point: 12:25:9C:14:65:8A
|
||||
Mode: Master Channel: 161 (5.805 GHz)
|
||||
Tx-Power: 23 dBm Link Quality: unknown/70
|
||||
Signal: unknown Noise: -94 dBm
|
||||
Bit Rate: unknown
|
||||
Encryption: WPA2 PSK (CCMP)
|
||||
Type: nl80211 HW Mode(s): 802.11nac
|
||||
Hardware: 11AB:2A55 11AB:0000 [Marvell 88W8864]
|
||||
TX power offset: none
|
||||
Frequency offset: none
|
||||
Supports VAPs: yes PHY name: phy0
|
||||
|
||||
wlan1 ESSID: unknown
|
||||
Access Point: 00:00:00:00:00:00
|
||||
Mode: Client Channel: unknown (unknown)
|
||||
Tx-Power: unknown Link Quality: unknown/70
|
||||
Signal: unknown Noise: unknown
|
||||
Bit Rate: unknown
|
||||
Encryption: unknown
|
||||
Type: nl80211 HW Mode(s): 802.11bgn
|
||||
Hardware: 11AB:2A55 11AB:0000 [Marvell 88W8864]
|
||||
TX power offset: none
|
||||
Frequency offset: none
|
||||
Supports VAPs: yes PHY name: phy1
|
||||
|
||||
91
network/testdata/iwinfo_6_teams.txt
vendored
Normal file
91
network/testdata/iwinfo_6_teams.txt
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
wlan0 ESSID: "254"
|
||||
Access Point: 00:25:9C:14:65:8A
|
||||
Mode: Master Channel: 161 (5.805 GHz)
|
||||
Tx-Power: 23 dBm Link Quality: unknown/70
|
||||
Signal: unknown Noise: -96 dBm
|
||||
Bit Rate: unknown
|
||||
Encryption: WPA2 PSK (CCMP)
|
||||
Type: nl80211 HW Mode(s): 802.11nac
|
||||
Hardware: 11AB:2A55 11AB:0000 [Marvell 88W8864]
|
||||
TX power offset: none
|
||||
Frequency offset: none
|
||||
Supports VAPs: yes PHY name: phy0
|
||||
|
||||
wlan0-1 ESSID: "1678"
|
||||
Access Point: 02:25:9C:14:65:8A
|
||||
Mode: Master Channel: 161 (5.805 GHz)
|
||||
Tx-Power: 23 dBm Link Quality: unknown/70
|
||||
Signal: unknown Noise: -96 dBm
|
||||
Bit Rate: unknown
|
||||
Encryption: WPA2 PSK (CCMP)
|
||||
Type: nl80211 HW Mode(s): 802.11nac
|
||||
Hardware: 11AB:2A55 11AB:0000 [Marvell 88W8864]
|
||||
TX power offset: none
|
||||
Frequency offset: none
|
||||
Supports VAPs: yes PHY name: phy0
|
||||
|
||||
wlan0-2 ESSID: "2910"
|
||||
Access Point: 06:25:9C:14:65:8A
|
||||
Mode: Master Channel: 161 (5.805 GHz)
|
||||
Tx-Power: 23 dBm Link Quality: 23/70
|
||||
Signal: -85 dBm Noise: -96 dBm
|
||||
Bit Rate: 6.0 MBit/s
|
||||
Encryption: WPA2 PSK (CCMP)
|
||||
Type: nl80211 HW Mode(s): 802.11nac
|
||||
Hardware: 11AB:2A55 11AB:0000 [Marvell 88W8864]
|
||||
TX power offset: none
|
||||
Frequency offset: none
|
||||
Supports VAPs: yes PHY name: phy0
|
||||
|
||||
wlan0-3 ESSID: "604"
|
||||
Access Point: 0A:25:9C:14:65:8A
|
||||
Mode: Master Channel: 161 (5.805 GHz)
|
||||
Tx-Power: 23 dBm Link Quality: unknown/70
|
||||
Signal: unknown Noise: -96 dBm
|
||||
Bit Rate: unknown
|
||||
Encryption: WPA2 PSK (CCMP)
|
||||
Type: nl80211 HW Mode(s): 802.11nac
|
||||
Hardware: 11AB:2A55 11AB:0000 [Marvell 88W8864]
|
||||
TX power offset: none
|
||||
Frequency offset: none
|
||||
Supports VAPs: yes PHY name: phy0
|
||||
|
||||
wlan0-4 ESSID: "8"
|
||||
Access Point: 0E:25:9C:14:65:8A
|
||||
Mode: Master Channel: 161 (5.805 GHz)
|
||||
Tx-Power: 23 dBm Link Quality: unknown/70
|
||||
Signal: unknown Noise: -96 dBm
|
||||
Bit Rate: unknown
|
||||
Encryption: WPA2 PSK (CCMP)
|
||||
Type: nl80211 HW Mode(s): 802.11nac
|
||||
Hardware: 11AB:2A55 11AB:0000 [Marvell 88W8864]
|
||||
TX power offset: none
|
||||
Frequency offset: none
|
||||
Supports VAPs: yes PHY name: phy0
|
||||
|
||||
wlan0-5 ESSID: "2471"
|
||||
Access Point: 12:25:9C:14:65:8A
|
||||
Mode: Master Channel: 161 (5.805 GHz)
|
||||
Tx-Power: 23 dBm Link Quality: 55/70
|
||||
Signal: -55 dBm Noise: -96 dBm
|
||||
Bit Rate: 6.0 MBit/s
|
||||
Encryption: WPA2 PSK (CCMP)
|
||||
Type: nl80211 HW Mode(s): 802.11nac
|
||||
Hardware: 11AB:2A55 11AB:0000 [Marvell 88W8864]
|
||||
TX power offset: none
|
||||
Frequency offset: none
|
||||
Supports VAPs: yes PHY name: phy0
|
||||
|
||||
wlan1 ESSID: unknown
|
||||
Access Point: 00:00:00:00:00:00
|
||||
Mode: Client Channel: unknown (unknown)
|
||||
Tx-Power: unknown Link Quality: unknown/70
|
||||
Signal: unknown Noise: unknown
|
||||
Bit Rate: unknown
|
||||
Encryption: unknown
|
||||
Type: nl80211 HW Mode(s): 802.11bgn
|
||||
Hardware: 11AB:2A55 11AB:0000 [Marvell 88W8864]
|
||||
TX power offset: none
|
||||
Frequency offset: none
|
||||
Supports VAPs: yes PHY name: phy1
|
||||
|
||||
65
network/testdata/iwinfo_invalid.txt
vendored
Normal file
65
network/testdata/iwinfo_invalid.txt
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
wlan0 ESSID: "254"
|
||||
Access Point: 00:25:9C:14:65:8A
|
||||
Mode: Master Channel: 161 (5.805 GHz)
|
||||
Tx-Power: 23 dBm Link Quality: unknown/70
|
||||
Signal: unknown Noise: -96 dBm
|
||||
Bit Rate: unknown
|
||||
Encryption: WPA2 PSK (CCMP)
|
||||
Type: nl80211 HW Mode(s): 802.11nac
|
||||
Hardware: 11AB:2A55 11AB:0000 [Marvell 88W8864]
|
||||
TX power offset: none
|
||||
Frequency offset: none
|
||||
Supports VAPs: yes PHY name: phy0
|
||||
|
||||
wlan0-1 ESSID: "1678"
|
||||
Access Point: 02:25:9C:14:65:8A
|
||||
Mode: Master Channel: 161 (5.805 GHz)
|
||||
Tx-Power: 23 dBm Link Quality: unknown/70
|
||||
Signal: unknown Noise: -96 dBm
|
||||
Bit Rate: unknown
|
||||
Encryption: WPA2 PSK (CCMP)
|
||||
Type: nl80211 HW Mode(s): 802.11nac
|
||||
Hardware: 11AB:2A55 11AB:0000 [Marvell 88W8864]
|
||||
TX power offset: none
|
||||
Frequency offset: none
|
||||
Supports VAPs: yes PHY name: phy0
|
||||
|
||||
wlan0-2 ESSID: "2910"
|
||||
Access Point: 06:25:9C:14:65:8A
|
||||
Mode: Master Channel: 161 (5.805 GHz)
|
||||
Tx-Power: 23 dBm Link Quality: 23/70
|
||||
Signal: -85 dBm Noise: -96 dBm
|
||||
Bit Rate: 6.0 MBit/s
|
||||
Encryption: WPA2 PSK (CCMP)
|
||||
Type: nl80211 HW Mode(s): 802.11nac
|
||||
Hardware: 11AB:2A55 11AB:0000 [Marvell 88W8864]
|
||||
TX power offset: none
|
||||
Frequency offset: none
|
||||
Supports VAPs: yes PHY name: phy0
|
||||
|
||||
wlan0-3 ESSID: "604"
|
||||
Access Point: 0A:25:9C:14:65:8A
|
||||
Mode: Master Channel: 161 (5.805 GHz)
|
||||
Tx-Power: 23 dBm Link Quality: unknown/70
|
||||
Signal: unknown Noise: -96 dBm
|
||||
Bit Rate: unknown
|
||||
Encryption: WPA2 PSK (CCMP)
|
||||
Type: nl80211 HW Mode(s): 802.11nac
|
||||
Hardware: 11AB:2A55 11AB:0000 [Marvell 88W8864]
|
||||
TX power offset: none
|
||||
Frequency offset: none
|
||||
Supports VAPs: yes PHY name: phy0
|
||||
|
||||
wlan1 ESSID: unknown
|
||||
Access Point: 00:00:00:00:00:00
|
||||
Mode: Client Channel: unknown (unknown)
|
||||
Tx-Power: unknown Link Quality: unknown/70
|
||||
Signal: unknown Noise: unknown
|
||||
Bit Rate: unknown
|
||||
Encryption: unknown
|
||||
Type: nl80211 HW Mode(s): 802.11bgn
|
||||
Hardware: 11AB:2A55 11AB:0000 [Marvell 88W8864]
|
||||
TX power offset: none
|
||||
Frequency offset: none
|
||||
Supports VAPs: yes PHY name: phy1
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
.modal-large {
|
||||
width: 60%;
|
||||
}
|
||||
.ds-status, .radio-status, .robot-status, .battery-status, .bypass-status, .trip-time, .packet-loss {
|
||||
.ds-status, .radio-status, .robot-status, .bypass-status, .trip-time, .packet-loss {
|
||||
background-color: #aaa;
|
||||
color: #000;
|
||||
border: 1px solid #999;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
var websocket;
|
||||
var scoreIsReady;
|
||||
var lowBatteryThreshold = 6;
|
||||
|
||||
// Sends a websocket message to load a team into an alliance station.
|
||||
var substituteTeam = function(team, position) {
|
||||
@@ -72,30 +73,42 @@ var confirmCommit = function(isReplay) {
|
||||
var handleArenaStatus = function(data) {
|
||||
// Update the team status view.
|
||||
$.each(data.AllianceStations, function(station, stationStatus) {
|
||||
var wifiStatus = data.TeamWifiStatuses[station];
|
||||
$("#status" + station + " .radio-status").text(wifiStatus.TeamId);
|
||||
|
||||
if (stationStatus.DsConn) {
|
||||
// Format the driver station status box.
|
||||
var dsConn = stationStatus.DsConn;
|
||||
$("#status" + station + " .ds-status").attr("data-status-ok", dsConn.DsLinked);
|
||||
$("#status" + station + " .robot-status").attr("data-status-ok", dsConn.RobotLinked);
|
||||
|
||||
// Format the radio status box according to the connection status of the robot radio.
|
||||
var radioOkay = stationStatus.Team && stationStatus.Team.Id === wifiStatus.TeamId && wifiStatus.RadioLinked;
|
||||
$("#status" + station + " .radio-status").attr("data-status-ok", radioOkay);
|
||||
|
||||
// Format the robot status box.
|
||||
var robotOkay = dsConn.BatteryVoltage > lowBatteryThreshold && dsConn.RobotLinked;
|
||||
$("#status" + station + " .robot-status").attr("data-status-ok", robotOkay);
|
||||
if (stationStatus.DsConn.SecondsSinceLastRobotLink > 1 && stationStatus.DsConn.SecondsSinceLastRobotLink < 1000) {
|
||||
$("#status" + station + " .robot-status").text(stationStatus.DsConn.SecondsSinceLastRobotLink.toFixed());
|
||||
} else {
|
||||
$("#status" + station + " .robot-status").text("");
|
||||
$("#status" + station + " .robot-status").text(dsConn.BatteryVoltage.toFixed(1) + "V");
|
||||
}
|
||||
var lowBatteryThreshold = 6;
|
||||
if (matchStates[data.MatchState] === "PRE_MATCH" || matchStates[data.MatchState] === "TIMEOUT_ACTIVE" ||
|
||||
matchStates[data.MatchState] === "POST_TIMEOUT") {
|
||||
lowBatteryThreshold = 12;
|
||||
}
|
||||
$("#status" + station + " .battery-status").attr("data-status-ok",
|
||||
dsConn.BatteryVoltage > lowBatteryThreshold && dsConn.RobotLinked);
|
||||
$("#status" + station + " .battery-status").text(dsConn.BatteryVoltage.toFixed(1) + "V");
|
||||
} else {
|
||||
$("#status" + station + " .ds-status").attr("data-status-ok", "");
|
||||
$("#status" + station + " .ds-status").text("");
|
||||
$("#status" + station + " .robot-status").attr("data-status-ok", "");
|
||||
$("#status" + station + " .robot-status").text("");
|
||||
$("#status" + station + " .battery-status").attr("data-status-ok", "");
|
||||
$("#status" + station + " .battery-status").text("");
|
||||
|
||||
// Format the robot status box according to whether the AP is configured with the correct SSID.
|
||||
var expectedTeamId = stationStatus.Team ? stationStatus.Team.Id : 0;
|
||||
if (wifiStatus.TeamId === expectedTeamId) {
|
||||
if (wifiStatus.RadioLinked) {
|
||||
$("#status" + station + " .radio-status").attr("data-status-ok", true);
|
||||
} else {
|
||||
$("#status" + station + " .radio-status").attr("data-status-ok", "");
|
||||
}
|
||||
} else {
|
||||
$("#status" + station + " .radio-status").attr("data-status-ok", false);
|
||||
}
|
||||
}
|
||||
|
||||
if (stationStatus.Estop) {
|
||||
|
||||
@@ -63,9 +63,9 @@
|
||||
<div class="col-lg-6 well well-darkblue">
|
||||
<div class="row form-group">
|
||||
<div class="col-lg-4">Blue Teams</div>
|
||||
<div class="col-lg-2" data-toggle="tooltip" title="Driver Station (Tx/Rx Mbits/s)">DS</div>
|
||||
<div class="col-lg-2" data-toggle="tooltip" title="Robot">R</div>
|
||||
<div class="col-lg-2" data-toggle="tooltip" title="Battery">B</div>
|
||||
<div class="col-lg-2" data-toggle="tooltip" title="Driver Station">DS</div>
|
||||
<div class="col-lg-2" data-toggle="tooltip" title="Radio (AP Configured SSID)">Rad</div>
|
||||
<div class="col-lg-2" data-toggle="tooltip" title="Robot">Rbt</div>
|
||||
<div class="col-lg-2" data-toggle="tooltip" title="Bypass/Disable">Byp</div>
|
||||
</div>
|
||||
{{template "matchPlayTeam" dict "team" .Match.Blue1 "color" "B" "position" 1 "data" .}}
|
||||
@@ -75,9 +75,9 @@
|
||||
<div class="col-lg-6 well well-darkred">
|
||||
<div class="row form-group">
|
||||
<div class="col-lg-4">Red Teams</div>
|
||||
<div class="col-lg-2" data-toggle="tooltip" title="Driver Station (Tx/Rx Mbits/s)">DS</div>
|
||||
<div class="col-lg-2" data-toggle="tooltip" title="Robot">R</div>
|
||||
<div class="col-lg-2" data-toggle="tooltip" title="Battery">B</div>
|
||||
<div class="col-lg-2" data-toggle="tooltip" title="Driver Station">DS</div>
|
||||
<div class="col-lg-2" data-toggle="tooltip" title="Radio (AP Configured SSID)">Rad</div>
|
||||
<div class="col-lg-2" data-toggle="tooltip" title="Robot">Rbt</div>
|
||||
<div class="col-lg-2" data-toggle="tooltip" title="Bypass/Disable">Byp</div>
|
||||
</div>
|
||||
{{template "matchPlayTeam" dict "team" .Match.Red3 "color" "R" "position" 3 "data" .}}
|
||||
@@ -271,8 +271,8 @@
|
||||
{{if not .data.AllowSubstitution}}disabled{{end}}>
|
||||
</div>
|
||||
<div class="col-lg-2 col-no-padding"><div class="ds-status"></div></div>
|
||||
<div class="col-lg-2 col-no-padding"><div class="radio-status"></div></div>
|
||||
<div class="col-lg-2 col-no-padding"><div class="robot-status"></div></div>
|
||||
<div class="col-lg-2 col-no-padding"><div class="battery-status"></div></div>
|
||||
<div class="col-lg-2 col-no-padding">
|
||||
<div class="bypass-status" onclick="toggleBypass('{{.color}}{{.position}}');"></div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user