|
@@ -19,6 +19,7 @@ import androidx.annotation.NonNull;
|
|
|
import androidx.preference.PreferenceManager;
|
|
|
|
|
|
import com.alibaba.fastjson.JSON;
|
|
|
+import com.alibaba.fastjson.JSONArray;
|
|
|
import com.alibaba.fastjson.JSONObject;
|
|
|
import com.wdkl.ncs.s433.transbox.common.Constants;
|
|
|
import com.wdkl.ncs.s433.transbox.receiver.NetworkConnectChangedReceiver;
|
|
@@ -40,10 +41,20 @@ import org.greenrobot.eventbus.ThreadMode;
|
|
|
import java.io.DataOutputStream;
|
|
|
import java.io.IOException;
|
|
|
import java.io.OutputStream;
|
|
|
+import java.nio.ByteBuffer;
|
|
|
import java.text.SimpleDateFormat;
|
|
|
+import java.util.ArrayList;
|
|
|
import java.util.Arrays;
|
|
|
import java.util.Calendar;
|
|
|
import java.util.Date;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.Iterator;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.Timer;
|
|
|
+import java.util.concurrent.ExecutorService;
|
|
|
+import java.util.concurrent.Executors;
|
|
|
+import java.util.zip.CRC32;
|
|
|
|
|
|
public class MainActivity extends SerialPortActivity {
|
|
|
String TAG = "MainActivity";
|
|
@@ -51,6 +62,15 @@ public class MainActivity extends SerialPortActivity {
|
|
|
ConnectivityManager connectivityManager;
|
|
|
private SmdtManager smdtManager;
|
|
|
|
|
|
+ private final ExecutorService send433ThreadPool = Executors.newSingleThreadExecutor();
|
|
|
+ private Send433Thread send433Thread;
|
|
|
+ private static List<String> s433Data = new ArrayList<>();
|
|
|
+
|
|
|
+ //缓存本机收到的消息和时间
|
|
|
+ Map<String,Long> receivedCache = new HashMap<>();
|
|
|
+ //调度延迟任务
|
|
|
+ Timer timer = new Timer();
|
|
|
+
|
|
|
@Override
|
|
|
protected void onCreate(Bundle savedInstanceState) {
|
|
|
super.onCreate(savedInstanceState);
|
|
@@ -106,6 +126,7 @@ public class MainActivity extends SerialPortActivity {
|
|
|
sb.append("服务器端口:" + tcpServerPort + "\n");
|
|
|
sb.append("本机MAC地址:" + Constants.MAC_ADDR + "\n");
|
|
|
sb.append("本机IP地址:" + localIP + "\n");
|
|
|
+ sb.append("APP版本: V" + BuildConfig.VERSION_NAME);
|
|
|
settingInfo.setText(sb.toString());
|
|
|
Log.e(TAG, "init: "+ localIP);
|
|
|
TextView tcpstatus = (TextView) findViewById(R.id.serialStatus);
|
|
@@ -153,7 +174,7 @@ public class MainActivity extends SerialPortActivity {
|
|
|
protected void onDataReceived(byte[] buffer, int size) {
|
|
|
runOnUiThread(new Runnable() {
|
|
|
public void run() {
|
|
|
- if (mReception != null) {
|
|
|
+ /*if (mReception != null) {
|
|
|
byte[] bytes = Arrays.copyOfRange(buffer, 0, size);
|
|
|
String receiveData = bytesToHex(bytes);
|
|
|
String receiveString = new String(bytes, 0, size);
|
|
@@ -161,7 +182,7 @@ public class MainActivity extends SerialPortActivity {
|
|
|
if (receiveString.contains("\r") || receiveString.contains("\n")) {
|
|
|
Log.e("Application", "有回车键: ");
|
|
|
}
|
|
|
- /*if (receiveData.startsWith("fe")) {
|
|
|
+ if (receiveData.startsWith("fe")) {
|
|
|
byte[] cardCode = ArrayUtils.subarray(bytes, 5, 9);
|
|
|
//小端字节顺序,需要将数组反转
|
|
|
ArrayUtils.reverse(cardCode);
|
|
@@ -181,13 +202,18 @@ public class MainActivity extends SerialPortActivity {
|
|
|
TcpClient.getInstance().sendMsg(AlarmMessageUtil.getAlarmMessage(mac));
|
|
|
}
|
|
|
|
|
|
- }*/
|
|
|
+ }
|
|
|
|
|
|
- }
|
|
|
+ }*/
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
+ @Override
|
|
|
+ protected void onDataReceivedString(String data) {
|
|
|
+ Log.e("TAG", "receiveData: " + data);
|
|
|
+ }
|
|
|
+
|
|
|
private void openNetworkDebug() {
|
|
|
try {
|
|
|
Process p = Runtime.getRuntime().exec("su");
|
|
@@ -298,47 +324,240 @@ public class MainActivity extends SerialPortActivity {
|
|
|
respondModel.setData(Constants.MAC_ADDR);
|
|
|
TcpClient.getInstance().sendMsg(respondModel.toJson());
|
|
|
|
|
|
- switch (tcpModel.getType()) {
|
|
|
- case TIME:
|
|
|
- if (tcpModel.getAction() == TcpAction.TimeAction.SYNC) {
|
|
|
- //同步时间
|
|
|
- //action: SYNC_TIME, data: {"time_zone":"PRC","time":"1683626639"}
|
|
|
- JSONObject jsonObject = JSON.parseObject(tcpModel.getData().toString());
|
|
|
- String timezone = jsonObject.getString("time_zone");
|
|
|
- long time = Long.parseLong(jsonObject.getString("time")) * 1000;
|
|
|
-
|
|
|
- setSysTime(time, timezone);
|
|
|
- }
|
|
|
- break;
|
|
|
- case DEVICE:
|
|
|
- if (tcpModel.getAction() == TcpAction.DeviceAction.APP_UPDATE) {
|
|
|
+ try {
|
|
|
+ switch (tcpModel.getType()) {
|
|
|
+ case TIME:
|
|
|
+ if (tcpModel.getAction() == TcpAction.TimeAction.SYNC) {
|
|
|
+ //同步时间
|
|
|
+ //action: SYNC_TIME, data: {"time_zone":"PRC","time":"1683626639"}
|
|
|
+ JSONObject jsonObject = JSON.parseObject(tcpModel.getData().toString());
|
|
|
+ String timezone = jsonObject.getString("time_zone");
|
|
|
+ long time = Long.parseLong(jsonObject.getString("time")) * 1000;
|
|
|
+
|
|
|
+ setSysTime(time, timezone);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case DEVICE:
|
|
|
+ if (tcpModel.getAction() == TcpAction.DeviceAction.APP_UPDATE) {
|
|
|
+ //
|
|
|
+ } else if (tcpModel.getAction() == TcpAction.DeviceAction.RESTART) {
|
|
|
+ AutoRebootUtil.restartApp();
|
|
|
+ } else if (tcpModel.getAction() == TcpAction.DeviceAction.REBOOT) {
|
|
|
+ AutoRebootUtil.reboot();
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case SIDE:
|
|
|
//
|
|
|
- } else if (tcpModel.getAction() == TcpAction.DeviceAction.RESTART) {
|
|
|
- AutoRebootUtil.restartApp();
|
|
|
- } else if (tcpModel.getAction() == TcpAction.DeviceAction.REBOOT) {
|
|
|
- AutoRebootUtil.reboot();
|
|
|
+ break;
|
|
|
+ case S433:
|
|
|
+ //$8C:FC:A0:F1:01:A7{"action":"SOS_CALL","type":"S433","data":{"title":"12房紧急呼叫"}}#
|
|
|
+ //{"action":"SYNC_TIME","data":{"time_zone":"Asia/Shanghai","receiverMac":"8c:fc:a0:f1:01:de","time":1684909246},"type":"S433"}
|
|
|
+ //{"action":"CALL","data":{"title":"11房-21床呼叫","receiverMacs":["8c:fc:A0:F1:01:A7","8c:fc:a0:f1:01:de"]},"toId":489,"type":"S433"}
|
|
|
+ JSONObject s433Send = new JSONObject();
|
|
|
+ s433Send.put("action", tcpModel.getAction());
|
|
|
+ JSONObject data = new JSONObject();
|
|
|
+ JSONObject tcpData = null;
|
|
|
+ if (tcpModel.getData() != null) {
|
|
|
+ tcpData = JSON.parseObject(tcpModel.getData().toString());
|
|
|
+ }
|
|
|
+
|
|
|
+ if (tcpModel.getAction() == TcpAction.S433Action.SOS_CALL
|
|
|
+ || tcpModel.getAction() == TcpAction.S433Action.CALL
|
|
|
+ || tcpModel.getAction() == TcpAction.S433Action.CANCEL) {
|
|
|
+ //解析数据并通过433发射出去
|
|
|
+ if (tcpData != null) {
|
|
|
+ if (tcpData.getString("receiverMacs") != null) {
|
|
|
+ //给多个点阵屏发送
|
|
|
+ JSONArray macs = JSON.parseArray(tcpData.getString("receiverMacs"));
|
|
|
+ String title = tcpData.getString("title");
|
|
|
+ data.put("title", title);
|
|
|
+ s433Send.put("data", data);
|
|
|
+ for (int i = 0; i < macs.size(); i++) {
|
|
|
+ s433Data.add(macs.get(i) + s433Send.toJSONString());
|
|
|
+ }
|
|
|
+
|
|
|
+ send433Data();
|
|
|
+ }
|
|
|
+ if (tcpData.getString("receiverMac") != null) {
|
|
|
+ //给单个点阵屏发送
|
|
|
+ String mac = tcpData.getString("receiverMac");
|
|
|
+ data.put("title", tcpData.getString("title"));
|
|
|
+ s433Send.put("data", data);
|
|
|
+ s433Data.add(mac + s433Send.toJSONString());
|
|
|
+
|
|
|
+ send433Data();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else if (tcpModel.getAction() == TcpAction.S433Action.DATA) {
|
|
|
+ //屏参
|
|
|
+ if (tcpData != null) {
|
|
|
+ data.put("led_style", tcpData.getString("led_style"));
|
|
|
+ data.put("led_size", tcpData.getIntValue("led_size"));
|
|
|
+ data.put("volume", tcpData.getIntValue("volume"));
|
|
|
+
|
|
|
+ if (tcpData.getString("receiverMac") != null) {
|
|
|
+ //给单个点阵屏发送
|
|
|
+ String mac = tcpData.getString("receiverMac");
|
|
|
+ s433Send.put("data", data);
|
|
|
+ s433Data.add(mac + s433Send.toJSONString());
|
|
|
+
|
|
|
+ send433Data();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else if (tcpModel.getAction() == TcpAction.S433Action.SYNC_TIME) {
|
|
|
+ // 时间同步
|
|
|
+ if (tcpData != null) {
|
|
|
+ data.put("time", tcpData.getLong("time").toString());
|
|
|
+ data.put("time_zone", tcpData.getString("time_zone"));
|
|
|
+
|
|
|
+ if (tcpData.getString("receiverMac") != null) {
|
|
|
+ //给单个点阵屏发送
|
|
|
+ String mac = tcpData.getString("receiverMac");
|
|
|
+ s433Send.put("data", data);
|
|
|
+ s433Data.add(mac + s433Send.toJSONString());
|
|
|
+ send433Data();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else if (tcpModel.getAction() == TcpAction.S433Action.EXIST) {
|
|
|
+ if (tcpData != null) {
|
|
|
+ if (tcpData.getString("receiverMac") != null) {
|
|
|
+ //给单个点阵屏发送
|
|
|
+ String mac = tcpData.getString("receiverMac");
|
|
|
+ s433Data.add(mac + s433Send.toJSONString());
|
|
|
+
|
|
|
+ send433Data();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ Log.e(TAG, "handleTcpMsg exception");
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void send433Data() {
|
|
|
+ if (send433Thread == null) {
|
|
|
+ send433Thread = new Send433Thread();
|
|
|
+ }
|
|
|
+ send433ThreadPool.execute(send433Thread);
|
|
|
+ }
|
|
|
+
|
|
|
+ public class Send433Thread implements Runnable {
|
|
|
+ @Override
|
|
|
+ public void run() {
|
|
|
+ if (s433Data.size() > 0) {
|
|
|
+ Iterator<String> iterator = s433Data.iterator();
|
|
|
+ while (iterator.hasNext()) {
|
|
|
+ try {
|
|
|
+ //拼接头尾
|
|
|
+ String data = "$" + iterator.next() + "#";
|
|
|
+ Log.e(TAG, "send 433 data ==>" + data);
|
|
|
+ //433发送
|
|
|
+ sendMsg(data, mOutputStream, 120);
|
|
|
+ iterator.remove();
|
|
|
+
|
|
|
+ Thread.sleep(100);
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
}
|
|
|
- break;
|
|
|
- case SIDE:
|
|
|
- //
|
|
|
- break;
|
|
|
- case S433:
|
|
|
- if (tcpModel.getAction() == TcpAction.S433Action.SOS_CALL
|
|
|
- || tcpModel.getAction() == TcpAction.S433Action.CALL
|
|
|
- || tcpModel.getAction() == TcpAction.S433Action.CANCEL) {
|
|
|
- //解析数据并通过433发射出去
|
|
|
- } else if (tcpModel.getAction() == TcpAction.S433Action.DATA) {
|
|
|
- //
|
|
|
- } else if (tcpModel.getAction() == TcpAction.S433Action.SYNC_TIME) {
|
|
|
- //
|
|
|
- } else if (tcpModel.getAction() == TcpAction.S433Action.EXIST) {
|
|
|
- //
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 消息分包发送,包格式如下:每个包可传输的数据内容是发送缓冲区的大小减去21个字节。21个字节为包控制数据
|
|
|
+ * 保证接收端接收后可校验包的正确性。假设发送缓冲区是60个字节,那么每个包最多可发送39个字节的消息数据,
|
|
|
+ * 因为包已经校验准确性,接收端只需合并包内容数据即可。消息id使用整个消息内容的crc校验值
|
|
|
+ * 帧头 有效数据长度 包序 总包数 消息id 包内容 crc包校验
|
|
|
+ * 2B 1B 1B 1B 8B MAX-18 8B
|
|
|
+ *
|
|
|
+ * @param msg
|
|
|
+ * @param outputStream 输出
|
|
|
+ * @param packageLength 芯片发射分包长度(60,修改后为120)
|
|
|
+ */
|
|
|
+ private void sendMsg(String msg, OutputStream outputStream, int packageLength) throws IOException {
|
|
|
+
|
|
|
+ char[] alphabet = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_".toCharArray();
|
|
|
+
|
|
|
+ try {
|
|
|
+ byte[] msgBytes = msg.getBytes();
|
|
|
+ //msgBytesLength 要发送的消息总长度,
|
|
|
+ int msgBytesLength = msgBytes.length;
|
|
|
+ //packageContentLength 每个包可发送的长度
|
|
|
+ int packageContentLength = packageLength - 21;
|
|
|
+ //要发送包的个数
|
|
|
+ int packages = msgBytesLength / packageContentLength + (msgBytesLength % packageContentLength != 0 ? 1 : 0);
|
|
|
+// String msgId = NanoIdUtils.randomNanoId(new Random(), alphabet, 5);
|
|
|
+ CRC32 crc32Msg = new CRC32();
|
|
|
+ crc32Msg.update(msgBytes);
|
|
|
+ long msgId = crc32Msg.getValue();
|
|
|
+// byte[] packageContent = new byte[packageContentLength];
|
|
|
+ byte[] start = String2Byte("AA55");
|
|
|
+ for (int i = 0; i < packages; i++) {
|
|
|
+ ByteBuffer buffer = ByteBuffer.allocate(packageLength - 8); //校验数据之前的数据
|
|
|
+ buffer.put(start);
|
|
|
+ //消息内容实际数据长度
|
|
|
+ byte contentLength = (byte) Math.min(packageContentLength, msgBytesLength - i * packageContentLength);
|
|
|
+ buffer.put(contentLength);
|
|
|
+ //包序
|
|
|
+ buffer.put((byte) (i + 1));
|
|
|
+ //总包数
|
|
|
+ buffer.put((byte) packages);
|
|
|
+ //消息id字节
|
|
|
+ byte[] msgIdBytes = ByteBuffer.allocate(8).putLong(msgId).array(); //msgId.getBytes();
|
|
|
+ buffer.put(msgIdBytes);
|
|
|
+ //包内容
|
|
|
+ byte[] subarray = ArrayUtils.subarray(msgBytes, i * packageContentLength, i * packageContentLength + contentLength);
|
|
|
+ ByteBuffer wrap = ByteBuffer.allocate(packageContentLength).put(subarray);
|
|
|
+ if (subarray.length < packageContentLength) {
|
|
|
+ wrap.put(new byte[packageContentLength - subarray.length]);
|
|
|
}
|
|
|
- //发送433数据
|
|
|
- break;
|
|
|
+ buffer.put(wrap.array());
|
|
|
+ CRC32 crc32 = new CRC32();
|
|
|
+ //计算校验
|
|
|
+ crc32.update(buffer.array());
|
|
|
+ Log.i(TAG, "sendMsg: "+ArrayUtils.toString(buffer.array()));
|
|
|
+ long value = crc32.getValue();
|
|
|
+ Log.i(TAG, "Crc: " + value);
|
|
|
+ byte[] crc = ByteBuffer.allocate(8).putLong(value).array();
|
|
|
+ ByteBuffer bufferSend = ByteBuffer.allocate(packageLength);
|
|
|
+ //把crc校验加入到包尾
|
|
|
+ bufferSend.put(buffer.array());
|
|
|
+ bufferSend.put(crc);
|
|
|
+ //发送包
|
|
|
+ Log.i(TAG, "sendMsg: 发送:"+ArrayUtils.toString(bufferSend.array()));
|
|
|
+ outputStream.write(bufferSend.array());
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ } catch (IOException e) {
|
|
|
+ throw e;
|
|
|
}
|
|
|
+
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 16进制字符串转换成byte数组
|
|
|
+ */
|
|
|
+ public byte[] String2Byte(String s) {
|
|
|
+ s = s.replace(" ", "");
|
|
|
+ s = s.replace("#", "");
|
|
|
+ byte[] baKeyword = new byte[s.length() / 2];
|
|
|
+ for (int i = 0; i < baKeyword.length; i++) {
|
|
|
+ try {
|
|
|
+ baKeyword[i] = (byte) (0xff & Integer.parseInt(s.substring(i * 2, i * 2 + 2), 16));
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return baKeyword;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
private ConnectivityManager.NetworkCallback callback = new ConnectivityManager.NetworkCallback(){
|
|
|
@Override
|
|
|
public void onAvailable(@NonNull Network network) {
|