whyclj
2021-01-05 64aba412c2b739d67795b14a3cae069d311697f9
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
package com.intelligt.modbus.jlibmodbus.master;
 
import com.intelligt.modbus.jlibmodbus.Modbus;
import com.intelligt.modbus.jlibmodbus.data.CommStatus;
import com.intelligt.modbus.jlibmodbus.exception.ModbusIOException;
import com.intelligt.modbus.jlibmodbus.exception.ModbusNumberException;
import com.intelligt.modbus.jlibmodbus.exception.ModbusProtocolException;
import com.intelligt.modbus.jlibmodbus.msg.ModbusRequestBuilder;
import com.intelligt.modbus.jlibmodbus.msg.ModbusRequestFactory;
import com.intelligt.modbus.jlibmodbus.msg.base.ModbusFileRecord;
import com.intelligt.modbus.jlibmodbus.msg.base.ModbusMessage;
import com.intelligt.modbus.jlibmodbus.msg.base.ModbusRequest;
import com.intelligt.modbus.jlibmodbus.msg.base.ModbusResponse;
import com.intelligt.modbus.jlibmodbus.msg.base.mei.MEIReadDeviceIdentification;
import com.intelligt.modbus.jlibmodbus.msg.base.mei.ReadDeviceIdentificationCode;
import com.intelligt.modbus.jlibmodbus.msg.response.*;
import com.intelligt.modbus.jlibmodbus.net.ModbusConnection;
import com.intelligt.modbus.jlibmodbus.net.transport.ModbusTransport;
import com.intelligt.modbus.jlibmodbus.utils.FrameEvent;
import com.intelligt.modbus.jlibmodbus.utils.FrameEventListener;
import com.intelligt.modbus.jlibmodbus.utils.FrameEventListenerList;
import com.intelligt.modbus.jlibmodbus.utils.ModbusExceptionCode;
 
/*
 * Copyright (C) 2016 "Invertor" Factory", JSC
 * [http://www.sbp-invertor.ru]
 *
 * This file is part of JLibModbus.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Authors: Vladislav Y. Kochedykov, software engineer.
 * email: vladislav.kochedykov@gmail.com
 */
abstract public class ModbusMaster implements FrameEventListenerList {
 
    final private ModbusConnection conn;
    final private BroadcastResponse broadcastResponse = new BroadcastResponse();
    private int transactionId = 0;
    private long requestTime = 0;
 
    public ModbusMaster(ModbusConnection conn) {
        this.conn = conn;
    }
 
    public int getTransactionId() {
        return transactionId;
    }
 
    public void setTransactionId(int transactionId) {
        this.transactionId = Math.min(Math.abs(transactionId), Modbus.TRANSACTION_ID_MAX_VALUE);
    }
 
    protected ModbusConnection getConnection() {
        return conn;
    }
 
    /**
     * this method allows you to implement your own behavior of connect method.
     *
     * @throws ModbusIOException
     */
    protected void connectImpl() throws ModbusIOException {
        getConnection().open();
    }
 
    /**
     * this method allows you to implement your own behavior of disconnect method.
     *
     * @throws ModbusIOException
     */
    protected void disconnectImpl() throws ModbusIOException {
        getConnection().close();
    }
 
    final public void connect() throws ModbusIOException {
        if (!isConnected()) {
            connectImpl();
        }
    }
 
    final public void disconnect() throws ModbusIOException {
        if (isConnected()) {
            disconnectImpl();
        }
    }
 
    public boolean isConnected() {
        return getConnection().isOpened();
    }
 
    protected void sendRequest(ModbusMessage msg) throws ModbusIOException {
        ModbusTransport transport = getConnection().getTransport();
        if (transport == null)
            throw new ModbusIOException("transport is null");
        transport.send(msg);
        requestTime = System.currentTimeMillis();
    }
 
    protected ModbusMessage readResponse(ModbusRequest request) throws ModbusProtocolException, ModbusNumberException, ModbusIOException {
        return getConnection().getTransport().readResponse(request);
    }
 
