setup entries output log message on successful application

clarified some database concepts - updated regression and setup packages
accordingly
This commit is contained in:
JetSetIlly 2023-02-14 08:39:55 +00:00
parent 3aa5885ebe
commit 68cf99620e
10 changed files with 134 additions and 134 deletions

View file

@ -27,14 +27,9 @@ type SerialisedEntry []string
// Entry represents the generic entry in the database.
type Entry interface {
// ID returns the string that is used to identify the entry type in
// EntryType returns the string that is used to identify the entry type in
// the database
ID() string
// String should return information about the entry in a human readable
// format. by contrast, machine readable representation is returned by the
// Serialise function
String() string
EntryType() string
// return the Entry data as an instance of SerialisedEntry.
Serialise() (SerialisedEntry, error)

View file

@ -125,7 +125,7 @@ func (db *Session) EndSession(commitChanges bool) error {
return err
}
s.WriteString(recordHeader(k, v.ID()))
s.WriteString(recordHeader(k, v.EntryType()))
for i := 0; i < len(ser); i++ {
s.WriteString(fieldSep)

View file

@ -32,7 +32,7 @@ import (
"github.com/jetsetilly/gopher2600/setup"
)
const logEntryID = "log"
const logEntryType = "log"
const (
logFieldCartName int = iota
@ -87,20 +87,9 @@ func deserialiseLogEntry(fields database.SerialisedEntry) (database.Entry, error
return reg, nil
}
// ID implements the database.Entry interface.
func (reg LogRegression) ID() string {
return logEntryID
}
// String implements the database.Entry interface.
func (reg LogRegression) String() string {
s := strings.Builder{}
s.WriteString(fmt.Sprintf("[%s] %s [%s] frames=%d", reg.ID(), reg.CartLoad.ShortName(), reg.TVtype, reg.NumFrames))
if reg.Notes != "" {
s.WriteString(fmt.Sprintf(" [%s]", reg.Notes))
}
return s.String()
// EntryType implements the database.Entry interface.
func (reg LogRegression) EntryType() string {
return logEntryType
}
// Serialise implements the database.Entry interface.
@ -121,6 +110,17 @@ func (reg LogRegression) CleanUp() error {
return nil
}
// String implements the regressions.Regressor interface
func (reg LogRegression) String() string {
s := strings.Builder{}
s.WriteString(fmt.Sprintf("[%s] %s [%s] frames=%d", reg.EntryType(), reg.CartLoad.ShortName(), reg.TVtype, reg.NumFrames))
if reg.Notes != "" {
s.WriteString(fmt.Sprintf(" [%s]", reg.Notes))
}
return s.String()
}
// regress implements the regression.Regressor interface.
func (reg *LogRegression) regress(newRegression bool, output io.Writer, msg string) (bool, string, error) {
// make sure logger is clear

View file

@ -33,7 +33,7 @@ import (
"github.com/jetsetilly/gopher2600/recorder"
)
const playbackEntryID = "playback"
const playbackEntryType = "playback"
const (
playbackFieldScript int = iota
@ -68,19 +68,9 @@ func deserialisePlaybackEntry(fields database.SerialisedEntry) (database.Entry,
return reg, nil
}
// ID implements the database.Entry interface.
func (reg PlaybackRegression) ID() string {
return playbackEntryID
}
// String implements the database.Entry interface.
func (reg PlaybackRegression) String() string {
s := strings.Builder{}
s.WriteString(fmt.Sprintf("[%s] %s", reg.ID(), filepath.Base(reg.Script)))
if reg.Notes != "" {
s.WriteString(fmt.Sprintf(" [%s]", reg.Notes))
}
return s.String()
// EntryType implements the database.Entry interface.
func (reg PlaybackRegression) EntryType() string {
return playbackEntryType
}
// Serialise implements the database.Entry interface.
@ -101,6 +91,16 @@ func (reg PlaybackRegression) CleanUp() error {
return err
}
// String implements the regression.Regressor interface.
func (reg PlaybackRegression) String() string {
s := strings.Builder{}
s.WriteString(fmt.Sprintf("[%s] %s", reg.EntryType(), filepath.Base(reg.Script)))
if reg.Notes != "" {
s.WriteString(fmt.Sprintf(" [%s]", reg.Notes))
}
return s.String()
}
// regress implements the regression.Regressor interface.
func (reg *PlaybackRegression) regress(newRegression bool, output io.Writer, msg string) (_ bool, _ string, rerr error) {
output.Write([]byte(msg))

View file

@ -40,6 +40,11 @@ const regressionScripts = "scripts"
type Regressor interface {
database.Entry
// String should return information about the entry in a human readable
// format. by contrast, machine readable representation is returned by the
// Serialise function
String() string
// perform the regression test for the regression type. the newRegression
// flag is for convenience really (or "logical binding", as the structured
// programmers would have it)
@ -54,15 +59,15 @@ type Regressor interface {
// when starting a database session we need to register what entries we will
// find in the database.
func initDBSession(db *database.Session) error {
if err := db.RegisterEntryType(videoEntryID, deserialiseVideoEntry); err != nil {
if err := db.RegisterEntryType(videoEntryType, deserialiseVideoEntry); err != nil {
return err
}
if err := db.RegisterEntryType(playbackEntryID, deserialisePlaybackEntry); err != nil {
if err := db.RegisterEntryType(playbackEntryType, deserialisePlaybackEntry); err != nil {
return err
}
if err := db.RegisterEntryType(logEntryID, deserialiseLogEntry); err != nil {
if err := db.RegisterEntryType(logEntryType, deserialiseLogEntry); err != nil {
return err
}
@ -87,7 +92,11 @@ func RegressList(output io.Writer) error {
defer db.EndSession(false)
return db.ForEach(func(key int, e database.Entry) error {
output.Write([]byte(fmt.Sprintf("%03d %s\n", key, e.String())))
r, ok := e.(Regressor)
if !ok {
return fmt.Errorf("regression: list: %s does not implement the Regressor interfrace", e.EntryType())
}
output.Write([]byte(fmt.Sprintf("%03d %s\n", key, r.String())))
return nil
})
}
@ -246,7 +255,7 @@ func RegressCleanup(output io.Writer, confirmation io.Reader) error {
// no support required
default:
return fmt.Errorf("not supported (%s)", reg.ID())
return fmt.Errorf("not supported (%s)", reg.EntryType())
}
return nil

View file

@ -33,7 +33,7 @@ import (
"github.com/jetsetilly/gopher2600/setup"
)
const videoEntryID = "video"
const videoEntryType = "video"
const (
videoFieldCartName int = iota
@ -120,36 +120,9 @@ func deserialiseVideoEntry(fields database.SerialisedEntry) (database.Entry, err
return reg, nil
}
// ID implements the database.Entry interface.
func (reg VideoRegression) ID() string {
return videoEntryID
}
// String implements the database.Entry interface.
func (reg VideoRegression) String() string {
s := strings.Builder{}
state := ""
switch reg.State {
case StateNone:
state = ""
case StateTV:
state = " [TV state]"
case StatePorts:
state = " [ports state]"
case StateTimer:
state = " [timer state]"
case StateCPU:
state = " [cpu state]"
default:
state = " [with state]"
}
s.WriteString(fmt.Sprintf("[%s] %s [%s] frames=%d%s", reg.ID(), reg.CartLoad.ShortName(), reg.TVtype, reg.NumFrames, state))
if reg.Notes != "" {
s.WriteString(fmt.Sprintf(" [%s]", reg.Notes))
}
return s.String()
// EntryType implements the database.Entry interface.
func (reg VideoRegression) EntryType() string {
return videoEntryType
}
// Serialise implements the database.Entry interface.
@ -177,6 +150,33 @@ func (reg VideoRegression) CleanUp() error {
return err
}
// String implements the regression.Regressor interface
func (reg VideoRegression) String() string {
s := strings.Builder{}
state := ""
switch reg.State {
case StateNone:
state = ""
case StateTV:
state = " [TV state]"
case StatePorts:
state = " [ports state]"
case StateTimer:
state = " [timer state]"
case StateCPU:
state = " [cpu state]"
default:
state = " [with state]"
}
s.WriteString(fmt.Sprintf("[%s] %s [%s] frames=%d%s", reg.EntryType(), reg.CartLoad.ShortName(), reg.TVtype, reg.NumFrames, state))
if reg.Notes != "" {
s.WriteString(fmt.Sprintf(" [%s]", reg.Notes))
}
return s.String()
}
// regress implements the regression.Regressor interface.
func (reg *VideoRegression) regress(newRegression bool, output io.Writer, msg string) (_ bool, _ string, rerr error) {
output.Write([]byte(msg))

View file

@ -25,10 +25,11 @@ import (
"github.com/jetsetilly/gopher2600/hardware/riot/ports/plugging"
)
const panelSetupID = "panel"
const panelSetupEntryType = "panel"
const (
panelSetupFieldCartHash int = iota
panelSetupFieldCartName
panelSetupFieldP0
panelSetupFieldP1
panelSetupFieldCol
@ -39,6 +40,7 @@ const (
// PanelSetup is used to adjust the VCS's front panel.
type PanelSetup struct {
cartHash string
cartName string
p0 bool
p1 bool
@ -61,6 +63,7 @@ func deserialisePanelSetupEntry(fields database.SerialisedEntry) (database.Entry
var err error
set.cartHash = fields[panelSetupFieldCartHash]
set.cartName = fields[panelSetupFieldCartName]
if set.p0, err = strconv.ParseBool(fields[panelSetupFieldP0]); err != nil {
return nil, fmt.Errorf("panel: invalid player 0 setting")
@ -79,20 +82,16 @@ func deserialisePanelSetupEntry(fields database.SerialisedEntry) (database.Entry
return set, nil
}
// ID implements the database.Entry interface.
func (set PanelSetup) ID() string {
return panelSetupID
}
// String implements the database.Entry interface.
func (set PanelSetup) String() string {
return fmt.Sprintf("%s, p0=%v, p1=%v, col=%v\n", set.cartHash, set.p0, set.p1, set.col)
// EntryType implements the database.Entry interface.
func (set PanelSetup) EntryType() string {
return panelSetupEntryType
}
// Serialise implements the database.Entry interface.
func (set *PanelSetup) Serialise() (database.SerialisedEntry, error) {
return database.SerialisedEntry{
set.cartHash,
set.cartName,
strconv.FormatBool(set.p0),
strconv.FormatBool(set.p1),
strconv.FormatBool(set.col),
@ -113,42 +112,42 @@ func (set PanelSetup) matchCartHash(hash string) bool {
}
// apply implements setupEntry interface.
func (set PanelSetup) apply(vcs *hardware.VCS) error {
func (set PanelSetup) apply(vcs *hardware.VCS) (string, error) {
if set.p0 {
inp := ports.InputEvent{Port: plugging.PortPanel, Ev: ports.PanelSetPlayer0Pro, D: true}
if _, err := vcs.Input.HandleInputEvent(inp); err != nil {
return err
return "", err
}
} else {
inp := ports.InputEvent{Port: plugging.PortPanel, Ev: ports.PanelSetPlayer0Pro, D: false}
if _, err := vcs.Input.HandleInputEvent(inp); err != nil {
return err
return "", err
}
}
if set.p1 {
inp := ports.InputEvent{Port: plugging.PortPanel, Ev: ports.PanelSetPlayer1Pro, D: true}
if _, err := vcs.Input.HandleInputEvent(inp); err != nil {
return err
return "", err
}
} else {
inp := ports.InputEvent{Port: plugging.PortPanel, Ev: ports.PanelSetPlayer1Pro, D: false}
if _, err := vcs.Input.HandleInputEvent(inp); err != nil {
return err
return "", err
}
}
if set.col {
inp := ports.InputEvent{Port: plugging.PortPanel, Ev: ports.PanelSetColor, D: true}
if _, err := vcs.Input.HandleInputEvent(inp); err != nil {
return err
return "", err
}
} else {
inp := ports.InputEvent{Port: plugging.PortPanel, Ev: ports.PanelSetColor, D: false}
if _, err := vcs.Input.HandleInputEvent(inp); err != nil {
return err
return "", err
}
}
return nil
return fmt.Sprintf("panel preset: %s: %s", set.cartName, set.notes), nil
}

View file

@ -23,11 +23,11 @@ import (
"github.com/jetsetilly/gopher2600/patch"
)
const patchID = "patch"
const patchEntryType = "patch"
const (
patchFieldCartHash int = iota
patchFieldPatchFile
patchFieldCartName
patchFieldNotes
numPatchFields
)
@ -35,9 +35,9 @@ const (
// Patch is used to patch cartridge memory after cartridge has been
// attached/loaded.
type Patch struct {
cartHash string
patchFile string
notes string
cartHash string
cartName string
notes string
}
func deserialisePatchEntry(fields database.SerialisedEntry) (database.Entry, error) {
@ -52,30 +52,24 @@ func deserialisePatchEntry(fields database.SerialisedEntry) (database.Entry, err
}
set.cartHash = fields[patchFieldCartHash]
set.patchFile = fields[patchFieldPatchFile]
set.cartName = fields[patchFieldCartName]
set.notes = fields[patchFieldNotes]
return set, nil
}
// ID implements the database.Entry interface.
func (set Patch) ID() string {
return patchID
}
// String implements the database.Entry interface.
func (set Patch) String() string {
return fmt.Sprintf("%s, %s", set.cartHash, set.patchFile)
// EntryType implements the database.Entry interface.
func (set Patch) EntryType() string {
return patchEntryType
}
// Serialise implements the database.Entry interface.
func (set *Patch) Serialise() (database.SerialisedEntry, error) {
return database.SerialisedEntry{
set.cartHash,
set.patchFile,
set.notes,
},
nil
set.cartHash,
set.cartName,
set.notes,
}, nil
}
// CleanUp implements the database.Entry interface.
@ -90,10 +84,10 @@ func (set Patch) matchCartHash(hash string) bool {
}
// apply implements setupEntry interface.
func (set Patch) apply(vcs *hardware.VCS) error {
_, err := patch.CartridgeMemory(vcs.Mem.Cart, set.patchFile)
func (set Patch) apply(vcs *hardware.VCS) (string, error) {
_, err := patch.CartridgeMemory(vcs.Mem.Cart, set.cartHash)
if err != nil {
return fmt.Errorf("patch: %w", err)
return "", fmt.Errorf("patch: %w", err)
}
return nil
return fmt.Sprintf("patching cartridge: %s: %s", set.cartName, set.notes), nil
}

View file

@ -22,6 +22,7 @@ import (
"github.com/jetsetilly/gopher2600/cartridgeloader"
"github.com/jetsetilly/gopher2600/database"
"github.com/jetsetilly/gopher2600/hardware"
"github.com/jetsetilly/gopher2600/logger"
"github.com/jetsetilly/gopher2600/resources"
)
@ -39,23 +40,24 @@ type setupEntry interface {
// match the hash stored in the database with the user supplied hash
matchCartHash(hash string) bool
// apply changes indicated in the entry to the VCS
apply(vcs *hardware.VCS) error
// apply changes indicated in the entry to the VCS. returned string value
// will be used for log message
apply(vcs *hardware.VCS) (string, error)
}
// when starting a database session we must add the details of the entry types
// that will be found in the database.
func initDBSession(db *database.Session) error {
// add entry types
if err := db.RegisterEntryType(panelSetupID, deserialisePanelSetupEntry); err != nil {
if err := db.RegisterEntryType(panelSetupEntryType, deserialisePanelSetupEntry); err != nil {
return err
}
if err := db.RegisterEntryType(patchID, deserialisePatchEntry); err != nil {
if err := db.RegisterEntryType(patchEntryType, deserialisePatchEntry); err != nil {
return err
}
if err := db.RegisterEntryType(televisionID, deserialiseTelevisionEntry); err != nil {
if err := db.RegisterEntryType(televisionEntryType, deserialiseTelevisionEntry); err != nil {
return err
}
@ -96,10 +98,11 @@ func AttachCartridge(vcs *hardware.VCS, cartload cartridgeloader.Loader, resetVC
}
if set.matchCartHash(vcs.Mem.Cart.Hash) {
err := set.apply(vcs)
msg, err := set.apply(vcs)
if err != nil {
return err
}
logger.Logf("setup", msg)
}
return nil

View file

@ -22,12 +22,12 @@ import (
"github.com/jetsetilly/gopher2600/hardware"
)
const televisionID = "tv"
const televisionEntryType = "tv"
const (
televisionFieldCartHash int = iota
televisionFieldCartName
televisionFieldSpec
televisionFieldNotes
numtelevisionFields
)
@ -35,8 +35,8 @@ const (
// attached/loaded.
type television struct {
cartHash string
cartName string
spec string
notes string
}
func deserialiseTelevisionEntry(fields database.SerialisedEntry) (database.Entry, error) {
@ -51,28 +51,23 @@ func deserialiseTelevisionEntry(fields database.SerialisedEntry) (database.Entry
}
set.cartHash = fields[televisionFieldCartHash]
set.cartName = fields[televisionFieldCartName]
set.spec = fields[televisionFieldSpec]
set.notes = fields[televisionFieldNotes]
return set, nil
}
// ID implements the database.Entry interface.
func (set television) ID() string {
return televisionID
}
// String implements the database.Entry interface.
func (set television) String() string {
return fmt.Sprintf("%s, %s", set.cartHash, set.spec)
// EntryType implements the database.Entry interface.
func (set television) EntryType() string {
return televisionEntryType
}
// Serialise implements the database.Entry interface.
func (set *television) Serialise() (database.SerialisedEntry, error) {
return database.SerialisedEntry{
set.cartHash,
set.cartName,
set.spec,
set.notes,
}, nil
}
@ -88,7 +83,7 @@ func (set television) matchCartHash(hash string) bool {
}
// apply implements setupEntry interface.
func (set television) apply(vcs *hardware.VCS) error {
func (set television) apply(vcs *hardware.VCS) (string, error) {
// because the apply function is run after attaching the cartridge to the
// VCS, any setup entries will take precedence over any spec in the
// cartridge filename.
@ -97,5 +92,10 @@ func (set television) apply(vcs *hardware.VCS) error {
// original spec request is AUTO. In other words, a setup entry will not
// take precedence over an explicit startup option.
return vcs.TV.SetSpecConditional(set.spec)
err := vcs.TV.SetSpecConditional(set.spec)
if err != nil {
return "", err
}
return fmt.Sprintf("forcing %s mode: %s", set.spec, set.cartName), nil
}