package main // Create a new virtual serial device: // socat PTY,link=/dev/ttyS10 PTY,link=/dev/ttyS11 import ( "log" "os" "path" "github.com/fsnotify/fsnotify" "github.com/tarm/serial" "github.com/mkideal/cli" ) // TODO: Make the error code optional (as a parameter) func stop() { log.Println("Have a nice day.") os.Exit(0) } func waitAndExecuteCommandsFromDevice(device string) { config := &serial.Config{Name: device, Baud: 115200} serial, err := serial.OpenPort(config) if err != nil { log.Fatal(err) } log.Printf("Open the connection to the n3rdpad over %s", device) // Read from serial buffer := make([]byte, 128) for { num, err := serial.Read(buffer) if err != nil { log.Fatal(err) } // TODO: Do something with the incomind commands log.Printf("%q", buffer[:num]) } } func watchForConfigChanges(configFile string) { watcher, err := fsnotify.NewWatcher() if err != nil { log.Fatal(err) } defer watcher.Close() done := make(chan bool) go func() { for { select { case event := <-watcher.Events: // The file is changed. This is the only event we are interested // in. If the file is renamed, removed or something else, we drop // an error to the user. if event.Op&fsnotify.Write == fsnotify.Write || event.Op&fsnotify.Remove == fsnotify.Remove { if _, err := os.Stat(event.Name); err == nil { log.Println("Reload the config file ...") // TODO: Parse the config file // TODO: Handle errors in the config file // TODO: Write a mapping for the mapping of the keys to the binary format the avr wants. // TODO: Establish a connection to the device // TODO: Transfer the new key mappings // TODO: Close the connection? } } // Some editors have a feature calles "swap save" (like vim), which // will trigger a simple WRITE event, instead it wil trigger RENAME // -> CHMOD -> REMOVE in that order. So we lost track of the file. // To prevent this, we add the file again, if it exist after a // REMOVE event. if event.Op&fsnotify.Remove == fsnotify.Remove { if _, err := os.Stat(event.Name); err == nil { watcher.Add(event.Name) } else { log.Fatal("Lost the configuration file. We stop now.") stop() } } case err := <-watcher.Errors: log.Println("We have the following problem with the configuration file: ", err) log.Println("Try the fix this problem by yourself and restart the agent.") stop() } } }() err = watcher.Add(configFile) if err != nil { log.Fatal("Can't find the configuration file. Please create one.") } <-done } type argT struct { cli.Helper ConfigFile string `cli:"c,config" usage:"specify a keymap config file"` Device string `cli:"d,device" usage:"specify the serial port, the n3rdpad is hooked to"` } func main() { configPath, _ := os.Getwd() configFile := path.Join(configPath, "keymap.conf") cli.Run(&argT{}, func(ctx *cli.Context) error { args := ctx.Argv().(*argT) if args.ConfigFile != "" { configFile = args.ConfigFile } if args.Device == "" { log.Fatal("You need to specify the serial device with the --device parameter.") stop(); } if !args.Help { // Start up the application log.Println("Using the configuration file", configFile) go watchForConfigChanges(configFile) waitAndExecuteCommandsFromDevice(args.Device) stop() } return nil }) }