|
- package main
-
- import (
- "bytes"
- "encoding/gob"
- "encoding/json"
- "flag"
- "fmt"
- "log"
- "math"
- "net/http"
- "os"
- "os/signal"
- "strconv"
- "strings"
- "sync"
- "time"
- "io/ioutil"
- //"./utils"
- "gopkg.in/natefinch/lumberjack.v2"
- "os/exec"
- "github.com/boltdb/bolt"
-
- "github.com/yosssi/gmq/mqtt"
- "github.com/yosssi/gmq/mqtt/client"
-
- "github.com/gorilla/mux"
- "github.com/gorilla/websocket"
- "github.com/gorilla/handlers"
- )
-
- const (
- // Time allowed to write the file to the client.
- writeWait = 10 * time.Second
-
- // Time allowed to read the next pong message from the client.
- pongWait = 60 * time.Second
-
- // Send pings to client with this period. Must be less than pongWait.
- pingPeriod = (pongWait * 9) / 10
-
- beaconPeriod = 2 * time.Second
- )
-
- // data structures
-
- type Settings struct {
- Location_confidence int64 `json:"location_confidence"`
- Last_seen_threshold int64 `json:"last_seen_threshold"`
- Beacon_metrics_size int `json:"beacon_metrics_size"`
- HA_send_interval int64 `json:"ha_send_interval"`
- HA_send_changes_only bool `json:"ha_send_changes_only"`
- }
-
- type Incoming_json struct {
- Hostname string `json:"hostname"`
- MAC string `json:"mac"`
- RSSI int64 `json:"rssi"`
- Is_scan_response string `json:"is_scan_response"`
- Ttype string `json:"type"`
- Data string `json:"data"`
- Beacon_type string `json:"beacon_type"`
- UUID string `json:"uuid"`
- Major string `json:"major"`
- Minor string `json:"minor"`
- TX_power string `json:"tx_power"`
- Namespace string `json:"namespace"`
- Instance_id string `json:"instance_id"`
- // button stuff
- HB_ButtonCounter int64 `json:"hb_button_counter"`
- HB_ButtonCounter_Prev int64 `json:"hb_button_counter"`
- HB_Battery int64 `json:"hb_button_battery"`
- HB_RandomNonce string `json:"hb_button_random"`
- HB_ButtonMode string `json:"hb_button_mode"`
- }
-
- type Advertisement struct {
- ttype string
- content string
- seen int64
- }
-
- type beacon_metric struct {
- location string
- distance float64
- rssi int64
- timestamp int64
- }
-
- type Location struct {
- name string
- lock sync.RWMutex
- }
-
- type Best_location struct {
- distance float64
- name string
- last_seen int64
- }
-
- type HTTP_location struct {
- Previous_confident_location string `json:"previous_confident_location"`
- Distance float64 `json:"distance"`
- Name string `json:"name"`
- Beacon_name string `json:"beacon_name"`
- Beacon_id string `json:"beacon_id"`
- Beacon_type string `json:"beacon_type"`
- HB_Battery int64 `json:"hb_button_battery"`
- HB_ButtonMode string `json:"hb_button_mode"`
- HB_ButtonCounter int64 `json:"hb_button_counter"`
- Location string `json:"location"`
- Last_seen int64 `json:"last_seen"`
-
- }
-
- type Location_change struct {
- Beacon_ref Beacon `json:"beacon_info"`
- Name string `json:"name"`
- Beacon_name string `json:"beacon_name"`
- Previous_location string `json:"previous_location"`
- New_location string `json:"new_location"`
- Timestamp int64 `json:"timestamp"`
- }
-
- type HA_message struct {
- Beacon_id string `json:"id"`
- Beacon_name string `json:"name"`
- Distance float64 `json:"distance"`
- }
-
- type HTTP_locations_list struct {
- Beacons []HTTP_location `json:"beacons"`
- //Buttons []Button `json:"buttons"`
- }
-
- type Beacon struct {
- Name string `json:"name"`
- Beacon_id string `json:"beacon_id"`
- Beacon_type string `json:"beacon_type"`
- Beacon_location string `json:"beacon_location"`
- Last_seen int64 `json:"last_seen"`
- Incoming_JSON Incoming_json `json:"incoming_json"`
- Distance float64 `json:"distance"`
- Previous_location string
- Previous_confident_location string
- expired_location string
- Location_confidence int64
- Location_history []string
- beacon_metrics []beacon_metric
-
- HB_ButtonCounter int64 `json:"hb_button_counter"`
- HB_ButtonCounter_Prev int64 `json:"hb_button_counter"`
- HB_Battery int64 `json:"hb_button_battery"`
- HB_RandomNonce string `json:"hb_button_random"`
- HB_ButtonMode string `json:"hb_button_mode"`
-
- }
-
- type Button struct {
- Name string `json:"name"`
- Button_id string `json:"button_id"`
- Button_type string `json:"button_type"`
- Button_location string `json:"button_location"`
- Incoming_JSON Incoming_json `json:"incoming_json"`
- Distance float64 `json:"distance"`
- Last_seen int64 `json:"last_seen"`
-
- HB_ButtonCounter int64 `json:"hb_button_counter"`
- HB_Battery int64 `json:"hb_button_battery"`
- HB_RandomNonce string `json:"hb_button_random"`
- HB_ButtonMode string `json:"hb_button_mode"`
- }
-
- type Beacons_list struct {
- Beacons map[string]Beacon `json:"beacons"`
- lock sync.RWMutex
- }
-
- type Locations_list struct {
- locations map[string]Location
- lock sync.RWMutex
- }
-
- var clients = make(map[*websocket.Conn]bool) // connected clients
- var broadcast = make(chan Message) // broadcast channel
-
-
- // Define our message object
- type Message struct {
- Email string `json:"email"`
- Username string `json:"username"`
- Message string `json:"message"`
- }
-
- // Struttura per il parsing JSON multiplo
- type RawReading struct {
- Timestamp string `json:"timestamp"`
- Type string `json:"type"`
- MAC string `json:"mac"`
- RSSI int `json:"rssi"`
- RawData string `json:"rawData"`
- }
-
-
- // GLOBALS
-
- var BEACONS Beacons_list
-
- var Buttons_list map[string]Button
-
- var cli *client.Client
-
- var http_results HTTP_locations_list
- var http_results_lock sync.RWMutex
-
- var Latest_beacons_list map[string]Beacon
- var latest_list_lock sync.RWMutex
-
- var db *bolt.DB
- var err error
-
- var world = []byte("presence")
-
-
-
- var (
- ///logpath = flag.String("logpath", "/data/var/log/presence/presence.log", "Log Path")
- )
-
- var (
- // Websocket http upgrader
- upgrader = websocket.Upgrader{
- ReadBufferSize: 1024,
- WriteBufferSize: 1024,
- CheckOrigin: func(r *http.Request) bool {
- return true
- },
- }
- )
-
-
-
- var settings = Settings{
- Location_confidence: 4,
- Last_seen_threshold: 15,
- Beacon_metrics_size: 30,
- HA_send_interval: 5,
- HA_send_changes_only: false,
- }
-
- // utility function
- func parseButtonState(raw string) int64 {
- raw = strings.ToUpper(raw)
-
- // Minew B7 / C7 / D7 - frame tipo: 0201060303E1FF1216E1FFA103...
- if strings.HasPrefix(raw, "0201060303E1FF12") && len(raw) >= 38 {
- // La posizione 34-38 (indice 26:30) contiene il buttonCounter su 2 byte (hex)
- buttonField := raw[34:38] // NB: offset 34-38 zero-based
- if buttonValue, err := strconv.ParseInt(buttonField, 16, 64); err == nil {
- return buttonValue
- }
- }
-
- // Ingics (02010612FF590)
- if strings.HasPrefix(raw, "02010612FF590") && len(raw) >= 24 {
- counterField := raw[22:24]
- buttonState, err := strconv.ParseInt(counterField, 16, 64)
- if err == nil {
- return buttonState
- }
- }
-
- // Aggiungeremo qui facilmente nuovi beacon in futuro
-
- return 0
- }
-
- func twos_comp(inp string) int64 {
- i, _ := strconv.ParseInt("0x"+inp, 0, 64)
-
-
-
- return i - 256
- }
-
- func getBeaconID(incoming Incoming_json) string {
- unique_id := fmt.Sprintf("%s", incoming.MAC)
- /*if incoming.Beacon_type == "ibeacon" {
- unique_id = fmt.Sprintf("%s_%s_%s", incoming.UUID, incoming.Major, incoming.Minor)
- } else if incoming.Beacon_type == "eddystone" {
- unique_id = fmt.Sprintf("%s_%s", incoming.Namespace, incoming.Instance_id)
- } else if incoming.Beacon_type == "hb_button" {
- unique_id = fmt.Sprintf("%s_%s", incoming.Namespace, incoming.Instance_id)
- }*/
- return unique_id
- }
-
- func incomingBeaconFilter(incoming Incoming_json) Incoming_json {
- out_json := incoming
- if incoming.Beacon_type == "hb_button" {
- //do additional checks here to detect if a Habby Bubbles Button
- // looks like 020104020a0011ff045600012d3859db59e1000b9453
-
- raw_data := incoming.Data
- //company_id := []byte{0x04, 0x56}
- //product_id := []byte{0x00, 0x01}
- hb_button_prefix_str := fmt.Sprintf("02010612FF5900")
- if strings.HasPrefix(raw_data, hb_button_prefix_str) {
- out_json.Namespace = "ddddeeeeeeffff5544ff"
- //out_json.Instance_id = raw_data[24:36]
- counter_str := fmt.Sprintf("0x%s", raw_data[22:24])
- counter, _ := strconv.ParseInt(counter_str, 0, 64)
- out_json.HB_ButtonCounter = counter
-
- battery_str := fmt.Sprintf("0x%s%s", raw_data[20:22],raw_data[18:20])
- ////fmt.Println("battery has %s\n", battery_str)
-
- battery, _ := strconv.ParseInt(battery_str, 0, 64)
- out_json.HB_Battery = battery
-
- out_json.TX_power = fmt.Sprintf("0x%s", "4")
-
- out_json.Beacon_type = "hb_button"
- out_json.HB_ButtonMode = "presence_button"
-
-
- ///fmt.Println("Button adv has %#v\n", out_json)
- }
- }
-
- return out_json
- }
-
-
-
-
- func processButton(bbeacon Beacon, cl *client.Client) {
- btn := Button{Name: bbeacon.Name}
- btn.Button_id = bbeacon.Beacon_id
- btn.Button_type = bbeacon.Beacon_type
- btn.Button_location = bbeacon.Previous_location
- btn.Incoming_JSON = bbeacon.Incoming_JSON
- btn.Distance = bbeacon.Distance
- btn.Last_seen = bbeacon.Last_seen
- btn.HB_ButtonCounter = bbeacon.HB_ButtonCounter
- btn.HB_Battery = bbeacon.HB_Battery
- btn.HB_RandomNonce = bbeacon.HB_RandomNonce
- btn.HB_ButtonMode = bbeacon.HB_ButtonMode
-
- nonce, ok := Buttons_list[btn.Button_id]
- if !ok || nonce.HB_RandomNonce != btn.HB_RandomNonce {
- // send the button message to MQTT
- sendButtonMessage(btn, cl)
- }
- Buttons_list[btn.Button_id] = btn
- }
-
- func getiBeaconDistance(rssi int64, power string) float64 {
-
- ratio := float64(rssi) * (1.0 / float64(twos_comp(power)))
- //fmt.Printf("beaconpower: rssi %d ratio %e power %e \n",rssi, ratio, float64(twos_comp(power)))
- distance := 100.0
- if ratio < 1.0 {
- distance = math.Pow(ratio, 10)
- } else {
- distance = (0.89976)*math.Pow(ratio, 7.7095) + 0.111
- }
- return distance
- }
-
- func getBeaconDistance(incoming Incoming_json) float64 {
- distance := 1000.0
-
- distance = getiBeaconDistance(incoming.RSSI, incoming.TX_power)
- //distance = math.Abs(float64(incoming.RSSI))
-
- return distance
- }
-
- func getAverageDistance(beacon_metrics []beacon_metric) float64 {
- total := 0.0
-
- for _, v := range beacon_metrics {
- total += v.distance
- }
- return (total / float64(len(beacon_metrics)))
- }
-
- func sendHARoomMessage(beacon_id string, beacon_name string, distance float64, location string, cl *client.Client) {
- //first make the json
- ha_msg, err := json.Marshal(HA_message{Beacon_id: beacon_id, Beacon_name: beacon_name, Distance: distance})
- if err != nil {
- panic(err)
- }
-
- //send the message to HA
- err = cl.Publish(&client.PublishOptions{
- QoS: mqtt.QoS1,
- TopicName: []byte("afa-systems/presence/ha/" + location),
- Message: ha_msg,
- })
- if err != nil {
- panic(err)
- }
- }
-
- func sendButtonMessage(btn Button, cl *client.Client) {
- //first make the json
- btn_msg, err := json.Marshal(btn)
- if err != nil {
- panic(err)
- }
-
- //send the message to HA
- err = cl.Publish(&client.PublishOptions{
- QoS: mqtt.QoS1,
- TopicName: []byte("afa-systems/presence/button/" + btn.Button_id),
- Message: btn_msg,
- })
- if err != nil {
- panic(err)
- }
-
-
- }
-
- func sendButtonPressed(bcn Beacon, cl *client.Client) {
- //first make the json
- btn_msg, err := json.Marshal(bcn)
- if err != nil {
- panic(err)
- }
-
- //send the message to HA
- err = cl.Publish(&client.PublishOptions{
- QoS: mqtt.QoS1,
- TopicName: []byte("afa-systems/presence/button/" + bcn.Beacon_id),
- Message: btn_msg,
- })
- if err != nil {
- panic(err)
- }
- ///utils.Log.Printf("%s pressed ",bcn.Beacon_id)
- 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)
- ///utils.Log.Printf("%s",s)
- err, out, errout := Shellout(s)
- if err != nil {
- log.Printf("error: %v\n", err)
- }
- fmt.Println("--- stdout ---")
- fmt.Println(out)
- fmt.Println("--- stderr ---")
- fmt.Println(errout)
-
- // create the file if it doesn't exists with O_CREATE, Set the file up for read write, add the append flag and set the permission
- //f, err := os.OpenFile("/data/conf/presence/db.json", os.O_CREATE|os.O_RDWR|os.O_APPEND, 0660)
- //if err != nil {
- // log.Fatal(err)
- //}
- // write to file, f.Write()
- //f.Write(btn_msg)
- }
-
-
- func getLikelyLocations(settings Settings, locations_list Locations_list, cl *client.Client) {
- // create the http results structure
- http_results_lock.Lock()
- http_results = HTTP_locations_list{}
- http_results.Beacons = make([]HTTP_location, 0)
- ///http_results.Buttons = make([]Button, 0)
- http_results_lock.Unlock()
-
- should_persist := false
-
- // iterate through the beacons we want to search for
- for _, beacon := range BEACONS.Beacons {
-
- if len(beacon.beacon_metrics) == 0 {
- ////fmt.Printf("beacon_metrics = 0:\n")
- continue
- }
-
- if (int64(time.Now().Unix()) - (beacon.beacon_metrics[len(beacon.beacon_metrics)-1].timestamp)) > settings.Last_seen_threshold {
- ////fmt.Printf("beacon_metrics timestamp = %s %s \n",beacon.Name, beacon.beacon_metrics[len(beacon.beacon_metrics)-1].timestamp )
- if (beacon.expired_location == "expired") {
- //beacon.Location_confidence = - 1
- continue
- } else {
- beacon.expired_location = "expired"
- msg := Message{
- Email: beacon.Previous_confident_location,
- Username: beacon.Name,
- Message: beacon.expired_location,}
- res1B, _ := json.Marshal(msg)
- fmt.Println(string(res1B))
-
- if err != nil {
- log.Printf("error: %v", err)
-
- }
- // Send the newly received message to the broadcast channel
- broadcast <- msg
- }
- } else {
- beacon.expired_location = ""
-
- }
-
- best_location := Best_location{}
-
- // go through its beacon metrics and pick out the location that appears most often
- loc_list := make(map[string]float64)
- seen_weight := 1.5
- rssi_weight := 0.75
- for _, metric := range beacon.beacon_metrics {
- loc, ok := loc_list[metric.location]
- if !ok {
- loc = seen_weight + (rssi_weight * (1.0 - (float64(metric.rssi) / -100.0)))
- } else {
- loc = loc + seen_weight + (rssi_weight * (1.0 - (float64(metric.rssi) / -100.0)))
- }
- loc_list[metric.location] = loc
- }
- //fmt.Printf("beacon: %s list: %#v\n", beacon.Name, loc_list)
- // now go through the list and find the largest, that's the location
- best_name := ""
- ts := 0.0
- for name, times_seen := range loc_list {
- if times_seen > ts {
- best_name = name
- ts = times_seen
- }
- }
- /////fmt.Printf("BEST LOCATION FOR %s IS: %s with score: %f\n", beacon.Name, best_name, ts)
- 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}
-
- // //filter, only let this location become best if it was X times in a row
- // if best_location.name == beacon.Previous_location {
- // beacon.Location_confidence = beacon.Location_confidence + 1
- // } else {
- // beacon.Location_confidence = 0
- // /////fmt.Printf("beacon.Location_confidence %f\n", beacon.Location_confidence)
- // }
-
- // Aggiungiamo il nuovo best_location allo storico
- beacon.Location_history = append(beacon.Location_history, best_location.name)
- if len(beacon.Location_history) > 10 {
- beacon.Location_history = beacon.Location_history[1:] // manteniamo solo gli ultimi 10
- }
-
- // Calcoliamo la location più votata nello storico
- location_counts := make(map[string]int)
- for _, loc := range beacon.Location_history {
- location_counts[loc]++
- }
-
- max_count := 0
- most_common_location := ""
- for loc, count := range location_counts {
- if count > max_count {
- max_count = count
- most_common_location = loc
- }
- }
-
- // Applichiamo un filtro: consideriamo il cambio solo se almeno 7 su 10 votano per una location
- if max_count >= 7 {
- beacon.Previous_location = most_common_location
- if most_common_location == beacon.Previous_confident_location {
- beacon.Location_confidence++
- } else {
- beacon.Location_confidence = 1
- beacon.Previous_confident_location = most_common_location
- }
- }
-
- //create an http result from this
- r := HTTP_location{}
- r.Distance = best_location.distance
- r.Name = beacon.Name
- r.Beacon_name = beacon.Name
- r.Beacon_id = beacon.Beacon_id
- r.Beacon_type = beacon.Beacon_type
- r.HB_Battery = beacon.HB_Battery
- r.HB_ButtonMode = beacon.HB_ButtonMode
- r.HB_ButtonCounter = beacon.HB_ButtonCounter
- r.Location = best_location.name
- r.Last_seen = best_location.last_seen
-
- ////fmt.Printf("beacon.Location_confidence %s, settings.Location_confidence %s, beacon.Previous_confident_location %s: best_location.name %s\n",beacon.Location_confidence, settings.Location_confidence, beacon.Previous_confident_location, best_location.name)
-
- if (beacon.Location_confidence == settings.Location_confidence && beacon.Previous_confident_location != best_location.name) || beacon.expired_location == "expired" {
- // location has changed, send an mqtt message
-
- should_persist = true
- fmt.Printf("detected a change!!! %#v\n\n", beacon)
- if (beacon.Previous_confident_location== "expired"&& beacon.expired_location=="") {
- msg := Message{
- Email: beacon.Previous_confident_location,
- Username: beacon.Name,
- Message: "OK",}
- res1B, _ := json.Marshal(msg)
- fmt.Println(string(res1B))
-
- if err != nil {
- log.Printf("error: %v", err)
-
- }
- // Send the newly received message to the broadcast channel
- broadcast <- msg
- }
- beacon.Location_confidence = 0
- location := ""
- if (beacon.expired_location == "expired") {
-
- location = "expired"
- } else {
- location = best_location.name
- }
- //first make the json
- 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()})
- if err != nil {
- continue
- }
-
- //send the message
- err = cl.Publish(&client.PublishOptions{
- QoS: mqtt.QoS1,
- TopicName: []byte("afa-systems/presence/changes"),
- Message: js,
- })
- if err != nil {
- panic(err)
- }
-
-
-
- // Read in a new message as JSON and map it to a Message object
- //err := ws.ReadJSON(&msg)
-
-
- /*msg := Message{
- Email: "apple",
- Username: "peach",
- Message: "change",}
- res1B, _ := json.Marshal(msg)
- fmt.Println(string(res1B))
-
-
-
- if err != nil {
- log.Printf("error: %v", err)
-
- }
- // Send the newly received message to the broadcast channel
- broadcast <- msg*/
-
-
- ///utils.Log.Printf("%s changes ",beacon.Beacon_id)
- 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)
- ///utils.Log.Printf("%s",s)
- err, out, errout := Shellout(s)
- if err != nil {
- log.Printf("error: %v\n", err)
- }
- fmt.Println("--- stdout ---")
- fmt.Println(out)
- fmt.Println("--- stderr ---")
- fmt.Println(errout)
- //////beacon.logger.Printf("Log content: user id %v \n", best_location.name)
- if settings.HA_send_changes_only {
- sendHARoomMessage(beacon.Beacon_id, beacon.Name, best_location.distance, best_location.name, cl)
- }
-
- if (beacon.expired_location == "expired") {
-
- beacon.Previous_confident_location = "expired"
- r.Location = "expired"
- } else {
- beacon.Previous_confident_location = best_location.name
- }
- ///beacon.Previous_confident_location = best_location.name
-
- }
-
- beacon.Previous_location = best_location.name
- r.Previous_confident_location = beacon.expired_location
- BEACONS.Beacons[beacon.Beacon_id] = beacon
-
- http_results_lock.Lock()
- http_results.Beacons = append(http_results.Beacons, r)
- http_results_lock.Unlock()
-
- if best_location.name != "" {
- if !settings.HA_send_changes_only {
- secs := int64(time.Now().Unix())
- if secs%settings.HA_send_interval == 0 {
- sendHARoomMessage(beacon.Beacon_id, beacon.Name, best_location.distance, best_location.name, cl)
- }
- }
- }
-
- /////fmt.Printf("\n\n%s is most likely in %s with average distance %f \n\n", beacon.Name, best_location.name, best_location.distance)
- ////beacon.logger.Printf("Log content: user id %v \n", beacon.Name)
- // publish this to a topic
- // Publish a message.
- err := cl.Publish(&client.PublishOptions{
- QoS: mqtt.QoS0,
- TopicName: []byte("afa-systems/presence"),
- Message: []byte(fmt.Sprintf("%s is most likely in %s with average distance %f", beacon.Name, best_location.name, best_location.distance)),
- })
- if err != nil {
- panic(err)
- }
-
- }
-
- /*for _, button := range Buttons_list {
- http_results.Buttons = append(http_results.Buttons, button)
- }*/
-
- if should_persist {
- persistBeacons()
- }
- }
-
-
- /*func doSomething(bcon Beacon, testo string ) {
- bcon.logger.Printf("Log content: user id %v \n", beacon.Name)
- }*/
-
- func IncomingMQTTProcessor(updateInterval time.Duration, cl *client.Client, db *bolt.DB, logger []*user) chan<- Incoming_json {
-
- incoming_msgs_chan := make(chan Incoming_json, 2000)
-
- // load initial BEACONS
- BEACONS.Beacons = make(map[string]Beacon)
- // retrieve the data
-
- // create bucket if not exist
- err = db.Update(func(tx *bolt.Tx) error {
- _, err := tx.CreateBucketIfNotExists(world)
- if err != nil {
- return err
- }
- return nil
- })
-
- err = db.View(func(tx *bolt.Tx) error {
- bucket := tx.Bucket(world)
- if bucket == nil {
- return err
- }
-
- key := []byte("beacons_list")
- val := bucket.Get(key)
- if val != nil {
- buf := bytes.NewBuffer(val)
- dec := gob.NewDecoder(buf)
- err = dec.Decode(&BEACONS)
- if err != nil {
- log.Fatal("decode error:", err)
- }
- }
-
- key = []byte("buttons_list")
- val = bucket.Get(key)
- if val != nil {
- buf := bytes.NewBuffer(val)
- dec := gob.NewDecoder(buf)
- err = dec.Decode(&Buttons_list)
- if err != nil {
- log.Fatal("decode error:", err)
- }
- }
-
- key = []byte("settings")
- val = bucket.Get(key)
- if val != nil {
- buf := bytes.NewBuffer(val)
- dec := gob.NewDecoder(buf)
- err = dec.Decode(&settings)
- if err != nil {
- log.Fatal("decode error:", err)
- }
- }
-
- return nil
- })
-
- if err != nil {
- log.Fatal(err)
- }
-
- //debug list them out
-
- /*fmt.Println("Database beacons:")
- for _, beacon := range BEACONS.Beacons {
- fmt.Println("Database has known beacon: " + beacon.Beacon_id + " " + beacon.Name)
- dog := new(user)
- //createUser( beacon.Name, true)
-
- //user1 := createUser( beacon.Name, true)
- //doSomething(beacon, "hello")
- //
-
- userFIle := &lumberjack.Logger{
- Filename: "/data/presence/presence/beacon_log_" + beacon.Name + ".log",
- MaxSize: 250, // mb
- MaxBackups: 5,
- MaxAge: 10, // in days
- }
- dog.id = beacon.Name
- dog.logger = log.New(userFIle, "User: ", log.Ldate|log.Ltime|log.Lshortfile)
- dog.logger.Printf("Log content: user id %v \n", beacon.Name)
- logger=append(logger,dog)
- }
- fmt.Println("leng has %d\n",len(logger))
- fmt.Printf("%v", logger)
- fmt.Println("Settings has %#v\n", settings)*/
- /**/
- Latest_beacons_list = make(map[string]Beacon)
-
- Buttons_list = make(map[string]Button)
-
- //create a map of locations, looked up by hostnames
- locations_list := Locations_list{}
- ls := make(map[string]Location)
- locations_list.locations = ls
-
- ticker := time.NewTicker(updateInterval)
-
- go func() {
- for {
- select {
-
- case <-ticker.C:
- getLikelyLocations(settings, locations_list, cl)
- case incoming := <-incoming_msgs_chan:
- func() {
- defer func() {
- if err := recover(); err != nil {
- log.Println("work failed:", err)
- }
- }()
-
- incoming = incomingBeaconFilter(incoming)
- this_beacon_id := getBeaconID(incoming)
-
- now := time.Now().Unix()
-
- ///fmt.Println("sawbeacon " + this_beacon_id + " at " + incoming.Hostname)
- //logger["FCB8351F5A21"].logger.Printf("Log content: user id \n")
- //if this beacon isn't in our search list, add it to the latest_beacons pile.
- beacon, ok := BEACONS.Beacons[this_beacon_id]
- if !ok {
- //should be unique
- //if it's already in list, forget it.
- latest_list_lock.Lock()
- x, ok := Latest_beacons_list[this_beacon_id]
- if ok {
- //update its timestamp
- x.Last_seen = now
- x.Incoming_JSON = incoming
- x.Distance = getBeaconDistance(incoming)
-
- Latest_beacons_list[this_beacon_id] = x
- } else {
- 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)}
- }
- for k, v := range Latest_beacons_list {
- if (now - v.Last_seen) > 10 { // 10 seconds
- delete(Latest_beacons_list, k)
- }
- }
- latest_list_lock.Unlock()
- //continue
- return
- }
-
- beacon.Incoming_JSON = incoming
- beacon.Last_seen = now
- beacon.Beacon_type = incoming.Beacon_type
- beacon.HB_ButtonCounter = incoming.HB_ButtonCounter
- beacon.HB_Battery = incoming.HB_Battery
- beacon.HB_RandomNonce = incoming.HB_RandomNonce
- beacon.HB_ButtonMode = incoming.HB_ButtonMode
- ////fmt.Println("button pressed " + this_beacon_id + " at " + strconv.Itoa(int(incoming.HB_ButtonCounter)) )
-
-
-
- if beacon.beacon_metrics == nil {
- beacon.beacon_metrics = make([]beacon_metric, settings.Beacon_metrics_size)
- }
- //create metric for this beacon
- this_metric := beacon_metric{}
- this_metric.distance = getBeaconDistance(incoming)
- this_metric.timestamp = now
- this_metric.rssi = int64(incoming.RSSI)
- this_metric.location = incoming.Hostname
- beacon.beacon_metrics = append(beacon.beacon_metrics, this_metric)
- ///fmt.Printf("APPENDING a metric from %s len %d\n", beacon.Name, len(beacon.beacon_metrics))
- if len(beacon.beacon_metrics) > settings.Beacon_metrics_size {
- //fmt.Printf("deleting a metric from %s len %d\n", beacon.Name, len(beacon.beacon_metrics))
- beacon.beacon_metrics = append(beacon.beacon_metrics[:0], beacon.beacon_metrics[0+1:]...)
- }
- //fmt.Printf("%#v\n", beacon.beacon_metrics)
- if beacon.HB_ButtonCounter_Prev != beacon.HB_ButtonCounter {
- beacon.HB_ButtonCounter_Prev = incoming.HB_ButtonCounter
- // send the button message to MQTT
- sendButtonPressed(beacon, cl)
- }
- BEACONS.Beacons[beacon.Beacon_id] = beacon
-
- /*if beacon.Beacon_type == "hb_button" {
- processButton(beacon, cl)
- }*/
-
- //lookup location by hostname in locations
- location, ok := locations_list.locations[incoming.Hostname]
- if !ok {
- //create the location
- locations_list.locations[incoming.Hostname] = Location{}
- location, ok = locations_list.locations[incoming.Hostname]
- location.name = incoming.Hostname
- }
- locations_list.locations[incoming.Hostname] = location
- }()
- }
- }
- }()
-
- return incoming_msgs_chan
- }
- func ParseTimeStamp(utime string) (string, error) {
- i, err := strconv.ParseInt(utime, 10, 64)
- if err != nil {
- return "", err
- }
- t := time.Unix(i, 0)
- return t.Format(time.UnixDate), nil
- }
-
- var http_host_path_ptr *string
- //var https_host_path_ptr *string
- var httpws_host_path_ptr *string
- //var httpwss_host_path_ptr *string
-
-
- type Todo struct {
- Id string `json:"id"`
- Value string `json:"value" binding:"required"`
- }
-
- type Job interface {
- ExitChan() chan error
- Run(todos map[string]Todo) (map[string]Todo, error)
- }
-
- func ProcessJobs(jobs chan Job, db string) {
- for {
- j := <-jobs
-
- todos := make(map[string]Todo, 0)
- content, err := ioutil.ReadFile(db)
- if err == nil {
- if err = json.Unmarshal(content, &todos); err == nil {
- todosMod, err := j.Run(todos)
-
- if err == nil && todosMod != nil {
- b, err := json.Marshal(todosMod)
- if err == nil {
- err = ioutil.WriteFile(db, b, 0644)
- }
- }
- }
- }
-
- j.ExitChan() <- err
- }
- }
-
- type user struct {
- id string
- logger *log.Logger
- }
-
-
- const ShellToUse = "bash"
-
- func Shellout(command string) (error, string, string) {
- var stdout bytes.Buffer
- var stderr bytes.Buffer
- ///utils.Log.Printf("command: %s",command)
- cmd := exec.Command(ShellToUse, "-c", command)
- cmd.Stdout = &stdout
- cmd.Stderr = &stderr
- err := cmd.Run()
- return err, stdout.String(), stderr.String()
- }
-
-
- func createUser(id string, logWanted bool) user {
- var l *log.Logger
-
- if logWanted {
- // Here the log content will be added in the user log file
- userFIle := &lumberjack.Logger{
- Filename: "/data/var/log/presence/presence/log_" + id + ".log",
- MaxSize: 250, // mb
- MaxBackups: 5,
- MaxAge: 10, // in days
- }
- l = log.New(userFIle, "User: ", log.Ldate|log.Ltime|log.Lshortfile)
- } else {
- // Here the log content will go nowhere
- l = log.New(ioutil.Discard, "User: ", log.Ldate|log.Ltime|log.Lshortfile)
- }
- return user{id, l}
- }
-
-
- func main() {
-
- loggers:=[]*user{}
- // initialize empty-object json file if not found
- //if _, err := ioutil.ReadFile(Db); err != nil {
- // str := "{}"
- // if err = ioutil.WriteFile(Db, []byte(str), 0644); err != nil {
- // log.Fatal(err)
- // }
- //}
-
- // create channel to communicate over
- //jobs := make(chan Job)
-
- // start watching jobs channel for work
- //go ProcessJobs(jobs, Db)
-
- // create dependencies
- //client := &TodoClient{Jobs: jobs}
- //handlers := &TodoHandlers{Client: client}
- //work := WorkRequest{Name: name, Delay: delay}
- //jobs <- work
-
-
-
-
-
- http_host_path_ptr = flag.String("http_host_path", "0.0.0.0:8080", "The host:port that the HTTP server should listen on")
- //https_host_path_ptr = flag.String("https_host_path", "0.0.0.0:5443", "The host:port that the HTTP server should listen on")
- httpws_host_path_ptr = flag.String("httpws_host_path", "0.0.0.0:8088", "The host:port websocket listen")
- //httpwss_host_path_ptr = flag.String("httpwss_host_path", "0.0.0.0:8443", "The host:port secure websocket listen")
-
- mqtt_host_ptr := flag.String("mqtt_host", "localhost:1883", "The host:port of the MQTT server to listen for beacons on")
- mqtt_username_ptr := flag.String("mqtt_username", "none", "The username needed to connect to the MQTT server, 'none' if it doesn't need one")
- mqtt_password_ptr := flag.String("mqtt_password", "none", "The password needed to connect to the MQTT server, 'none' if it doesn't need one")
- mqtt_client_id_ptr := flag.String("mqtt_client_id", "presence-detector", "The client ID for the MQTT server")
-
- flag.Parse()
-
- ///utils.NewLog(*logpath)
- ///utils.Log.Println("hello")
- // Set up channel on which to send signal notifications.
- sigc := make(chan os.Signal, 1)
- signal.Notify(sigc, os.Interrupt, os.Kill)
-
- // Create an MQTT Client.
- cli := client.New(&client.Options{
- // Define the processing of the error handler.
- ErrorHandler: func(err error) {
- fmt.Println(err)
- },
- })
- // Terminate the Client.
- defer cli.Terminate()
-
- //open the database
- db, err = bolt.Open("/data/conf/presence/presence.db", 0644, nil)
- if err != nil {
- log.Fatal(err)
- }
- defer db.Close()
-
- // Connect to the MQTT Server.
- err = cli.Connect(&client.ConnectOptions{
- Network: "tcp",
- Address: *mqtt_host_ptr,
- ClientID: []byte(*mqtt_client_id_ptr),
- UserName: []byte(*mqtt_username_ptr),
- Password: []byte(*mqtt_password_ptr),
- })
- if err != nil {
- panic(err)
- }
-
- incoming_updates_chan := IncomingMQTTProcessor(1*time.Second, cli, db, loggers)
-
- // Subscribe to topics.
- err = cli.Subscribe(&client.SubscribeOptions{
- SubReqs: []*client.SubReq{
- &client.SubReq{
- TopicFilter: []byte("publish_out/#"),
- QoS: mqtt.QoS0,
- Handler: func(topicName, message []byte) {
- msgStr := string(message)
- t := strings.Split(string(topicName), "/")
- hostname := t[1]
-
- //Formato JSON multiplo
- //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"}]
- if strings.HasPrefix(msgStr, "[") {
- var readings []RawReading
- err := json.Unmarshal(message, &readings)
- if err != nil {
- log.Printf("Errore parsing JSON: %v", err)
- return
- }
-
- for _, reading := range readings {
- if reading.Type == "Gateway" {
- continue
- }
- incoming := Incoming_json{
- Hostname: hostname,
- MAC: reading.MAC,
- RSSI: int64(reading.RSSI),
- Data: reading.RawData,
- HB_ButtonCounter: parseButtonState(reading.RawData),
- }
- incoming_updates_chan <- incoming
- }
- } else {
- //Formato CSV
- //ingics solo annuncio
- //publish_out/171061001180 $GPRP,C83F8F17DB35,F5B0B0419FEF,-44,02010612FF590080BC280102FFFFFFFF000000000000,1749648798
- //ingics tasto premuto
- //publish_out/171061001180 $GPRP,C83F8F17DB35,F5B0B0419FEF,-44,02010612FF590080BC280103FFFFFFFF000000000000,1749648798
- s := strings.Split(string(message), ",")
- if len(s) < 6 {
- log.Printf("Messaggio CSV non valido: %s", msgStr)
- return
- }
-
- rawdata := s[4]
- buttonCounter := parseButtonState(rawdata)
- if buttonCounter > 0 {
- incoming := Incoming_json{}
- i, _ := strconv.ParseInt(s[3], 10, 64)
- incoming.Hostname = hostname
- incoming.Beacon_type = "hb_button"
- incoming.MAC = s[1]
- incoming.RSSI = i
- incoming.Data = rawdata
- incoming.HB_ButtonCounter = buttonCounter
-
- read_line := strings.TrimRight(string(s[5]), "\r\n")
- it, err33 := strconv.Atoi(read_line)
- if err33 != nil {
- fmt.Println(it)
- fmt.Println(err33)
- os.Exit(2)
- }
- incoming_updates_chan <- incoming
- }
- }
- },
- },
- },
- })
- if err != nil {
- panic(err)
- }
-
-
- fmt.Println("CONNECTED TO MQTT")
- fmt.Println("\n ")
- fmt.Println("Visit http://" + *http_host_path_ptr + " on your browser to see the web interface")
- fmt.Println("\n ")
-
- go startServer()
-
- // Wait for receiving a signal.
- <-sigc
-
- // Disconnect the Network Connection.
- if err := cli.Disconnect(); err != nil {
- panic(err)
- }
- }
-
- func startServer() {
-
- headersOk := handlers.AllowedHeaders([]string{"X-Requested-With"})
- originsOk := handlers.AllowedOrigins([]string{"*"})
- methodsOk := handlers.AllowedMethods([]string{"GET", "HEAD", "POST", "PUT", "DELETE","OPTIONS"})
-
- // Set up HTTP server
- r := mux.NewRouter()
- r.HandleFunc("/api/results", resultsHandler)
-
- r.HandleFunc("/api/beacons/{beacon_id}", beaconsDeleteHandler).Methods("DELETE")
- r.HandleFunc("/api/beacons", beaconsListHandler).Methods("GET")
- 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
- r.HandleFunc("/api/beacons", beaconsAddHandler).Methods("PUT")
-
- r.HandleFunc("/api/latest-beacons", latestBeaconsListHandler).Methods("GET")
-
-
- r.HandleFunc("/api/settings", settingsListHandler).Methods("GET")
- r.HandleFunc("/api/settings", settingsEditHandler).Methods("POST")
-
- r.PathPrefix("/js/").Handler(http.StripPrefix("/js/", http.FileServer(http.Dir("static_html/js/"))))
- r.PathPrefix("/css/").Handler(http.StripPrefix("/css/", http.FileServer(http.Dir("static_html/css/"))))
- r.PathPrefix("/img/").Handler(http.StripPrefix("/img/", http.FileServer(http.Dir("static_html/img/"))))
- r.PathPrefix("/").Handler(http.FileServer(http.Dir("static_html/")))
-
- http.Handle("/", r)
-
- mxWS := mux.NewRouter()
- mxWS.HandleFunc("/ws/api/beacons", serveWs)
- mxWS.HandleFunc("/ws/api/beacons/latest", serveLatestBeaconsWs)
- mxWS.HandleFunc("/ws/broadcast", handleConnections)
- http.Handle("/ws/", mxWS)
-
-
-
- go func() {
- log.Fatal(http.ListenAndServe(*httpws_host_path_ptr, nil))
- }()
-
- // Start listening for incoming chat messages
- go handleMessages()
-
- ///"/conf/etc/cert/certs/services/htdocs/majornet.crt", "/conf/etc/cert/private/services/htdocs/majornet.key"
- http.ListenAndServe(*http_host_path_ptr, handlers.CORS(originsOk, headersOk, methodsOk)(r))
-
- }
-
-
- func handleConnections(w http.ResponseWriter, r *http.Request) {
- // Upgrade initial GET request to a websocket
- ws, err := upgrader.Upgrade(w, r, nil)
- if err != nil {
- log.Fatal(err)
- }
- // Make sure we close the connection when the function returns
- defer ws.Close()
-
- // Register our new client
- clients[ws] = true
-
- for {
- var msg Message
- // Read in a new message as JSON and map it to a Message object
- err := ws.ReadJSON(&msg)
- if err != nil {
- log.Printf("error: %v", err)
- delete(clients, ws)
- break
- }
- // Send the newly received message to the broadcast channel
- broadcast <- msg
- }
- }
-
- func handleMessages() {
- for {
- // Grab the next message from the broadcast channel
- msg := <-broadcast
- // Send it out to every client that is currently connected
- for client := range clients {
- err := client.WriteJSON(msg)
- if err != nil {
- log.Printf("error: %v", err)
- client.Close()
- delete(clients, client)
- }
- }
- }
- }
-
-
-
-
-
- func resultsHandler(w http.ResponseWriter, r *http.Request) {
- http_results_lock.RLock()
- js, err := json.Marshal(http_results)
- http_results_lock.RUnlock()
- if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
-
- w.Write(js)
- }
-
- func beaconsListHandler(w http.ResponseWriter, r *http.Request) {
- latest_list_lock.RLock()
- js, err := json.Marshal(BEACONS)
- latest_list_lock.RUnlock()
- if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
-
- w.Write(js)
- }
-
- func persistBeacons() error {
- // gob it first
- buf := &bytes.Buffer{}
- enc := gob.NewEncoder(buf)
- if err := enc.Encode(BEACONS); err != nil {
- return err
- }
-
- key := []byte("beacons_list")
- // store some data
- err = db.Update(func(tx *bolt.Tx) error {
- bucket, err := tx.CreateBucketIfNotExists(world)
- if err != nil {
- return err
- }
-
- err = bucket.Put(key, []byte(buf.String()))
- if err != nil {
- return err
- }
- return nil
- })
- return nil
- }
-
- func persistSettings() error {
- // gob it first
- buf := &bytes.Buffer{}
- enc := gob.NewEncoder(buf)
- if err := enc.Encode(settings); err != nil {
- return err
- }
-
- key := []byte("settings")
- // store some data
- err = db.Update(func(tx *bolt.Tx) error {
- bucket, err := tx.CreateBucketIfNotExists(world)
- if err != nil {
- return err
- }
-
- err = bucket.Put(key, []byte(buf.String()))
- if err != nil {
- return err
- }
- return nil
- })
- return nil
- }
-
- func beaconsAddHandler(w http.ResponseWriter, r *http.Request) {
- decoder := json.NewDecoder(r.Body)
- var in_beacon Beacon
- err = decoder.Decode(&in_beacon)
- if err != nil {
- http.Error(w, err.Error(), 400)
- return
- }
-
- //make sure name and beacon_id are present
- if (len(strings.TrimSpace(in_beacon.Name)) == 0) || (len(strings.TrimSpace(in_beacon.Beacon_id)) == 0) {
- http.Error(w, "name and beacon_id cannot be blank", 400)
- return
- }
-
- BEACONS.Beacons[in_beacon.Beacon_id] = in_beacon
-
- err := persistBeacons()
- if err != nil {
- http.Error(w, "trouble persisting beacons list, create bucket", 500)
- return
- }
-
- w.Write([]byte("ok"))
- }
-
- func beaconsDeleteHandler(w http.ResponseWriter, r *http.Request) {
- vars := mux.Vars(r)
- beacon_id := vars["beacon_id"]
- delete(BEACONS.Beacons, beacon_id)
-
- _, ok := Buttons_list[beacon_id]
- if ok {
- delete(Buttons_list, beacon_id)
- }
-
- err := persistBeacons()
- if err != nil {
- http.Error(w, "trouble persisting beacons list, create bucket", 500)
- return
- }
-
- w.Write([]byte("ok"))
- }
-
- func latestBeaconsListHandler(w http.ResponseWriter, r *http.Request) {
- latest_list_lock.RLock()
- var la = make([]Beacon, 0)
- for _, b := range Latest_beacons_list {
- la = append(la, b)
- }
- latest_list_lock.RUnlock()
- js, err := json.Marshal(la)
- if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
-
- w.Write(js)
- }
-
- func settingsListHandler(w http.ResponseWriter, r *http.Request) {
- js, err := json.Marshal(settings)
- if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
-
- w.Write(js)
- }
-
- func settingsEditHandler(w http.ResponseWriter, r *http.Request) {
- decoder := json.NewDecoder(r.Body)
- var in_settings Settings
- err = decoder.Decode(&in_settings)
- if err != nil {
- http.Error(w, err.Error(), 400)
- return
- }
-
- //make sure values are > 0
- if (in_settings.Location_confidence <= 0) ||
- (in_settings.Last_seen_threshold <= 0) ||
- (in_settings.HA_send_interval <= 0) {
- http.Error(w, "values must be greater than 0", 400)
- return
- }
-
- settings = in_settings
-
- err := persistSettings()
- if err != nil {
- http.Error(w, "trouble persisting settings, create bucket", 500)
- return
- }
-
- w.Write([]byte("ok"))
- }
-
- //websocket stuff
- func reader(ws *websocket.Conn) {
- defer ws.Close()
- ws.SetReadLimit(512)
- ws.SetReadDeadline(time.Now().Add(pongWait))
- ws.SetPongHandler(func(string) error { ws.SetReadDeadline(time.Now().Add(pongWait)); return nil })
- for {
- _, _, err := ws.ReadMessage()
- if err != nil {
- break
- }
- }
- }
-
- func writer(ws *websocket.Conn) {
- pingTicker := time.NewTicker(pingPeriod)
- beaconTicker := time.NewTicker(beaconPeriod)
- defer func() {
- pingTicker.Stop()
- beaconTicker.Stop()
- ws.Close()
- }()
- for {
- select {
- case <-beaconTicker.C:
-
- http_results_lock.RLock()
- js, err := json.Marshal(http_results)
- http_results_lock.RUnlock()
-
- if err != nil {
- js = []byte("error")
- }
-
- ws.SetWriteDeadline(time.Now().Add(writeWait))
- if err := ws.WriteMessage(websocket.TextMessage, js); err != nil {
- return
- }
- case <-pingTicker.C:
- ws.SetWriteDeadline(time.Now().Add(writeWait))
- if err := ws.WriteMessage(websocket.PingMessage, []byte{}); err != nil {
- return
- }
- }
- }
- }
-
- func serveWs(w http.ResponseWriter, r *http.Request) {
- ws, err := upgrader.Upgrade(w, r, nil)
- if err != nil {
- if _, ok := err.(websocket.HandshakeError); !ok {
- log.Println(err)
- }
- return
- }
-
- go writer(ws)
- reader(ws)
- }
-
- func latestBeaconWriter(ws *websocket.Conn) {
- pingTicker := time.NewTicker(pingPeriod)
- beaconTicker := time.NewTicker(beaconPeriod)
- defer func() {
- pingTicker.Stop()
- beaconTicker.Stop()
- ws.Close()
- }()
- for {
- select {
- case <-beaconTicker.C:
-
- latest_list_lock.RLock()
- var la = make([]Beacon, 0)
- for _, b := range Latest_beacons_list {
- la = append(la, b)
- }
- latest_list_lock.RUnlock()
- js, err := json.Marshal(la)
-
- if err != nil {
- js = []byte("error")
- }
-
- ws.SetWriteDeadline(time.Now().Add(writeWait))
- if err := ws.WriteMessage(websocket.TextMessage, js); err != nil {
- return
- }
- case <-pingTicker.C:
- ws.SetWriteDeadline(time.Now().Add(writeWait))
- if err := ws.WriteMessage(websocket.PingMessage, []byte{}); err != nil {
- return
- }
- }
- }
- }
-
- func serveLatestBeaconsWs(w http.ResponseWriter, r *http.Request) {
- ws, err := upgrader.Upgrade(w, r, nil)
- if err != nil {
- if _, ok := err.(websocket.HandshakeError); !ok {
- log.Println(err)
- }
- return
- }
-
- go latestBeaconWriter(ws)
- reader(ws)
- }
|