Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.
 
 
 
 

1390 Zeilen
38 KiB

  1. package main
  2. import (
  3. "bytes"
  4. "encoding/gob"
  5. "encoding/json"
  6. "flag"
  7. "fmt"
  8. "io/ioutil"
  9. "log"
  10. "math"
  11. "net/http"
  12. "os"
  13. "os/signal"
  14. "strconv"
  15. "strings"
  16. "sync"
  17. "time"
  18. //"./utils"
  19. "os/exec"
  20. "github.com/boltdb/bolt"
  21. "gopkg.in/natefinch/lumberjack.v2"
  22. "github.com/yosssi/gmq/mqtt"
  23. "github.com/yosssi/gmq/mqtt/client"
  24. "github.com/gorilla/handlers"
  25. "github.com/gorilla/mux"
  26. "github.com/gorilla/websocket"
  27. )
  28. const (
  29. // Time allowed to write the file to the client.
  30. writeWait = 10 * time.Second
  31. // Time allowed to read the next pong message from the client.
  32. pongWait = 60 * time.Second
  33. // Send pings to client with this period. Must be less than pongWait.
  34. pingPeriod = (pongWait * 9) / 10
  35. beaconPeriod = 2 * time.Second
  36. )
  37. // data structures
  38. type Settings struct {
  39. Location_confidence int64 `json:"location_confidence"`
  40. Last_seen_threshold int64 `json:"last_seen_threshold"`
  41. Beacon_metrics_size int `json:"beacon_metrics_size"`
  42. HA_send_interval int64 `json:"ha_send_interval"`
  43. HA_send_changes_only bool `json:"ha_send_changes_only"`
  44. }
  45. type Incoming_json struct {
  46. Hostname string `json:"hostname"`
  47. MAC string `json:"mac"`
  48. RSSI int64 `json:"rssi"`
  49. Is_scan_response string `json:"is_scan_response"`
  50. Ttype string `json:"type"`
  51. Data string `json:"data"`
  52. Beacon_type string `json:"beacon_type"`
  53. UUID string `json:"uuid"`
  54. Major string `json:"major"`
  55. Minor string `json:"minor"`
  56. TX_power string `json:"tx_power"`
  57. Namespace string `json:"namespace"`
  58. Instance_id string `json:"instance_id"`
  59. // button stuff
  60. HB_ButtonCounter int64 `json:"hb_button_counter"`
  61. HB_ButtonCounter_Prev int64 `json:"hb_button_counter"`
  62. HB_Battery int64 `json:"hb_button_battery"`
  63. HB_RandomNonce string `json:"hb_button_random"`
  64. HB_ButtonMode string `json:"hb_button_mode"`
  65. }
  66. type Advertisement struct {
  67. ttype string
  68. content string
  69. seen int64
  70. }
  71. type beacon_metric struct {
  72. location string
  73. distance float64
  74. rssi int64
  75. timestamp int64
  76. }
  77. type Location struct {
  78. name string
  79. lock sync.RWMutex
  80. }
  81. type Best_location struct {
  82. distance float64
  83. name string
  84. last_seen int64
  85. }
  86. type HTTP_location struct {
  87. Previous_confident_location string `json:"previous_confident_location"`
  88. Distance float64 `json:"distance"`
  89. Name string `json:"name"`
  90. Beacon_name string `json:"beacon_name"`
  91. Beacon_id string `json:"beacon_id"`
  92. Beacon_type string `json:"beacon_type"`
  93. HB_Battery int64 `json:"hb_button_battery"`
  94. HB_ButtonMode string `json:"hb_button_mode"`
  95. HB_ButtonCounter int64 `json:"hb_button_counter"`
  96. Location string `json:"location"`
  97. Last_seen int64 `json:"last_seen"`
  98. }
  99. type Location_change struct {
  100. Beacon_ref Beacon `json:"beacon_info"`
  101. Name string `json:"name"`
  102. Beacon_name string `json:"beacon_name"`
  103. Previous_location string `json:"previous_location"`
  104. New_location string `json:"new_location"`
  105. Timestamp int64 `json:"timestamp"`
  106. }
  107. type HA_message struct {
  108. Beacon_id string `json:"id"`
  109. Beacon_name string `json:"name"`
  110. Distance float64 `json:"distance"`
  111. }
  112. type HTTP_locations_list struct {
  113. Beacons []HTTP_location `json:"beacons"`
  114. //Buttons []Button `json:"buttons"`
  115. }
  116. type Beacon struct {
  117. Name string `json:"name"`
  118. Beacon_id string `json:"beacon_id"`
  119. Beacon_type string `json:"beacon_type"`
  120. Beacon_location string `json:"beacon_location"`
  121. Last_seen int64 `json:"last_seen"`
  122. Incoming_JSON Incoming_json `json:"incoming_json"`
  123. Distance float64 `json:"distance"`
  124. Previous_location string
  125. Previous_confident_location string
  126. expired_location string
  127. Location_confidence int64
  128. Location_history []string
  129. beacon_metrics []beacon_metric
  130. HB_ButtonCounter int64 `json:"hb_button_counter"`
  131. HB_ButtonCounter_Prev int64 `json:"hb_button_counter"`
  132. HB_Battery int64 `json:"hb_button_battery"`
  133. HB_RandomNonce string `json:"hb_button_random"`
  134. HB_ButtonMode string `json:"hb_button_mode"`
  135. }
  136. type Button struct {
  137. Name string `json:"name"`
  138. Button_id string `json:"button_id"`
  139. Button_type string `json:"button_type"`
  140. Button_location string `json:"button_location"`
  141. Incoming_JSON Incoming_json `json:"incoming_json"`
  142. Distance float64 `json:"distance"`
  143. Last_seen int64 `json:"last_seen"`
  144. HB_ButtonCounter int64 `json:"hb_button_counter"`
  145. HB_Battery int64 `json:"hb_button_battery"`
  146. HB_RandomNonce string `json:"hb_button_random"`
  147. HB_ButtonMode string `json:"hb_button_mode"`
  148. }
  149. type Beacons_list struct {
  150. Beacons map[string]Beacon `json:"beacons"`
  151. lock sync.RWMutex
  152. }
  153. type Locations_list struct {
  154. locations map[string]Location
  155. lock sync.RWMutex
  156. }
  157. var clients = make(map[*websocket.Conn]bool) // connected clients
  158. var broadcast = make(chan Message) // broadcast channel
  159. // Define our message object
  160. type Message struct {
  161. Email string `json:"email"`
  162. Username string `json:"username"`
  163. Message string `json:"message"`
  164. }
  165. // Struttura per il parsing JSON multiplo
  166. type RawReading struct {
  167. Timestamp string `json:"timestamp"`
  168. Type string `json:"type"`
  169. MAC string `json:"mac"`
  170. RSSI int `json:"rssi"`
  171. RawData string `json:"rawData"`
  172. }
  173. // GLOBALS
  174. var BEACONS Beacons_list
  175. var Buttons_list map[string]Button
  176. var cli *client.Client
  177. var http_results HTTP_locations_list
  178. var http_results_lock sync.RWMutex
  179. var Latest_beacons_list map[string]Beacon
  180. var latest_list_lock sync.RWMutex
  181. var db *bolt.DB
  182. var err error
  183. var world = []byte("presence")
  184. var settings = Settings{
  185. Location_confidence: 4,
  186. Last_seen_threshold: 15,
  187. Beacon_metrics_size: 30,
  188. HA_send_interval: 5,
  189. HA_send_changes_only: false,
  190. }
  191. // utility function
  192. func parseButtonState(raw string) int64 {
  193. raw = strings.ToUpper(raw)
  194. // Minew B7 / C7 / D7 - frame tipo: 0201060303E1FF1216E1FFA103...
  195. if strings.HasPrefix(raw, "0201060303E1FF12") && len(raw) >= 38 {
  196. // La posizione 34-38 (indice 26:30) contiene il buttonCounter su 2 byte (hex)
  197. buttonField := raw[34:38] // NB: offset 34-38 zero-based
  198. if buttonValue, err := strconv.ParseInt(buttonField, 16, 64); err == nil {
  199. return buttonValue
  200. }
  201. }
  202. // Ingics (02010612FF590)
  203. if strings.HasPrefix(raw, "02010612FF590") && len(raw) >= 24 {
  204. counterField := raw[22:24]
  205. buttonState, err := strconv.ParseInt(counterField, 16, 64)
  206. if err == nil {
  207. return buttonState
  208. }
  209. }
  210. // Aggiungeremo qui facilmente nuovi beacon in futuro
  211. return 0
  212. }
  213. func twos_comp(inp string) int64 {
  214. i, _ := strconv.ParseInt("0x"+inp, 0, 64)
  215. return i - 256
  216. }
  217. func getBeaconID(incoming Incoming_json) string {
  218. unique_id := fmt.Sprintf("%s", incoming.MAC)
  219. return unique_id
  220. }
  221. func incomingBeaconFilter(incoming Incoming_json) Incoming_json {
  222. out_json := incoming
  223. if incoming.Beacon_type == "hb_button" {
  224. raw_data := incoming.Data
  225. hb_button_prefix_str := fmt.Sprintf("02010612FF5900")
  226. if strings.HasPrefix(raw_data, hb_button_prefix_str) {
  227. out_json.Namespace = "ddddeeeeeeffff5544ff"
  228. //out_json.Instance_id = raw_data[24:36]
  229. counter_str := fmt.Sprintf("0x%s", raw_data[22:24])
  230. counter, _ := strconv.ParseInt(counter_str, 0, 64)
  231. out_json.HB_ButtonCounter = counter
  232. battery_str := fmt.Sprintf("0x%s%s", raw_data[20:22], raw_data[18:20])
  233. ////fmt.Println("battery has %s\n", battery_str)
  234. battery, _ := strconv.ParseInt(battery_str, 0, 64)
  235. out_json.HB_Battery = battery
  236. out_json.TX_power = fmt.Sprintf("0x%s", "4")
  237. out_json.Beacon_type = "hb_button"
  238. out_json.HB_ButtonMode = "presence_button"
  239. ///fmt.Println("Button adv has %#v\n", out_json)
  240. }
  241. }
  242. return out_json
  243. }
  244. func processButton(bbeacon Beacon, cl *client.Client) {
  245. btn := Button{Name: bbeacon.Name}
  246. btn.Button_id = bbeacon.Beacon_id
  247. btn.Button_type = bbeacon.Beacon_type
  248. btn.Button_location = bbeacon.Previous_location
  249. btn.Incoming_JSON = bbeacon.Incoming_JSON
  250. btn.Distance = bbeacon.Distance
  251. btn.Last_seen = bbeacon.Last_seen
  252. btn.HB_ButtonCounter = bbeacon.HB_ButtonCounter
  253. btn.HB_Battery = bbeacon.HB_Battery
  254. btn.HB_RandomNonce = bbeacon.HB_RandomNonce
  255. btn.HB_ButtonMode = bbeacon.HB_ButtonMode
  256. nonce, ok := Buttons_list[btn.Button_id]
  257. if !ok || nonce.HB_RandomNonce != btn.HB_RandomNonce {
  258. // send the button message to MQTT
  259. sendButtonMessage(btn, cl)
  260. }
  261. Buttons_list[btn.Button_id] = btn
  262. }
  263. func getiBeaconDistance(rssi int64, power string) float64 {
  264. ratio := float64(rssi) * (1.0 / float64(twos_comp(power)))
  265. //fmt.Printf("beaconpower: rssi %d ratio %e power %e \n",rssi, ratio, float64(twos_comp(power)))
  266. distance := 100.0
  267. if ratio < 1.0 {
  268. distance = math.Pow(ratio, 10)
  269. } else {
  270. distance = (0.89976)*math.Pow(ratio, 7.7095) + 0.111
  271. }
  272. return distance
  273. }
  274. func getBeaconDistance(incoming Incoming_json) float64 {
  275. distance := 1000.0
  276. distance = getiBeaconDistance(incoming.RSSI, incoming.TX_power)
  277. //distance = math.Abs(float64(incoming.RSSI))
  278. return distance
  279. }
  280. func getAverageDistance(beacon_metrics []beacon_metric) float64 {
  281. total := 0.0
  282. for _, v := range beacon_metrics {
  283. total += v.distance
  284. }
  285. return (total / float64(len(beacon_metrics)))
  286. }
  287. func sendHARoomMessage(beacon_id string, beacon_name string, distance float64, location string, cl *client.Client) {
  288. //first make the json
  289. ha_msg, err := json.Marshal(HA_message{Beacon_id: beacon_id, Beacon_name: beacon_name, Distance: distance})
  290. if err != nil {
  291. panic(err)
  292. }
  293. //send the message to HA
  294. err = cl.Publish(&client.PublishOptions{
  295. QoS: mqtt.QoS1,
  296. TopicName: []byte("afa-systems/presence/ha/" + location),
  297. Message: ha_msg,
  298. })
  299. if err != nil {
  300. panic(err)
  301. }
  302. }
  303. func sendButtonMessage(btn Button, cl *client.Client) {
  304. //first make the json
  305. btn_msg, err := json.Marshal(btn)
  306. if err != nil {
  307. panic(err)
  308. }
  309. //send the message to HA
  310. err = cl.Publish(&client.PublishOptions{
  311. QoS: mqtt.QoS1,
  312. TopicName: []byte("afa-systems/presence/button/" + btn.Button_id),
  313. Message: btn_msg,
  314. })
  315. if err != nil {
  316. panic(err)
  317. }
  318. }
  319. func sendButtonPressed(bcn Beacon, cl *client.Client) {
  320. //first make the json
  321. btn_msg, err := json.Marshal(bcn)
  322. if err != nil {
  323. panic(err)
  324. }
  325. //send the message to HA
  326. err = cl.Publish(&client.PublishOptions{
  327. QoS: mqtt.QoS1,
  328. TopicName: []byte("afa-systems/presence/button/" + bcn.Beacon_id),
  329. Message: btn_msg,
  330. })
  331. if err != nil {
  332. panic(err)
  333. }
  334. ///utils.Log.Printf("%s pressed ",bcn.Beacon_id)
  335. s := fmt.Sprintf("/usr/bin/php /usr/local/presence/alarm_handler.php --idt=%s --idr=%s --st=%d", bcn.Beacon_id, bcn.Incoming_JSON.Hostname, bcn.HB_ButtonCounter)
  336. ///utils.Log.Printf("%s",s)
  337. err, out, errout := Shellout(s)
  338. if err != nil {
  339. log.Printf("error: %v\n", err)
  340. }
  341. fmt.Println("--- stdout ---")
  342. fmt.Println(out)
  343. fmt.Println("--- stderr ---")
  344. fmt.Println(errout)
  345. }
  346. func getLikelyLocations(settings Settings, locations_list Locations_list, cl *client.Client) {
  347. // create the http results structure
  348. http_results_lock.Lock()
  349. http_results = HTTP_locations_list{}
  350. http_results.Beacons = make([]HTTP_location, 0)
  351. ///http_results.Buttons = make([]Button, 0)
  352. http_results_lock.Unlock()
  353. should_persist := false
  354. // iterate through the beacons we want to search for
  355. for _, beacon := range BEACONS.Beacons {
  356. if len(beacon.beacon_metrics) == 0 {
  357. continue
  358. }
  359. if (int64(time.Now().Unix()) - (beacon.beacon_metrics[len(beacon.beacon_metrics)-1].timestamp)) > settings.Last_seen_threshold {
  360. if beacon.expired_location == "expired" {
  361. continue
  362. } else {
  363. beacon.expired_location = "expired"
  364. msg := Message{
  365. Email: beacon.Previous_confident_location,
  366. Username: beacon.Name,
  367. Message: beacon.expired_location}
  368. res1B, _ := json.Marshal(msg)
  369. fmt.Println(string(res1B))
  370. if err != nil {
  371. log.Printf("error: %v", err)
  372. }
  373. broadcast <- msg
  374. }
  375. } else {
  376. beacon.expired_location = ""
  377. }
  378. best_location := Best_location{}
  379. loc_list := make(map[string]float64)
  380. seen_weight := 1.5
  381. rssi_weight := 0.75
  382. for _, metric := range beacon.beacon_metrics {
  383. loc, ok := loc_list[metric.location]
  384. if !ok {
  385. loc = seen_weight + (rssi_weight * (1.0 - (float64(metric.rssi) / -100.0)))
  386. } else {
  387. loc = loc + seen_weight + (rssi_weight * (1.0 - (float64(metric.rssi) / -100.0)))
  388. }
  389. loc_list[metric.location] = loc
  390. }
  391. best_name := ""
  392. ts := 0.0
  393. for name, times_seen := range loc_list {
  394. if times_seen > ts {
  395. best_name = name
  396. ts = times_seen
  397. }
  398. }
  399. /////fmt.Printf("BEST LOCATION FOR %s IS: %s with score: %f\n", beacon.Name, best_name, ts)
  400. best_location = Best_location{name: best_name, distance: beacon.beacon_metrics[len(beacon.beacon_metrics)-1].distance, last_seen: beacon.beacon_metrics[len(beacon.beacon_metrics)-1].timestamp}
  401. beacon.Location_history = append(beacon.Location_history, best_location.name)
  402. if len(beacon.Location_history) > 10 {
  403. beacon.Location_history = beacon.Location_history[1:] // manteniamo solo gli ultimi 10
  404. }
  405. // Calcoliamo la location più votata nello storico
  406. location_counts := make(map[string]int)
  407. for _, loc := range beacon.Location_history {
  408. location_counts[loc]++
  409. }
  410. max_count := 0
  411. most_common_location := ""
  412. for loc, count := range location_counts {
  413. if count > max_count {
  414. max_count = count
  415. most_common_location = loc
  416. }
  417. }
  418. if max_count >= 7 {
  419. beacon.Previous_location = most_common_location
  420. if most_common_location == beacon.Previous_confident_location {
  421. beacon.Location_confidence++
  422. } else {
  423. beacon.Location_confidence = 1
  424. beacon.Previous_confident_location = most_common_location
  425. }
  426. }
  427. //create an http result from this
  428. r := HTTP_location{}
  429. r.Distance = best_location.distance
  430. r.Name = beacon.Name
  431. r.Beacon_name = beacon.Name
  432. r.Beacon_id = beacon.Beacon_id
  433. r.Beacon_type = beacon.Beacon_type
  434. r.HB_Battery = beacon.HB_Battery
  435. r.HB_ButtonMode = beacon.HB_ButtonMode
  436. r.HB_ButtonCounter = beacon.HB_ButtonCounter
  437. r.Location = best_location.name
  438. r.Last_seen = best_location.last_seen
  439. if (beacon.Location_confidence == settings.Location_confidence && beacon.Previous_confident_location != best_location.name) || beacon.expired_location == "expired" {
  440. should_persist = true
  441. fmt.Printf("detected a change!!! %#v\n\n", beacon)
  442. if beacon.Previous_confident_location == "expired" && beacon.expired_location == "" {
  443. msg := Message{
  444. Email: beacon.Previous_confident_location,
  445. Username: beacon.Name,
  446. Message: "OK"}
  447. res1B, _ := json.Marshal(msg)
  448. fmt.Println(string(res1B))
  449. if err != nil {
  450. log.Printf("error: %v", err)
  451. }
  452. broadcast <- msg
  453. }
  454. beacon.Location_confidence = 0
  455. location := ""
  456. if beacon.expired_location == "expired" {
  457. location = "expired"
  458. } else {
  459. location = best_location.name
  460. }
  461. //first make the json
  462. js, err := json.Marshal(Location_change{Beacon_ref: beacon, Name: beacon.Name, Beacon_name: beacon.Name, Previous_location: beacon.Previous_confident_location, New_location: location, Timestamp: time.Now().Unix()})
  463. if err != nil {
  464. continue
  465. }
  466. //send the message
  467. err = cl.Publish(&client.PublishOptions{
  468. QoS: mqtt.QoS1,
  469. TopicName: []byte("afa-systems/presence/changes"),
  470. Message: js,
  471. })
  472. if err != nil {
  473. panic(err)
  474. }
  475. s := fmt.Sprintf("/usr/bin/php /usr/local/presence/alarm_handler.php --idt=%s --idr=%s --loct=%s", beacon.Beacon_id, beacon.Incoming_JSON.Hostname, location)
  476. ///utils.Log.Printf("%s",s)
  477. err, out, errout := Shellout(s)
  478. if err != nil {
  479. log.Printf("error: %v\n", err)
  480. }
  481. fmt.Println("--- stdout ---")
  482. fmt.Println(out)
  483. fmt.Println("--- stderr ---")
  484. fmt.Println(errout)
  485. //////beacon.logger.Printf("Log content: user id %v \n", best_location.name)
  486. if settings.HA_send_changes_only {
  487. sendHARoomMessage(beacon.Beacon_id, beacon.Name, best_location.distance, best_location.name, cl)
  488. }
  489. if beacon.expired_location == "expired" {
  490. beacon.Previous_confident_location = "expired"
  491. r.Location = "expired"
  492. } else {
  493. beacon.Previous_confident_location = best_location.name
  494. }
  495. ///beacon.Previous_confident_location = best_location.name
  496. }
  497. beacon.Previous_location = best_location.name
  498. r.Previous_confident_location = beacon.expired_location
  499. BEACONS.Beacons[beacon.Beacon_id] = beacon
  500. http_results_lock.Lock()
  501. http_results.Beacons = append(http_results.Beacons, r)
  502. http_results_lock.Unlock()
  503. if best_location.name != "" {
  504. if !settings.HA_send_changes_only {
  505. secs := int64(time.Now().Unix())
  506. if secs%settings.HA_send_interval == 0 {
  507. sendHARoomMessage(beacon.Beacon_id, beacon.Name, best_location.distance, best_location.name, cl)
  508. }
  509. }
  510. }
  511. err := cl.Publish(&client.PublishOptions{
  512. QoS: mqtt.QoS0,
  513. TopicName: []byte("afa-systems/presence"),
  514. Message: []byte(fmt.Sprintf("%s is most likely in %s with average distance %f", beacon.Name, best_location.name, best_location.distance)),
  515. })
  516. if err != nil {
  517. panic(err)
  518. }
  519. }
  520. if should_persist {
  521. persistBeacons()
  522. }
  523. }
  524. /*func doSomething(bcon Beacon, testo string ) {
  525. bcon.logger.Printf("Log content: user id %v \n", beacon.Name)
  526. }*/
  527. func IncomingMQTTProcessor(updateInterval time.Duration, cl *client.Client, db *bolt.DB, logger []*user) chan<- Incoming_json {
  528. incoming_msgs_chan := make(chan Incoming_json, 2000)
  529. // load initial BEACONS
  530. BEACONS.Beacons = make(map[string]Beacon)
  531. // retrieve the data
  532. // create bucket if not exist
  533. err = db.Update(func(tx *bolt.Tx) error {
  534. _, err := tx.CreateBucketIfNotExists(world)
  535. if err != nil {
  536. return err
  537. }
  538. return nil
  539. })
  540. err = db.View(func(tx *bolt.Tx) error {
  541. bucket := tx.Bucket(world)
  542. if bucket == nil {
  543. return err
  544. }
  545. key := []byte("beacons_list")
  546. val := bucket.Get(key)
  547. if val != nil {
  548. buf := bytes.NewBuffer(val)
  549. dec := gob.NewDecoder(buf)
  550. err = dec.Decode(&BEACONS)
  551. if err != nil {
  552. log.Fatal("decode error:", err)
  553. }
  554. }
  555. key = []byte("buttons_list")
  556. val = bucket.Get(key)
  557. if val != nil {
  558. buf := bytes.NewBuffer(val)
  559. dec := gob.NewDecoder(buf)
  560. err = dec.Decode(&Buttons_list)
  561. if err != nil {
  562. log.Fatal("decode error:", err)
  563. }
  564. }
  565. key = []byte("settings")
  566. val = bucket.Get(key)
  567. if val != nil {
  568. buf := bytes.NewBuffer(val)
  569. dec := gob.NewDecoder(buf)
  570. err = dec.Decode(&settings)
  571. if err != nil {
  572. log.Fatal("decode error:", err)
  573. }
  574. }
  575. return nil
  576. })
  577. if err != nil {
  578. log.Fatal(err)
  579. }
  580. Latest_beacons_list = make(map[string]Beacon)
  581. Buttons_list = make(map[string]Button)
  582. locations_list := Locations_list{}
  583. ls := make(map[string]Location)
  584. locations_list.locations = ls
  585. ticker := time.NewTicker(updateInterval)
  586. go func() {
  587. for {
  588. select {
  589. case <-ticker.C:
  590. getLikelyLocations(settings, locations_list, cl)
  591. case incoming := <-incoming_msgs_chan:
  592. func() {
  593. defer func() {
  594. if err := recover(); err != nil {
  595. log.Println("work failed:", err)
  596. }
  597. }()
  598. incoming = incomingBeaconFilter(incoming)
  599. this_beacon_id := getBeaconID(incoming)
  600. now := time.Now().Unix()
  601. beacon, ok := BEACONS.Beacons[this_beacon_id]
  602. if !ok {
  603. latest_list_lock.Lock()
  604. x, ok := Latest_beacons_list[this_beacon_id]
  605. if ok {
  606. x.Last_seen = now
  607. x.Incoming_JSON = incoming
  608. x.Distance = getBeaconDistance(incoming)
  609. Latest_beacons_list[this_beacon_id] = x
  610. } else {
  611. Latest_beacons_list[this_beacon_id] = Beacon{Beacon_id: this_beacon_id, Beacon_type: incoming.Beacon_type, Last_seen: now, Incoming_JSON: incoming, Beacon_location: incoming.Hostname, Distance: getBeaconDistance(incoming)}
  612. }
  613. for k, v := range Latest_beacons_list {
  614. if (now - v.Last_seen) > 10 { // 10 seconds
  615. delete(Latest_beacons_list, k)
  616. }
  617. }
  618. latest_list_lock.Unlock()
  619. //continue
  620. return
  621. }
  622. beacon.Incoming_JSON = incoming
  623. beacon.Last_seen = now
  624. beacon.Beacon_type = incoming.Beacon_type
  625. beacon.HB_ButtonCounter = incoming.HB_ButtonCounter
  626. beacon.HB_Battery = incoming.HB_Battery
  627. beacon.HB_RandomNonce = incoming.HB_RandomNonce
  628. beacon.HB_ButtonMode = incoming.HB_ButtonMode
  629. ////fmt.Println("button pressed " + this_beacon_id + " at " + strconv.Itoa(int(incoming.HB_ButtonCounter)) )
  630. if beacon.beacon_metrics == nil {
  631. beacon.beacon_metrics = make([]beacon_metric, settings.Beacon_metrics_size)
  632. }
  633. //create metric for this beacon
  634. this_metric := beacon_metric{}
  635. this_metric.distance = getBeaconDistance(incoming)
  636. this_metric.timestamp = now
  637. this_metric.rssi = int64(incoming.RSSI)
  638. this_metric.location = incoming.Hostname
  639. beacon.beacon_metrics = append(beacon.beacon_metrics, this_metric)
  640. ///fmt.Printf("APPENDING a metric from %s len %d\n", beacon.Name, len(beacon.beacon_metrics))
  641. if len(beacon.beacon_metrics) > settings.Beacon_metrics_size {
  642. //fmt.Printf("deleting a metric from %s len %d\n", beacon.Name, len(beacon.beacon_metrics))
  643. beacon.beacon_metrics = append(beacon.beacon_metrics[:0], beacon.beacon_metrics[0+1:]...)
  644. }
  645. //fmt.Printf("%#v\n", beacon.beacon_metrics)
  646. if beacon.HB_ButtonCounter_Prev != beacon.HB_ButtonCounter {
  647. beacon.HB_ButtonCounter_Prev = incoming.HB_ButtonCounter
  648. // send the button message to MQTT
  649. sendButtonPressed(beacon, cl)
  650. }
  651. BEACONS.Beacons[beacon.Beacon_id] = beacon
  652. /*if beacon.Beacon_type == "hb_button" {
  653. processButton(beacon, cl)
  654. }*/
  655. //lookup location by hostname in locations
  656. location, ok := locations_list.locations[incoming.Hostname]
  657. if !ok {
  658. //create the location
  659. locations_list.locations[incoming.Hostname] = Location{}
  660. location, ok = locations_list.locations[incoming.Hostname]
  661. location.name = incoming.Hostname
  662. }
  663. locations_list.locations[incoming.Hostname] = location
  664. }()
  665. }
  666. }
  667. }()
  668. return incoming_msgs_chan
  669. }
  670. func ParseTimeStamp(utime string) (string, error) {
  671. i, err := strconv.ParseInt(utime, 10, 64)
  672. if err != nil {
  673. return "", err
  674. }
  675. t := time.Unix(i, 0)
  676. return t.Format(time.UnixDate), nil
  677. }
  678. var http_host_path_ptr *string
  679. // var https_host_path_ptr *string
  680. var httpws_host_path_ptr *string
  681. //var httpwss_host_path_ptr *string
  682. type Todo struct {
  683. Id string `json:"id"`
  684. Value string `json:"value" binding:"required"`
  685. }
  686. type Job interface {
  687. ExitChan() chan error
  688. Run(todos map[string]Todo) (map[string]Todo, error)
  689. }
  690. func ProcessJobs(jobs chan Job, db string) {
  691. for {
  692. j := <-jobs
  693. todos := make(map[string]Todo, 0)
  694. content, err := ioutil.ReadFile(db)
  695. if err == nil {
  696. if err = json.Unmarshal(content, &todos); err == nil {
  697. todosMod, err := j.Run(todos)
  698. if err == nil && todosMod != nil {
  699. b, err := json.Marshal(todosMod)
  700. if err == nil {
  701. err = ioutil.WriteFile(db, b, 0644)
  702. }
  703. }
  704. }
  705. }
  706. j.ExitChan() <- err
  707. }
  708. }
  709. type user struct {
  710. id string
  711. logger *log.Logger
  712. }
  713. const ShellToUse = "bash"
  714. func Shellout(command string) (error, string, string) {
  715. var stdout bytes.Buffer
  716. var stderr bytes.Buffer
  717. ///utils.Log.Printf("command: %s",command)
  718. cmd := exec.Command(ShellToUse, "-c", command)
  719. cmd.Stdout = &stdout
  720. cmd.Stderr = &stderr
  721. err := cmd.Run()
  722. return err, stdout.String(), stderr.String()
  723. }
  724. func createUser(id string, logWanted bool) user {
  725. var l *log.Logger
  726. if logWanted {
  727. // Here the log content will be added in the user log file
  728. userFIle := &lumberjack.Logger{
  729. Filename: "/data/var/log/presence/presence/log_" + id + ".log",
  730. MaxSize: 250, // mb
  731. MaxBackups: 5,
  732. MaxAge: 10, // in days
  733. }
  734. l = log.New(userFIle, "User: ", log.Ldate|log.Ltime|log.Lshortfile)
  735. } else {
  736. // Here the log content will go nowhere
  737. l = log.New(ioutil.Discard, "User: ", log.Ldate|log.Ltime|log.Lshortfile)
  738. }
  739. return user{id, l}
  740. }
  741. func main() {
  742. loggers := []*user{}
  743. http_host_path_ptr = flag.String("http_host_path", "0.0.0.0:8080", "The host:port that the HTTP server should listen on")
  744. //https_host_path_ptr = flag.String("https_host_path", "0.0.0.0:5443", "The host:port that the HTTP server should listen on")
  745. httpws_host_path_ptr = flag.String("httpws_host_path", "0.0.0.0:8088", "The host:port websocket listen")
  746. //httpwss_host_path_ptr = flag.String("httpwss_host_path", "0.0.0.0:8443", "The host:port secure websocket listen")
  747. mqtt_host_ptr := flag.String("mqtt_host", "localhost:1883", "The host:port of the MQTT server to listen for beacons on")
  748. mqtt_username_ptr := flag.String("mqtt_username", "none", "The username needed to connect to the MQTT server, 'none' if it doesn't need one")
  749. mqtt_password_ptr := flag.String("mqtt_password", "none", "The password needed to connect to the MQTT server, 'none' if it doesn't need one")
  750. mqtt_client_id_ptr := flag.String("mqtt_client_id", "presence-detector", "The client ID for the MQTT server")
  751. flag.Parse()
  752. ///utils.NewLog(*logpath)
  753. ///utils.Log.Println("hello")
  754. // Set up channel on which to send signal notifications.
  755. sigc := make(chan os.Signal, 1)
  756. signal.Notify(sigc, os.Interrupt, os.Kill)
  757. // Create an MQTT Client.
  758. cli := client.New(&client.Options{
  759. // Define the processing of the error handler.
  760. ErrorHandler: func(err error) {
  761. fmt.Println(err)
  762. },
  763. })
  764. // Terminate the Client.
  765. defer cli.Terminate()
  766. //open the database
  767. db, err = bolt.Open("/data/conf/presence/presence.db", 0644, nil)
  768. if err != nil {
  769. log.Fatal(err)
  770. }
  771. defer db.Close()
  772. // Connect to the MQTT Server.
  773. err = cli.Connect(&client.ConnectOptions{
  774. Network: "tcp",
  775. Address: *mqtt_host_ptr,
  776. ClientID: []byte(*mqtt_client_id_ptr),
  777. UserName: []byte(*mqtt_username_ptr),
  778. Password: []byte(*mqtt_password_ptr),
  779. })
  780. if err != nil {
  781. panic(err)
  782. }
  783. incoming_updates_chan := IncomingMQTTProcessor(1*time.Second, cli, db, loggers)
  784. // Subscribe to topics.
  785. err = cli.Subscribe(&client.SubscribeOptions{
  786. SubReqs: []*client.SubReq{
  787. &client.SubReq{
  788. TopicFilter: []byte("publish_out/#"),
  789. QoS: mqtt.QoS0,
  790. Handler: func(topicName, message []byte) {
  791. msgStr := string(message)
  792. t := strings.Split(string(topicName), "/")
  793. hostname := t[1]
  794. //Formato JSON multiplo
  795. //publish_out/170361001234 [{"timestamp":"2025-06-11T11:27:28.492Z","type":"Gateway","mac":"E4B3230DB5CC","nums":10},{"timestamp":"2025-06-11T11:27:28.483Z","mac":"36CE2D7CA4E5","rssi":-27,"rawData":"1EFF0600010F20226F50BB5F834F6C9CE3D876B0C3F665882955B368D3B96C"},{"timestamp":"2025-06-11T11:27:28.586Z","mac":"36CE2D7CA4E5","rssi":-30,"rawData":"1EFF0600010F20226F50BB5F834F6C9CE3D876B0C3F665882955B368D3B96C"},{"timestamp":"2025-06-11T11:27:28.612Z","mac":"406260A302FC","rssi":-35,"rawData":"02011A020A0B0BFF4C001006371AAE2F6F5B"},{"timestamp":"2025-06-11T11:27:28.798Z","mac":"36CE2D7CA4E5","rssi":-28,"rawData":"1EFF0600010F20226F50BB5F834F6C9CE3D876B0C3F665882955B368D3B96C"},{"timestamp":"2025-06-11T11:27:28.905Z","mac":"36CE2D7CA4E5","rssi":-30,"rawData":"1EFF0600010F20226F50BB5F834F6C9CE3D876B0C3F665882955B368D3B96C"},{"timestamp":"2025-06-11T11:27:28.945Z","mac":"C300003947DF","rssi":-32,"rawData":"0201061AFF4C000215FDA50693A4E24FB1AFCFC6EB0764782500000000C5"},{"timestamp":"2025-06-11T11:27:29.013Z","mac":"36CE2D7CA4E5","rssi":-29,"rawData":"1EFF0600010F20226F50BB5F834F6C9CE3D876B0C3F665882955B368D3B96C"},{"timestamp":"2025-06-11T11:27:29.120Z","mac":"36CE2D7CA4E5","rssi":-27,"rawData":"1EFF0600010F20226F50BB5F834F6C9CE3D876B0C3F665882955B368D3B96C"},{"timestamp":"2025-06-11T11:27:29.166Z","mac":"406260A302FC","rssi":-34,"rawData":"02011A020A0B0BFF4C001006371AAE2F6F5B"},{"timestamp":"2025-06-11T11:27:29.337Z","mac":"36CE2D7CA4E5","rssi":-26,"rawData":"1EFF0600010F20226F50BB5F834F6C9CE3D876B0C3F665882955B368D3B96C"}]
  796. if strings.HasPrefix(msgStr, "[") {
  797. var readings []RawReading
  798. err := json.Unmarshal(message, &readings)
  799. if err != nil {
  800. log.Printf("Errore parsing JSON: %v", err)
  801. return
  802. }
  803. for _, reading := range readings {
  804. if reading.Type == "Gateway" {
  805. continue
  806. }
  807. incoming := Incoming_json{
  808. Hostname: hostname,
  809. MAC: reading.MAC,
  810. RSSI: int64(reading.RSSI),
  811. Data: reading.RawData,
  812. HB_ButtonCounter: parseButtonState(reading.RawData),
  813. }
  814. incoming_updates_chan <- incoming
  815. }
  816. } else {
  817. //Formato CSV
  818. //ingics solo annuncio
  819. //publish_out/171061001180 $GPRP,C83F8F17DB35,F5B0B0419FEF,-44,02010612FF590080BC280102FFFFFFFF000000000000,1749648798
  820. //ingics tasto premuto
  821. //publish_out/171061001180 $GPRP,C83F8F17DB35,F5B0B0419FEF,-44,02010612FF590080BC280103FFFFFFFF000000000000,1749648798
  822. s := strings.Split(string(message), ",")
  823. if len(s) < 6 {
  824. log.Printf("Messaggio CSV non valido: %s", msgStr)
  825. return
  826. }
  827. rawdata := s[4]
  828. buttonCounter := parseButtonState(rawdata)
  829. if buttonCounter > 0 {
  830. incoming := Incoming_json{}
  831. i, _ := strconv.ParseInt(s[3], 10, 64)
  832. incoming.Hostname = hostname
  833. incoming.Beacon_type = "hb_button"
  834. incoming.MAC = s[1]
  835. incoming.RSSI = i
  836. incoming.Data = rawdata
  837. incoming.HB_ButtonCounter = buttonCounter
  838. read_line := strings.TrimRight(string(s[5]), "\r\n")
  839. it, err33 := strconv.Atoi(read_line)
  840. if err33 != nil {
  841. fmt.Println(it)
  842. fmt.Println(err33)
  843. os.Exit(2)
  844. }
  845. incoming_updates_chan <- incoming
  846. }
  847. }
  848. },
  849. },
  850. },
  851. })
  852. if err != nil {
  853. panic(err)
  854. }
  855. fmt.Println("CONNECTED TO MQTT")
  856. fmt.Println("\n ")
  857. fmt.Println("Visit http://" + *http_host_path_ptr + " on your browser to see the web interface")
  858. fmt.Println("\n ")
  859. go startServer()
  860. // Wait for receiving a signal.
  861. <-sigc
  862. // Disconnect the Network Connection.
  863. if err := cli.Disconnect(); err != nil {
  864. panic(err)
  865. }
  866. }
  867. func startServer() {
  868. headersOk := handlers.AllowedHeaders([]string{"X-Requested-With"})
  869. originsOk := handlers.AllowedOrigins([]string{"*"})
  870. methodsOk := handlers.AllowedMethods([]string{"GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS"})
  871. // Set up HTTP server
  872. r := mux.NewRouter()
  873. r.HandleFunc("/api/results", resultsHandler)
  874. r.HandleFunc("/api/beacons/{beacon_id}", beaconsDeleteHandler).Methods("DELETE")
  875. r.HandleFunc("/api/beacons", beaconsListHandler).Methods("GET")
  876. r.HandleFunc("/api/beacons", beaconsAddHandler).Methods("POST") //since beacons are hashmap, just have put and post be same thing. it'll either add or modify that entry
  877. r.HandleFunc("/api/beacons", beaconsAddHandler).Methods("PUT")
  878. r.HandleFunc("/api/latest-beacons", latestBeaconsListHandler).Methods("GET")
  879. r.HandleFunc("/api/settings", settingsListHandler).Methods("GET")
  880. r.HandleFunc("/api/settings", settingsEditHandler).Methods("POST")
  881. r.PathPrefix("/js/").Handler(http.StripPrefix("/js/", http.FileServer(http.Dir("static_html/js/"))))
  882. r.PathPrefix("/css/").Handler(http.StripPrefix("/css/", http.FileServer(http.Dir("static_html/css/"))))
  883. r.PathPrefix("/img/").Handler(http.StripPrefix("/img/", http.FileServer(http.Dir("static_html/img/"))))
  884. r.PathPrefix("/").Handler(http.FileServer(http.Dir("static_html/")))
  885. http.Handle("/", r)
  886. mxWS := mux.NewRouter()
  887. mxWS.HandleFunc("/ws/api/beacons", serveWs)
  888. mxWS.HandleFunc("/ws/api/beacons/latest", serveLatestBeaconsWs)
  889. mxWS.HandleFunc("/ws/broadcast", handleConnections)
  890. http.Handle("/ws/", mxWS)
  891. go func() {
  892. log.Fatal(http.ListenAndServe(*httpws_host_path_ptr, nil))
  893. }()
  894. // Start listening for incoming chat messages
  895. go handleMessages()
  896. ///"/conf/etc/cert/certs/services/htdocs/majornet.crt", "/conf/etc/cert/private/services/htdocs/majornet.key"
  897. http.ListenAndServe(*http_host_path_ptr, handlers.CORS(originsOk, headersOk, methodsOk)(r))
  898. }
  899. func handleConnections(w http.ResponseWriter, r *http.Request) {
  900. // Upgrade initial GET request to a websocket
  901. ws, err := upgrader.Upgrade(w, r, nil)
  902. if err != nil {
  903. log.Fatal(err)
  904. }
  905. // Make sure we close the connection when the function returns
  906. defer ws.Close()
  907. // Register our new client
  908. clients[ws] = true
  909. for {
  910. var msg Message
  911. // Read in a new message as JSON and map it to a Message object
  912. err := ws.ReadJSON(&msg)
  913. if err != nil {
  914. log.Printf("error: %v", err)
  915. delete(clients, ws)
  916. break
  917. }
  918. // Send the newly received message to the broadcast channel
  919. broadcast <- msg
  920. }
  921. }
  922. func handleMessages() {
  923. for {
  924. // Grab the next message from the broadcast channel
  925. msg := <-broadcast
  926. // Send it out to every client that is currently connected
  927. for client := range clients {
  928. err := client.WriteJSON(msg)
  929. if err != nil {
  930. log.Printf("error: %v", err)
  931. client.Close()
  932. delete(clients, client)
  933. }
  934. }
  935. }
  936. }
  937. func resultsHandler(w http.ResponseWriter, r *http.Request) {
  938. http_results_lock.RLock()
  939. js, err := json.Marshal(http_results)
  940. http_results_lock.RUnlock()
  941. if err != nil {
  942. http.Error(w, err.Error(), http.StatusInternalServerError)
  943. return
  944. }
  945. w.Write(js)
  946. }
  947. func beaconsListHandler(w http.ResponseWriter, r *http.Request) {
  948. latest_list_lock.RLock()
  949. js, err := json.Marshal(BEACONS)
  950. latest_list_lock.RUnlock()
  951. if err != nil {
  952. http.Error(w, err.Error(), http.StatusInternalServerError)
  953. return
  954. }
  955. w.Write(js)
  956. }
  957. func persistBeacons() error {
  958. // gob it first
  959. buf := &bytes.Buffer{}
  960. enc := gob.NewEncoder(buf)
  961. if err := enc.Encode(BEACONS); err != nil {
  962. return err
  963. }
  964. key := []byte("beacons_list")
  965. // store some data
  966. err = db.Update(func(tx *bolt.Tx) error {
  967. bucket, err := tx.CreateBucketIfNotExists(world)
  968. if err != nil {
  969. return err
  970. }
  971. err = bucket.Put(key, []byte(buf.String()))
  972. if err != nil {
  973. return err
  974. }
  975. return nil
  976. })
  977. return nil
  978. }
  979. func persistSettings() error {
  980. // gob it first
  981. buf := &bytes.Buffer{}
  982. enc := gob.NewEncoder(buf)
  983. if err := enc.Encode(settings); err != nil {
  984. return err
  985. }
  986. key := []byte("settings")
  987. // store some data
  988. err = db.Update(func(tx *bolt.Tx) error {
  989. bucket, err := tx.CreateBucketIfNotExists(world)
  990. if err != nil {
  991. return err
  992. }
  993. err = bucket.Put(key, []byte(buf.String()))
  994. if err != nil {
  995. return err
  996. }
  997. return nil
  998. })
  999. return nil
  1000. }
  1001. func beaconsAddHandler(w http.ResponseWriter, r *http.Request) {
  1002. decoder := json.NewDecoder(r.Body)
  1003. var in_beacon Beacon
  1004. err = decoder.Decode(&in_beacon)
  1005. if err != nil {
  1006. http.Error(w, err.Error(), 400)
  1007. return
  1008. }
  1009. //make sure name and beacon_id are present
  1010. if (len(strings.TrimSpace(in_beacon.Name)) == 0) || (len(strings.TrimSpace(in_beacon.Beacon_id)) == 0) {
  1011. http.Error(w, "name and beacon_id cannot be blank", 400)
  1012. return
  1013. }
  1014. BEACONS.Beacons[in_beacon.Beacon_id] = in_beacon
  1015. err := persistBeacons()
  1016. if err != nil {
  1017. http.Error(w, "trouble persisting beacons list, create bucket", 500)
  1018. return
  1019. }
  1020. w.Write([]byte("ok"))
  1021. }
  1022. func beaconsDeleteHandler(w http.ResponseWriter, r *http.Request) {
  1023. vars := mux.Vars(r)
  1024. beacon_id := vars["beacon_id"]
  1025. delete(BEACONS.Beacons, beacon_id)
  1026. _, ok := Buttons_list[beacon_id]
  1027. if ok {
  1028. delete(Buttons_list, beacon_id)
  1029. }
  1030. err := persistBeacons()
  1031. if err != nil {
  1032. http.Error(w, "trouble persisting beacons list, create bucket", 500)
  1033. return
  1034. }
  1035. w.Write([]byte("ok"))
  1036. }
  1037. func latestBeaconsListHandler(w http.ResponseWriter, r *http.Request) {
  1038. latest_list_lock.RLock()
  1039. var la = make([]Beacon, 0)
  1040. for _, b := range Latest_beacons_list {
  1041. la = append(la, b)
  1042. }
  1043. latest_list_lock.RUnlock()
  1044. js, err := json.Marshal(la)
  1045. if err != nil {
  1046. http.Error(w, err.Error(), http.StatusInternalServerError)
  1047. return
  1048. }
  1049. w.Write(js)
  1050. }
  1051. func settingsListHandler(w http.ResponseWriter, r *http.Request) {
  1052. js, err := json.Marshal(settings)
  1053. if err != nil {
  1054. http.Error(w, err.Error(), http.StatusInternalServerError)
  1055. return
  1056. }
  1057. w.Write(js)
  1058. }
  1059. func settingsEditHandler(w http.ResponseWriter, r *http.Request) {
  1060. decoder := json.NewDecoder(r.Body)
  1061. var in_settings Settings
  1062. err = decoder.Decode(&in_settings)
  1063. if err != nil {
  1064. http.Error(w, err.Error(), 400)
  1065. return
  1066. }
  1067. //make sure values are > 0
  1068. if (in_settings.Location_confidence <= 0) ||
  1069. (in_settings.Last_seen_threshold <= 0) ||
  1070. (in_settings.HA_send_interval <= 0) {
  1071. http.Error(w, "values must be greater than 0", 400)
  1072. return
  1073. }
  1074. settings = in_settings
  1075. err := persistSettings()
  1076. if err != nil {
  1077. http.Error(w, "trouble persisting settings, create bucket", 500)
  1078. return
  1079. }
  1080. w.Write([]byte("ok"))
  1081. }
  1082. func reader(ws *websocket.Conn) {
  1083. defer ws.Close()
  1084. ws.SetReadLimit(512)
  1085. ws.SetReadDeadline(time.Now().Add(pongWait))
  1086. ws.SetPongHandler(func(string) error { ws.SetReadDeadline(time.Now().Add(pongWait)); return nil })
  1087. for {
  1088. _, _, err := ws.ReadMessage()
  1089. if err != nil {
  1090. break
  1091. }
  1092. }
  1093. }
  1094. func writer(ws *websocket.Conn) {
  1095. pingTicker := time.NewTicker(pingPeriod)
  1096. beaconTicker := time.NewTicker(beaconPeriod)
  1097. defer func() {
  1098. pingTicker.Stop()
  1099. beaconTicker.Stop()
  1100. ws.Close()
  1101. }()
  1102. for {
  1103. select {
  1104. case <-beaconTicker.C:
  1105. http_results_lock.RLock()
  1106. js, err := json.Marshal(http_results)
  1107. http_results_lock.RUnlock()
  1108. if err != nil {
  1109. js = []byte("error")
  1110. }
  1111. ws.SetWriteDeadline(time.Now().Add(writeWait))
  1112. if err := ws.WriteMessage(websocket.TextMessage, js); err != nil {
  1113. return
  1114. }
  1115. case <-pingTicker.C:
  1116. ws.SetWriteDeadline(time.Now().Add(writeWait))
  1117. if err := ws.WriteMessage(websocket.PingMessage, []byte{}); err != nil {
  1118. return
  1119. }
  1120. }
  1121. }
  1122. }
  1123. func serveWs(w http.ResponseWriter, r *http.Request) {
  1124. ws, err := upgrader.Upgrade(w, r, nil)
  1125. if err != nil {
  1126. if _, ok := err.(websocket.HandshakeError); !ok {
  1127. log.Println(err)
  1128. }
  1129. return
  1130. }
  1131. go writer(ws)
  1132. reader(ws)
  1133. }
  1134. func latestBeaconWriter(ws *websocket.Conn) {
  1135. pingTicker := time.NewTicker(pingPeriod)
  1136. beaconTicker := time.NewTicker(beaconPeriod)
  1137. defer func() {
  1138. pingTicker.Stop()
  1139. beaconTicker.Stop()
  1140. ws.Close()
  1141. }()
  1142. for {
  1143. select {
  1144. case <-beaconTicker.C:
  1145. latest_list_lock.RLock()
  1146. var la = make([]Beacon, 0)
  1147. for _, b := range Latest_beacons_list {
  1148. la = append(la, b)
  1149. }
  1150. latest_list_lock.RUnlock()
  1151. js, err := json.Marshal(la)
  1152. if err != nil {
  1153. js = []byte("error")
  1154. }
  1155. ws.SetWriteDeadline(time.Now().Add(writeWait))
  1156. if err := ws.WriteMessage(websocket.TextMessage, js); err != nil {
  1157. return
  1158. }
  1159. case <-pingTicker.C:
  1160. ws.SetWriteDeadline(time.Now().Add(writeWait))
  1161. if err := ws.WriteMessage(websocket.PingMessage, []byte{}); err != nil {
  1162. return
  1163. }
  1164. }
  1165. }
  1166. }
  1167. func serveLatestBeaconsWs(w http.ResponseWriter, r *http.Request) {
  1168. ws, err := upgrader.Upgrade(w, r, nil)
  1169. if err != nil {
  1170. if _, ok := err.(websocket.HandshakeError); !ok {
  1171. log.Println(err)
  1172. }
  1173. return
  1174. }
  1175. go latestBeaconWriter(ws)
  1176. reader(ws)
  1177. }