-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds MQTT digital output writer callbacks
Adds a corresponding digital output and corresponding writer abstraction. On initializing the handler, a callback is added to trigger a write based on a specific payload.
- Loading branch information
Showing
6 changed files
with
247 additions
and
62 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
package unipitt | ||
|
||
import ( | ||
"log" | ||
"os" | ||
"path" | ||
) | ||
|
||
const ( | ||
// DoFilename to check for | ||
DoFilename = "do_value" | ||
// DoTrueValue digital output true value to write | ||
DoTrueValue = "1\n" | ||
// DoFalseValue digital output false value to write | ||
DoFalseValue = "0\n" | ||
// DoFolderRegex regular expression used for finding folders which contain digital output | ||
DoFolderRegex = "do_[0-9]_[0-9]{2}" | ||
) | ||
|
||
// DigitalOutput represents the digital outputs of the unipi board | ||
type DigitalOutput interface { | ||
Update(bool) error | ||
} | ||
|
||
// DigitalOutputWriter implements the digital output specifically for writing outputs to files | ||
type DigitalOutputWriter struct { | ||
Topic string | ||
Path string | ||
} | ||
|
||
// Update writes the updated value to the digital output | ||
func (d *DigitalOutputWriter) Update(value bool) (err error) { | ||
f, err := os.Create(path.Join(d.Path, DoFilename)) | ||
defer f.Close() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if value { | ||
_, err = f.WriteString(DoTrueValue) | ||
} else { | ||
_, err = f.WriteString(DoFalseValue) | ||
} | ||
if err == nil { | ||
log.Printf("Update value of digital output %s to %t\n", d.Topic, value) | ||
} | ||
return err | ||
} | ||
|
||
// NewDigitalOutputWriter creates a new digital output writer instance from a a given matching folder | ||
func NewDigitalOutputWriter(folder string) (d *DigitalOutputWriter) { | ||
// Read topic as the trailing folder path | ||
_, topic := path.Split(folder) | ||
return &DigitalOutputWriter{Topic: topic, Path: folder} | ||
} | ||
|
||
// FindDigitalOutputWriters generates the output writes from a given path | ||
func FindDigitalOutputWriters(root string) (writerMap map[string]DigitalOutputWriter, err error) { | ||
paths, err := findPathsByRegex(root, DoFolderRegex) | ||
if err != nil { | ||
log.Println(err) | ||
return | ||
} | ||
log.Printf("Found %d matching digital output paths\n", len(paths)) | ||
writerMap = make(map[string]DigitalOutputWriter) | ||
var d *DigitalOutputWriter | ||
for _, path := range paths { | ||
d = NewDigitalOutputWriter(path) | ||
writerMap[d.Topic] = *d | ||
} | ||
return | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
package unipitt | ||
|
||
import ( | ||
"bytes" | ||
"io/ioutil" | ||
"os" | ||
"path/filepath" | ||
"testing" | ||
) | ||
|
||
func TestNewDigitalOutputWriter(t *testing.T) { | ||
folder := "foo/bar" | ||
d := NewDigitalOutputWriter(folder) | ||
if d.Topic != "bar" { | ||
t.Fatalf("Expected topic %s, got %s\n", "bar", d.Topic) | ||
} | ||
if d.Path != folder { | ||
t.Fatalf("Expected path %s, got %s\n", folder, d.Path) | ||
} | ||
} | ||
|
||
func TestUpdateDigitalOutputWriter(t *testing.T) { | ||
folder := "do_2_01" | ||
|
||
// Create temporary folder, only if it does not exist already | ||
sysFsRoot, err := ioutil.TempDir("", "unipitt") | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
defer os.RemoveAll(sysFsRoot) | ||
doFolder := filepath.Join(sysFsRoot, folder) | ||
if _, pathErr := os.Stat(doFolder); pathErr != nil { | ||
err := os.Mkdir(doFolder, os.ModePerm) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
} | ||
fname := filepath.Join(doFolder, DoFilename) | ||
|
||
d := NewDigitalOutputWriter(doFolder) | ||
|
||
cases := []struct { | ||
Given bool | ||
Expected string | ||
}{ | ||
{Given: false, Expected: DoFalseValue}, | ||
{Given: true, Expected: DoTrueValue}, | ||
} | ||
for _, testCase := range cases { | ||
err := d.Update(testCase.Given) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
f, err := os.Open(fname) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
b := make([]byte, 2) | ||
f.Read(b) | ||
if !bytes.Equal(b, []byte(testCase.Expected)) { | ||
t.Fatalf("Expected %s, got %s\n", testCase.Expected, string(b)) | ||
} | ||
} | ||
} | ||
|
||
func TestUpdateDigitalOutputWriterBogusFolder(t *testing.T) { | ||
folder := "/foo/bar" | ||
d := NewDigitalOutputWriter(folder) | ||
err := d.Update(false) | ||
if err == nil { | ||
t.Fatal("Expected an error, found none") | ||
} | ||
} | ||
|
||
func TestFindDigitalOutputWriters(t *testing.T) { | ||
folder := "do_2_01" | ||
|
||
// Create temporary folder, only if it does not exist already | ||
sysFsRoot, err := ioutil.TempDir("", "unipitt") | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
defer os.RemoveAll(sysFsRoot) | ||
doFolder := filepath.Join(sysFsRoot, folder) | ||
if _, pathErr := os.Stat(doFolder); pathErr != nil { | ||
err := os.Mkdir(doFolder, os.ModePerm) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
} | ||
writerMap, err := FindDigitalOutputWriters(sysFsRoot) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
if writer, ok := writerMap[folder]; !ok { | ||
t.Fatalf("Expected to find writer with topic %s in map for name %s\n", writer.Topic, folder) | ||
} | ||
|
||
} | ||
|
||
func TestFindDigitalOutputWritersNoFolder(t *testing.T) { | ||
folder := "foo" | ||
|
||
_, err := FindDigitalOutputWriters(folder) | ||
if err == nil { | ||
t.Fatal("Expected no mapping to be found") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package unipitt | ||
|
||
import ( | ||
"os" | ||
"path/filepath" | ||
"regexp" | ||
) | ||
|
||
// findPathsByRegex find matching paths where a regex matches (on the name of a given oflder, not full path) | ||
func findPathsByRegex(root string, pattern string) (paths []string, err error) { | ||
regex, err := regexp.Compile(pattern) | ||
// Walk the folder structure | ||
err = filepath.Walk(root, | ||
func(path string, info os.FileInfo, err error) error { | ||
if err != nil { | ||
return err | ||
} | ||
if regex.MatchString(info.Name()) { | ||
paths = append(paths, path) | ||
} | ||
return err | ||
}) | ||
return | ||
} |