diff --git a/cmd/audio-player/looper/Makefile b/cmd/audio-player/looper/Makefile index 8471c5f6..ef86a490 100644 --- a/cmd/audio-player/looper/Makefile +++ b/cmd/audio-player/looper/Makefile @@ -8,10 +8,10 @@ PATH := /usr/local/go/bin:$(PATH) .SILENT:clean install: as_root copy_files build - @echo "Install complete" + @echo "Install complete" install_hard: as_root hard_copy_files build - @echo "Install complete" + @echo "Install complete" as_root: ifneq ($(USER),root) @@ -24,6 +24,11 @@ copy_files: else \ cp rc.local /etc; \ fi + if [ -f /etc/netsender.conf ] ; then \ + echo "/etc/netsender.conf left unmodified" ; \ + else \ + cp netsender.conf /etc; \ + fi hard_copy_files: if [ -f /etc/rc.local ] ; then \ @@ -31,6 +36,11 @@ hard_copy_files: cp /etc/rc.local /etc/rc.local.bak ; \ fi cp -f rc.local /etc + if [ -f /etc/netsender.conf ] ; then \ + echo "Backed up netsender.conf to /etc/netsender.conf.bak" ; \ + cp /etc/netsender.conf /etc/netsender.conf.bak ; \ + fi + cp -f netsender.conf /etc build: if grep -q 'Raspberry Pi 3' '/proc/device-tree/model'; then \ diff --git a/cmd/audio-player/looper/README.md b/cmd/audio-player/looper/README.md index 0157f05c..33225c7b 100644 --- a/cmd/audio-player/looper/README.md +++ b/cmd/audio-player/looper/README.md @@ -9,6 +9,8 @@ # Pi Setup: 1) sudo make install_hard - 2) systemctl enable rc-local - 3) sudo systemctl start rc-local.service - 4) To check that install steps were successful, restart device and confirm that sound plays. + 2) modify ma field in /etc/netsender.conf file to be mac of device + 3) modify audio file flag in ./looper execution command in /etc/rc.local to be path of file we'd like to play + 4) systemctl enable rc-local + 5) sudo systemctl start rc-local.service + 6) To check that install steps were successful, restart device and confirm that sound plays. diff --git a/cmd/audio-player/looper/main.go b/cmd/audio-player/looper/main.go index 8f1cbdda..358ed00d 100644 --- a/cmd/audio-player/looper/main.go +++ b/cmd/audio-player/looper/main.go @@ -33,7 +33,12 @@ import ( "fmt" "io" "os/exec" + "strconv" + "time" + "bitbucket.org/ausocean/iot/pi/netlogger" + "bitbucket.org/ausocean/iot/pi/netsender" + "bitbucket.org/ausocean/iot/pi/sds" "bitbucket.org/ausocean/utils/logger" "gopkg.in/natefinch/lumberjack.v2" ) @@ -48,6 +53,18 @@ const ( logSuppress = true ) +// Netsender related consts. +const ( + netSendRetryTime = 5 * time.Second + defaultSleepTime = 60 // Seconds +) + +// Looper modes. +const ( + modeNormal = "Normal" + modePaused = "Paused" +) + func main() { filePtr := flag.String("path", "", "Path to sound file we wish to play.") flag.Parse() @@ -60,11 +77,23 @@ func main() { MaxAge: logMaxAge, } - // Create logger that we call methods on to log. - log := logger.New(logVerbosity, fileLog, logSuppress) + // Create a netlogger to deal with logging to cloud. + nl := netlogger.New() + + // Create logger that we call methods on to l. + l := logger.New(logVerbosity, io.MultiWriter(fileLog, nl), logSuppress) // Call initialisation code that is specific to the platform (pi 0 or 3). - initCommand(log) + initCommand(l) + + // Create netsender client. + ns, err := netsender.New(l, nil, readPin(), nil) + if err != nil { + l.Log(logger.Fatal, "could not initialise netsender client", "error", err) + } + + // This routine will deal with things that need to happen with the netsender client. + go run(ns, l, nl) // Repeatedly play audio file. var numPlays int @@ -75,28 +104,28 @@ func main() { // stdout and stderr. outPipe, err := cmd.StdoutPipe() if err != nil { - log.Log(logger.Error, "failed to pipe stdout", "error", err) + l.Log(logger.Error, "failed to pipe stdout", "error", err) } errPipe, err := cmd.StderrPipe() if err != nil { - log.Log(logger.Error, "failed to pipe stderr", "error", err) + l.Log(logger.Error, "failed to pipe stderr", "error", err) } // Start playback of the audio file. err = cmd.Start() if err != nil { - log.Log(logger.Error, "start failed", "error", err.Error()) + l.Log(logger.Error, "start failed", "error", err.Error()) continue } numPlays++ - log.Log(logger.Debug, "playing audio", "numPlays", numPlays) + l.Log(logger.Debug, "playing audio", "numPlays", numPlays) // Copy any std out to a buffer for logging. var outBuff bytes.Buffer go func() { _, err = io.Copy(&outBuff, outPipe) if err != nil { - log.Log(logger.Error, "failed to copy out pipe", "error", err) + l.Log(logger.Error, "failed to copy out pipe", "error", err) } }() @@ -105,28 +134,111 @@ func main() { go func() { _, err = io.Copy(&errBuff, errPipe) if err != nil { - log.Log(logger.Error, "failed to copy error pipe", "error", err) + l.Log(logger.Error, "failed to copy error pipe", "error", err) } }() // Wait for playback to complete. err = cmd.Wait() if err != nil { - log.Log(logger.Error, "failed to wait for execution finish", "error", err.Error()) + l.Log(logger.Error, "failed to wait for execution finish", "error", err.Error()) } - log.Log(logger.Debug, "stdout received", "stdout", string(outBuff.Bytes())) + l.Log(logger.Debug, "stdout received", "stdout", string(outBuff.Bytes())) - // If there was any errors on stderr, log them. + // If there was any errors on stderr, l them. if errBuff.Len() != 0 { - log.Log(logger.Error, "errors from stderr", "stderr", string(errBuff.Bytes())) + l.Log(logger.Error, "errors from stderr", "stderr", string(errBuff.Bytes())) } } } -func checkPath(cmd string, log *logger.Logger) { +// run is a routine to deal with netsender related tasks. +func run(ns *netsender.Sender, l *logger.Logger, nl *netlogger.Logger) { + var vs int + for { + err := ns.Run() + if err != nil { + l.Log(logger.Warning, "Run Failed. Retrying...", "error", err) + time.Sleep(netSendRetryTime) + continue + } + + err = nl.Send(ns) + if err != nil { + l.Log(logger.Warning, "Logs could not be sent", "error", err.Error()) + } + + // If var sum hasn't changed we skip rest of loop. + newVs := ns.VarSum() + if vs == newVs { + sleep(ns, l) + continue + } + vs = newVs + + vars, err := ns.Vars() + if err != nil { + l.Log(logger.Error, "netSender failed to get vars", "error", err) + time.Sleep(netSendRetryTime) + continue + } + + // Configure looper based on vars. + err = update(vars) + if err != nil { + l.Log(logger.Warning, "couldn't update with new vars", "error", err) + sleep(ns, l) + continue + } + + // TODO: consider handling of any modes ? We'd likely have paused and + // normal for the audio looper. + switch ns.Mode() { + case modePaused: + case modeNormal: + } + } +} + +// checkPath wraps the use of lookPath to check the existence of executables +// that will be used by the audio looper. +func checkPath(cmd string, l *logger.Logger) { path, err := exec.LookPath(cmd) if err != nil { - log.Log(logger.Fatal, fmt.Sprintf("couldn't find %s", cmd), "error", err) + l.Log(logger.Fatal, fmt.Sprintf("couldn't find %s", cmd), "error", err) } - log.Log(logger.Debug, fmt.Sprintf("found %s", cmd), "path", path) + l.Log(logger.Debug, fmt.Sprintf("found %s", cmd), "path", path) +} + +// sleep uses a delay to halt the program based on the monitoring period +// netsender parameter (mp) defined in the netsender.conf config. +func sleep(ns *netsender.Sender, l *logger.Logger) { + t, err := strconv.Atoi(ns.Param("mp")) + if err != nil { + l.Log(logger.Error, "could not get sleep time, using default", "error", err) + t = defaultSleepTime + } + time.Sleep(time.Duration(t) * time.Second) +} + +// readPin provides a callback function of consistent signature for use by +// netsender to retrieve software defined pin values. +func readPin() func(pin *netsender.Pin) error { + return func(pin *netsender.Pin) error { + switch { + case pin.Name == "X23": + pin.Value = -1 + case pin.Name[0] == 'X': + return sds.ReadSystem(pin) + default: + pin.Value = -1 + } + return nil + } +} + +// update is currently a stub, but might used to update looper related params +// in future. +func update(v map[string]string) error { + return nil } diff --git a/cmd/audio-player/looper/netsender.conf b/cmd/audio-player/looper/netsender.conf new file mode 100644 index 00000000..8d9e6a04 --- /dev/null +++ b/cmd/audio-player/looper/netsender.conf @@ -0,0 +1,10 @@ +ma 00:00:00:00:00:01 +dk 0 +wi +ip T0 +op +mp 60 +ap 0 +tg +hw +sh vidgrind.appspot.com