ThinkraceUtil.java
14.4 KB
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
package com.ruoyi.system.protocol;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.global.IDeviceException;
import com.ruoyi.common.global.ResultCode;
import com.ruoyi.system.protocol.adapter.ICommandAdapter;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.AttributeKey;
import io.netty.util.CharsetUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
public class ThinkraceUtil {
// 命令过期时间
private static final Integer COMMAND_TTL = 300;
private static final String COMMAND_SN = "command:sn";
private static final String COMMAND_SEND = "command:send:";
public static final Integer COMMAND_LABEL_SURE = 1;
public static final Integer COMMAND_LABEL_INIT = 0;
public static final Long SN_LENGTH = 1000000L;
private static RedisCache REDIS_CACHE;
/**
* 指令列表
*/
private static final Map<String, ICommandAdapter> REGEX_MAP = new HashMap<>();
/**
* 时区
*/
public static String UTC_8 = "8";
/**
* 命令结束符
*/
public static String END_SYMBOL = "#";
/**
* 设备号
*/
public static String IMEI = "IMEI";
public static String generatorSn() {
Long commandSn;
// 防止出现线程安全 重复覆盖值的情况
// 指令流水号是6位的 不足补0
commandSn = REDIS_CACHE.increment(COMMAND_SN);
if (commandSn == null || commandSn > SN_LENGTH) {
commandSn = 0L;
REDIS_CACHE.setCacheObject(COMMAND_SN, commandSn.intValue());
}
String commandSnStr = commandSn.toString();
int snLen = (SN_LENGTH.toString().length() - 1) - commandSnStr.length();
StringBuilder sb = new StringBuilder();
//长度不足6位补零
for (int i = 0; i < snLen; i++) {
sb.append("0");
}
return sb + commandSnStr;
}
public static String commandLabelInit(String imei, String sn) {
// 存入redis处理会有网络io的消耗 所以redis和服务器最好是在同一台服务器 原则是30s 增加容错
String key = getCommandKey(imei, sn);
REDIS_CACHE.expire(key, COMMAND_LABEL_INIT, COMMAND_TTL, TimeUnit.SECONDS);
return key;
}
private static String getCommandKey(String imei, String sn) {
return COMMAND_SEND + imei + ":" + sn;
}
public static Integer getCommandSn(String key) {
return REDIS_CACHE.getCacheObject(key);
}
public static class REQ {
/**
* 登陆包
*/
public static final String LOGIN_REQ = "IWAP00";
public static final String GPS_REQ = "IWAP01";
public static final String MORE_GPS_REQ = "IWAP02";
public static final String KEEPLIVE_REQ = "IWAP03";
public static final String NOW_GPS_REQ = "IWAP15";
/**
* 电量报警
*/
public static final String POWER_REQ = "IWAP04";
/**
* 这个可能在文档内是被划线了的可能没有启用过 语音传输没有在设备找到按钮
*/
@Deprecated
public static final String SPEECH_REQ = "IWAP05";
/**
* 语音上行
*/
public static final String SPEECH_UP_REQ = "IWAP07";
/**
* 设备报警与地址回复包
*/
public static final String ALARM_REQ = "IWAP10";
public static final String IMAGE_REQ = "IWAP42";
public static final String HEART_REQ = "IWAP49";
// 这两协议 在st2和智能手表标准协议中都有
public static final String TEMPERATURE_50_REQ = "IWAP50";
public static final String QUERY_BLUETOOTH_REQ = "IWAP51";
public static final String CDMA_GPS_REQ = "IWAP91";
public static final String CDMA_WIFI_GPS_REQ = "IWAP92";
public static final String BLUETOOTH_REQ = "IWAPBL";
public static final String HEART_BLOOD_REQ = "IWAPHT";
public static final String HEALTH_REQ = "IWAPJK";
public static final String TEMPERATURE_REQ = "IWAPTP";
public static final String VERSION_REQ = "IWAPVR";
public static final String WEARING_REQ = "IWAPWR";
/**
* 12 上传心率、血压、血氧、血糖
*/
public static final String HEART_RATE_BLOOD_PRESSURE_OXYGEN_SUGAR_REQ = "IWAPHP";
public static final String BASE_STATION_TIMING_GPS_REQ = "IWAPTM";
/**
* 14 同步天气
*/
public static final String WEATHER_REQ = "IWAPWT";
/**
* 确认已读文字
*/
public static final String SURE_TEXT_REQ = "IWAP94";
public static final String ECG_DATA_REQ = "IWAPHD";
/**
* 设备响应
*/
public static final String CONTACT_REQ = "IWAP14";
public static final String SOS_REQ = "IWAP12";
public static final String UPLOAD_GPS_TIME_REQ = "IWAP16";
public static final String RECOVERY_RESET_REQ = "IWAP17";
public static final String RESET_POWER_REQ = "IWAP18";
public static final String SET_SERVER_INFO_REQ = "IWAP19";
public static final String SET_LANGUAGE_TIME_REQ = "IWAP20";
public static final String SHUTDOWN_REQ = "IWAP31";
public static final String CALL_TEL_REQ = "IWAP32";
public static final String WORK_MODE_REQ = "IWAP33";
public static final String SHORTCUT_COMMAND = "IWAP40";
public static final String PHOTOGRAPH_REQ = "IWAP46";
public static final String HEART_CHECK_REQ = "IWAP50";
public static final String CONTACT_BOOK_REQ = "IWAP51";
public static final String DEL_CONTACT_BOOK_REQ = "IWAP52";
public static final String WHITE_BUTTON_REQ = "IWAP84";
public static final String FIND_CLIENT_REQ = "IWAP88";
public static final String MOTION_CHECK_REQ = "IWAPMC";
public static final String SMS_COMMAND_REQ = "IWAPSM";
public static final String HOURLY_SYSTEM_REQ = "IWAPTF";
public static final String CLIENT_BIND_CONTACT_REQ = "IWAPWL";
public static final String HEART_RATE_CHECK_REQ = "IWAPXL";
public static final String BLOOD_PRESSURE_CHECK_REQ = "IWAPXY";
}
public static class RES {
/**
* 登陆包
*/
public static final String LOGIN_RES = "IWBP00";
/**
* 定位
*/
public static final String GPS_RES = "IWBP01";
/**
* 多基站GPS定位
*/
public static final String MORE_GPS_RES = "IWBP02";
/**
* 心跳
*/
public static final String KEEPLIVE_RES = "IWBP03";
public static final String HEART_RES = "IWBP49";
public static final String TEMPERATURE_50_RES = "IWBP50";
public static final String QUERY_BLUETOOTH_RES = "IWBP51";
public static final String CDMA_GPS_RES = "IWBP91";
public static final String CDMA_WIFI_GPS_RES = "IWBP92";
/**
* 电源报警
*/
public static final String POWER_RES = "IWBP04";
/**
* 设备报警与地址回复包
*/
public static final String ALARM_RES = "IWBP10";
public static final String IMAGE_RES = "IWBP42";
public static final String BLUETOOTH_RES = "IWBPBL";
public static final String HEART_BLOOD_RES = "IWBPHT";
public static final String HEALTH_RES = "IWBPJK";
public static final String TEMPERATURE_RES = "IWBPTP";
public static final String HEART_RATE_BLOOD_PRESSURE_OXYGEN_SUGAR_RES = "IWBPHP";
/**
* 基站校时并获取经纬度协议
*/
public static final String BASE_STATION_TIMING_GPS_RES = "IWBPTM";
;
/**
* 确认已读文字
*/
public static final String SURE_TEXT_RES = "IWBP94";
public static final String ECG_DATA_RES = "IWBPHD";
@Deprecated
public static final String SPEECH_RES = "IWBP05";
public static final String SPEECH_UP_RES = "IWBP07";
public static final String WEATHER_RES = "IWBPWT";
}
/**
* 下发指令集
*/
public static class ACTIVE {
public static final String CONTACT_ACTIVE = "IWBP14";
public static final String RECOVERY_RESET_ACTIVE = "IWBP17";
public static final String UPLOAD_GPS_TIME_ACTIVE = "IWBP15";
public static final String SOS_ACTIVE = "IWBP12";
public static final String NOW_GPS_ACTIVE = "IWBP16";
public static final String RESET_POWER_ACTIVE = "IWBP18";
public static final String SET_SERVER_INFO_ACTIVE = "IWBP19";
public static final String SET_LANGUAGE_TIME_ACTIVE = "IWBP20";
public static final String SHUTDOWN_ACTIVE = "IWBP31";
public static final String CALL_TEL_ACTIVE = "IWBP32";
public static final String WORK_MODE_ACTIVE = "IWBP33";
public static final String SHORTCUT_COMMAND_ACTIVE = "IWBP40";
public static final String PHOTOGRAPH_ACTIVE = "IWBP46";
public static final String HEART_CHECK_ACTIVE = "IWBP50";
public static final String CONTACT_BOOK_ACTIVE = "IWBP51";
public static final String DEL_CONTACT_BOOK_ACTIVE = "IWBP52";
public static final String WHITE_BUTTON_ACTIVE = "IWBP84";
public static final String FIND_CLIENT_ACTIVE = "IWBP88";
public static final String MOTION_CHECK_ACTIVE = "IWBPMC";
public static final String SMS_COMMAND_ACTIVE = "IWBPSM";
public static final String HOURLY_SYSTEM_ACTIVE = "IWBPTF";
public static final String CLIENT_BIND_CONTACT_ACTIVE = "IWBPWL";
public static final String HEART_RATE_CHECK_ACTIVE = "IWBPXL";
public static final String BLOOD_PRESSURE_CHECK_ACTIVE = "IWBPXY";
}
public static ICommandAdapter getAdapter(String key) {
return REGEX_MAP.get(key);
}
public static void addAdapter(String key, ICommandAdapter adapter) {
REGEX_MAP.put(key, adapter);
}
public static void removeDevice(String imei) {
DeviceManager.removeDevice(imei);
}
public static boolean hasDevice(Channel channel) {
return DeviceManager.hasDevice(channel);
}
public static void sendCommand(String imei, String command) {
DeviceManager.sendCommand(imei, command);
}
public static boolean askCommand(String txt, String imei, String command, String commandSn) {
return DeviceManager.askCommand(txt, imei, command, commandSn);
}
public static void setDevice(String imei, ChannelHandlerContext ctx) {
DeviceManager.onlineDevice(imei, ctx);
}
/**
* 管理设备
*
* @author 20412
*/
protected static class DeviceManager {
private static final Logger log = LoggerFactory.getLogger(DeviceManager.class);
private static final ConcurrentHashMap<String, Channel> DEVICE_MAP = new ConcurrentHashMap<>();
/**
* 设备上线
*
* @param imei 设备号
* @param ctx 当前连接的处理器上下文
*/
public static void onlineDevice(String imei, ChannelHandlerContext ctx) {
if (DEVICE_MAP.containsKey(imei)) {
// 关闭之前的连接
Channel channel = DEVICE_MAP.get(imei);
channel.close();
}
// 为当前连接设置id
Channel channel = ctx.channel();
channel.attr(AttributeKey.valueOf(ThinkraceUtil.IMEI)).set(imei);
// 继续传递事件给下一个 ChannelHandler
ctx.fireChannelActive();
init(imei, channel);
log.info("设备上线:设备号为:{}", imei);
}
/**
* 移除设备 以及资源管理器
*
* @param imei 设备号
*/
public static void removeDevice(String imei) {
if (DEVICE_MAP.containsKey(imei)) {
clean(imei);
log.info("设备离线,设备号为:{}", imei);
}
}
/**
* 向设备发送指令
*
* @param imei 设备号
* @param command 指令
* @return obj 处理结果
*/
public static void sendCommand(String imei, String command) {
log.info("服务器发起:{}", command);
Channel channel = DEVICE_MAP.get(imei);
if (Objects.isNull(channel)) {
throw new IDeviceException(ResultCode.CODE_207, "设备" + imei + "不在线");
}
channel.writeAndFlush(Unpooled.copiedBuffer(command, CharsetUtil.UTF_8));
}
/**
* 判断信道内是否有设备注册
*
* @param channel 通道
* @return result 如果存在返回 --true 如果不存在返回 --false
*/
public static boolean hasDevice(Channel channel) {
AttributeKey<String> key = AttributeKey.valueOf(ThinkraceUtil.IMEI);
//netty移除了这个map的remove方法,这里的判断谨慎一点
return (channel.hasAttr(key) || channel.attr(key).get() != null);
}
// 设备上线 创建资源管理器
private static void init(String imei, Channel channel) {
// 设备上线
DEVICE_MAP.put(imei, channel);
}
// 离线删除创建的资源管理器
private static void clean(String imei) {
DEVICE_MAP.remove(imei);
}
/**
* 指令响应
*
* @param imei 设备号 可以为 -- null
* @param command 指令
* @param commandSn 指令流水号
* @return obj 处理结果
*/
public static boolean askCommand(String txt, String imei, String command, String commandSn) {
log.info("{}:设备:{},命令流水号:{},响应命令:{}", txt, imei, commandSn, command);
// 通过事件通知 另外单独一个类通知
String key = getCommandKey(imei, commandSn);
REDIS_CACHE.expire(key, COMMAND_LABEL_SURE, COMMAND_TTL, TimeUnit.SECONDS);
return true;
}
}
public static void initRedisThinkraceCommandParam(RedisCache redisCache) {
REDIS_CACHE = redisCache;
REDIS_CACHE.setNx(COMMAND_SN, 0);
}
}