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;