github.com/jingruilea/kubeedge@v1.2.0-beta.0.0.20200410162146-4bb8902b3879/mappers/bluetooth_mapper/controller/controller.go (about)

     1  /*
     2  Copyright 2019 The KubeEdge Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8     http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package controller
    18  
    19  import (
    20  	"encoding/json"
    21  	"strings"
    22  
    23  	MQTT "github.com/eclipse/paho.mqtt.golang"
    24  	"github.com/paypal/gatt"
    25  	"github.com/paypal/gatt/examples/option"
    26  	"k8s.io/klog"
    27  
    28  	"github.com/kubeedge/kubeedge/mappers/bluetooth_mapper/action_manager"
    29  	"github.com/kubeedge/kubeedge/mappers/bluetooth_mapper/configuration"
    30  	"github.com/kubeedge/kubeedge/mappers/bluetooth_mapper/data_converter"
    31  	"github.com/kubeedge/kubeedge/mappers/bluetooth_mapper/helper"
    32  	"github.com/kubeedge/kubeedge/mappers/bluetooth_mapper/scheduler"
    33  	"github.com/kubeedge/kubeedge/mappers/bluetooth_mapper/watcher"
    34  )
    35  
    36  // constants which can be used to convey topic information
    37  const (
    38  	MapperTopicPrefix              = "$ke/device/bluetooth-mapper/"
    39  	WatcherTopicSuffix             = "/watcher/create"
    40  	SchedulerCreateTopicSuffix     = "/scheduler/create"
    41  	SchedulerDeleteTopicSuffix     = "/scheduler/delete"
    42  	ActionManagerCreateTopicSuffix = "/action-manager/create"
    43  	ActionManagerDeleteTopicSuffix = "/action-manager/delete"
    44  )
    45  
    46  var topicMap = make(map[string]MQTT.MessageHandler)
    47  
    48  //Config contains the configuration used by the controller
    49  type Config struct {
    50  	Mqtt          configuration.Mqtt          `yaml:"mqtt"`
    51  	Device        configuration.Device        `yaml:"device"`
    52  	Watcher       watcher.Watcher             `yaml:"watcher"`
    53  	Scheduler     scheduler.Scheduler         `yaml:"scheduler"`
    54  	ActionManager actionmanager.ActionManager `yaml:"action-manager"`
    55  	Converter     dataconverter.Converter     `yaml:"data-converter"`
    56  }
    57  
    58  // initTopicMap initializes topics to their respective handler functions
    59  func (c *Config) initTopicMap() {
    60  	topicMap[MapperTopicPrefix+c.Device.ID+WatcherTopicSuffix] = c.handleWatchMessage
    61  	topicMap[MapperTopicPrefix+c.Device.ID+SchedulerCreateTopicSuffix] = c.handleScheduleCreateMessage
    62  	topicMap[MapperTopicPrefix+c.Device.ID+SchedulerDeleteTopicSuffix] = c.handleScheduleDeleteMessage
    63  	topicMap[MapperTopicPrefix+c.Device.ID+ActionManagerCreateTopicSuffix] = c.handleActionCreateMessage
    64  	topicMap[MapperTopicPrefix+c.Device.ID+ActionManagerDeleteTopicSuffix] = c.handleActionDeleteMessage
    65  }
    66  
    67  //Start starts the controller of the mapper
    68  func (c *Config) Start() {
    69  	c.initTopicMap()
    70  	helper.MqttConnect(c.Mqtt.Mode, c.Mqtt.InternalServer, c.Mqtt.Server)
    71  	subscribeAllTopics()
    72  	helper.ControllerWg.Add(1)
    73  	device, err := gatt.NewDevice(option.DefaultClientOptions...)
    74  	if err != nil {
    75  		klog.Fatalf("Failed to open device, err: %s\n", err)
    76  		return
    77  	}
    78  	go c.Watcher.Initiate(device, c.Device.Name, c.Device.ID, c.ActionManager.Actions, c.Converter)
    79  
    80  	<-watcher.DeviceConnected
    81  	for _, action := range c.ActionManager.Actions {
    82  		if action.PerformImmediately {
    83  			action.PerformOperation(c.Converter.DataRead)
    84  		}
    85  	}
    86  
    87  	for _, schedule := range c.Scheduler.Schedules {
    88  		helper.ControllerWg.Add(1)
    89  		go schedule.ExecuteSchedule(c.ActionManager.Actions, c.Converter.DataRead, c.Device.ID)
    90  	}
    91  	helper.ControllerWg.Wait()
    92  }
    93  
    94  //subscribeAllTopics subscribes to mqtt topics associated with mapper
    95  func subscribeAllTopics() {
    96  	for key, value := range topicMap {
    97  		helper.TokenClient = helper.Client.Subscribe(key, 0, value)
    98  		if helper.TokenClient.Wait() && helper.TokenClient.Error() != nil {
    99  			klog.Errorf("subscribe() Error in topic: %s is: %s", key, helper.TokenClient.Error())
   100  		}
   101  	}
   102  }
   103  
   104  //handleWatchMessage is the MQTT handler function for changing watcher configuration at runtime
   105  func (c *Config) handleWatchMessage(client MQTT.Client, message MQTT.Message) {
   106  	newWatch := watcher.Watcher{}
   107  	err := json.Unmarshal(message.Payload(), &newWatch)
   108  	if err != nil {
   109  		klog.Errorf("Error in unmarshalling:  %s", err)
   110  	}
   111  	c.Watcher = newWatch
   112  	configuration.Config.Watcher = c.Watcher
   113  	klog.Infof("New watcher has been started")
   114  	klog.Infof("New Watcher: %v", c.Watcher)
   115  }
   116  
   117  //handleScheduleCreateMessage is the MQTT handler function for adding schedules at runtime
   118  func (c *Config) handleScheduleCreateMessage(client MQTT.Client, message MQTT.Message) {
   119  	newSchedules := []scheduler.Schedule{}
   120  	err := json.Unmarshal(message.Payload(), &newSchedules)
   121  	if err != nil {
   122  		klog.Errorf("Error in unmarshalling: %s", err)
   123  	}
   124  	for _, newSchedule := range newSchedules {
   125  		scheduleExists := false
   126  		for scheduleIndex, schedule := range c.Scheduler.Schedules {
   127  			if schedule.Name == newSchedule.Name {
   128  				c.Scheduler.Schedules[scheduleIndex] = newSchedule
   129  				scheduleExists = true
   130  				break
   131  			}
   132  		}
   133  		if scheduleExists {
   134  			c.Scheduler.Schedules = append(c.Scheduler.Schedules, newSchedule)
   135  			klog.Infof("Schedule: %s has been updated", newSchedule.Name)
   136  			klog.Infof("Updated Schedule: %v", newSchedule)
   137  		} else {
   138  			klog.Infof("Schedule: %s has been added", newSchedule.Name)
   139  			klog.Infof("New Schedule: %v", newSchedule)
   140  		}
   141  		configuration.Config.Scheduler = c.Scheduler
   142  		helper.ControllerWg.Add(1)
   143  		newSchedule.ExecuteSchedule(c.ActionManager.Actions, c.Converter.DataRead, c.Device.ID)
   144  	}
   145  }
   146  
   147  //handleScheduleDeleteMessage is the MQTT handler function for deleting schedules at runtime
   148  func (c *Config) handleScheduleDeleteMessage(client MQTT.Client, message MQTT.Message) {
   149  	schedulesToBeDeleted := []scheduler.Schedule{}
   150  	err := json.Unmarshal(message.Payload(), &schedulesToBeDeleted)
   151  	if err != nil {
   152  		klog.Errorf("Error in unmarshalling:  %s", err)
   153  	}
   154  	for _, scheduleToBeDeleted := range schedulesToBeDeleted {
   155  		scheduleExists := false
   156  		for index, schedule := range c.Scheduler.Schedules {
   157  			if strings.EqualFold(schedule.Name, scheduleToBeDeleted.Name) {
   158  				scheduleExists = true
   159  				copy(c.Scheduler.Schedules[index:], c.Scheduler.Schedules[index+1:])
   160  				c.Scheduler.Schedules = c.Scheduler.Schedules[:len(c.Scheduler.Schedules)-1]
   161  				break
   162  			}
   163  		}
   164  		configuration.Config.Scheduler = c.Scheduler
   165  		if !scheduleExists {
   166  			klog.Errorf("Schedule: %s does not exist", scheduleToBeDeleted.Name)
   167  		} else {
   168  			klog.Infof("Schedule: %s has been deleted ", scheduleToBeDeleted.Name)
   169  		}
   170  	}
   171  }
   172  
   173  //handleActionCreateMessage MQTT handler function for adding actions at runtime
   174  func (c *Config) handleActionCreateMessage(client MQTT.Client, message MQTT.Message) {
   175  	newActions := []actionmanager.Action{}
   176  	err := json.Unmarshal(message.Payload(), &newActions)
   177  	if err != nil {
   178  		klog.Errorf("Error in unmarshalling:  %s", err)
   179  	}
   180  	for _, newAction := range newActions {
   181  		actionExists := false
   182  		for actionIndex, action := range c.ActionManager.Actions {
   183  			if action.Name == newAction.Name {
   184  				c.ActionManager.Actions[actionIndex] = newAction
   185  				actionExists = true
   186  				break
   187  			}
   188  		}
   189  		if actionExists {
   190  			c.ActionManager.Actions = append(c.ActionManager.Actions, newAction)
   191  			klog.Infof("Action: %s has been updated", newAction.Name)
   192  			klog.Infof("Updated Action: %v", newAction)
   193  		} else {
   194  			klog.Infof("Action: %s has been added ", newAction.Name)
   195  			klog.Infof("New Action: %v", newAction)
   196  		}
   197  		configuration.Config.ActionManager = c.ActionManager
   198  		if newAction.PerformImmediately {
   199  			newAction.PerformOperation(c.Converter.DataRead)
   200  		}
   201  	}
   202  }
   203  
   204  //handleActionDeleteMessage MQTT handler function for deleting actions at runtime
   205  func (c *Config) handleActionDeleteMessage(client MQTT.Client, message MQTT.Message) {
   206  	actionsToBeDeleted := []actionmanager.Action{}
   207  	err := json.Unmarshal(message.Payload(), &actionsToBeDeleted)
   208  	if err != nil {
   209  		klog.Errorf("Error in unmarshalling:  %s", err)
   210  	}
   211  	for _, actionToBeDeleted := range actionsToBeDeleted {
   212  		actionExists := false
   213  		for index, action := range c.ActionManager.Actions {
   214  			if strings.EqualFold(action.Name, actionToBeDeleted.Name) {
   215  				actionExists = true
   216  				copy(c.ActionManager.Actions[index:], c.ActionManager.Actions[index+1:])
   217  				c.ActionManager.Actions = c.ActionManager.Actions[:len(c.ActionManager.Actions)-1]
   218  				break
   219  			}
   220  		}
   221  		configuration.Config.ActionManager = c.ActionManager
   222  		if !actionExists {
   223  			klog.Errorf("Action: %s did not exist", actionToBeDeleted.Name)
   224  		} else {
   225  			klog.Infof("Action: %s has been deleted ", actionToBeDeleted.Name)
   226  		}
   227  	}
   228  }