    /**
     * this function allows you to process your own ModbusRequest.
     * Each request class has a compliant response class for instance ReadHoldingRegistersRequest and ReadHoldingRegistersResponse.
     * If you process an instance of ReadHoldingRegistersRequest this function exactly either returns a ReadHoldingRegistersResponse instance or throws an exception.
     *
     * @param request an instance of ModbusRequest.
     * @return an instance of ModbusResponse.
     * @throws ModbusProtocolException
     * @throws ModbusIOException
     * @see ModbusRequestFactory
     * @see ModbusRequest
     * @see ModbusResponse
     * @see com.intelligt.modbus.jlibmodbus.msg.request
     * @see com.intelligt.modbus.jlibmodbus.msg.request
     */
    synchronized public ModbusResponse processRequest(ModbusRequest request) throws ModbusProtocolException, ModbusIOException {
        try {
            sendRequest(request);
            if (request.getServerAddress() != Modbus.BROADCAST_ID) {
                do {
                    try {
                        ModbusResponse msg = (ModbusResponse) readResponse(request);
                        request.validateResponse(msg);
                        /*
                         * if you have received an ACKNOWLEDGE,
                         * it means that operation is in processing and you should be waiting for the answer
                         */
                        if (msg.getModbusExceptionCode() != ModbusExceptionCode.ACKNOWLEDGE) {
                            if (msg.isException())
                                throw new ModbusProtocolException(msg.getModbusExceptionCode());
                            return msg;
                        }
                    } catch (ModbusNumberException mne) {
                        Modbus.log().warning(mne.getLocalizedMessage());
                    }
                } while (System.currentTimeMillis() - requestTime < getConnection().getReadTimeout());
                /*
                 * throw an exception if there is a response timeout
                 */
                throw new ModbusIOException("Response timeout.");
            } else {
            /*
             return because slaves do not respond broadcast requests
             */
                broadcastResponse.setFunction(request.getFunction());
                return broadcastResponse;
            }
        } catch (ModbusIOException mioe) {
            disconnect();
            throw mioe;
        }
    }
 
    /**
     * ModbusMaster will block for only this amount of time.
     * If the timeout expires, a ModbusTransportException is raised, though the ModbusMaster is still valid.
     *
     * @param timeout the specified timeout, in milliseconds.
     */
    public void setResponseTimeout(int timeout) {
        try {
            getConnection().setReadTimeout(timeout);
        } catch (Exception e) {
            Modbus.log().warning(e.getLocalizedMessage());
        }
    }
 
    /**
     * This function code is used to read the contents of a contiguous block of holding registers in a
     * remote device. The Request PDU specifies the starting register address and the number of
     * registers. In the PDU Registers are addressed starting at zero. Therefore registers numbered
     * 1-16 are addressed as 0-15.
     *
     * @param serverAddress a slave address
     * @param startAddress  starting register address
     * @param quantity      the number of registers
     * @return the register data
     * @throws ModbusProtocolException if modbus-exception is received
     * @throws ModbusNumberException   if response is invalid
     * @throws ModbusIOException       if remote slave is unavailable
     */
    final public int[] readHoldingRegisters(int serverAddress, int startAddress, int quantity) throws
            ModbusProtocolException, ModbusNumberException, ModbusIOException {
        ModbusRequest request = ModbusRequestBuilder.getInstance().buildReadHoldingRegisters(serverAddress, startAddress, quantity);
        ReadHoldingRegistersResponse response = (ReadHoldingRegistersResponse) processRequest(request);
        return response.getRegisters();
    }
 
    /**
     * This function code is used to read from 1 to 125 contiguous input registers in a remote
     * device. The Request PDU specifies the starting register address and the number of registers.
     * In the PDU Registers are addressed starting at zero. Therefore input registers numbered 1-16
     * are addressed as 0-15.
     *
     * @param serverAddress a slave address
     * @param startAddress  starting register address
     * @param quantity      the number of registers
     * @return the register data
     * @throws ModbusProtocolException if modbus-exception is received
     * @throws ModbusNumberException   if response is invalid
     * @throws ModbusIOException       if remote slave is unavailable
     */
    final public int[] readInputRegisters(int serverAddress, int startAddress, int quantity) throws
            ModbusProtocolException, ModbusNumberException, ModbusIOException {
        ModbusRequest request = ModbusRequestBuilder.getInstance().buildReadInputRegisters(serverAddress, startAddress, quantity);
        ReadHoldingRegistersResponse response = (ReadInputRegistersResponse) processRequest(request);
        return response.getRegisters();
    }
 
