github.com/jingruilea/kubeedge@v1.2.0-beta.0.0.20200410162146-4bb8902b3879/mappers/modbus_mapper/src/devicetwin.js (about) 1 const constant = require('./constant'); 2 const common = require('./common'); 3 const Buffer = require('buffer').Buffer; 4 const uuidv4 = require('uuid/v4'); 5 const util = require('util'); 6 const async = require('async'); 7 8 class DeviceTwin { 9 constructor(mqttClient) { 10 this.mqttClient = mqttClient; 11 } 12 13 // transferType transfer data according to the dpl configuration 14 transferType(visitor, property, data, callback) { 15 let transData; 16 async.waterfall([ 17 function(callback) { 18 if (visitor.visitorConfig.isRegisterSwap) { 19 common.switchRegister(data, (switchedData)=>{ 20 callback(null, switchedData); 21 }); 22 } else { 23 callback(null, data); 24 } 25 }, 26 function(internalData, callback) { 27 if (visitor.visitorConfig.isSwap && (visitor.visitorConfig.register === 'HoldingRegister' || visitor.visitorConfig.register === 'InputRegister')) { 28 common.switchByte(internalData, (switchedData)=>{ 29 callback(null, switchedData); 30 }); 31 } else { 32 callback(null, internalData); 33 } 34 } 35 ], function(err, transedData) { 36 transData = transedData; 37 }); 38 this.transferDataType(visitor, property, transData, callback); 39 } 40 41 // transferDataType transfer data according to the dpl configuration 42 transferDataType(visitor, property, data, callback) { 43 let transData; 44 switch(property.dataType) { 45 case 'int': 46 case 'float': 47 if (visitor.visitorConfig.register === 'DiscreteInputRegister' || visitor.visitorConfig.register === 'CoilRegister') { 48 common.bitArrayToInt(data, (num)=>{ 49 transData = num; 50 }); 51 } else if (visitor.visitorConfig.register === 'HoldingRegister' || visitor.visitorConfig.register === 'InputRegister') { 52 common.twoByteArrayToInt(data, (num)=>{ 53 transData = num; 54 }) 55 } 56 57 if (visitor.visitorConfig.scale !=0 && transData != null) { 58 transData = transData * visitor.visitorConfig.scale; 59 } 60 61 if (property.dataType === 'int') { 62 transData = parseInt(transData); 63 } 64 65 if (property.maximum !== null && transData > parseFloat(property.maximum)) { 66 logger.info("read data is larger than max value, use max value") 67 transData = parseInt(property.maximum); 68 } else if (property.minimum !== null && transData < parseFloat(property.minimum)) { 69 logger.info("read data is smaller than min value, use min value") 70 transData = parseInt(property.minimum); 71 } 72 73 callback(transData); 74 break; 75 case 'string': 76 let buf = new Buffer.from(data); 77 transData = buf.toString('utf8') 78 callback(transData); 79 break; 80 case 'boolean': 81 if (data[0] == 0 || data[0] == 1){ 82 transData = Boolean(data[0]); 83 } else { 84 transData = null; 85 } 86 callback(transData); 87 break; 88 default: 89 logger.error('unknown dataType: ', property.dataType); 90 callback(null); 91 break; 92 } 93 } 94 95 // updateActual update actual value to edge mqtt 96 updateActual(deviceID, property, value) { 97 let reply_msg = { 98 event_id: "", 99 timestamp: new Date().getTime() 100 }; 101 let twin = {}; 102 twin[property.name] = { 103 actual: { 104 value: String(value), 105 metadata: { 106 timestamp: new Date().getTime() 107 } 108 }, 109 metadata: { 110 tyep: property.dataType 111 } 112 }; 113 reply_msg.twin = twin; 114 this.mqttClient.publish(constant.defaultTopicPrefix + deviceID + constant.twinUpdateTopic, JSON.stringify(reply_msg)); 115 } 116 117 // dealUpdate set latest actual value of devicetwin into actualVal map 118 dealUpdate(transData, property, deviceID, actualVals) { 119 if (!actualVals.has(util.format("%s-%s", deviceID, property.name))) { 120 this.updateActual(deviceID, property, transData); 121 actualVals.set(util.format("%s-%s", deviceID, property.name), String(transData)); 122 logger.info("update devicetwin[%s] of device[%s] successfully", property.name, deviceID); 123 } else { 124 this.compareActuals(transData, actualVals.get(util.format("%s-%s", deviceID, property.name)), (changed)=>{ 125 if (changed) { 126 this.updateActual(deviceID, property, transData); 127 actualVals.set(util.format("%s-%s", deviceID, property.name), String(transData)); 128 logger.info("update devicetwin[%s] of device[%s] successfully", property.name, deviceID); 129 } 130 }); 131 } 132 } 133 134 // getActuals publish get devicetwin msg to edge mqtt 135 getActuals(deviceID) { 136 let payload_msg = { 137 event_id: "", 138 timestamp: new Date().getTime() 139 }; 140 this.mqttClient.publish(constant.defaultTopicPrefix + deviceID + constant.twinGetTopic, JSON.stringify(payload_msg)); 141 } 142 143 // setActuals set device property and actual value map 144 setActuals(getMsg, callback) { 145 let deviceTwin = getMsg.twin; 146 let PropActuals = new Map(); 147 Object.keys(deviceTwin).forEach(function(key){ 148 if (deviceTwin[key].hasOwnProperty('actual')) { 149 PropActuals.set(key, deviceTwin[key].actual.value); 150 } 151 }) 152 callback(PropActuals); 153 } 154 155 // setExpecteds set device property and expected value map 156 setExpecteds(getMsg, callback) { 157 let deviceTwin = getMsg.twin; 158 let ProExpect = new Map(); 159 Object.keys(deviceTwin).forEach(function(key){ 160 if (deviceTwin[key].hasOwnProperty('expected') && !deviceTwin[key].hasOwnProperty('actual') || JSON.stringify(deviceTwin[key].actual) == '{}') { 161 ProExpect.set(key, deviceTwin[key].expected.value); 162 } 163 }) 164 callback(ProExpect); 165 } 166 167 // compareActuals compare if data is changed 168 compareActuals(data, cachedActuals, callback) { 169 let changed = false; 170 if (data != cachedActuals) { 171 changed = true; 172 } 173 callback(changed); 174 } 175 176 // UpdateDirectActuals update all devicetwin property to edge mqtt 177 UpdateDirectActuals(devIns, deviceID, actualVals) { 178 if (devIns.has(deviceID)) { 179 let deviceName = devIns.get(deviceID).name; 180 this.generateDirectGetMsg(deviceName, deviceID, actualVals, (directGetMsg)=>{ 181 this.mqttClient.publish(constant.defaultDirectTopicPrefix + deviceID + constant.directGetTopic, JSON.stringify(directGetMsg)); 182 }); 183 } 184 } 185 186 // generateDirectGetMsg generate Direct Get Msg in message format 187 generateDirectGetMsg(deviceName, deviceID, actualVals, callback) { 188 let header = { 189 msg_id: uuidv4(), 190 parent_msg_id: "", 191 timestamp: new Date().getTime(), 192 sync: false 193 }; 194 let route = { 195 source: "eventbus", 196 group: "", 197 operation: "upload", 198 resource: util.format("%s%s%s", constant.defaultDirectTopicPrefix, deviceID, constant.directGetTopic) 199 }; 200 let content = { 201 data: actualVals, 202 device_name: deviceName, 203 device_id: deviceID, 204 timestamp: new Date().getTime() 205 }; 206 let directGetMsg = { 207 header: header, 208 route: route, 209 content: content 210 }; 211 callback(directGetMsg); 212 } 213 214 // syncExpected check whether expected value should be update to device 215 static syncExpected(delta, key, callback) { 216 let deviceTwin = delta.twin[key]; 217 if (!delta.twin.hasOwnProperty(key)) { 218 logger.error("Invalid device twin ", key); 219 return; 220 } 221 if (!deviceTwin.hasOwnProperty('actual') || 222 (deviceTwin.hasOwnProperty('expected') && deviceTwin.expected.hasOwnProperty('metadata') && deviceTwin.actual.hasOwnProperty('metadata') && 223 deviceTwin.expected.metadata.timestamp > deviceTwin.actual.metadata.timestamp && 224 deviceTwin.expected.value !== deviceTwin.actual.value)) { 225 callback(deviceTwin.expected.value); 226 } 227 } 228 } 229 230 module.exports = DeviceTwin;