mirror of
https://github.com/Team254/cheesy-arena-lite.git
synced 2026-03-09 13:46:44 -04:00
Decode ArmorBlock status register from PLC and surface individual statuses on Match Play page.
This commit is contained in:
@@ -662,6 +662,11 @@ func (arena *Arena) checkCanStartMatch() error {
|
||||
if arena.Plc.GetFieldEstop() {
|
||||
return fmt.Errorf("Cannot start match while field emergency stop is active.")
|
||||
}
|
||||
for name, status := range arena.Plc.GetArmorBlockStatuses() {
|
||||
if !status {
|
||||
return fmt.Errorf("Cannot start match while PLC ArmorBlock '%s' is not connected.", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -89,11 +89,13 @@ func (arena *Arena) generateArenaStatusMessage() interface{} {
|
||||
AllianceStations map[string]*AllianceStation
|
||||
TeamWifiStatuses map[string]network.TeamWifiStatus
|
||||
MatchState
|
||||
CanStartMatch bool
|
||||
PlcIsHealthy bool
|
||||
FieldEstop bool
|
||||
CanStartMatch bool
|
||||
PlcIsHealthy bool
|
||||
FieldEstop bool
|
||||
PlcArmorBlockStatuses map[string]bool
|
||||
}{arena.CurrentMatch.Id, arena.AllianceStations, teamWifiStatuses, arena.MatchState,
|
||||
arena.checkCanStartMatch() == nil, arena.Plc.IsHealthy, arena.Plc.GetFieldEstop()}
|
||||
arena.checkCanStartMatch() == nil, arena.Plc.IsHealthy, arena.Plc.GetFieldEstop(),
|
||||
arena.Plc.GetArmorBlockStatuses()}
|
||||
}
|
||||
|
||||
func (arena *Arena) generateAudienceDisplayModeMessage() interface{} {
|
||||
|
||||
27
plc/armorblock_string.go
Normal file
27
plc/armorblock_string.go
Normal file
@@ -0,0 +1,27 @@
|
||||
// Code generated by "stringer -type=armorBlock"; DO NOT EDIT.
|
||||
|
||||
package plc
|
||||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[redDs-0]
|
||||
_ = x[blueDs-1]
|
||||
_ = x[shieldGenerator-2]
|
||||
_ = x[controlPanel-3]
|
||||
_ = x[armorBlockCount-4]
|
||||
}
|
||||
|
||||
const _armorBlock_name = "redDsblueDsshieldGeneratorcontrolPanelarmorBlockCount"
|
||||
|
||||
var _armorBlock_index = [...]uint8{0, 5, 11, 26, 38, 53}
|
||||
|
||||
func (i armorBlock) String() string {
|
||||
if i < 0 || i >= armorBlock(len(_armorBlock_index)-1) {
|
||||
return "armorBlock(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _armorBlock_name[_armorBlock_index[i]:_armorBlock_index[i+1]]
|
||||
}
|
||||
21
plc/plc.go
21
plc/plc.go
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/Team254/cheesy-arena/websocket"
|
||||
"github.com/goburrow/modbus"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -114,6 +115,17 @@ const (
|
||||
coilCount
|
||||
)
|
||||
|
||||
// Bitmask for decoding fieldIoConnection into individual ArmorBlock connection statuses.
|
||||
type armorBlock int
|
||||
|
||||
const (
|
||||
redDs armorBlock = iota
|
||||
blueDs
|
||||
shieldGenerator
|
||||
controlPanel
|
||||
armorBlockCount
|
||||
)
|
||||
|
||||
func (plc *Plc) SetAddress(address string) {
|
||||
plc.address = address
|
||||
plc.resetConnection()
|
||||
@@ -177,6 +189,15 @@ func (plc *Plc) Run() {
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a map of ArmorBlocks I/O module names to whether they are connected properly.
|
||||
func (plc *Plc) GetArmorBlockStatuses() map[string]bool {
|
||||
statuses := make(map[string]bool, armorBlockCount)
|
||||
for i := 0; i < int(armorBlockCount); i++ {
|
||||
statuses[strings.Title(armorBlock(i).String())] = plc.registers[fieldIoConnection]&(1<<i) > 0
|
||||
}
|
||||
return statuses
|
||||
}
|
||||
|
||||
// Returns the state of the field emergency stop button (true if e-stop is active).
|
||||
func (plc *Plc) GetFieldEstop() bool {
|
||||
return plc.IsEnabled() && !plc.inputs[fieldEstop]
|
||||
|
||||
@@ -34,3 +34,32 @@ func TestBoolToByte(t *testing.T) {
|
||||
assert.Equal(t, bools, byteToBool(bytes, len(bools)))
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetArmorBlockStatuses(t *testing.T) {
|
||||
var plc Plc
|
||||
|
||||
plc.registers[fieldIoConnection] = 0
|
||||
assert.Equal(t, map[string]bool{"RedDs": false, "BlueDs": false, "ShieldGenerator": false, "ControlPanel": false},
|
||||
plc.GetArmorBlockStatuses())
|
||||
plc.registers[fieldIoConnection] = 1
|
||||
assert.Equal(t, map[string]bool{"RedDs": true, "BlueDs": false, "ShieldGenerator": false, "ControlPanel": false},
|
||||
plc.GetArmorBlockStatuses())
|
||||
plc.registers[fieldIoConnection] = 2
|
||||
assert.Equal(t, map[string]bool{"RedDs": false, "BlueDs": true, "ShieldGenerator": false, "ControlPanel": false},
|
||||
plc.GetArmorBlockStatuses())
|
||||
plc.registers[fieldIoConnection] = 4
|
||||
assert.Equal(t, map[string]bool{"RedDs": false, "BlueDs": false, "ShieldGenerator": true, "ControlPanel": false},
|
||||
plc.GetArmorBlockStatuses())
|
||||
plc.registers[fieldIoConnection] = 8
|
||||
assert.Equal(t, map[string]bool{"RedDs": false, "BlueDs": false, "ShieldGenerator": false, "ControlPanel": true},
|
||||
plc.GetArmorBlockStatuses())
|
||||
plc.registers[fieldIoConnection] = 5
|
||||
assert.Equal(t, map[string]bool{"RedDs": true, "BlueDs": false, "ShieldGenerator": true, "ControlPanel": false},
|
||||
plc.GetArmorBlockStatuses())
|
||||
plc.registers[fieldIoConnection] = 10
|
||||
assert.Equal(t, map[string]bool{"RedDs": false, "BlueDs": true, "ShieldGenerator": false, "ControlPanel": true},
|
||||
plc.GetArmorBlockStatuses())
|
||||
plc.registers[fieldIoConnection] = 15
|
||||
assert.Equal(t, map[string]bool{"RedDs": true, "BlueDs": true, "ShieldGenerator": true, "ControlPanel": true},
|
||||
plc.GetArmorBlockStatuses())
|
||||
}
|
||||
|
||||
@@ -187,6 +187,9 @@ var handleArenaStatus = function(data) {
|
||||
$("#plcStatus").attr("data-ready", false);
|
||||
}
|
||||
$("#fieldEstop").attr("data-ready", !data.FieldEstop);
|
||||
$.each(data.PlcArmorBlockStatuses, function(name, status) {
|
||||
$("#plc" + name + "Status").attr("data-ready", status);
|
||||
});
|
||||
};
|
||||
|
||||
// Handles a websocket message to update the match time countdown.
|
||||
|
||||
@@ -123,6 +123,9 @@
|
||||
<p>
|
||||
<span class="label label-scoring" id="plcStatus"></span><br />
|
||||
<span class="label label-scoring" id="fieldEstop">E-Stop</span>
|
||||
{{range $name, $status := .PlcArmorBlockStatuses}}
|
||||
<br /><span class="label label-scoring" id="plc{{$name}}Status">{{$name}}</span>
|
||||
{{end}}
|
||||
</p>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
@@ -72,14 +72,15 @@ func (web *Web) matchPlayHandler(w http.ResponseWriter, r *http.Request) {
|
||||
isReplay := matchResult != nil
|
||||
data := struct {
|
||||
*model.EventSettings
|
||||
PlcIsEnabled bool
|
||||
MatchesByType map[string]MatchPlayList
|
||||
CurrentMatchType string
|
||||
Match *model.Match
|
||||
AllowSubstitution bool
|
||||
IsReplay bool
|
||||
PlcIsEnabled bool
|
||||
MatchesByType map[string]MatchPlayList
|
||||
CurrentMatchType string
|
||||
Match *model.Match
|
||||
AllowSubstitution bool
|
||||
IsReplay bool
|
||||
PlcArmorBlockStatuses map[string]bool
|
||||
}{web.arena.EventSettings, web.arena.Plc.IsEnabled(), matchesByType, currentMatchType, web.arena.CurrentMatch,
|
||||
web.arena.CurrentMatch.ShouldAllowSubstitution(), isReplay}
|
||||
web.arena.CurrentMatch.ShouldAllowSubstitution(), isReplay, web.arena.Plc.GetArmorBlockStatuses()}
|
||||
err = template.ExecuteTemplate(w, "base", data)
|
||||
if err != nil {
|
||||
handleWebErr(w, err)
|
||||
|
||||
Reference in New Issue
Block a user