    /**
     * This function code is used to read from 1 to 2000 contiguous status of coils in a remote
     * device. The Request PDU specifies the starting address, i.e. the address of the first coil
     * specified, and the number of coils. In the PDU Coils are addressed starting at zero. Therefore
     * coils numbered 1-16 are addressed as 0-15.
     * If the returned output quantity is not a multiple of eight, the remaining coils in the final boolean array
     * will be padded with FALSE.
     *
     * @param serverAddress a slave address
     * @param startAddress  the address of the first coil
     * @param quantity      the number of coils
     * @return the coils
     * @throws ModbusProtocolException if modbus-exception is received
     * @throws ModbusNumberException   if response is invalid
     * @throws ModbusIOException       if remote slave is unavailable
     */
    final synchronized public boolean[] readCoils(int serverAddress, int startAddress, int quantity) throws
            ModbusProtocolException, ModbusNumberException, ModbusIOException {
        ModbusRequest request = ModbusRequestBuilder.getInstance().buildReadCoils(serverAddress, startAddress, quantity);
        ReadCoilsResponse response = (ReadCoilsResponse) processRequest(request);
        return response.getCoils();
    }
 
    /**
     * This function code is used to read from 1 to 2000 contiguous status of discrete inputs in a
     * remote device. The Request PDU specifies the starting address, i.e. the address of the first
     * input specified, and the number of inputs. In the PDU Discrete Inputs are addressed starting
     * at zero. Therefore Discrete inputs numbered 1-16 are addressed as 0-15.
     * If the returned input quantity is not a multiple of eight, the remaining inputs in the final boolean array
     * will be padded with FALSE.
     *
     * @param serverAddress a slave address
     * @param startAddress  the address of the first input
     * @param quantity      the number of inputs
     * @return the discrete inputs
     * @throws ModbusProtocolException if modbus-exception is received
     * @throws ModbusNumberException   if response is invalid
     * @throws ModbusIOException       if remote slave is unavailable
     */
    final public boolean[] readDiscreteInputs(int serverAddress, int startAddress, int quantity) throws
            ModbusProtocolException, ModbusNumberException, ModbusIOException {
        ModbusRequest request = ModbusRequestBuilder.getInstance().buildReadDiscreteInputs(serverAddress, startAddress, quantity);
        ReadDiscreteInputsResponse response = (ReadDiscreteInputsResponse) processRequest(request);
        return response.getCoils();
    }
 
    /**
     * This function code is used to write a single output to either ON or OFF in a remote device.
     * The requested ON/OFF state is specified by a constant in the request data field. A value of
     * TRUE requests the output to be ON. A value of FALSE requests it to be OFF.
     * The Request PDU specifies the address of the coil to be forced. Coils are addressed starting
     * at zero. Therefore coil numbered 1 is addressed as 0.
     *
     * @param serverAddress a slave address
     * @param startAddress  the address of the coil to be forced
     * @param flag          the request data field
     * @throws ModbusProtocolException if modbus-exception is received
     * @throws ModbusNumberException   if response is invalid
     * @throws ModbusIOException       if remote slave is unavailable
     */
    final public void writeSingleCoil(int serverAddress, int startAddress, boolean flag) throws
            ModbusProtocolException, ModbusNumberException, ModbusIOException {
        processRequest(ModbusRequestBuilder.getInstance().buildWriteSingleCoil(serverAddress, startAddress, flag));
    }
 
    /**
     * This function code is used to write a single holding register in a remote device.
     * The Request PDU specifies the address of the register to be written. Registers are addressed
     * starting at zero. Therefore register numbered 1 is addressed as 0.
     *
     * @param serverAddress a slave address
     * @param startAddress  the address of the register to be written
     * @param register      value
     * @throws ModbusProtocolException if modbus-exception is received
     * @throws ModbusNumberException   if response is invalid
     * @throws ModbusIOException       if remote slave is unavailable
     */
    final public void writeSingleRegister(int serverAddress, int startAddress, int register) throws
            ModbusProtocolException, ModbusNumberException, ModbusIOException {
        processRequest(ModbusRequestBuilder.getInstance().buildWriteSingleRegister(serverAddress, startAddress, register));
    }
 
