agent.go 4.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. package main
  2. import (
  3. "log"
  4. "os"
  5. "path"
  6. "errors"
  7. "github.com/fsnotify/fsnotify"
  8. "github.com/mkideal/cli"
  9. "github.com/tarm/serial"
  10. )
  11. func stop(exitCode int) {
  12. log.Println("Have a nice day.")
  13. os.Exit(exitCode)
  14. }
  15. type message struct {
  16. msgType byte
  17. data []byte
  18. }
  19. const (
  20. MSG_TYPE_ANSWER_OK = 0x01
  21. MSG_TYPE_ANSWER_NOOK = 0x02
  22. MSG_TYPE_AGENTID = 0x03
  23. MSG_TYPE_CONFIG = 0x04
  24. )
  25. func decodeByteString(bytes []byte, num int) (msg message, err error) {
  26. if bytes[0] == 0x3c && bytes[1] == 0x3e &&
  27. bytes[num-1] == 0x0A && bytes[num-2] == 0x0D {
  28. return message{msgType: bytes[2], data: bytes[3:num-2]}, nil
  29. }
  30. return message{msgType: 0x00, data: make([]byte, 1)}, errors.New("Can't decode the byte array.")
  31. }
  32. func waitAndExecuteCommandsFromDevice(serial *serial.Port) {
  33. buffer := make([]byte, 40)
  34. for {
  35. num, err := serial.Read(buffer)
  36. if err != nil {
  37. log.Fatal(err)
  38. }
  39. input, err := decodeByteString(buffer, num)
  40. if err != nil {
  41. log.Fatal(err)
  42. } else {
  43. switch (input.msgType) {
  44. case MSG_TYPE_AGENTID:
  45. log.Printf("Key pressed: %d", input.data[0])
  46. default:
  47. log.Fatal("Can't recognize the message type.")
  48. }
  49. }
  50. }
  51. }
  52. func watchForConfigChanges(configFile string, serial *serial.Port) {
  53. watcher, err := fsnotify.NewWatcher()
  54. if err != nil {
  55. log.Fatal(err)
  56. }
  57. defer watcher.Close()
  58. done := make(chan bool)
  59. go func() {
  60. for {
  61. select {
  62. case event := <-watcher.Events:
  63. // The file is changed. This is the only event we are interested
  64. // in. If the file is renamed, removed or something else, we drop
  65. // an error to the user.
  66. if event.Op&fsnotify.Write == fsnotify.Write ||
  67. event.Op&fsnotify.Remove == fsnotify.Remove {
  68. if _, err := os.Stat(event.Name); err == nil {
  69. log.Println("Reload the config file ...")
  70. // TODO: Parse the config file
  71. // TODO: Handle errors in the config file
  72. // TODO: Write a mapping for the mapping of the keys to the binary format the avr wants.
  73. // TODO: Establish a connection to the device
  74. // TODO: Transfer the new key mappings
  75. // TODO: Close the connection?
  76. }
  77. }
  78. // Some editors have a feature called "swap save" (like vim), which
  79. // will trigger a simple WRITE event, instead it wil trigger RENAME
  80. // -> CHMOD -> REMOVE in that order. So we lost track of the file.
  81. // To prevent this, we add the file again, if it exist after a
  82. // REMOVE event.
  83. if event.Op&fsnotify.Remove == fsnotify.Remove {
  84. if _, err := os.Stat(event.Name); err == nil {
  85. watcher.Add(event.Name)
  86. } else {
  87. log.Fatal("Lost the configuration file. We stop now.")
  88. stop(1)
  89. }
  90. }
  91. case err := <-watcher.Errors:
  92. log.Println("We have the following problem with the configuration file: ", err)
  93. log.Println("Try the fix this problem by yourself and restart the agent.")
  94. stop(2)
  95. }
  96. }
  97. }()
  98. err = watcher.Add(configFile)
  99. if err != nil {
  100. log.Fatal("Can't find the configuration file. Please create one.")
  101. }
  102. <-done
  103. }
  104. type argT struct {
  105. cli.Helper
  106. ConfigFile string `cli:"c,config" usage:"specify a keymap config file"`
  107. Device string `cli:"d,device" usage:"specify the serial port, the n3rdpad is hooked to"`
  108. }
  109. func main() {
  110. configPath, _ := os.Getwd()
  111. configFile := path.Join(configPath, "keymap.conf")
  112. cli.Run(&argT{}, func(ctx *cli.Context) error {
  113. args := ctx.Argv().(*argT)
  114. if args.ConfigFile != "" {
  115. configFile = args.ConfigFile
  116. }
  117. if args.Device == "" {
  118. log.Fatal("You need to specify the serial device with the --device parameter.")
  119. stop(3)
  120. }
  121. if !args.Help {
  122. // Start up the application
  123. log.Println("Using the configuration file", configFile)
  124. // Open the serial connection
  125. config := &serial.Config{Name: args.Device, Baud: 115200}
  126. serial, err := serial.OpenPort(config)
  127. if err != nil {
  128. log.Fatal(err)
  129. return nil
  130. }
  131. log.Printf("Open the connection to the n3rdpad over %s", args.Device)
  132. go watchForConfigChanges(configFile, serial)
  133. waitAndExecuteCommandsFromDevice(serial)
  134. stop(0)
  135. }
  136. return nil
  137. })
  138. }