github.com/jingruilea/kubeedge@v1.2.0-beta.0.0.20200410162146-4bb8902b3879/edge/pkg/devicetwin/process.go (about)

     1  package devicetwin
     2  
     3  import (
     4  	"encoding/base64"
     5  	"encoding/json"
     6  	"errors"
     7  	"strings"
     8  	"sync"
     9  	"time"
    10  
    11  	"k8s.io/klog"
    12  
    13  	beehiveContext "github.com/kubeedge/beehive/pkg/core/context"
    14  	"github.com/kubeedge/beehive/pkg/core/model"
    15  	"github.com/kubeedge/kubeedge/edge/pkg/devicetwin/dtclient"
    16  	"github.com/kubeedge/kubeedge/edge/pkg/devicetwin/dtcommon"
    17  	"github.com/kubeedge/kubeedge/edge/pkg/devicetwin/dtcontext"
    18  	"github.com/kubeedge/kubeedge/edge/pkg/devicetwin/dtmodule"
    19  	"github.com/kubeedge/kubeedge/edge/pkg/devicetwin/dttype"
    20  )
    21  
    22  var (
    23  	//EventActionMap map for event to action
    24  	EventActionMap map[string]map[string]string
    25  	//ActionModuleMap map for action to module
    26  	ActionModuleMap map[string]string
    27  )
    28  
    29  //RegisterDTModule register dtmodule
    30  func (dt *DeviceTwin) RegisterDTModule(name string) {
    31  	module := dtmodule.DTModule{
    32  		Name: name,
    33  	}
    34  
    35  	dt.DTContexts.CommChan[name] = make(chan interface{}, 128)
    36  	dt.HeartBeatToModule[name] = make(chan interface{}, 128)
    37  	module.InitWorker(dt.DTContexts.CommChan[name], dt.DTContexts.ConfirmChan,
    38  		dt.HeartBeatToModule[name], dt.DTContexts)
    39  	dt.DTModules[name] = module
    40  
    41  }
    42  
    43  //distributeMsg distribute message to diff module
    44  func (dt *DeviceTwin) distributeMsg(m interface{}) error {
    45  	msg, ok := m.(model.Message)
    46  	if !ok {
    47  		klog.Errorf("Distribute message, msg is nil")
    48  		return errors.New("Distribute message, msg is nil")
    49  	}
    50  	message := dttype.DTMessage{Msg: &msg}
    51  	if message.Msg.GetParentID() != "" {
    52  		klog.Infof("Send msg to the %s module in twin", dtcommon.CommModule)
    53  		confirmMsg := dttype.DTMessage{Msg: model.NewMessage(message.Msg.GetParentID()), Action: dtcommon.Confirm}
    54  		if err := dt.DTContexts.CommTo(dtcommon.CommModule, &confirmMsg); err != nil {
    55  			return err
    56  		}
    57  	}
    58  	if !classifyMsg(&message) {
    59  		return errors.New("Not found action")
    60  	}
    61  	if ActionModuleMap == nil {
    62  		initActionModuleMap()
    63  	}
    64  
    65  	if moduleName, exist := ActionModuleMap[message.Action]; exist {
    66  		//how to deal write channel error
    67  		klog.Infof("Send msg to the %s module in twin", moduleName)
    68  		if err := dt.DTContexts.CommTo(moduleName, &message); err != nil {
    69  			return err
    70  		}
    71  	} else {
    72  		klog.Info("Not found deal module for msg")
    73  		return errors.New("Not found deal module for msg")
    74  	}
    75  
    76  	return nil
    77  }
    78  
    79  func initEventActionMap() {
    80  	EventActionMap = make(map[string]map[string]string)
    81  	EventActionMap[dtcommon.MemETPrefix] = make(map[string]string)
    82  	EventActionMap[dtcommon.DeviceETPrefix] = make(map[string]string)
    83  	EventActionMap[dtcommon.MemETPrefix][dtcommon.MemETDetailResultSuffix] = dtcommon.MemDetailResult
    84  	EventActionMap[dtcommon.MemETPrefix][dtcommon.MemETUpdateSuffix] = dtcommon.MemUpdated
    85  	EventActionMap[dtcommon.MemETPrefix][dtcommon.MemETGetSuffix] = dtcommon.MemGet
    86  	EventActionMap[dtcommon.DeviceETPrefix][dtcommon.DeviceETStateGetSuffix] = dtcommon.DeviceStateGet
    87  	EventActionMap[dtcommon.DeviceETPrefix][dtcommon.DeviceETUpdatedSuffix] = dtcommon.DeviceUpdated
    88  	EventActionMap[dtcommon.DeviceETPrefix][dtcommon.DeviceETStateUpdateSuffix] = dtcommon.DeviceStateUpdate
    89  	EventActionMap[dtcommon.DeviceETPrefix][dtcommon.TwinETUpdateSuffix] = dtcommon.TwinUpdate
    90  	EventActionMap[dtcommon.DeviceETPrefix][dtcommon.TwinETCloudSyncSuffix] = dtcommon.TwinCloudSync
    91  	EventActionMap[dtcommon.DeviceETPrefix][dtcommon.TwinETGetSuffix] = dtcommon.TwinGet
    92  }
    93  
    94  func initActionModuleMap() {
    95  	ActionModuleMap = make(map[string]string)
    96  	//membership twin device event , not lifecycle event
    97  	ActionModuleMap[dtcommon.MemDetailResult] = dtcommon.MemModule
    98  	ActionModuleMap[dtcommon.MemGet] = dtcommon.MemModule
    99  	ActionModuleMap[dtcommon.MemUpdated] = dtcommon.MemModule
   100  	ActionModuleMap[dtcommon.TwinGet] = dtcommon.TwinModule
   101  	ActionModuleMap[dtcommon.TwinUpdate] = dtcommon.TwinModule
   102  	ActionModuleMap[dtcommon.TwinCloudSync] = dtcommon.TwinModule
   103  	ActionModuleMap[dtcommon.DeviceUpdated] = dtcommon.DeviceModule
   104  	ActionModuleMap[dtcommon.DeviceStateGet] = dtcommon.DeviceModule
   105  	ActionModuleMap[dtcommon.DeviceStateUpdate] = dtcommon.DeviceModule
   106  	ActionModuleMap[dtcommon.Connected] = dtcommon.CommModule
   107  	ActionModuleMap[dtcommon.Disconnected] = dtcommon.CommModule
   108  	ActionModuleMap[dtcommon.LifeCycle] = dtcommon.CommModule
   109  	ActionModuleMap[dtcommon.Confirm] = dtcommon.CommModule
   110  }
   111  
   112  // SyncSqlite sync sqlite
   113  func SyncSqlite(context *dtcontext.DTContext) error {
   114  	klog.Info("Begin to sync sqlite ")
   115  	rows, queryErr := dtclient.QueryDeviceAll()
   116  	if queryErr != nil {
   117  		klog.Errorf("Query sqlite failed while syncing sqlite, err: %#v", queryErr)
   118  		return queryErr
   119  	}
   120  	if rows == nil {
   121  		klog.Info("Query sqlite nil while syncing sqlite")
   122  		return nil
   123  	}
   124  	for _, device := range *rows {
   125  		err := SyncDeviceFromSqlite(context, device.ID)
   126  		if err != nil {
   127  			continue
   128  		}
   129  	}
   130  	return nil
   131  
   132  }
   133  
   134  //SyncDeviceFromSqlite sync device from sqlite
   135  func SyncDeviceFromSqlite(context *dtcontext.DTContext, deviceID string) error {
   136  	klog.Infof("Sync device detail info from DB of device %s", deviceID)
   137  	_, exist := context.GetDevice(deviceID)
   138  	if !exist {
   139  		var deviceMutex sync.Mutex
   140  		context.DeviceMutex.Store(deviceID, &deviceMutex)
   141  	}
   142  
   143  	defer context.Unlock(deviceID)
   144  	context.Lock(deviceID)
   145  
   146  	devices, err := dtclient.QueryDevice("id", deviceID)
   147  	if err != nil {
   148  		klog.Errorf("query device failed: %v", err)
   149  		return err
   150  	}
   151  	if len(*devices) <= 0 {
   152  		return errors.New("Not found device from db")
   153  	}
   154  	device := (*devices)[0]
   155  
   156  	deviceAttr, err := dtclient.QueryDeviceAttr("deviceid", deviceID)
   157  	if err != nil {
   158  		klog.Errorf("query device attr failed: %v", err)
   159  		return err
   160  	}
   161  	attributes := make([]dtclient.DeviceAttr, 0)
   162  	for _, attr := range *deviceAttr {
   163  		attributes = append(attributes, attr)
   164  	}
   165  
   166  	deviceTwin, err := dtclient.QueryDeviceTwin("deviceid", deviceID)
   167  	if err != nil {
   168  		klog.Errorf("query device twin failed: %v", err)
   169  		return err
   170  	}
   171  	twins := make([]dtclient.DeviceTwin, 0)
   172  	for _, twin := range *deviceTwin {
   173  		twins = append(twins, twin)
   174  	}
   175  
   176  	context.DeviceList.Store(deviceID, &dttype.Device{
   177  		ID:          deviceID,
   178  		Name:        device.Name,
   179  		Description: device.Description,
   180  		State:       device.State,
   181  		LastOnline:  device.LastOnline,
   182  		Attributes:  dttype.DeviceAttrToMsgAttr(attributes),
   183  		Twin:        dttype.DeviceTwinToMsgTwin(twins)})
   184  
   185  	return nil
   186  }
   187  
   188  func classifyMsg(message *dttype.DTMessage) bool {
   189  	if EventActionMap == nil {
   190  		initEventActionMap()
   191  	}
   192  	var identity string
   193  	var action string
   194  	msgSource := message.Msg.GetSource()
   195  	if strings.Compare(msgSource, "bus") == 0 {
   196  		idLoc := 3
   197  		topic := message.Msg.GetResource()
   198  		topicByte, err := base64.URLEncoding.DecodeString(topic)
   199  		if err != nil {
   200  			return false
   201  		}
   202  		topic = string(topicByte)
   203  
   204  		klog.Infof("classify the msg with the topic %s", topic)
   205  		splitString := strings.Split(topic, "/")
   206  		if len(splitString) == 4 {
   207  			if strings.HasPrefix(topic, dtcommon.LifeCycleConnectETPrefix) {
   208  				action = dtcommon.LifeCycle
   209  			} else if strings.HasPrefix(topic, dtcommon.LifeCycleDisconnectETPrefix) {
   210  				action = dtcommon.LifeCycle
   211  			} else {
   212  				return false
   213  			}
   214  		} else {
   215  			identity = splitString[idLoc]
   216  			loc := strings.Index(topic, identity)
   217  			nextLoc := loc + len(identity)
   218  			prefix := topic[0:loc]
   219  			suffix := topic[nextLoc:]
   220  			klog.Infof("%s %s", prefix, suffix)
   221  			if v, exist := EventActionMap[prefix][suffix]; exist {
   222  				action = v
   223  			} else {
   224  				return false
   225  			}
   226  		}
   227  		message.Msg.Content = []byte((message.Msg.Content).(string))
   228  		message.Identity = identity
   229  		message.Action = action
   230  		klog.Infof("Classify the msg to action %s", action)
   231  		return true
   232  	} else if (strings.Compare(msgSource, "edgemgr") == 0) || (strings.Compare(msgSource, "devicecontroller") == 0) {
   233  		switch message.Msg.Content.(type) {
   234  		case []byte:
   235  			klog.Info("Message content type is []byte, no need to marshal again")
   236  		default:
   237  			content, err := json.Marshal(message.Msg.Content)
   238  			if err != nil {
   239  				return false
   240  			}
   241  			message.Msg.Content = content
   242  		}
   243  		if strings.Contains(message.Msg.Router.Resource, "membership/detail") {
   244  			message.Action = dtcommon.MemDetailResult
   245  			return true
   246  		} else if strings.Contains(message.Msg.Router.Resource, "membership") {
   247  			message.Action = dtcommon.MemUpdated
   248  			return true
   249  		} else if strings.Contains(message.Msg.Router.Resource, "twin/cloud_updated") {
   250  			message.Action = dtcommon.TwinCloudSync
   251  			resources := strings.Split(message.Msg.Router.Resource, "/")
   252  			message.Identity = resources[1]
   253  			return true
   254  		} else if strings.Contains(message.Msg.Router.Operation, "updated") {
   255  			resources := strings.Split(message.Msg.Router.Resource, "/")
   256  			if len(resources) == 2 && strings.Compare(resources[0], "device") == 0 {
   257  				message.Action = dtcommon.DeviceUpdated
   258  				message.Identity = resources[1]
   259  			}
   260  			return true
   261  		}
   262  		return false
   263  
   264  	} else if strings.Compare(msgSource, "edgehub") == 0 {
   265  		if strings.Compare(message.Msg.Router.Resource, "node/connection") == 0 {
   266  			message.Action = dtcommon.LifeCycle
   267  			return true
   268  		}
   269  		return false
   270  	}
   271  	return false
   272  }
   273  
   274  func (dt *DeviceTwin) runDeviceTwin() {
   275  
   276  	moduleNames := []string{dtcommon.MemModule, dtcommon.TwinModule, dtcommon.DeviceModule, dtcommon.CommModule}
   277  	for _, v := range moduleNames {
   278  		dt.RegisterDTModule(v)
   279  		go dt.DTModules[v].Start()
   280  	}
   281  	go func() {
   282  		for {
   283  			select {
   284  			case <-beehiveContext.Done():
   285  				klog.Warning("Stop DeviceTwin ModulesContext Receive loop")
   286  				return
   287  			default:
   288  
   289  			}
   290  			if msg, ok := beehiveContext.Receive("twin"); ok == nil {
   291  				klog.Info("DeviceTwin receive msg")
   292  				err := dt.distributeMsg(msg)
   293  				if err != nil {
   294  					klog.Warningf("distributeMsg failed: %v", err)
   295  				}
   296  			}
   297  		}
   298  	}()
   299  
   300  	for {
   301  		select {
   302  		case <-time.After((time.Duration)(60) * time.Second):
   303  			//range to check whether has bug
   304  			for dtmName := range dt.DTModules {
   305  				health, ok := dt.DTContexts.ModulesHealth.Load(dtmName)
   306  				if ok {
   307  					now := time.Now().Unix()
   308  					if now-health.(int64) > 60*2 {
   309  						klog.Infof("%s health %v is old, and begin restart", dtmName, health)
   310  						go dt.DTModules[dtmName].Start()
   311  					}
   312  				}
   313  			}
   314  			for _, v := range dt.HeartBeatToModule {
   315  				v <- "ping"
   316  			}
   317  		case <-beehiveContext.Done():
   318  			for _, v := range dt.HeartBeatToModule {
   319  				v <- "stop"
   320  			}
   321  			klog.Warning("Stop DeviceTwin ModulesHealth load loop")
   322  			return
   323  		}
   324  	}
   325  }