From 1fb6c86ea12f325bfdb372d98400c8a904c6e927 Mon Sep 17 00:00:00 2001
From: Anton Tananaev <anton@traccar.org>
Date: Sat, 20 Aug 2022 09:00:27 -0700
Subject: Support TK103 cell info (fix #4925)

---
 src/main/java/org/traccar/helper/PatternUtil.java  |  2 +-
 .../org/traccar/protocol/Tk103ProtocolDecoder.java | 49 +++++++++++++++++++++-
 2 files changed, 48 insertions(+), 3 deletions(-)

(limited to 'src/main/java/org')

diff --git a/src/main/java/org/traccar/helper/PatternUtil.java b/src/main/java/org/traccar/helper/PatternUtil.java
index 74813e1d9..a46c7b7b4 100644
--- a/src/main/java/org/traccar/helper/PatternUtil.java
+++ b/src/main/java/org/traccar/helper/PatternUtil.java
@@ -63,7 +63,7 @@ public final class PatternUtil {
 
         for (int i = 0; i < pattern.length(); i++) {
             try {
-                Matcher matcher = Pattern.compile("(" + pattern.substring(0, i) + ").*").matcher(input);
+                Matcher matcher = Pattern.compile("(" + pattern.substring(0, i) + ")[\\s\\S]*").matcher(input);
                 if (matcher.matches()) {
                     result.patternMatch = pattern.substring(0, i);
                     result.patternTail = pattern.substring(i);
diff --git a/src/main/java/org/traccar/protocol/Tk103ProtocolDecoder.java b/src/main/java/org/traccar/protocol/Tk103ProtocolDecoder.java
index e197a8a41..b343c3b33 100644
--- a/src/main/java/org/traccar/protocol/Tk103ProtocolDecoder.java
+++ b/src/main/java/org/traccar/protocol/Tk103ProtocolDecoder.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2012 - 2021 Anton Tananaev (anton@traccar.org)
+ * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org)
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -99,6 +99,16 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder {
             .any()
             .compile();
 
+    private static final Pattern PATTERN_CELL = new PatternBuilder()
+            .text("(")
+            .number("(d{12})")                   // device id
+            .expression(".{4}")                  // type
+            .number("(?:d{15})?,")               // imei
+            .expression("(.+),")                 // cell
+            .number("(d{8})")                    // odometer
+            .text(")")
+            .compile();
+
     private static final Pattern PATTERN_NETWORK = new PatternBuilder()
             .text("(").optional()
             .number("(d{12})")                   // device id
@@ -297,6 +307,39 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder {
         return position;
     }
 
+    private Position decodeCell(Channel channel, SocketAddress remoteAddress, String sentence) {
+        Parser parser = new Parser(PATTERN_CELL, sentence);
+        if (!parser.matches()) {
+            return null;
+        }
+
+        DeviceSession deviceSession = getDeviceSession(channel, remoteAddress, parser.next());
+        if (deviceSession == null) {
+            return null;
+        }
+
+        Position position = new Position(getProtocolName());
+        position.setDeviceId(deviceSession.getDeviceId());
+
+        getLastLocation(position, null);
+
+        Network network = new Network();
+
+        String[] cells = parser.next().split("\n");
+        for (String cell : cells) {
+            String[] values = cell.substring(1, cell.length() - 1).split(",");
+            network.addCellTower(CellTower.from(
+                    Integer.parseInt(values[0]), Integer.parseInt(values[1]),
+                    Integer.parseInt(values[2]), Integer.parseInt(values[3])));
+        }
+
+        position.setNetwork(network);
+
+        position.set(Position.KEY_ODOMETER, parser.nextLong(16, 0));
+
+        return position;
+    }
+
     private Position decodeNetwork(Channel channel, SocketAddress remoteAddress, String sentence) {
         Parser parser = new Parser(PATTERN_NETWORK, sentence);
         if (!parser.matches()) {
@@ -422,7 +465,9 @@ public class Tk103ProtocolDecoder extends BaseProtocolDecoder {
             }
         }
 
-        if (sentence.contains("ZC20")) {
+        if (sentence.indexOf('{') > 0 && sentence.indexOf('}') > 0) {
+            return decodeCell(channel, remoteAddress, sentence);
+        } else if (sentence.contains("ZC20")) {
             return decodeBattery(channel, remoteAddress, sentence);
         } else if (sentence.contains("BZ00")) {
             return decodeNetwork(channel, remoteAddress, sentence);
-- 
cgit v1.2.3