diff options
author | xengineering <mail2xengineering@protonmail.com> | 2021-06-19 11:08:57 +0200 |
---|---|---|
committer | xengineering <mail2xengineering@protonmail.com> | 2021-06-20 13:57:58 +0200 |
commit | 963b86e49fad07a3722646b9d7f4b1c09d199eb1 (patch) | |
tree | 5ccb6b66bf9ce1c53e3e97fa7057e44b66512295 /src | |
parent | 198023be0f62ef6bb35cdd5ae78f90ab0a6f187e (diff) | |
download | birdscan-963b86e49fad07a3722646b9d7f4b1c09d199eb1.tar birdscan-963b86e49fad07a3722646b9d7f4b1c09d199eb1.tar.zst birdscan-963b86e49fad07a3722646b9d7f4b1c09d199eb1.zip |
Implement continuous Python Daemon
Diffstat (limited to 'src')
-rw-r--r-- | src/camera.go | 63 | ||||
-rw-r--r-- | src/main.go | 38 | ||||
-rw-r--r-- | src/subprocess.go | 102 | ||||
-rw-r--r-- | src/web.go | 2 |
4 files changed, 134 insertions, 71 deletions
diff --git a/src/camera.go b/src/camera.go index 519cc65..fe1140e 100644 --- a/src/camera.go +++ b/src/camera.go @@ -2,14 +2,6 @@ package main -import ( - "log" - "os/exec" - "os" - "path/filepath" - "bufio" -) - type Camera struct { statemachine Machine } @@ -44,63 +36,10 @@ func NewCamera() Camera { func runCameraHooks(last string, next string, m *Machine) { if last == "idle" && next == "single_picture" { - go singlePicture(m) + ipc.WriteLineTo("picamera", "single_picture\n") } } -func singlePicture(m *Machine) { - - // create command - var cmd *exec.Cmd - if !config.Flag.Debug { - cmd = exec.Command("/usr/bin/python3", "/usr/lib/python3.9/site-packages/birdscan/") - } else { // debug mode - pwd,err := os.Getwd() - if err != nil { - log.Fatal(err) - } - repoDir := filepath.Dir(pwd) - log.Printf("Repository path is assumed to be = '%s'", repoDir) - pythonPackage := repoDir + "/python/birdscan" - cmd = exec.Command("/usr/bin/python3", pythonPackage, "--debug") - } - - // connect stdout of python process - stdout,err := cmd.StdoutPipe() - if err != nil { - log.Print(err) - } - defer stdout.Close() - - // run command - err = cmd.Start() - if err != nil { - log.Print(err) - } - - scanner := bufio.NewScanner(stdout) - for scanner.Scan() { - text := scanner.Text() - log.Printf("Python returned '%s'", text) - if text == "ok" { - break - } - } - - err = cmd.Wait() // wait until command execution and io is complete - if err != nil { - log.Print(err) - } - - // process result - m.SendEvent("single_picture_taken") -} - func (cam *Camera) run() { cam.statemachine.Run() } - -// read until '\n' -func readLine(buff *[]byte, ) { - -} diff --git a/src/main.go b/src/main.go index 3218df9..dcb201e 100644 --- a/src/main.go +++ b/src/main.go @@ -4,11 +4,19 @@ package main import ( "log" + "os" + "os/signal" + "syscall" ) var ( config RuntimeConfig camera Camera + ipc IpcServer +) + +const ( + IPC_SOCKET = "/tmp/birdscan.sock" // FIXME ) func main() { @@ -19,13 +27,27 @@ func main() { // parse flags and read config config = GetRuntimeConfig() - // create camera - camera = NewCamera() - - // start goroutines - server := NewWebServer() - go server.run() // http server / user interface - - // run camera + // create services + camera = NewCamera() // main service + ipc = NewIpcServer() // inter process communication + web := NewWebServer() // web frontend + + // allow graceful exit + var listener = make(chan os.Signal) + signal.Notify(listener, syscall.SIGTERM) + signal.Notify(listener, syscall.SIGINT) + go func() { + signal := <-listener + log.Printf("Got signal '%+v'", signal) + ipc.Cleanup() + os.Exit(0) + }() + + // start secondary services + go ipc.Run(IPC_SOCKET) + go PicameraSubprocess(IPC_SOCKET) // subprocess to use Python picamera + go web.run() + + // start main service camera.run() } diff --git a/src/subprocess.go b/src/subprocess.go new file mode 100644 index 0000000..d16d4fe --- /dev/null +++ b/src/subprocess.go @@ -0,0 +1,102 @@ +// vim: shiftwidth=4 tabstop=4 noexpandtab + +package main + +import ( + "log" + "os" + "os/exec" + "path/filepath" + "net" + "net/textproto" + "bufio" +) + +type IpcServer struct { + connections map[string]net.Conn + listener net.Listener +} + +func NewIpcServer() IpcServer { + return IpcServer{ + connections: make(map[string]net.Conn), + } +} + +func (ipc *IpcServer) Run(ipcSocket string) { + log.Println("Running IPC server") + var err error + ipc.listener,err = net.Listen("unix", ipcSocket) + if err != nil { + log.Fatal(err) + } + for { + connection,err := ipc.listener.Accept() + if err != nil { + log.Print(err) + } + bufioreader := bufio.NewReader(connection) + tpreader := textproto.NewReader(bufioreader) + line,err := tpreader.ReadLine() + log.Printf("IPC: '%s'", line) + ipc.connections[line] = connection + go ipc.HandleConnection(line) + } +} + +func (ipc *IpcServer) HandleConnection(connection string) { + for { + msg := ipc.ReadLineFrom(connection) + if msg != "" { + log.Printf("Got IPC message '%s' from '%s'", msg, connection) + if connection == "picamera" { + camera.statemachine.SendEvent(msg) + } + } + } +} + +func (ipc *IpcServer) WriteLineTo(connection string, line string) { + conn := ipc.connections[connection] + _,err := conn.Write([]byte(line)) + if err != nil { + log.Print(err) + } +} + +func (ipc *IpcServer) ReadLineFrom(connection string) string { + conn := ipc.connections[connection] + bufioreader := bufio.NewReader(conn) + tpreader := textproto.NewReader(bufioreader) + line,_ := tpreader.ReadLine() + return line +} + +func (ipc *IpcServer) Cleanup() { + log.Println("Releasing IPC socket ...") + for _,connection := range ipc.connections { + connection.Close() + } + ipc.listener.Close() +} + +func PicameraSubprocess(ipcSocket string) { + var cmd *exec.Cmd + if config.Flag.Debug { + pwd,err := os.Getwd() + if err != nil { + log.Fatal(err) + } + repoDir := filepath.Dir(pwd) + pythonPackage := repoDir + "/python/birdscan" + cmd = exec.Command("python3", pythonPackage, "--debug", "-s", ipcSocket) + } else { + cmd = exec.Command("python3", "/usr/lib/python3.9/site-packages/birdscan/", "-s", ipcSocket) + } + log.Println("Starting picamera service") + err := cmd.Run() + log.Println("picamera service terminated!") + if err != nil { + log.Print(err) + } +} @@ -43,7 +43,7 @@ func (server *WebServer) run() { router.Post("/api/reboot", rebootHandler) router.Post("/api/poweroff", poweroffHandler) - log.Println("Binding to 'http://" + server.config.BindAddress + ":" + server.config.BindPort + "'") + log.Println("Web service binding to 'http://" + server.config.BindAddress + ":" + server.config.BindPort + "'") log.Fatal(http.ListenAndServe(server.config.BindAddress + ":" + server.config.BindPort, router)) } |