summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxengineering <mail2xengineering@protonmail.com>2021-06-18 10:00:10 +0200
committerxengineering <mail2xengineering@protonmail.com>2021-06-18 14:26:47 +0200
commit7634ee33120e7ef601ba1e806c63fe61c53e46bb (patch)
tree1a5225c9fefcc1bf69b143593c7c3001c0fbbc33
parent52972b810a9a42cef5c88f9947d64a0705f243ac (diff)
downloadbirdscan-7634ee33120e7ef601ba1e806c63fe61c53e46bb.tar
birdscan-7634ee33120e7ef601ba1e806c63fe61c53e46bb.tar.zst
birdscan-7634ee33120e7ef601ba1e806c63fe61c53e46bb.zip
WIP: Implement Transport
-rw-r--r--README.md47
-rw-r--r--config/default.json8
-rw-r--r--src/camera.go7
-rw-r--r--src/main.go72
-rw-r--r--src/runtime_config.go62
-rw-r--r--src/transport.go125
-rw-r--r--src/web.go8
7 files changed, 266 insertions, 63 deletions
diff --git a/README.md b/README.md
index 7ea1b71..e8f7716 100644
--- a/README.md
+++ b/README.md
@@ -3,3 +3,50 @@
A software to take beautiful pictures of birds with a Raspberry Pi Camera.
+# Usage
+
+## Prepare the Target Fileserver
+
+TODO
+
+Some notes:
+```
+ssh-keygen -y -f ~/.ssh/id_rsa # generates and prints public key of this private one
+sudo mkdir /srv/birdscan
+sudo chown -R birdscan:birdscan /srv/birdscan
+sudo chmod -R 700 /srv/birdscan
+sudo setfacl -m u:myuser:rwx /srv/birdscan
+```
+
+## Install and configure birdscan on main Server
+
+Install and configure birdscan in these few steps on an Arch Linux system:
+
+```
+mkdir -p ~/ABS # create a directory for the arch build system
+cd ~/ABS
+git clone https://aur.archlinux.org/python-picamera.git # download picamera from AUR
+cd picamera
+makepkg -si # build and install picamera dependency
+mkdir ../birdscan
+cd ../birdscan
+# (download PKGBUILD for birdscan to current directory FIXME: insert link here)
+makepkg -si # build and install birdscan
+sudo ssh-keyscan -t rsa <fileserver_ip_or_host> >> /etc/ssh/ssh_known_hosts # add host key of fileserver
+sudo vim /etc/birdscan/config.json # edit config file
+sudo systemctl enable --now birdscan # start and enable birdscan
+```
+
+Change the configuration like this:
+
+```
+sudo vim /etc/birdscan/config.json
+sudo systemctl restart birdscan
+```
+
+If something does not work you can get a live log of birdscan like this:
+
+```
+journalctl -fu birdscan
+```
+
diff --git a/config/default.json b/config/default.json
index 6288daf..be86cfb 100644
--- a/config/default.json
+++ b/config/default.json
@@ -2,5 +2,13 @@
"webserver":{
"bind_address":"127.0.0.1",
"bind_port":"8080"
+ },
+ "file_transport":{
+ "enabled":false,
+ "local_path_to_ssh_private_key":"/dev/null",
+ "fileserver_host_or_ip":"127.0.0.1",
+ "fileserver_ssh_port":"22",
+ "remote_user":"birdscan",
+ "remote_path_to_target_directory":"/srv/birdscan/"
}
}
diff --git a/src/camera.go b/src/camera.go
index cc29f54..e836cf3 100644
--- a/src/camera.go
+++ b/src/camera.go
@@ -50,7 +50,7 @@ func singlePicture(m *Machine) {
// create command
var cmd *exec.Cmd
- if !debug {
+ if !config.Flag.Debug {
cmd = exec.Command("python3", "/usr/lib/python3.9/site-packages/birdscan/")
} else {
cmd = exec.Command("python3", "../python/birdscan", "--debug")
@@ -68,7 +68,7 @@ func singlePicture(m *Machine) {
if err != nil {
log.Print(err)
}
- data,err := io.ReadAll(stdout)
+ _,err = io.ReadAll(stdout)
if err != nil {
log.Print(err)
}
@@ -78,8 +78,9 @@ func singlePicture(m *Machine) {
}
// process result
- log.Println(string(data))
m.SendEvent("single_picture_taken")
+ log.Println("Picture taken. Sending transport_request ...")
+ transporter.statemachine.SendEvent("transport_request")
}
func (cam *Camera) run() {
diff --git a/src/main.go b/src/main.go
index 01581ff..572e5f9 100644
--- a/src/main.go
+++ b/src/main.go
@@ -3,77 +3,37 @@
package main
import (
- "flag"
"log"
- "os"
- "io/ioutil"
- "encoding/json"
)
var (
+ config RuntimeConfig
camera Camera
- debug bool
+ transporter Transporter
)
-type config struct {
- WebConfig webConfig `json:"webserver"`
-}
-
func main() {
- // read command line arguments
- configPath := readFlags()
+ // disable log timestamp because systemd takes care of that
+ log.SetFlags(0)
- // set up log and print startup message
- log.SetFlags(0) // disable timestamp because systemd takes care of that
- log.Println("Starting birdscan")
+ // parse flags and read config
+ config = GetRuntimeConfig()
- // read config file
- cfg := readConfig(configPath)
+ // print startup message
+ log.Println("Starting birdscan")
- // setup camera state machine
+ // create camera
camera = NewCamera()
+ // setup transporter
+ transporter = NewTransporter()
+ go transporter.Run() // daemon to copy files via rsync
+
// start goroutines
- server := NewWebServer(&cfg.WebConfig)
- go server.run() // http server
+ server := NewWebServer()
+ go server.run() // http server / user interface
- // run camera state machine
+ // run camera
camera.run()
}
-
-func readFlags() string {
- var retval string
- flag.StringVar(&retval, "c", "/etc/birdscan/config.json", "Path to birdscan configuration file")
- flag.BoolVar(&debug, "d", false, "A debug flag to be used by source repository Makefile")
- flag.Parse()
- return retval
-}
-
-func readConfig(path string) config {
-
- log.Printf("Reading config file %s", path)
- var retval config
-
- // open the config file
- configFile, err := os.Open(path)
- defer configFile.Close()
- if err != nil {
- log.Fatalf("Could not open configuration file %s", path)
- }
-
- // read byte content
- byteData, err := ioutil.ReadAll(configFile)
- if err != nil {
- log.Fatalf("Could not read configuration file %s", path)
- }
-
- // parse content to config structs
- err = json.Unmarshal(byteData, &retval)
- if err != nil {
- log.Fatalf("Could not parse configuration file %s", path)
- }
-
- return retval
-}
-
diff --git a/src/runtime_config.go b/src/runtime_config.go
new file mode 100644
index 0000000..07e4a7c
--- /dev/null
+++ b/src/runtime_config.go
@@ -0,0 +1,62 @@
+// vim: shiftwidth=4 tabstop=4 noexpandtab
+
+package main
+
+import (
+ "log"
+ "io/ioutil"
+ "os"
+ "flag"
+ "encoding/json"
+)
+
+type RuntimeConfig struct {
+ Flag FlagConfig
+ Web WebConfig `json:"webserver"`
+ Transport TransportConfig `json:"file_transport"`
+}
+
+func GetRuntimeConfig() RuntimeConfig {
+
+ retval := RuntimeConfig{}
+
+ // read CLI parameters
+ retval.Flag.read()
+
+ // open the config file
+ configFile, err := os.Open(retval.Flag.ConfigPath)
+ defer configFile.Close()
+ if err != nil {
+ log.Fatalf("Could not open configuration file %s", retval.Flag.ConfigPath)
+ }
+
+ // read byte content
+ byteData, err := ioutil.ReadAll(configFile)
+ if err != nil {
+ log.Fatalf("Could not read configuration file %s", retval.Flag.ConfigPath)
+ }
+
+ // parse content to config structs
+ err = json.Unmarshal(byteData, &retval)
+ if err != nil {
+ log.Fatalf("Could not parse configuration file %s", retval.Flag.ConfigPath)
+ }
+
+ // patch default config in case of debugging
+ if retval.Flag.Debug {
+ retval.Transport.Enabled = true
+ }
+
+ return retval
+}
+
+type FlagConfig struct {
+ ConfigPath string
+ Debug bool
+}
+
+func (flagConfig *FlagConfig) read() {
+ flag.StringVar(&(flagConfig.ConfigPath), "c", "/etc/birdscan/config.json", "Path to birdscan configuration file")
+ flag.BoolVar(&(flagConfig.Debug), "d", false, "A debug flag to be used by source repository Makefile")
+ flag.Parse()
+}
diff --git a/src/transport.go b/src/transport.go
new file mode 100644
index 0000000..3e91355
--- /dev/null
+++ b/src/transport.go
@@ -0,0 +1,125 @@
+// vim: shiftwidth=4 tabstop=4 noexpandtab
+
+package main
+
+import (
+ "time"
+ "log"
+ "os/exec"
+ "fmt"
+ "io"
+)
+
+type Transporter struct {
+ statemachine Machine
+}
+
+type TransportConfig struct {
+ Enabled bool `json:"enabled"`
+ LocalPathToSshPrivateKey string `json:"local_path_to_ssh_private_key"`
+ FileserverHostOrIp string `json:"fileserver_host_or_ip"`
+ FileserverSshPort string `json:"fileserver_ssh_port"`
+ RemoteUser string `json:"remote_user"`
+ RemotePathToTargetDirectory string `json:"remote_path_to_target_directory"`
+}
+
+func NewTransporter() Transporter {
+ return Transporter{
+ statemachine: Machine{
+ name: "transporter",
+ initial: "idle",
+ states: StateMap{
+ "idle": MachineState{ // nothing to transport
+ on: TransitionMap{
+ "transport_request": MachineTransition{
+ to: "transport",
+ },
+ },
+ },
+ "transport": MachineState{ // rsync process for transport is running
+ on: TransitionMap{
+ "transport_request": MachineTransition{
+ to: "transport_queue",
+ },
+ "transport_finished": MachineTransition{
+ to: "idle",
+ },
+ },
+ },
+ "transport_queue": MachineState{ // like transport but also a pending transport request
+ on: TransitionMap{
+ "transport_finished": MachineTransition{
+ to: "transport",
+ },
+ },
+ },
+ },
+ api: make(chan string),
+ state_listeners: make([]*(chan string), 0),
+ hook: runTransporterHooks,
+ },
+ }
+}
+
+func (transp *Transporter) Run() {
+ transp.statemachine.Run()
+}
+
+func runTransporterHooks(last string, next string, m *Machine) {
+ if last == "idle" && next == "transport" {
+ go transportData(m)
+ }
+ if last == "transport_queue" && next == "transport" {
+ go transportData(m)
+ }
+}
+
+func transportData(m *Machine) {
+ if config.Transport.Enabled {
+ if !config.Flag.Debug {
+
+ // generate command string from config
+ cmdString := fmt.Sprintf(
+ "\"rsync --remove-source-files -rltgoDv -e 'ssh -p %s -i %s' /var/lib/birdscan/ %s@%s:%s\"",
+ config.Transport.FileserverSshPort,
+ config.Transport.LocalPathToSshPrivateKey,
+ config.Transport.RemoteUser,
+ config.Transport.FileserverHostOrIp,
+ config.Transport.RemotePathToTargetDirectory,
+ )
+
+ // create command
+ cmd := exec.Command(
+ "/bin/bash",
+ "-c",
+ cmdString,
+ )
+ log.Printf("Executing: '%s'", cmd.String())
+
+ // get stderr
+ stderr,err := cmd.StderrPipe()
+ if err != nil {
+ log.Print(err)
+ }
+ defer stderr.Close()
+
+ // execute command and fetch stderr
+ err = cmd.Start()
+ if err != nil {
+ log.Print(err)
+ }
+ stderrData,err := io.ReadAll(stderr)
+ if err != nil {
+ log.Print(err)
+ }
+ err = cmd.Wait()
+ if err != nil {
+ log.Print(err)
+ log.Println(string(stderrData))
+ }
+ } else {
+ time.Sleep(8 * time.Second)
+ }
+ }
+ m.SendEvent("transport_finished")
+}
diff --git a/src/web.go b/src/web.go
index 53f8c0a..ab15aba 100644
--- a/src/web.go
+++ b/src/web.go
@@ -16,17 +16,17 @@ const (
)
type WebServer struct {
- config *webConfig
+ config *WebConfig
}
-type webConfig struct {
+type WebConfig struct {
BindAddress string `json:"bind_address"`
BindPort string `json:"bind_port"`
}
-func NewWebServer(cfg *webConfig) WebServer {
+func NewWebServer() WebServer {
server := WebServer{}
- server.config = cfg
+ server.config = &(config.Web)
return server
}