1function Modbus(uartPort, dirPort) { 2 var rtn = {} 3 var lastTid = 1 4 rtn.readTimeout = 500; 5 rtn.readTimeouthandler; 6 rtn.packetBufferLength = 100 7 rtn.packets = [] 8 9 rtn.stream = Serial(uartPort, dirPort) 10 rtn.protocal = 'rtu' 11 12 rtn.read = function (unitId, address, timeout, callback) { 13 var parsedAddress = parseAddress(address) 14 var funcCode = parsedAddress.fcRead 15 var length = parsedAddress.length 16 address = parsedAddress.address 17 18 rtn.readTimeout = timeout 19 rtn.readTimeouthandler = setTimeout(function() { 20 rtn.packets[tid].promiseReject('timeout') 21 }, rtn.readTimeout) 22 23 var tid = getTid() 24 25 var buff = makeDataPacket(tid, 0, unitId, funcCode, address, null, length) 26 if (rtn.protocal == 'rtu') { buff = buff.rtu } 27 28 var packet = { 29 onResponce: callback, 30 tx: { 31 funcCode: funcCode, 32 tid: tid, 33 address: address, 34 hex: buff.toString('hex') 35 }, 36 promiseResolve: null, 37 promiseReject: null, 38 rx: null 39 } 40 41 rtn.packets[tid] = packet 42 43 rtn.stream.send(buff) 44 console.log('read send ' + buff.toString('hex')) 45 return new Promise(function (resolve, reject) { 46 rtn.packets[tid].promiseResolve = resolve 47 rtn.packets[tid].promiseReject = reject 48 }) 49 50 } 51 rtn.write = function (unitId, address, value, timeout, callback) { 52 var parsedAddress = parseAddress(address) 53 var funcCode = parsedAddress.fcWrite 54 var length = parsedAddress.length 55 address = parsedAddress.address 56 var tid = getTid() 57 58 rtn.readTimeout = timeout 59 rtn.readTimeouthandler = setTimeout(function() { 60 rtn.packets[tid].promiseReject('timeout') 61 }, rtn.readTimeout) 62 63 if (funcCode == 5 && value == true) { value = 65280 } // To force a coil on you send FF00 not 0001 64 65 var buff = makeDataPacket(tid, 0, unitId, funcCode, address, value, length) 66 if (rtn.protocal == 'rtu') { buff = buff.rtu } 67 // console.log('buff ' + buff) 68 var packet = { 69 onResponce: callback, 70 tx: { 71 funcCode: funcCode, 72 tid: tid, 73 address: address, 74 hex: buff.toString('hex') 75 }, 76 rx: null 77 } 78 rtn.packets[tid] = packet 79 80 rtn.stream.send(buff) 81 console.log('write send ' + buff.toString('hex')) 82 83 return new Promise(function (resolve, reject) { 84 rtn.packets[tid].promiseResolve = resolve 85 rtn.packets[tid].promiseReject = reject 86 }) 87 } 88 var getTid = function () { 89 if (lastTid > rtn.packetBufferLength) { lastTid = 0 } 90 lastTid++ 91 if (rtn.protocal == 'rtu') { lastTid = 0 } 92 return lastTid 93 } 94 95 rtn.stream.onData = function (buf) { 96 console.log('onData: ' + buf.toString('hex')) 97 98 var modbusRes 99 if (rtn.protocal == "rtu") { modbusRes = parseResponseRtu(buf) } 100 101 var value = modbusRes.value 102 var tid = modbusRes.tid 103 if (rtn.protocal == "rtu") { tid = 0 } 104 105 var err = null 106 if (modbusRes.exceptionCode) { err = 'Exception Code: 02 - Illegal Data Address' } 107 108 rtn.packets[tid].rx = modbusRes 109 rtn.packets[tid].rx.hex = buf.toString('hex') 110 if (typeof (rtn.packets[tid].onResponce) == "function") { 111 rtn.packets[tid].onResponce(err, value) 112 } 113 114 if (err) { 115 rtn.packets[tid].promiseReject(err) 116 } 117 else { 118 // console.log('raw data:', value) 119 rtn.packets[tid].promiseResolve(value) 120 clearTimeout(rtn.readTimeouthandler) 121 } 122 123 } 124 return rtn 125} 126 127function parseResponseRtu(buffer) { 128 var res = {} 129 res.tid = 1 130 var buf = new Buffer(buffer) 131 res.unitId = buf.readInt8(0) //Unit Id - Byte 6 132 res.funcCode = buf.readInt8(1) //Function Code - Byte 7 133 res.byteCount = Math.abs(buf.readInt8(2)) //Byte Count - Byte 8 134 if (buf.length > 3) { 135 // res.value = buf.readIntBE(3, buf.length - 5) //Data - Bytes 9+ 136 // var arr = Array.prototype.slice.call(new Uint8Array(buf)) 137 // res.value = new Buffer(arr.slice(3, buf.length - 5)) 138 var dataBuffer = buf.subarray(3, buf.length - 2) 139 res.value = Array.prototype.slice.call(new Uint8Array(buf)) 140 } 141 142 return res 143} 144 145function crc16modbus(buf, previous) { 146 var TABLE = [0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241, 0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440, 0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40, 0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841, 0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40, 0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41, 0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641, 0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040, 0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240, 0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441, 0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41, 0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840, 0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41, 0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40, 0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640, 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041, 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240, 0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441, 0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41, 0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840, 0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41, 0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40, 0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640, 0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041, 0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241, 0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440, 0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40, 0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841, 0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40, 0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41, 0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641, 0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040]; 147 148 var crc = typeof previous !== 'undefined' ? ~~previous : 0xffff; 149 150 for (var index = 0; index < buf.length; index++) { 151 var byte = buf[index]; 152 crc = (TABLE[(crc ^ byte) & 0xff] ^ crc >> 8) & 0xffff; 153 } 154 155 return crc; 156} 157 158function makeDataPacket(transId, protoId, unitId, funcCode, address, data, length) { 159 160 if (typeof (data) == "boolean" && data) { data = 1 } 161 if (typeof (data) == "boolean" && !data) { data = 0 } 162 163 164 if (address == 0) { address = 65535 } 165 else { address = address - 1 } 166 167 var dataBytes = 0 168 if (funcCode == 15) { dataBytes = length } 169 if (funcCode == 16) { dataBytes = length * 2 } 170 171 var bufferLength = 12 172 if (funcCode == 15 || funcCode == 16) { bufferLength = 13 + dataBytes } 173 174 var byteCount = bufferLength - 6 175 176 var buf = new Buffer(bufferLength) 177 178 buf.writeUInt16BE(transId, 0) 179 buf.writeUInt16BE(protoId, 2) 180 buf.writeUInt16BE(byteCount, 4) 181 buf.writeUInt8(unitId, 6) 182 buf.writeUInt8(funcCode, 7) 183 buf.writeUInt16BE(address, 8) 184 185 if (funcCode == 1 || funcCode == 2 || funcCode == 3 || funcCode == 4) { 186 buf.writeUInt16BE(length, 10) 187 } 188 if (funcCode == 5 || funcCode == 6) { 189 buf.writeInt16BE(data, 10) 190 } 191 if (funcCode == 15 || funcCode == 16) { 192 buf.writeInt16BE(length, 10) 193 buf.writeUInt8(dataBytes, 12) 194 buf.writeInt32BE(data, 13) 195 } 196 197 var makeCrc = crc16modbus 198 199 var bufRtu = buf.slice(6, bufferLength) 200 var crc = makeCrc(bufRtu) 201 var arr = Array.prototype.slice.call(new Uint8Array(bufRtu)) 202 arr.push(0); 203 arr.push(0); 204 var buffRtu = new Buffer(arr); 205 buffRtu.writeUInt16LE(crc, buffRtu.length - 2) 206 //console.log('buffRtu:', buffRtu); 207 return { rtu: Array.prototype.slice.call(new Uint8Array(buffRtu)) } 208} 209 210function parseAddress(address) { 211 var rtn = {} 212 address = address.toLowerCase() 213 214 // 提取地址 215 // Data Type Short Hand Size Accessibility 216 // Descrite Input i 1 Bit Read Only 217 // Coil c 1 Bit Read / Write 218 // Input Register ir 16 Bits Read Only 219 // Holding Register hr 16 Bits Read / Write 220 var isRegister = address.replace('r', '').length !== address.length; 221 if (isRegister) { 222 rtn.address = address.substr(2) 223 rtn.type = address.substr(0, 2) 224 } 225 if (!isRegister) { 226 rtn.address = address.substr(1) 227 rtn.type = address.substr(0, 1) 228 } 229 230 var isRange = rtn.address.replace('-', '').length !== rtn.address.length 231 if (isRange) { 232 var range = rtn.address.split('-') 233 rtn.length = range[0] - range[1] 234 if (rtn.length < 0) { rtn.address = range[0] } 235 if (rtn.length > 0) { rtn.address = range[1] } 236 rtn.length = Math.abs(rtn.length) + 1 237 } 238 if (!isRange) { 239 rtn.length = 1 240 } 241 242 rtn.address = parseInt(rtn.address) 243 244 // 获取类型 245 // FC1 - Read Coil 246 // FC2 - Read Input 247 // FC3 - Read Holding Registers 248 // FC4 - Read Input Registers 249 // FC5 - Write Single Coil 250 // FC6 - Write Single Register 251 // FC15 - Write Multiple Coils 252 // FC16 - Write Multiple Registers 253 if (rtn.type == 'c') { 254 rtn.fcRead = 1 255 rtn.fcWrite = 5 256 } 257 if (rtn.type == 'i') { 258 rtn.fcRead = 2 259 } 260 if (rtn.type == 'hr' && !isRange) { 261 rtn.fcRead = 3 262 rtn.fcWrite = 6 263 } 264 if (rtn.type == 'hr' && isRange) { 265 rtn.fcRead = 3 266 rtn.fcWrite = 16 267 } 268 if (rtn.type == 'ir') { 269 rtn.fcRead = 4 270 } 271 272 return rtn 273} 274 275function Serial(uartPort, dirPort) { 276 var rtn = {} 277 var Uart = require('uart') 278 279 rtn.port = Uart.open(uartPort) 280 281 if (dirPort) { 282 var events = require('events'); 283 var event = new events(); 284 var Gpio = require('gpio') 285 var option = {}; 286 option.id = dirPort.id; 287 rtn.io = Gpio.open(option); 288 } 289 290 rtn.send = function (data) { 291 rtn.io.writeValue(dirPort.polarity === 0 ? 0 : 1); 292 rtn.port.write(data) 293 event.emit('send') 294 } 295 296 rtn.onData = function () { } 297 298 rtn.port.on('data', function (data) { 299 console.log("rtn onData"); 300 rtn.onData(data); 301 }) 302 303 event.on('send', function () { 304 rtn.io.writeValue(dirPort.polarity === 0 ? 1 : 0); 305 }) 306 307 return rtn 308} 309 310module.exports = Modbus;