mirror of
https://github.com/Team254/cheesy-arena-lite.git
synced 2026-03-09 21:56:50 -04:00
Add bandwidth monitoring.
This commit is contained in:
108
bandwidth_monitor.go
Normal file
108
bandwidth_monitor.go
Normal file
@@ -0,0 +1,108 @@
|
||||
// Copyright 2015 Team 254. All Rights Reserved.
|
||||
// Author: pat@patfairbank.com (Patrick Fairbank)
|
||||
//
|
||||
// Methods for monitoring team bandwidth usage across a managed switch.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/cdevr/WapSNMP"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
monitoringIntervalMs = 1000
|
||||
toRobotBytesOid = ".1.3.6.1.2.1.2.2.1.10"
|
||||
fromRobotBytesOid = ".1.3.6.1.2.1.2.2.1.16"
|
||||
red1Port = 2
|
||||
red2Port = 4
|
||||
red3Port = 6
|
||||
blue1Port = 8
|
||||
blue2Port = 10
|
||||
blue3Port = 12
|
||||
)
|
||||
|
||||
type BandwidthMonitor struct {
|
||||
snmpClient *wapsnmp.WapSNMP
|
||||
toRobotOid wapsnmp.Oid
|
||||
fromRobotOid wapsnmp.Oid
|
||||
lastToRobotBytes map[string]interface{}
|
||||
lastFromRobotBytes map[string]interface{}
|
||||
lastBytesTime time.Time
|
||||
}
|
||||
|
||||
// Loops indefinitely to query the managed switch via SNMP (Simple Network Management Protocol).
|
||||
func MonitorBandwidth() {
|
||||
monitor := BandwidthMonitor{toRobotOid: wapsnmp.MustParseOid(toRobotBytesOid),
|
||||
fromRobotOid: wapsnmp.MustParseOid(fromRobotBytesOid)}
|
||||
for {
|
||||
if eventSettings.NetworkSecurityEnabled && eventSettings.BandwidthMonitoringEnabled {
|
||||
err := monitor.updateBandwidth()
|
||||
if err != nil {
|
||||
log.Printf("Bandwidth monitoring error: %s", err)
|
||||
}
|
||||
}
|
||||
time.Sleep(time.Millisecond * monitoringIntervalMs)
|
||||
}
|
||||
}
|
||||
|
||||
func (monitor *BandwidthMonitor) updateBandwidth() error {
|
||||
if monitor.snmpClient != nil && monitor.snmpClient.Target != eventSettings.SwitchAddress {
|
||||
// Switch address has changed; must re-create the SNMP client.
|
||||
monitor.snmpClient.Close()
|
||||
monitor.snmpClient = nil
|
||||
}
|
||||
|
||||
if monitor.snmpClient == nil {
|
||||
var err error
|
||||
monitor.snmpClient, err = wapsnmp.NewWapSNMP(eventSettings.SwitchAddress, eventSettings.SwitchPassword,
|
||||
wapsnmp.SNMPv2c, 2*time.Second, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve total number of bytes sent/received per port.
|
||||
toRobotBytes, err := monitor.snmpClient.GetTable(monitor.toRobotOid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fromRobotBytes, err := monitor.snmpClient.GetTable(monitor.fromRobotOid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Calculate the bandwidth usage over time.
|
||||
monitor.updateStationBandwidth("R1", red1Port, toRobotBytes, fromRobotBytes)
|
||||
monitor.updateStationBandwidth("R2", red2Port, toRobotBytes, fromRobotBytes)
|
||||
monitor.updateStationBandwidth("R3", red3Port, toRobotBytes, fromRobotBytes)
|
||||
monitor.updateStationBandwidth("B1", blue1Port, toRobotBytes, fromRobotBytes)
|
||||
monitor.updateStationBandwidth("B2", blue2Port, toRobotBytes, fromRobotBytes)
|
||||
monitor.updateStationBandwidth("B3", blue3Port, toRobotBytes, fromRobotBytes)
|
||||
|
||||
monitor.lastToRobotBytes = toRobotBytes
|
||||
monitor.lastFromRobotBytes = fromRobotBytes
|
||||
monitor.lastBytesTime = time.Now()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (monitor *BandwidthMonitor) updateStationBandwidth(station string, port int, toRobotBytes map[string]interface{},
|
||||
fromRobotBytes map[string]interface{}) {
|
||||
dsConn := mainArena.AllianceStations[station].DsConn
|
||||
if dsConn == nil {
|
||||
// No team assigned; just skip it.
|
||||
return
|
||||
}
|
||||
dsStatus := dsConn.DriverStationStatus
|
||||
secondsSinceLast := time.Now().Sub(monitor.lastBytesTime).Seconds()
|
||||
|
||||
toRobotBytesForPort := uint32(toRobotBytes[fmt.Sprintf("%s.%d", toRobotBytesOid, port)].(wapsnmp.Counter))
|
||||
lastToRobotBytesForPort := uint32(monitor.lastToRobotBytes[fmt.Sprintf("%s.%d", toRobotBytesOid, port)].(wapsnmp.Counter))
|
||||
dsStatus.MBpsToRobot = float64(toRobotBytesForPort-lastToRobotBytesForPort) / 1024 / 1024 / secondsSinceLast
|
||||
|
||||
fromRobotBytesForPort := uint32(fromRobotBytes[fmt.Sprintf("%s.%d", fromRobotBytesOid, port)].(wapsnmp.Counter))
|
||||
lastFromRobotBytesForPort := uint32(monitor.lastFromRobotBytes[fmt.Sprintf("%s.%d", fromRobotBytesOid, port)].(wapsnmp.Counter))
|
||||
dsStatus.MBpsFromRobot = float64(fromRobotBytesForPort-lastFromRobotBytesForPort) / 1024 / 1024 / secondsSinceLast
|
||||
}
|
||||
@@ -19,7 +19,8 @@ CREATE TABLE event_settings (
|
||||
apusername VARCHAR(255),
|
||||
appassword VARCHAR(255),
|
||||
switchaddress VARCHAR(255),
|
||||
switchpassword VARCHAR(255)
|
||||
switchpassword VARCHAR(255),
|
||||
bandwidthmonitoringenabled bool
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
|
||||
@@ -32,6 +32,8 @@ type DriverStationStatus struct {
|
||||
PacketCount int
|
||||
MissedPacketCount int
|
||||
DsRobotTripTimeMs int
|
||||
MBpsToRobot float64
|
||||
MBpsFromRobot float64
|
||||
}
|
||||
|
||||
type DriverStationConnection struct {
|
||||
@@ -71,6 +73,8 @@ func (dsConn *DriverStationConnection) Update() error {
|
||||
dsConn.DriverStationStatus.DsLinked = false
|
||||
dsConn.DriverStationStatus.RobotLinked = false
|
||||
dsConn.DriverStationStatus.BatteryVoltage = 0
|
||||
dsConn.DriverStationStatus.MBpsToRobot = 0
|
||||
dsConn.DriverStationStatus.MBpsFromRobot = 0
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -6,26 +6,27 @@
|
||||
package main
|
||||
|
||||
type EventSettings struct {
|
||||
Id int
|
||||
Name string
|
||||
Code string
|
||||
DisplayBackgroundColor string
|
||||
NumElimAlliances int
|
||||
SelectionRound2Order string
|
||||
SelectionRound3Order string
|
||||
TeamInfoDownloadEnabled bool
|
||||
RedGoalLightsAddress string
|
||||
BlueGoalLightsAddress string
|
||||
TbaPublishingEnabled bool
|
||||
TbaEventCode string
|
||||
TbaSecretId string
|
||||
TbaSecret string
|
||||
NetworkSecurityEnabled bool
|
||||
ApAddress string
|
||||
ApUsername string
|
||||
ApPassword string
|
||||
SwitchAddress string
|
||||
SwitchPassword string
|
||||
Id int
|
||||
Name string
|
||||
Code string
|
||||
DisplayBackgroundColor string
|
||||
NumElimAlliances int
|
||||
SelectionRound2Order string
|
||||
SelectionRound3Order string
|
||||
TeamInfoDownloadEnabled bool
|
||||
RedGoalLightsAddress string
|
||||
BlueGoalLightsAddress string
|
||||
TbaPublishingEnabled bool
|
||||
TbaEventCode string
|
||||
TbaSecretId string
|
||||
TbaSecret string
|
||||
NetworkSecurityEnabled bool
|
||||
ApAddress string
|
||||
ApUsername string
|
||||
ApPassword string
|
||||
SwitchAddress string
|
||||
SwitchPassword string
|
||||
BandwidthMonitoringEnabled bool
|
||||
}
|
||||
|
||||
const eventSettingsId = 0
|
||||
|
||||
1
main.go
1
main.go
@@ -24,6 +24,7 @@ func main() {
|
||||
listener, err := DsPacketListener()
|
||||
checkErr(err)
|
||||
go ListenForDsPackets(listener)
|
||||
go MonitorBandwidth()
|
||||
mainArena.Setup()
|
||||
mainArena.Run()
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ func SettingsPostHandler(w http.ResponseWriter, r *http.Request) {
|
||||
eventSettings.ApPassword = r.PostFormValue("apPassword")
|
||||
eventSettings.SwitchAddress = r.PostFormValue("switchAddress")
|
||||
eventSettings.SwitchPassword = r.PostFormValue("switchPassword")
|
||||
eventSettings.BandwidthMonitoringEnabled = r.PostFormValue("bandwidthMonitoringEnabled") == "on"
|
||||
err := db.SaveEventSettings(eventSettings)
|
||||
if err != nil {
|
||||
handleWebErr(w, err)
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
border: 1px solid #999;
|
||||
border-radius: 4px;
|
||||
padding: 0px;
|
||||
width: 45px;
|
||||
width: 50px;
|
||||
height: 27px;
|
||||
line-height: 25px;
|
||||
font-size: 14px;
|
||||
|
||||
@@ -14,6 +14,7 @@ var handleStatus = function(data) {
|
||||
$("#status" + station + " .team").text(stationStatus.DsConn.TeamId);
|
||||
var dsStatus = stationStatus.DsConn.DriverStationStatus;
|
||||
$("#status" + station + " .ds-status").attr("data-status-ok", dsStatus.DsLinked);
|
||||
$("#status" + station + " .ds-status").text(dsStatus.MBpsToRobot.toFixed(1) + "/" + dsStatus.MBpsFromRobot.toFixed(1));
|
||||
$("#status" + station + " .robot-status").attr("data-status-ok", dsStatus.RobotLinked);
|
||||
if (stationStatus.DsConn.SecondsSinceLastRobotLink > 1 && stationStatus.DsConn.SecondsSinceLastRobotLink < 1000) {
|
||||
$("#status" + station + " .robot-status").text(stationStatus.DsConn.SecondsSinceLastRobotLink.toFixed());
|
||||
@@ -33,6 +34,7 @@ var handleStatus = function(data) {
|
||||
$("#status" + station + " .packet-loss").text(dsStatus.MissedPacketCount);
|
||||
} 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", "");
|
||||
|
||||
@@ -64,6 +64,7 @@ var handleStatus = function(data) {
|
||||
if (stationStatus.DsConn) {
|
||||
var dsStatus = stationStatus.DsConn.DriverStationStatus;
|
||||
$("#status" + station + " .ds-status").attr("data-status-ok", dsStatus.DsLinked);
|
||||
$("#status" + station + " .ds-status").text(dsStatus.MBpsToRobot.toFixed(1) + "/" + dsStatus.MBpsFromRobot.toFixed(1));
|
||||
$("#status" + station + " .robot-status").attr("data-status-ok", dsStatus.RobotLinked);
|
||||
if (stationStatus.DsConn.SecondsSinceLastRobotLink > 1 && stationStatus.DsConn.SecondsSinceLastRobotLink < 1000) {
|
||||
$("#status" + station + " .robot-status").text(stationStatus.DsConn.SecondsSinceLastRobotLink.toFixed());
|
||||
@@ -79,6 +80,7 @@ var handleStatus = function(data) {
|
||||
$("#status" + station + " .battery-status").text(dsStatus.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", "");
|
||||
|
||||
@@ -125,10 +125,11 @@ interface FastEthernet0/24
|
||||
switchport mode access
|
||||
!
|
||||
interface GigabitEthernet0/1
|
||||
switchport mode dynamic desirable
|
||||
switchport trunk encapsulation dot1q
|
||||
switchport mode trunk
|
||||
!
|
||||
interface GigabitEthernet0/2
|
||||
switchport mode dynamic desirable
|
||||
switchport mode access
|
||||
!
|
||||
interface Vlan1
|
||||
ip address 10.0.0.61 255.255.255.0
|
||||
@@ -170,6 +171,8 @@ access-list 114 permit ip 10.0.4.0 0.0.0.255 host 10.0.100.50
|
||||
access-list 115 permit ip 10.0.5.0 0.0.0.255 host 10.0.100.50
|
||||
access-list 116 permit ip 10.0.6.0 0.0.0.255 host 10.0.100.50
|
||||
!
|
||||
snmp-server community 1234Five RO
|
||||
!
|
||||
line con 0
|
||||
exec-timeout 0 0
|
||||
line vty 0 4
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<div class="col-lg-6 well well-darkblue">
|
||||
<div class="row form-group">
|
||||
<div class="col-xs-3">Blue Teams</div>
|
||||
<div class="col-xs-1" data-toggle="tooltip" title="Driver Station">DS</div>
|
||||
<div class="col-xs-1" data-toggle="tooltip" title="Driver Station (Tx/Rx MB/s)">DS</div>
|
||||
<div class="col-xs-1" data-toggle="tooltip" title="Robot Status/Time Since Last Link">R</div>
|
||||
<div class="col-xs-1" data-toggle="tooltip" title="Battery">Bat</div>
|
||||
<div class="col-xs-1" data-toggle="tooltip" title="Bypassed/Disabled">Byp</div>
|
||||
@@ -26,7 +26,7 @@
|
||||
<div class="col-lg-6 well well-darkred">
|
||||
<div class="row form-group">
|
||||
<div class="col-xs-3">Red Teams</div>
|
||||
<div class="col-xs-1" data-toggle="tooltip" title="Driver Station">DS</div>
|
||||
<div class="col-xs-1" data-toggle="tooltip" title="Driver Station (Tx/Rx MB/s)">DS</div>
|
||||
<div class="col-xs-1" data-toggle="tooltip" title="Robot Status/Time Since Last Link">R</div>
|
||||
<div class="col-xs-1" data-toggle="tooltip" title="Battery">Bat</div>
|
||||
<div class="col-xs-1" data-toggle="tooltip" title="Bypassed/Disabled">Byp</div>
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
<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">DS</div>
|
||||
<div class="col-lg-2" data-toggle="tooltip" title="Driver Station (Tx/Rx MB/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="Bypass/Disable">Byp</div>
|
||||
@@ -75,7 +75,7 @@
|
||||
<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">DS</div>
|
||||
<div class="col-lg-2" data-toggle="tooltip" title="Driver Station (Tx/Rx MB/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="Bypass/Disable">Byp</div>
|
||||
|
||||
@@ -179,6 +179,12 @@
|
||||
<input type="password" class="form-control" name="switchPassword" value="{{.SwitchPassword}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-lg-7 control-label">Enable bandwidth monitoring</label>
|
||||
<div class="col-lg-1 checkbox">
|
||||
<input type="checkbox" name="bandwidthMonitoringEnabled"{{if .BandwidthMonitoringEnabled}} checked{{end}}>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<div class="form-group">
|
||||
<div class="col-lg-7 col-lg-offset-5">
|
||||
|
||||
Reference in New Issue
Block a user