door_daemon_zmq
authorBernhard Tittelbach <xro@realraum.at>
Tue, 24 Sep 2013 02:44:52 +0000 (02:44 +0000)
committerBernhard Tittelbach <xro@realraum.at>
Tue, 24 Sep 2013 02:44:52 +0000 (02:44 +0000)
go/door_daemon_zmq/handle_commands.go [new file with mode: 0644]
go/door_daemon_zmq/main.go [new file with mode: 0644]
go/door_daemon_zmq/serial_tty.go [new file with mode: 0644]
go/door_daemon_zmq/zeromq.go [new file with mode: 0644]

diff --git a/go/door_daemon_zmq/handle_commands.go b/go/door_daemon_zmq/handle_commands.go
new file mode 100644 (file)
index 0000000..d33070a
--- /dev/null
@@ -0,0 +1,68 @@
+// (c) Bernhard Tittelbach, 2013
+
+package main
+
+import (
+    "errors"
+)
+
+type DoorCmdHandler struct {
+    Checker func([][]byte)(error)
+    FirmwareChar string
+}
+
+var cmdToDoorCmdHandler = map[string]DoorCmdHandler {
+  "open": DoorCmdHandler{ checkCmdDoorControl, "o"},
+  "close": DoorCmdHandler{ checkCmdDoorControl, "c"},
+  "toggle": DoorCmdHandler{ checkCmdDoorControl, "t"},
+  "status": DoorCmdHandler{ checkCmdDoorControl, "s"},
+}
+
+// ---------- Command Handling Code -------------
+
+func checkCmdDoorControl(tokens [][]byte) (error) {
+    doorctrl_usage := "syntax: <open|close|toggle> <method> <nickname>"
+    if len(tokens) != 3 {
+        return errors.New(doorctrl_usage)
+    }
+    cmd := string(tokens[0])
+    if ! (cmd == "open" || cmd == "close" || cmd == "toggle") {
+        return errors.New(doorctrl_usage)
+    }
+    method := string(tokens[1])
+    if ! (method == "Button" || method == "ssh" || method == "SSH" || method == "Phone") {
+        return errors.New("method must be one either Button, SSH or Phone")
+    }
+    if len(tokens[2]) == 0 && method != "Button" {
+        return errors.New("Operator nickname must be given")
+    }
+    return nil
+}
+
+func checkCmdStatus(tokens [][]byte) (error) {
+    if len(tokens) != 1 {
+        return errors.New("status command does not accept arguments")
+    }
+    return nil
+}
+
+func HandleCommand(tokens [][]byte, topub chan <- [][]byte, serial_wr chan string, serial_rd chan [][]byte) ([][]byte, error){
+    if len(tokens) < 1 {
+        return nil, errors.New("No Command to handle")
+    }
+    
+    dch, present := cmdToDoorCmdHandler[string(tokens[0])]
+    if ! present {
+        return nil, errors.New("Unknown Command")
+    }
+
+    if err := dch.Checker(tokens); err != nil {
+        //return error to sender
+        return nil, err
+    }
+    
+    topub <- tokens
+    serial_wr <- dch.FirmwareChar
+    fw_reply := <- serial_rd
+    return fw_reply, nil
+}
diff --git a/go/door_daemon_zmq/main.go b/go/door_daemon_zmq/main.go
new file mode 100644 (file)
index 0000000..28be188
--- /dev/null
@@ -0,0 +1,59 @@
+// (c) Bernhard Tittelbach, 2013
+
+package main
+
+import (
+    "fmt"
+    "os"
+    "flag"
+    "log"
+)
+
+//~ func StringArrayToByteArray(ss []string) [][]byte {
+    //~ bb := make([][]byte, len(ss))
+    //~ for index, s := range(ss) {
+        //~ bb[index] = []byte(s)
+    //~ }
+    //~ return bb
+//~ }
+
+// ---------- Main Code -------------
+
+func usage() {
+    fmt.Fprintf(os.Stderr, "Usage: door_daemon_0mq <door tty device>\n")
+    flag.PrintDefaults()
+}
+
+func main() {
+    flag.Usage = usage
+    flag.Parse()
+
+    args := flag.Args()
+    if len(args) < 1 {
+        fmt.Fprintf(os.Stderr, "Input file is missing!\n");
+        usage()
+        os.Exit(1);
+    }
+    
+    cmd_chans, pub_chans := ZmqsInit("tcp://localhost:5555", "gmp://*:6666")   
+    
+    serial_wr, serial_rd, err := OpenAndHandleSerial(args[0], pub_chans.Out())
+    if err != nil {
+        close(serial_wr)
+        panic(err)
+    }
+    
+    serial_wr <- "f"
+    firmware_version := <- serial_rd
+    log.Print("Firmware version:", firmware_version)
+
+    for incoming_request := range cmd_chans.In() {
+        reply, err := HandleCommand(incoming_request, pub_chans.Out(), serial_wr, serial_rd)
+         if err != nil {
+            cmd_chans.Out() <- [][]byte{[]byte("ERROR"), []byte(err.Error())}
+            log.Print(err)
+         } else {
+            cmd_chans.Out() <- reply
+         }        
+    }
+}
diff --git a/go/door_daemon_zmq/serial_tty.go b/go/door_daemon_zmq/serial_tty.go
new file mode 100644 (file)
index 0000000..4ec786a
--- /dev/null
@@ -0,0 +1,60 @@
+// (c) Bernhard Tittelbach, 2013
+
+package main
+
+import (
+    "fmt"
+    "bufio"
+    "bytes"
+    "os"
+    "svn.spreadspace.org/realraum/go.svn/termios"
+    "log"
+)
+
+// ---------- Serial TTY Code -------------
+
+func openTTY(name string) (*os.File, error) {
+    file, err := os.OpenFile(name,os.O_RDWR, 0600) // For read access.
+    if err != nil {
+        log.Println(err.Error())
+        return nil, err
+    }
+    termios.Ttyfd(file.Fd())
+    termios.SetRaw()
+    return file, nil
+}
+
+func SerialWriter(in <- chan string, serial * os.File) {
+    for totty := range(in) {
+        serial.WriteString(totty)
+        serial.Sync()
+    }
+}
+
+func SerialReader(out chan <- [][]byte, topub chan <- [][]byte, serial * os.File) {
+    linescanner := bufio.NewScanner(serial)
+    linescanner.Split(bufio.ScanLines)
+    for linescanner.Scan() {
+        if err := linescanner.Err(); err != nil {
+            panic(fmt.Sprintf("Error in read from serial: %v\n",err.Error()))
+        }
+        text := bytes.Fields([]byte(linescanner.Text()))
+        if len(text) == 0 {
+            continue
+        }
+        out <- text
+        topub <- text
+    }
+}
+
+func OpenAndHandleSerial(filename string, topub chan <- [][]byte) (chan string, chan [][]byte, error) {
+    serial, err :=openTTY(filename)
+    if err != nil {
+        return nil, nil, err
+    }
+    var wr chan string
+    var rd chan [][]byte
+    go SerialWriter(wr, serial)
+    go SerialReader(rd, topub, serial)
+    return wr, rd, nil
+}
diff --git a/go/door_daemon_zmq/zeromq.go b/go/door_daemon_zmq/zeromq.go
new file mode 100644 (file)
index 0000000..70fe795
--- /dev/null
@@ -0,0 +1,65 @@
+// (c) Bernhard Tittelbach, 2013
+
+package main
+
+import (
+    zmq "github.com/vaughan0/go-zmq"
+ )
+
+// ---------- ZeroMQ Code -------------
+
+func ZmqsInit(cmd_port, pub_port string)  (cmd_chans, pub_chans *zmq.Channels) {
+
+    cmd_ctx, err := zmq.NewContext()
+    if err != nil {
+        panic(err)
+    }
+    //close only on panic, otherwise leave open:
+    defer func(){ if r:= recover(); r != nil { cmd_ctx.Close(); panic(r) } }()
+    
+    pub_ctx, err := zmq.NewContext()
+    if err != nil {
+        panic(err)
+    }
+    defer func() { if r:= recover(); r != nil { pub_ctx.Close(); panic(r) } }()
+    
+    cmd_sock, err := cmd_ctx.Socket(zmq.Rep)
+    if err != nil {
+        panic(err)
+    }
+    defer func() { if r:= recover(); r != nil { cmd_sock.Close(); panic(r) } }()
+
+    pub_sock, err := pub_ctx.Socket(zmq.Pub)
+    if err != nil {
+        panic(err)
+    }
+    defer func() { if r:= recover(); r != nil { pub_sock.Close(); panic(r) } }()
+
+    if err = cmd_sock.Bind(cmd_port); err != nil { // "tcp://*:5555"
+        panic(err)
+    }
+
+    if err = pub_sock.Bind(pub_port); err != nil { // "tcp://*:5556"
+        panic(err)
+    }
+    
+    cmd_chans = cmd_sock.Channels()
+    pub_chans = cmd_sock.Channels()
+    go zmqsHandleError(cmd_chans, pub_chans)
+    return
+}
+
+func zmqsHandleError(cmd_chans, pub_chans *zmq.Channels) {
+    for {
+        select {
+            case cmd_error := <- cmd_chans.Errors():
+                cmd_chans.Close()
+                pub_chans.Close()
+                panic(cmd_error)
+            case pub_error := <- pub_chans.Errors():
+                cmd_chans.Close()
+                pub_chans.Close()
+                panic(pub_error)
+        }
+    }
+}
\ No newline at end of file