    /**
     * This function code is used to force each coil in a sequence of coils to either ON or OFF in a
     * remote device. The Request PDU specifies the coil references to be forced. Coils are
     * addressed starting at zero. Therefore coil numbered 1 is addressed as 0.
     * The requested ON/OFF states are specified by contents of the request array of boolean. A logical 'true'
     * in position of the array requests the corresponding output to be ON. A logical 'false' requests
     * it to be OFF.
     *
     * @param serverAddress a slave address
     * @param startAddress  the address of the coils to be written
     * @param coils         the coils
     * @throws ModbusProtocolException if modbus-exception is received
     * @throws ModbusNumberException   if response is invalid
     * @throws ModbusIOException       if remote slave is unavailable
     */
    final public void writeMultipleCoils(int serverAddress, int startAddress, boolean[] coils) throws
            ModbusProtocolException, ModbusNumberException, ModbusIOException {
        processRequest(ModbusRequestBuilder.getInstance().buildWriteMultipleCoils(serverAddress, startAddress, coils));
    }
 
    /**
     * This function code is used to write a block of contiguous registers (1 to 123 registers) in a
     * remote device.
     * The requested written values are specified in the request data field. Data is packed as two
     * bytes per register.
     *
     * @param serverAddress a slave address
     * @param startAddress  the address of the registers to be written
     * @param registers     the register data
     * @throws ModbusProtocolException if modbus-exception is received
     * @throws ModbusNumberException   if response is invalid
     * @throws ModbusIOException       if remote slave is unavailable
     */
    final public void writeMultipleRegisters(int serverAddress, int startAddress, int[] registers) throws
            ModbusProtocolException, ModbusNumberException, ModbusIOException {
        processRequest(ModbusRequestBuilder.getInstance().buildWriteMultipleRegisters(serverAddress, startAddress, registers));
    }
 
    /**
     * This function code performs a combination of one read operation and one write operation in a
     * single MODBUS transaction. The write operation is performed before the read.
     * Holding registers are addressed starting at zero. Therefore holding registers 1-16 are
     * addressed in the PDU as 0-15.
     * The request specifies the starting address and number of holding registers to be read as well
     * as the starting address, number of holding registers, and the data to be written.
     *
     * @param serverAddress a slave address
     * @param readAddress   the address of the registers to be read
     * @param readQuantity  the number of registers to be read
     * @param writeAddress  the address of the registers to be written
     * @param registers     the number of registers to be written
     * @return the register value
     * @throws ModbusProtocolException if modbus-exception is received
     * @throws ModbusNumberException   if response is invalid
     * @throws ModbusIOException       if remote slave is unavailable
     */
    final public int[] readWriteMultipleRegisters(int serverAddress, int readAddress, int readQuantity, int writeAddress, int[] registers) throws
            ModbusProtocolException, ModbusNumberException, ModbusIOException {
        ModbusRequest request = ModbusRequestBuilder.getInstance().buildReadWriteMultipleRegisters(serverAddress, readAddress, readQuantity, writeAddress, registers);
        ReadWriteMultipleRegistersResponse response = (ReadWriteMultipleRegistersResponse) processRequest(request);
        return response.getRegisters();
    }
 
    /**
     * This function code allows to read the contents of a First-In-First-Out (FIFO) queue of register
     * in a remote device. The function returns the queued data.
     * Up to 31 queue data registers can be read.
     * The function reads the queue contents, but does not clear them.
     * If the queue count exceeds 31, an exception response is returned with an error code of 03
     * (Illegal Data Value).
     *
     * @param serverAddress      a slave address
     * @param fifoPointerAddress address of a fifo pointer register
     * @return the data register value
     * @throws ModbusProtocolException if modbus-exception is received
     * @throws ModbusNumberException   if response is invalid
     * @throws ModbusIOException       if remote slave is unavailable
     */
    final public int[] readFifoQueue(int serverAddress, int fifoPointerAddress) throws
            ModbusProtocolException, ModbusNumberException, ModbusIOException {
        ModbusRequest request = ModbusRequestBuilder.getInstance().buildReadFifoQueue(serverAddress, fifoPointerAddress);
        ReadFifoQueueResponse response = (ReadFifoQueueResponse) processRequest(request);
        return response.getFifoValueRegister();
    }
 
    /**
     * This function code is used to perform a file record read.
     * A file is an organization of records. Each file contains 10000 records, addressed 0000 to
     * 9999 decimal or 0X0000 to 0X270F. For example, record 12 is addressed as 12.
     * The function can read multiple groups of references.
     *
     * @param serverAddress a slave address
     * @param records       array of ModbusFileRecord
     * @return array of ModbusFileRecord has been read
     * @throws ModbusProtocolException if modbus-exception is received
     * @throws ModbusNumberException   if response is invalid
     * @throws ModbusIOException       if remote slave is unavailable
     */
    final public ModbusFileRecord[] readFileRecord(int serverAddress, ModbusFileRecord[] records) throws
            ModbusProtocolException, ModbusNumberException, ModbusIOException {
        ModbusRequest request = ModbusRequestBuilder.getInstance().buildReadFileRecord(serverAddress, records);
        ReadFileRecordResponse response = (ReadFileRecordResponse) processRequest(request);
        return response.getFileRecords();
    }
 
    /**
     * This function code is used to perform a file record write. All Request Data Lengths are
     * provided in terms of number of bytes and all Record Lengths are provided in terms of the
     * number of 16-bit words.
     * A file is an organization of records. Each file contains 10000 records, addressed 0000 to
     * 9999 decimal or 0X0000 to 0X270F. For example, record 12 is addressed as 12.
     * The function can write multiple groups of references.
     *
     * @param serverAddress a server address
     * @param record        the ModbusFileRecord to be written
     * @throws ModbusProtocolException if modbus-exception is received
     * @throws ModbusNumberException   if response is invalid
     * @throws ModbusIOException       if remote slave is unavailable
     */
    final public void writeFileRecord(int serverAddress, ModbusFileRecord record) throws
            ModbusProtocolException, ModbusNumberException, ModbusIOException {
        processRequest(ModbusRequestBuilder.getInstance().buildWriteFileRecord(serverAddress, record));
    }
 
    /**
     * This function code is used to modify the contents of a specified holding register using a
     * combination of an AND mask, an OR mask, and the register's current contents. The function
     * can be used to set or clear individual bits in the register.
     * The request specifies the holding register to be written, the data to be used as the AND
     * mask, and the data to be used as the OR mask. Registers are addressed starting at zero.
     * Therefore registers 1-16 are addressed as 0-15.
     * The function’s algorithm is:
     * Result = (Current Contents AND And_Mask) OR (Or_Mask AND (NOT And_Mask))
     * For example:
     * Hex Binary
     * Current Contents=    12  0001 0010
     * And_Mask =           F2  1111 0010
     * Or_Mask =            25  0010 0101
     * <p>
     * (NOT And_Mask)=      0D  0000 1101
     * <p>
     * Result =             17  0001 0111
     * <p>
     * Note:
     * y If the Or_Mask value is zero, the result is simply the logical ANDing of the current contents and
     * And_Mask. If the And_Mask value is zero, the result is equal to the Or_Mask value.
     * y The contents of the register can be read with the Read Holding Registers function (function code 03).
     * They could, however, be changed subsequently as the controller scans its user logic program.
     *
     * @param serverAddress slave id
     * @param startAddress  reference address
     * @param and           the AND mask
     * @param or            the OR mask
     * @throws ModbusProtocolException if modbus-exception is received
     * @throws ModbusNumberException   if response is invalid
     * @throws ModbusIOException       if remote slave is unavailable
     */
    final public void maskWriteRegister(int serverAddress, int startAddress, int and, int or) throws
            ModbusProtocolException, ModbusNumberException, ModbusIOException {
        processRequest(ModbusRequestBuilder.getInstance().buildMaskWriteRegister(serverAddress, startAddress, and, or));
    }
 
    /**
     * This function code is used to read the contents of eight Exception Status outputs in a remote
     * device.
     * The function provides a simple method for accessing this information, because the Exception
     * Output references are known (no output reference is needed in the function).
     * The normal response contains the status of the eight Exception Status outputs. The outputs
     * are packed into one data byte, with one bit per output. The status of the lowest output
     * reference is contained in the least significant bit of the byte.
     * The contents of the eight Exception Status outputs are device specific.
     *
     * @param serverAddress a slave address
     * @return the eight Exception Status outputs
     * @throws ModbusProtocolException if modbus-exception is received
     * @throws ModbusNumberException   if response is invalid
     * @throws ModbusIOException       if remote slave is unavailable
     */
    abstract public int readExceptionStatus(int serverAddress) throws
            ModbusProtocolException, ModbusNumberException, ModbusIOException;
 
    /**
     * This function code is used to read the description of the type, the current status, and other
     * information specific to a remote device.
     * The data contents are specific to each type of device.
     *
     * @param serverAddress slave address
     * @return a byte array of the device's specific data
     * @throws ModbusProtocolException if modbus-exception is received
     * @throws ModbusNumberException   if response is invalid
     * @throws ModbusIOException       if remote slave is unavailable
     */
    abstract public byte[] reportSlaveId(int serverAddress) throws
            ModbusProtocolException, ModbusNumberException, ModbusIOException;
 
    /**
     * This function code is used to get a status word and an event count from the remote device's
     * communication event counter.
     * By fetching the current count before and after a series of messages, a client can determine
     * whether the messages were handled normally by the remote device.
     * The device’s event counter is incremented once for each successful message completion. It
     * is not incremented for exception responses, poll commands, or fetch event counter
     * commands.
     * The event counter can be reset by means of the Diagnostics function (code 08), with a subfunction of Restart Communications Option (code 00 01) or Clear Counters and Diagnostic
     * Register (code 00 0A).
     *
     * @param serverAddress a slave address
     * @return the CommStatus instance
     * @throws ModbusProtocolException if modbus-exception is received
     * @throws ModbusNumberException   if response is invalid
     * @throws ModbusIOException       if remote slave is unavailable
     * @see CommStatus
     */
    abstract public CommStatus getCommEventCounter(int serverAddress) throws
            ModbusProtocolException, ModbusNumberException, ModbusIOException;
 
    /**
     * This function code is used to get a status word, event count, message count, and a field of
     * event bytes from the remote device.
     * The status word and event counts are identical to that returned by the Get Communications
     * Event Counter function (11, 0B hex).
     * The message counter contains the quantity of messages processed by the remote device
     * since its last restart, clear counters operation, or power–up. This count is identical to that
     * returned by the Diagnostic function (code 08), sub-function Return Bus Message Count (code
     * 11, 0B hex).
     * The event bytes field contains 0-64 bytes, with each byte corresponding to the status of one
     * MODBUS send or receive operation for the remote device. The remote device enters the
     * events into the field in chronological order. Byte 0 is the most recent event. Each new byte
     * flushes the oldest byte from the field.
     *
     * @param serverAddress a slave address
     * @return the CommStatus instance
     * @throws ModbusProtocolException if modbus-exception is received
     * @throws ModbusNumberException   if response is invalid
     * @throws ModbusIOException       if remote slave is unavailable
     * @see CommStatus
     */
    abstract public CommStatus getCommEventLog(int serverAddress) throws
            ModbusProtocolException, ModbusNumberException, ModbusIOException;
 
    /**
     * The data passed in the request data field is to be returned (looped back) in the response. The
     * entire response message should be identical to the request.
     *
     * @param serverAddress a slave address
     * @param queryData     request data field
     * @throws ModbusNumberException if server address is in-valid
     */
    abstract public void diagnosticsReturnQueryData(int serverAddress, int queryData) throws ModbusNumberException, ModbusProtocolException, ModbusIOException;
 
    /**
     * The remote device serial line port must be initialized and restarted, and all of its
     * communications event counters are cleared. If the port is currently in Listen Only Mode, no
     * response is returned. This function is the only one that brings the port out of Listen Only
     * Mode. If the port is not currently in Listen Only Mode, a normal response is returned. This
     * occurs before the restart is executed.
     * When the remote device receives the request, it attempts a restart and executes its power–up
     * confidence tests. Successful completion of the tests will bring the port online.
     * A request data field contents of FF 00 hex causes the port’s Communications Event Log to be
     * cleared also. Contents of 00 00 leave the log as it was prior to the restart.
     *
     * @param serverAddress a slave address
     * @param clearLog      causes the port’s Communications Event Log to be cleared
     * @throws ModbusNumberException if server address is in-valid
     */
    abstract public void diagnosticsRestartCommunicationsOption(int serverAddress, boolean clearLog) throws ModbusNumberException, ModbusProtocolException, ModbusIOException;
 
    /**
     * Returns the contents of the remote device’s 16–bit diagnostic register are returned in the response.
     *
     * @param serverAddress a slave address
     * @return 16–bit diagnostic register
     * @throws ModbusNumberException if server address is in-valid
     */
    abstract public int diagnosticsReturnDiagnosticRegister(int serverAddress) throws ModbusNumberException, ModbusProtocolException, ModbusIOException;
 
    /**
     * The character passed in the request data field becomes the end of message delimiter
     * for future messages (replacing the default LF character). This function is useful in cases of a
     * Line Feed is not required at the end of ASCII messages.
     *
     * @param serverAddress a slave address
     * @param delimiter     request data field
     * @throws ModbusNumberException if server address is in-valid
     */
    abstract public void diagnosticsChangeAsciiInputDelimiter(int serverAddress, int delimiter) throws ModbusNumberException, ModbusProtocolException, ModbusIOException;
 
    /**
     * Forces the addressed remote device to its Listen Only Mode for MODBUS communications.
     * This isolates it from the other devices on the network, allowing them to continue
     * communicating without interruption from the addressed remote device. No response is
     * returned.
     * When the remote device enters its Listen Only Mode, all active communication controls are
     * turned off. The Ready watchdog timer is allowed to expire, locking the controls off. While the
     * device is in this mode, any MODBUS messages addressed to it or broadcast are monitored,
     * but no actions will be taken and no responses will be sent.
     * The only function that will be processed after the mode is entered will be the Restart
     * Communications Option function (function code 8, sub-function 1).
     *
     * @param serverAddress a slave address
     * @throws ModbusNumberException if server address is in-valid
     */
    abstract public void diagnosticsForceListenOnlyMode(int serverAddress) throws ModbusNumberException, ModbusProtocolException, ModbusIOException;
 
    /**
     * The goal is to clear all counters and the diagnostic register. Counters are also cleared upon power–up.
     *
     * @param serverAddress a slave address
     * @throws ModbusNumberException if server address is in-valid
     */
    abstract public void diagnosticsClearCountersAndDiagnosticRegister(int serverAddress) throws ModbusNumberException, ModbusProtocolException, ModbusIOException;
 
    /**
     * The response data field returns the quantity of messages that the remote device has detected
     * on the communications system since its last restart, clear counters operation, or power–up.
     *
     * @param serverAddress a slave address
     * @return bus message count
     * @throws ModbusNumberException if server address is in-valid
     */
    abstract public int diagnosticsReturnBusMessageCount(int serverAddress) throws ModbusNumberException, ModbusProtocolException, ModbusIOException;
 
    /**
     * The response data field returns the quantity of CRC errors encountered by the remote device
     * since its last restart, clear counters operation, or power–up.
     *
     * @param serverAddress a slave address
     * @return CRC error count
     * @throws ModbusNumberException if server address is in-valid
     */
    abstract public int diagnosticsReturnBusCommunicationErrorCount(int serverAddress) throws ModbusNumberException, ModbusProtocolException, ModbusIOException;
 
    /**
     * The response data field returns the quantity of MODBUS exception responses returned by the
     * remote device since its last restart, clear counters operation, or power–up.
     *
     * @param serverAddress a slave address
     * @return MODBUS exception response count
     * @throws ModbusNumberException if server address is in-valid
     */
    abstract public int diagnosticsReturnBusExceptionErrorCount(int serverAddress) throws ModbusNumberException, ModbusProtocolException, ModbusIOException;
 
    /**
     * The response data field returns the quantity of messages addressed to the remote device, or
     * broadcast, that the remote device has processed since its last restart, clear counters
     * operation, or power–up.
     *
     * @param serverAddress a slave address
     * @return count of messages has been processed
     * @throws ModbusNumberException if server address is in-valid
     */
    abstract public int diagnosticsReturnSlaveMessageCount(int serverAddress) throws ModbusNumberException, ModbusProtocolException, ModbusIOException;
 
    /**
     * The response data field returns the quantity of messages addressed to the remote device for
     * which it has returned no response (neither a normal response nor an exception response),
     * since its last restart, clear counters operation, or power–up.
     *
     * @param serverAddress a slave address
     * @return no-response count
     * @throws ModbusNumberException if server address is in-valid
     */
    abstract public int diagnosticsReturnSlaveNoResponseCount(int serverAddress) throws ModbusNumberException, ModbusProtocolException, ModbusIOException;
 
    /**
     * The response data field returns the quantity of messages addressed to the remote device for
     * which it returned a Negative Acknowledge (NAK) exception response, since its last restart,
     * clear counters operation, or power–up.
     *
     * @param serverAddress a slave address
     * @return NAK count
     * @throws ModbusNumberException if server address is in-valid
     */
    abstract public int diagnosticsReturnSlaveNAKCount(int serverAddress) throws ModbusNumberException, ModbusProtocolException, ModbusIOException;
 
    /**
     * The response data field returns the quantity of messages addressed to the remote device for
     * which it returned a Slave Device Busy exception response, since its last restart, clear
     * counters operation, or power–up.
     *
     * @param serverAddress a slave address
     * @return Slave Device Busy exception response count
     * @throws ModbusNumberException if server address is in-valid
     */
    abstract public int diagnosticsReturnSlaveBusyCount(int serverAddress) throws ModbusNumberException, ModbusProtocolException, ModbusIOException;
 
    /**
     * The response data field returns the quantity of messages addressed to the remote device that
     * it could not handle due to a character overrun condition, since its last restart, clear counters
     * operation, or power–up. A character overrun is caused by data characters arriving at the port
     * faster than they can be stored, or by the loss of a character due to a hardware malfunction.
     *
     * @param serverAddress a slave address
     * @return character overrun count
     * @throws ModbusNumberException if server address is in-valid
     */
    abstract public int diagnosticsReturnBusCharacterOverrunCount(int serverAddress) throws ModbusNumberException, ModbusProtocolException, ModbusIOException;
 
    /**
     * Clears the overrun error counter and reset the error flag.
     *
     * @param serverAddress a slave address
     * @throws ModbusNumberException if server address is in-valid
     */
    abstract public void diagnosticsClearOverrunCounterAndFlag(int serverAddress) throws ModbusNumberException, ModbusProtocolException, ModbusIOException;
 
    /**
     * This function code allows reading the identification and additional information relative to the
     * physical and functional description of a remote device, only.
     * The Read Device Identification interface is modeled as an address space composed of a set
     * of addressable data elements. The data elements are called objects and an object Id
     * identifies them.
     * The interface consists of 3 categories of objects :
     * ƒ Basic Device Identification. All objects of this category are mandatory : VendorName,
     * Product code, and revision number.
     * ƒ Regular Device Identification. In addition to Basic data objects, the device provides
     * additional and optional identification and description data objects. All of the objects of
     * this category are defined in the standard but their implementation is optional .
     * ƒ Extended Device Identification. In addition to regular data objects, the device provides
     * additional and optional identification and description private data about the physical
     * device itself. All of these data are device dependent.
     * ObjectId    ObjectName/Description              Type            M/O         category
     * 0x00        VendorName                          ASCII String    Mandatory   Basic
     * 0x01        ProductCode                         ASCII String    Mandatory   Basic
     * 0x02        MajorMinorRevision                  ASCII String    Mandatory   Basic
     * 0x03        VendorUrl                           ASCII String    Optional    Regular
     * 0x04        ProductName                         ASCII String    Optional    Regular
     * 0x05        ModelName                           ASCII String    Optional    Regular
     * 0x06        UserApplicationName                 ASCII String    Optional    Regular
     * 0x07        Reserved Optional                                   Optional    Regular
     * …
     * 0x7F
     * 0x80        Private objects may be optionally   device          Optional    Extended
     * …           defined. The range [0x80 – 0xFF]   dependant
     * 0xFF        is Product dependant.
     */
    final public MEIReadDeviceIdentification readDeviceIdentification(int serverAddress, int objectId, ReadDeviceIdentificationCode readDeviceId) throws
            ModbusProtocolException, ModbusNumberException, ModbusIOException {
        EncapsulatedInterfaceTransportResponse response = (EncapsulatedInterfaceTransportResponse) processRequest(ModbusRequestBuilder.getInstance().buildReadDeviceIdentification(serverAddress, objectId, readDeviceId));
        return (MEIReadDeviceIdentification) response.getMei();
    }
 
    /* facade */
    @Override
    public void addListener(FrameEventListener listener) {
        getConnection().addListener(listener);
    }
 
    @Override
    public void removeListener(FrameEventListener listener) {
        getConnection().removeListener(listener);
    }
 
    @Override
    public void removeListeners() {
        getConnection().removeListeners();
    }
 
    @Override
    public void fireFrameReceivedEvent(FrameEvent event) {
        getConnection().fireFrameReceivedEvent(event);
    }
 
    @Override
    public void fireFrameSentEvent(FrameEvent event) {
        getConnection().fireFrameSentEvent(event);
    }
 
    @Override
    public int countListeners() {
        return getConnection().countListeners();
    }
}