-
-
Save i39/72687d2b4c806e3ad223fa542f1a57ba to your computer and use it in GitHub Desktop.
Golang/MQTT service to monitor webcam snapshots and send status
This file contains hidden or 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
| package main | |
| import ( | |
| "encoding/json" | |
| "io" | |
| "io/ioutil" | |
| "os" | |
| "sort" | |
| "time" | |
| log "github.com/Sirupsen/logrus" | |
| MQTT "github.com/eclipse/paho.mqtt.golang" | |
| ) | |
| type Cameras struct { | |
| Cameras []Camera | |
| } | |
| type Camera struct { | |
| CameraID string `json:"camera_id"` | |
| CameraName string `json:"camera_name"` | |
| ImageDir string `json:"image_dir"` | |
| LastImage string `json:"last_image"` | |
| Online bool `json:"online"` | |
| StatusCode string `json:"status_code"` | |
| Timestamp time.Time `json:"timestamp"` | |
| ErrorCount int `json:"error_count"` | |
| } | |
| type Data struct { | |
| Online bool | |
| Timestamp time.Time | |
| } | |
| // Create struct to be used by our custom sort function. | |
| type ByTime []os.FileInfo | |
| // Len, Swap and Less functions for sorting files by ModTime. | |
| func (f ByTime) Len() int { return len(f) } | |
| func (f ByTime) Swap(i, j int) { f[i], f[j] = f[j], f[i] } | |
| func (f ByTime) Less(i, j int) bool { return f[i].ModTime().After(f[j].ModTime()) } | |
| var ( | |
| image_dirs = []string{"/tmp/hab_camera-1", "/tmp/hab_camera-2"} | |
| mqttBroker = "tcp://HOSTNAME:8883" | |
| mqttClientId = "Camera-Monitor" | |
| errorThreshold = 2 | |
| snapshotCount = 0 | |
| ) | |
| func copyFile(src, dst string) (err error) { | |
| in, err := os.Open(src) | |
| if err != nil { | |
| return | |
| } | |
| defer in.Close() | |
| out, err := os.Create(dst) | |
| if err != nil { | |
| return | |
| } | |
| defer func() { | |
| cerr := out.Close() | |
| if err == nil { | |
| err = cerr | |
| } | |
| }() | |
| if _, err = io.Copy(out, in); err != nil { | |
| return | |
| } | |
| err = out.Sync() | |
| return | |
| } | |
| func main() { | |
| cameras := Cameras{ | |
| Cameras: []Camera{{"hab_camera-1", "Exterior (South)", "/tmp/hab_camera-1", "", false, "", time.Now(), 0}, {"hab_camera-2", "Exterior IR (South)", "/tmp/hab_camera-2", "", false, "", time.Now(), 0}}, | |
| } | |
| opts := MQTT.NewClientOptions() | |
| opts.AddBroker(mqttBroker) | |
| opts.SetClientID(mqttClientId) | |
| c := MQTT.NewClient(opts) | |
| if token := c.Connect(); token.Wait() && token.Error() != nil { | |
| log.Warn("Could not connect to: ", mqttBroker) | |
| } | |
| for index, cam := range cameras.Cameras { | |
| ticker := time.NewTicker(time.Millisecond * 3000) | |
| go func(index int, cam Camera) { | |
| for timestamp := range ticker.C { | |
| files, err := ioutil.ReadDir(cam.ImageDir) | |
| if err != nil { | |
| log.Fatal(err) | |
| } | |
| // Sort files by time if there happens to be more than one. | |
| if len(files) > 1 { | |
| sort.Sort(ByTime(files)) | |
| } | |
| if len(files) > 0 { | |
| // Check if the last image has been updated. | |
| if cameras.Cameras[index].LastImage == files[0].Name() { | |
| // Allow a threshold for unchanged images due to camera software performance. | |
| if cameras.Cameras[index].ErrorCount >= errorThreshold { | |
| cameras.Cameras[index].Online = false | |
| cameras.Cameras[index].StatusCode = "No new image found." | |
| } else { | |
| cameras.Cameras[index].ErrorCount++ | |
| } | |
| } else { | |
| cameras.Cameras[index].Online = true | |
| cameras.Cameras[index].ErrorCount = 0 | |
| cameras.Cameras[index].StatusCode = "" | |
| if snapshotCount == 0 || snapshotCount > 4 { | |
| cerr := copyFile(cam.ImageDir+"/"+files[0].Name(), cam.ImageDir+"/out.jpg") | |
| if cerr != nil { | |
| log.Warn("File copy failed") | |
| log.Warn(cerr) | |
| } | |
| snapshotCount = 0 | |
| } | |
| snapshotCount++ | |
| } | |
| cameras.Cameras[index].LastImage = files[0].Name() | |
| cameras.Cameras[index].Timestamp = timestamp | |
| } | |
| // Build message data packet and json.Marshal | |
| var data = &Data{} | |
| data.Timestamp = timestamp | |
| data.Online = cameras.Cameras[index].Online | |
| messageData, _ := json.Marshal(data) | |
| // Send MQTT message | |
| token := c.Publish("test/cameras/"+cam.CameraID, 0, true, messageData) | |
| token.Wait() | |
| } | |
| }(index, cam) | |
| } | |
| select {} | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment