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
package com.intelligt.modbus.jlibmodbus;
 
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
 
/*
 * 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
 */
final public class Modbus {
    final static public int MAX_CONNECTION_TIMEOUT = 3000;
    final static public int MAX_RESPONSE_TIMEOUT = 1000;
    final static public int MAX_PDU_LENGTH = 254;
    /*
       slave id + function code.
       Note that actually the modbus pdu doesn't include the "slave id" field.
     */
    final static public int MIN_PDU_LENGTH = 2;
    final static public int MAX_TCP_ADU_LENGTH = 260;
    final static public int MAX_RTU_ADU_LENGTH = 256;
    final static public int MAX_REGISTER_VALUE = 0xFFFF;
    final static public int MIN_START_ADDRESS = 0x0000;
    final static public int MAX_START_ADDRESS = 0xFFFF;
    final static public int MAX_READ_COIL_COUNT = 0x7D0;
    final static public int MAX_WRITE_COIL_COUNT = 0x7B0;
    final static public int MAX_READ_REGISTER_COUNT = 0x7D;
    final static public int MAX_WRITE_REGISTER_COUNT = 0x7B;
    final static public int MAX_FIFO_COUNT = 31;
    final static public int MIN_SERVER_ADDRESS = 1;
    final static public int MAX_SERVER_ADDRESS = 247;
    final static public int TCP_PORT = 502;
    final static public int PROTOCOL_ID = 0;
    final static public int TCP_DEFAULT_ID = 0xFF;
    final static public int BROADCAST_ID = 0x00;
    final static public int ASCII_CODE_CR = 0xd;
    final static public int ASCII_CODE_LF = 0xa;
    final static public int ASCII_CODE_COLON = 0x3a;
    final static public int COIL_VALUE_ON = 0xff00;
    final static public int COIL_VALUE_OFF = 0x0000;
    final static public int TRANSACTION_ID_MAX_VALUE = 0xFFFF;
    final static public int DEFAULT_READ_TIMEOUT = 1000;
 
    final static private Logger log = Logger.getLogger(Modbus.class.getName());
    static private LogLevel logLevel = LogLevel.LEVEL_RELEASE;
    static private boolean autoIncrementTransactionId = false;
 
    /**
     * the end of message delimiter, (LF character by default)
     */
    static private int asciiMsgDelimiter = Modbus.ASCII_CODE_LF;
 
    static {
        setLogLevel(logLevel);
        log().setUseParentHandlers(false);
        log().addHandler(new Handler() {
            @Override
            public void publish(LogRecord record) {
                System.out.println(record.getLevel().getName() + ": " + record.getMessage());
            }
 
            @Override
            public void flush() {
                //do nothing
            }
 
            @Override
            public void close() throws SecurityException {
                //do nothing
            }
        });
    }
 
    private Modbus() {
    }
 
    /**
     * getter for the version of the library.
     *
     * @return a string representing the version
     */
    static public String getVersion() {
        return Version.getVersion();
    }
 
    static public void setAsciiInputDelimiter(int asciiMsgDelimiter) {
        Modbus.asciiMsgDelimiter = asciiMsgDelimiter;
    }
 
    static public int getAsciiMsgDelimiter() {
        return asciiMsgDelimiter;
    }
 
    /**
     * getter for log level
     *
     * @return current log level
     */
    static public LogLevel getLogLevel() {
        return logLevel;
    }
 
    /**
     * changes the log level for all loggers used
     *
     * @param level - LogLevel instance
     * @see Modbus.LogLevel
     */
    static public void setLogLevel(LogLevel level) {
        logLevel = level;
        log.setLevel(level.value());
        for (Handler handler : log.getHandlers()) {
            handler.setLevel(level.value());
        }
    }
 
    static public boolean isLoggingEnabled() {
        return getLogLevel() != LogLevel.LEVEL_RELEASE;
    }
 
    /**
     * getter for default logger
     *
     * @return default logger
     */
    static public Logger log() {
        return log;
    }
 
    /**
     * converts ON/OFF value to boolean
     *
     * @param s 0xFF00 of 0x0000
     * @return true if s equals 0xFF00, else false
     */
    static public boolean toCoil(int s) {
        return (((short) s) & 0xffff) == Modbus.COIL_VALUE_ON;
    }
 
    /**
     * converts boolean to ON/OFF value
     *
     * @param c a coil value
     * @return 0xFF00 if coil value is true, else 0x0000
     */
    static public int fromCoil(boolean c) {
        return c ? Modbus.COIL_VALUE_ON : Modbus.COIL_VALUE_OFF;
    }
 
    /**
     * validates address of server
     *
     * @param serverAddress - The modbus server address
     * @return "true" if serverAddress is correct, else "false".
     */
    static public boolean checkServerAddress(int serverAddress) {
        /*
        * hook for server address is equals zero:
        * some of modbus tcp slaves sets the UnitId value to zero, not ignoring value in this field.
        */
        switch (serverAddress) {
            case 0x00:
                //Modbus.log().info("Broadcast message");
                return true;
            case 0xFF:
                //Modbus.log().info("Using 0xFF ModbusTCP default slave id");
                return true;
            default:
                return !(serverAddress < Modbus.MIN_SERVER_ADDRESS || serverAddress > Modbus.MAX_SERVER_ADDRESS);
        }
    }
 
    /**
     * validates is the value in the range from min to max.
     *
     * @param value - the value for checkFrame
     * @param min   - minimum
     * @param max   - maximum
     * @return "true" if value in the range from min to max, else "false".
     */
    static private boolean checkRange(int value, int min, int max) {
        return !(value < min || value > max);
    }
 
    /**
     * validates the value in the range from 1 to max.
     *
     * @param value - the value for checkFrame
     * @param max   - maximum
     * @return "true" if value in the range from 1 to max, else "false".
     */
    static private boolean checkQuantity(int value, int max) {
        return checkRange(value, 1, max);
    }
 
    /**
     * validates data offset in the range from Modbus.MIN_START_ADDRESS to max.
     *
     * @param value - the offset for checkFrame
     * @param max   - maximum
     * @return "true" if offset in the range from Modbus.MIN_START_ADDRESS to max, else "false".
     */
    static private boolean checkStartAddress(int value, int max) {
        return checkRange(value, MIN_START_ADDRESS, max);
    }
 
    /**
     * validates number of registers for read
     *
     * @param value - register count
     * @return "true" if quantity is correct, else "false".
     */
    static public boolean checkReadRegisterCount(int value) {
        return checkQuantity(value, Modbus.MAX_READ_REGISTER_COUNT);
    }
 
    /**
     * validates number of registers for write
     *
     * @param value - register count
     * @return "true" if quantity is correct, else "false".
     */
    static public boolean checkWriteRegisterCount(int value) {
        return checkQuantity(value, Modbus.MAX_WRITE_REGISTER_COUNT);
    }
 
    /**
     * validates number of coils for read
     *
     * @param value - coil count
     * @return "true" if quantity is correct, else "false".
     */
    static public boolean checkReadCoilCount(int value) {
        return checkQuantity(value, Modbus.MAX_READ_COIL_COUNT);
    }
 
    /**
     * validates number of coils for write
     *
     * @param value - coil count
     * @return "true" if quantity is correct, else "false".
     */
    static public boolean checkWriteCoilCount(int value) {
        return checkQuantity(value, Modbus.MAX_WRITE_COIL_COUNT);
    }
 
    /**
     * validates start address for read/write
     *
     * @param value - start address
     * @return "true" if start address is correct, else "false".
     */
    static public boolean checkStartAddress(int value) {
        return checkStartAddress(value, Modbus.MAX_START_ADDRESS);
    }
 
    /**
     * validates end address for read/write
     * end address = start address + quantity
     *
     * @param value - end address
     * @return "true" if end address is correct, else "false".
     */
    static public boolean checkEndAddress(int value) {
        return checkStartAddress(value, Modbus.MAX_START_ADDRESS + 1);
    }
 
    /**
     * validates register value
     *
     * @param value - value of register
     * @return "true" if register value is correct, else "false".
     */
    static public boolean checkRegisterValue(int value) {
        //return checkRange((short)value, 0, Modbus.MAX_REGISTER_VALUE);
        return checkRange(value, 0, Modbus.MAX_REGISTER_VALUE);
    }
 
    /**
     * returns autoIncrementTransactionId variable
     *
     * @return "true" if autoincrement of TransactionId field is enabled, else "false".
     */
    public static boolean isAutoIncrementTransactionId() {
        return Modbus.autoIncrementTransactionId;
    }
 
    /**
     * activates auto increment of TransactionId field. if you do not use ModbusTCP, invocation has no effect.
     *
     * @param autoIncrementTransactionId - new value of the autoIncrementTransactionId variable
     */
    public static void setAutoIncrementTransactionId(boolean autoIncrementTransactionId) {
        Modbus.autoIncrementTransactionId = autoIncrementTransactionId;
    }
 
    /**
     * logging.Level wrapper.
     */
    public enum LogLevel {
        LEVEL_RELEASE(Level.SEVERE),
        LEVEL_WARNINGS(Level.WARNING),
        LEVEL_VERBOSE(Level.INFO),
        LEVEL_DEBUG(Level.ALL);
 
        final private Level value;
 
        LogLevel(Level value) {
            this.value = value;
        }
 
        /**
         * getter
         *
         * @return logging.Level instance
         */
        private Level value() {
            return value;
        }
    }
}