From 94b23497349c0e3b1d63049b8e835d20c4886b74 Mon Sep 17 00:00:00 2001 From: Ricky Ng-Adam Date: Sun, 9 Dec 2018 19:11:20 +0800 Subject: [PATCH 1/4] hack location in --- .gitignore | 1 + app/build.properties | 6 +-- .../wifianalyzer/wifi/model/WiFiData.java | 40 ++++++++++++++++++- app/src/main/res/raw/shanghai.txt | 10 +++++ build-on-docker.sh | 17 ++++++++ 5 files changed, 70 insertions(+), 4 deletions(-) create mode 100644 app/src/main/res/raw/shanghai.txt create mode 100755 build-on-docker.sh diff --git a/.gitignore b/.gitignore index 005c150ae..06b384f7e 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ coverage libs release *IntegrationTest.java +caches diff --git a/app/build.properties b/app/build.properties index a2b05823b..6c9ef75c7 100644 --- a/app/build.properties +++ b/app/build.properties @@ -1,7 +1,7 @@ #Build Properties -#Sat Nov 24 09:14:14 EST 2018 +#Sun Dec 09 09:54:21 UTC 2018 version_minor=0 version_build=0 -version_patch=3 -version_store=44 +version_patch=4 version_major=2 +version_store=45 diff --git a/app/src/main/java/com/vrem/wifianalyzer/wifi/model/WiFiData.java b/app/src/main/java/com/vrem/wifianalyzer/wifi/model/WiFiData.java index eae833c12..19fe98b28 100644 --- a/app/src/main/java/com/vrem/wifianalyzer/wifi/model/WiFiData.java +++ b/app/src/main/java/com/vrem/wifianalyzer/wifi/model/WiFiData.java @@ -19,9 +19,14 @@ package com.vrem.wifianalyzer.wifi.model; import android.support.annotation.NonNull; +import android.widget.Toast; import com.vrem.wifianalyzer.MainContext; import com.vrem.wifianalyzer.vendor.model.VendorService; +import com.vrem.wifianalyzer.MainContext; +import com.vrem.util.FileUtils; +import com.vrem.wifianalyzer.R; +import android.content.res.Resources; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.IterableUtils; @@ -33,13 +38,30 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.HashMap; + +import org.json.JSONObject; public class WiFiData { public static final WiFiData EMPTY = new WiFiData(Collections.emptyList(), WiFiConnection.EMPTY, Collections.emptyList()); private final List wiFiDetails; - private final WiFiConnection wiFiConnection; private final List wiFiConfigurations; + private final WiFiConnection wiFiConnection; + private static JSONObject wiFiLocations = new JSONObject(); + static + { + try { + String content = FileUtils.readFile(MainContext.INSTANCE.getResources(), R.raw.shanghai); + wiFiLocations = new JSONObject(content); + Toast.makeText(MainContext.INSTANCE.getContext(), "Loaded locations with " + wiFiLocations.length() + " MAC -> location mappings", Toast.LENGTH_LONG).show(); + } catch(Exception e) { + Toast.makeText(MainContext.INSTANCE.getContext(), "Failed to load locations", Toast.LENGTH_LONG).show(); + } + } + private static String lastNotified = ""; + private static double currentDistance = 1000; public WiFiData(@NonNull List wiFiDetails, @NonNull WiFiConnection wiFiConnection, @NonNull List wiFiConfigurations) { this.wiFiDetails = wiFiDetails; @@ -117,6 +139,22 @@ public WiFiConnection getWiFiConnection() { private WiFiDetail copyWiFiDetail(WiFiDetail wiFiDetail) { VendorService vendorService = MainContext.INSTANCE.getVendorService(); String vendorName = vendorService.findVendorName(wiFiDetail.getBSSID()); + try { + String location = wiFiLocations.getString(wiFiDetail.getBSSID().toUpperCase()); + if(location != null) { + double distance = WiFiUtils.calculateDistance(wiFiDetail.getWiFiSignal().getPrimaryFrequency(), wiFiDetail.getWiFiSignal().getLevel()); + if(!lastNotified.equals(location) && distance <= currentDistance) { + Toast.makeText(MainContext.INSTANCE.getContext(), "You are in " + location, Toast.LENGTH_LONG).show(); + lastNotified = location; + currentDistance = distance; + } else { + // getting closer or further away + currentDistance = distance; + } + } + } catch(Exception e) { + + } WiFiAdditional wiFiAdditional = new WiFiAdditional(vendorName, wiFiConnection); return new WiFiDetail(wiFiDetail, wiFiAdditional); } diff --git a/app/src/main/res/raw/shanghai.txt b/app/src/main/res/raw/shanghai.txt new file mode 100644 index 000000000..432b4d47c --- /dev/null +++ b/app/src/main/res/raw/shanghai.txt @@ -0,0 +1,10 @@ +{ + "E0:3F:49:93:5C:DC": "B3#101 [AgoraSpace]", + "38:2C:4A:95:E8:34": "B8 lobby [Agora Space]", + "9C:3D:CF:74:8A:5B": "B8 lobby meeting room [Overhand 5G]", + "9C:3D:CF:74:8A:5C": "B8 lobby meeting room [Overhand 5G]", + "00:1C:58:41:C2:E0": "B8 server room [KITAPP01]", + "A0:48:1C:5C:E6:30": "B8 classroom [HP 4500]", + "08:62:66:BE:4C:F8": "B8 conference room [Agora Space]", + "08:62:66:be:4c:fc": "B8 Classroom Door A", +} \ No newline at end of file diff --git a/build-on-docker.sh b/build-on-docker.sh new file mode 100755 index 000000000..4e871241c --- /dev/null +++ b/build-on-docker.sh @@ -0,0 +1,17 @@ +#!/bin/sh +mkdir -p $(pwd)/caches/ + +if [ -z "$1" ]; then + COMMAND="gradle build" +else + COMMAND="$1" +fi + +#-v $(pwd)/caches/android-sdk:/home/user/android-sdk-linux \ +# -u $(id -u):$(id -g) \ +docker run -it \ + -w /home/user/project \ + -v $(pwd):/home/user/project \ + -v $(pwd)/caches/gradle-cache:/home/user/.gradle \ + -v $(pwd)/caches/android-cache:/home/user/.android \ + houtan/android-sdk $COMMAND From 29141ab65ff5441b83ec4ef8cdf1defa4c69a466 Mon Sep 17 00:00:00 2001 From: Ricky Ng-Adam Date: Sun, 9 Dec 2018 22:22:43 +0800 Subject: [PATCH 2/4] attempt at mapping and using permutations --- .../wifianalyzer/wifi/model/WiFiData.java | 52 ++++++++++----- app/src/main/res/raw/shanghai.txt | 63 +++++++++++++++++-- 2 files changed, 92 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/com/vrem/wifianalyzer/wifi/model/WiFiData.java b/app/src/main/java/com/vrem/wifianalyzer/wifi/model/WiFiData.java index 19fe98b28..0ec281499 100644 --- a/app/src/main/java/com/vrem/wifianalyzer/wifi/model/WiFiData.java +++ b/app/src/main/java/com/vrem/wifianalyzer/wifi/model/WiFiData.java @@ -61,7 +61,6 @@ public class WiFiData { } } private static String lastNotified = ""; - private static double currentDistance = 1000; public WiFiData(@NonNull List wiFiDetails, @NonNull WiFiConnection wiFiConnection, @NonNull List wiFiConfigurations) { this.wiFiDetails = wiFiDetails; @@ -87,6 +86,7 @@ public List getWiFiDetails(@NonNull Predicate predicate, results = sortAndGroup(results, sortBy, groupBy); } Collections.sort(results, sortBy.comparator()); + notifyLocation(results); return results; } @@ -113,6 +113,40 @@ List sortAndGroup(@NonNull List wiFiDetails, @NonNull So return results; } + private void notifyLocation(List wifiDetails) { + if(wifiDetails.size() < 3) { + return; + } + try { + String lookupKey = (wifiDetails.get(0).getBSSID() + " " + wifiDetails.get(1).getBSSID() + " " + wifiDetails.get(2).getBSSID()).toUpperCase(); + String location = wiFiLocations.getString(lookupKey); + if(location == null) { + location = wiFiLocations.getString((wifiDetails.get(1).getBSSID() + " " + wifiDetails.get(0).getBSSID() + " " + wifiDetails.get(2).getBSSID()).toUpperCase()); + } + if(location == null) { + location = wiFiLocations.getString((wifiDetails.get(0).getBSSID() + " " + wifiDetails.get(1).getBSSID()).toUpperCase()); + } + if(location == null) { + location = wiFiLocations.getString((wifiDetails.get(1).getBSSID() + " " + wifiDetails.get(0).getBSSID()).toUpperCase()); + } + if(location == null) { + location = wiFiLocations.getString(wifiDetails.get(0).getBSSID().toUpperCase()); + } + if(location == null) { + location = wiFiLocations.getString(wifiDetails.get(1).getBSSID().toUpperCase()); + } + if(location != null) { + if(!lastNotified.equals(location)) { + Toast.makeText(MainContext.INSTANCE.getContext(), "You are in " + location, Toast.LENGTH_LONG).show(); + lastNotified = location; + } + } else { + //Toast.makeText(MainContext.INSTANCE.getContext(), "Nothing found " + lookupKey, Toast.LENGTH_SHORT).show(); + } + } catch(Exception e) { + } + } + @NonNull private List getWiFiDetails(@NonNull Predicate predicate) { Collection selected = CollectionUtils.select(wiFiDetails, predicate); @@ -139,22 +173,6 @@ public WiFiConnection getWiFiConnection() { private WiFiDetail copyWiFiDetail(WiFiDetail wiFiDetail) { VendorService vendorService = MainContext.INSTANCE.getVendorService(); String vendorName = vendorService.findVendorName(wiFiDetail.getBSSID()); - try { - String location = wiFiLocations.getString(wiFiDetail.getBSSID().toUpperCase()); - if(location != null) { - double distance = WiFiUtils.calculateDistance(wiFiDetail.getWiFiSignal().getPrimaryFrequency(), wiFiDetail.getWiFiSignal().getLevel()); - if(!lastNotified.equals(location) && distance <= currentDistance) { - Toast.makeText(MainContext.INSTANCE.getContext(), "You are in " + location, Toast.LENGTH_LONG).show(); - lastNotified = location; - currentDistance = distance; - } else { - // getting closer or further away - currentDistance = distance; - } - } - } catch(Exception e) { - - } WiFiAdditional wiFiAdditional = new WiFiAdditional(vendorName, wiFiConnection); return new WiFiDetail(wiFiDetail, wiFiAdditional); } diff --git a/app/src/main/res/raw/shanghai.txt b/app/src/main/res/raw/shanghai.txt index 432b4d47c..3bcdf31f6 100644 --- a/app/src/main/res/raw/shanghai.txt +++ b/app/src/main/res/raw/shanghai.txt @@ -1,10 +1,61 @@ { "E0:3F:49:93:5C:DC": "B3#101 [AgoraSpace]", "38:2C:4A:95:E8:34": "B8 lobby [Agora Space]", - "9C:3D:CF:74:8A:5B": "B8 lobby meeting room [Overhand 5G]", - "9C:3D:CF:74:8A:5C": "B8 lobby meeting room [Overhand 5G]", - "00:1C:58:41:C2:E0": "B8 server room [KITAPP01]", - "A0:48:1C:5C:E6:30": "B8 classroom [HP 4500]", - "08:62:66:BE:4C:F8": "B8 conference room [Agora Space]", - "08:62:66:be:4c:fc": "B8 Classroom Door A", + "9C:3D:CF:74:8A:5B": "B8 lobby meeting room", + "9C:3D:CF:74:8A:5C": "B8 lobby meeting room", + "A0:48:1C:5C:E6:30": "B8 classroom", + "08:62:66:BE:4C:F8": "B8 conference room", + "08:62:66:BE:4C:FC": "B8 Classroom Door A", + "08:62:66:BE:4C:FC 08:62:66:BE:4C:F8 00:1C:58:41:C2:E0": "B#8 Door A", + "08:62:66:BE:4C:FC 08:62:66:BE:4C:F8": "B#8 Classroom Door A", + "08:62:66:BE:4C:F8 08:62:66:BE:4C:FC 00:1C:58:41:C2:E0": "B#8 Door B", + "08:62:66:BE:4C:F8 08:62:66:BE:4C:FC": "B#8 Door B", + "38:2C:4A:95:E8:30 38:2C:4A:95:E8:34 08:62:66:BE:4C:F8": "B#8 Piano small meeting room", + "9C:3D:CF:74:8A:5B 9C:3D:CF:74:8A:5C 38:2C:4A:95:E8:30": "B#8 Lobby meeting room", + "9C:3D:CF:74:8A:5B 9C:3D:CF:74:8A:5C": "B#8 Lobby meeting room", + "08:62:66:BE:4C:F8 38:2C:4A:95:E8:30 60:BB:0C:AF:6E:DA": "B#8 Coffee machine", + "08:62:66:BE:4C:F8 38:2C:4A:95:E8:30": "B#8 Coffee machine", + "08:62:66:BE:4C:F8 08:62:66:BE:4C:FC A0:48:1C:5C:E6:30": "B#8 Aquarium", + "B0:48:7A:51:1D:04 08:62:66:BE:4C:F8 08:62:66:BD:00:20": "B#8 Painter office", + "08:B2:66:BD:00:20 B0:48:7A:51:1D:04 08:62:66:BD:00:24": "B#8 Relaxation room", + "08:B2:66:BD:00:20 B0:48:7A:51:1D:04": "B#8 Relaxation room", + "08:B2:66:BD:00:20": "B#8 Relaxation room", + "08:62:66:BD:00:20 08:62:66:BD:00:24 B0:48:7A:51:1D:04": "B#8 Conference room", + "08:62:66:BD:00:20 08:62:66:BD:00:24": "B#8 Conference room", + "08:62:66:BD:00:20": "B#8 Conference room", + "00:1C:58:41:C2:E0 9C:3D:CF:74:8A:5C 08:62:66:BE:4C:F8": "B#8 Photography room", + "00:1C:58:41:C2:E0 9C:3D:CF:74:8A:5C": "B#8 Photography room", + "BE:DD:C2:10:7A:49 08:62:66:BE:4C:F8 38:2C:4A:95:E8:30": "B#8 Photography room", + "BE:DD:C2:10:7A:49 08:62:66:BE:4C:F8": "B#8 Photography room", + "BE:DD:C2:10:7A:49": "B#8 Photography room", + "A0:48:1C:5C:E6:30 08:62:66:BE:4C:F8 08:62:66:BE:4C:FC": "B#8 Classroom printer", + "A0:48:1C:5C:E6:30 08:62:66:BE:4C:F8": "B#8 Classroom printer", + "08:62:66:BE:4C:F8 08:62:66:BE:4C:F8 00:1C:58:41:C2:E0": "B#8 Dining table", + "08:62:66:BE:4C:F8 08:62:66:BE:4C:F8": "B#8 Dining table", + "38:2C:4A:95:E8:30 38:2C:4A:95:E8:34": "B#8 Men's restroom", + "38:2C:4A:95:E8:30": "B#8 Women's restroom", + "9C:3D:CF:74:8A:5C 60:BB:0C:AF:6E:DA 08:62:66:BE:4C:F8": "B#8 Coffee machine", + "9C:3D:CF:74:8A:5C 60:BB:0C:AF:6E:DA": "B#8 Coffee machine", + "38:2C:4A:95:E8:30 08:62:66:BE:4C:F8 60:BB:0C:AF:6E:DA": "B#8 Dining table", + "38:2C:4A:95:E8:30 08:62:66:BE:4C:F8": "B#8 Dining table", + "08:62:66:BE:4C:F8 00:1C:58:41:C2:E0 38:2C:4A:95:E8:30": "B#8 Photography room", + "08:62:66:BE:4C:F8 00:1C:58:41:C2:E0": "B#8 Photography room", + "00:1C:58:41:C2:E0 08:62:66:BE:4C:F8 38:2C:4A:95:E8:30": "B#8 Server room", + "00:1C:58:41:C2:E0 08:62:66:BE:4C:F8": "B#8 Server room", + "00:1C:58:41:C2:E0": "B#8 Server room", + "08:62:66:BD:00:20 B0:48:7A:51:1D:04 08:62:66:BD:00:24": "B#8 Relaxation room", + "08:62:66:BD:00:20 B0:48:7A:51:1D:04": "B#8 Relaxation room", + "B0:48:7A:51:1D:04 08:62:66:BE:4C:F8 08:62:66:BE:4C:F8": "B#8 Painter office", + "B0:48:7A:51:1D:04 08:62:66:BE:4C:F8": "B#8 Painter office", + "B0:48:7A:51:1D:04": "B#8 Painter office", + "08:62:66:BE:4C:F8 00:1C:58:41:C2:E0 08:62:66:BE:4C:FC": "B#8 Dart room", + "38:2C:4A:95:E8:30 08:62:66:BE:4C:F8 9C:3D:CF:74:8A:5B": "B#8 Washing machine room", + "08:62:66:BE:4C:F8 08:62:66:BE:4C:FC A0:48:1C:5C:E6:30 00:1C:58:51:C2:E0": "B#8 Dart room", + "08:62:66:BE:4C:FC 00:1C:58:41:C2:E0 A0:48:1C:5C:E6:30": "B#8 Aquarium", + "00:1C:58:41:C2:E0 08:62:66:BE:4C:F8 BE:DD:C2:10:7A:49": "B#8 Server room", + "BE:DD:C2:10:7A:49 08:62:66:BE:4C:F8 00:1C:58:41:C2:E0": "B#8 Photography room", + "C8:3A:35:5F:7E:D1 D8:50:E6:EA:7C:F8 20:A6:80:84:FC:64": "B#8 Stairwell", + "C8:3A:35:5F:7E:D1 D8:50:E6:EA:7C:F8": "B#8 Stairwell", + "D8:50:E6:EA:7C:F8 20:A6:80:84:FC:64": "B#8 Stairwell" + } \ No newline at end of file From f0237cb33729097d22063940ff59ad7bd8a9ba3b Mon Sep 17 00:00:00 2001 From: Ricky Ng-Adam Date: Mon, 10 Dec 2018 15:41:05 +0800 Subject: [PATCH 3/4] doc development --- DEVELOPMENT.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 DEVELOPMENT.md diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md new file mode 100644 index 000000000..04ceaa01c --- /dev/null +++ b/DEVELOPMENT.md @@ -0,0 +1,37 @@ +# Development + +## Developing Using Docker + +Benefits: + +* no need to modify your environment +* can try new dependencies by changing the Dockerfile +* use any text editor to maintaining + +Downsides: + +* slow +* no nice IDE +* some challenges keeping image and host directory in sync + +Pull the docker image, run it and run ```gradle build```: + +``` +./build-on-docker.sh +``` + +First time builds takes about 20 minutes (downloading dependencies, compilation and testing). + +Caches are stored in the ```caches/``` directory of the host. + +Edit-compiled-run is done as: + +``` +gradle --continuous assembleDebug +``` + +You can install as follows (```brew install android-platform-tools```): + +``` +adb install -r -g app-debug.apk +``` \ No newline at end of file From 55ad8dd907647663f3248efb7b527586335efdc6 Mon Sep 17 00:00:00 2001 From: Ricky Ng-Adam Date: Mon, 10 Dec 2018 15:41:24 +0800 Subject: [PATCH 4/4] update with Shanghai subway location --- app/src/main/res/raw/shanghai.txt | 204 +++++++++++++++++++++++++++++- 1 file changed, 202 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/raw/shanghai.txt b/app/src/main/res/raw/shanghai.txt index 3bcdf31f6..1d94ccf5e 100644 --- a/app/src/main/res/raw/shanghai.txt +++ b/app/src/main/res/raw/shanghai.txt @@ -56,6 +56,206 @@ "BE:DD:C2:10:7A:49 08:62:66:BE:4C:F8 00:1C:58:41:C2:E0": "B#8 Photography room", "C8:3A:35:5F:7E:D1 D8:50:E6:EA:7C:F8 20:A6:80:84:FC:64": "B#8 Stairwell", "C8:3A:35:5F:7E:D1 D8:50:E6:EA:7C:F8": "B#8 Stairwell", - "D8:50:E6:EA:7C:F8 20:A6:80:84:FC:64": "B#8 Stairwell" - + "D8:50:E6:EA:7C:F8 20:A6:80:84:FC:64": "B#8 Stairwell", + "06:69:6C:0C:53:3E": "静安寺2号线", + "0A:69:6C:0C:53:3F": "静安寺2号线", + "0A:69:6C:0C:52:C3": "静安寺2号线", + "06:69:6C:0C:53:3F": "静安寺2号线", + "EA:69:6C:32:74:4F": "静安寺2号线", + "0A:69:6C:22:DB:75": "静安寺2号线", + "DA:69:6C:32:70:FE": "静安寺2号线", + "06:69:6C:22:DB:75": "静安寺2号线", + "06:69:6C:22:DB:74": "静安寺2号线", + "06:69:6C:0C:52:93": "静安寺2号线", + "0A:69:6C:0C:53:3E": "静安寺2号线", + "5A:69:6C:32:6F:D6": "静安寺2号线", + "50:68:0A:D5:4B:F6": "静安寺2号线", + "EA:69:6C:32:72:7F": "静安寺2号线", + "06:69:6C:0C:52:92": "静安寺2号线", + "0A:69:6C:0C:59:5E": "南京西路2号线 ", + "0A:69:6C:0C:59:2F": "南京西路2号线 ", + "0A:69:6C:0C:59:2E": "南京西路2号线 ", + "4A:69:6C:32:78:55": "南京西路2号线 ", + "06:69:6C:0C:59:5E": "南京西路2号线 ", + "06:69:6C:0C:59:2F": "南京西路2号线 ", + "06:69:6C:0C:59:2E": "南京西路2号线 ", + "6A:69:6C:32:73:97": "南京西路2号线 ", + "06:69:6C:0C:59:5F": "南京西路2号线 ", + "06:69:6C:0C:59:BB": "南京西路2号线 ", + "0A:69:6C:0C:59:5F": "南京西路2号线 ", + "0A:69:6C:42:25:29": "人民广场2号线", + "06:69:6C:42:25:29": "人民广场2号线", + "06:69:6C:0C:54:2B": "人民广场2号线", + "AA:69:6C:32:71:2B": "人民广场2号线", + "4A:69:6C:20:E9:95": "人民广场2号线", + "0A:69:6C:0C:54:2B": "人民广场2号线", + "8A:69:6C:20:E1:D9": "人民广场2号线", + "06:69:6C:0C:53:5F": "南京东路2号线", + "06:69:6C:0C:53:16": "南京东路2号线", + "0A:69:6C:0C:54:AB": "南京东路2号线", + "06:69:6C:0C:53:17": "南京东路2号线", + "06:69:6C:0C:54:AA": "南京东路2号线", + "06:69:6C:0C:54:AB": "南京东路2号线", + "0A:69:6C:0C:52:AB": "南京东路2号线", + "2A:69:6C:20:E8:13": "南京东路2号线", + "06:69:6C:0C:52:AB": "南京东路2号线", + "0A:69:6C:0C:53:5E": "南京东路2号线", + "0A:69:6C:0C:53:16": "南京东路2号线", + "0A:69:6C:0C:53:17": "南京东路2号线", + "0A:69:6C:0C:54:AA": "南京东路2号线", + "1A:69:6C:32:74:32": "南京东路2号线", + "06:69:6C:0C:53:5E": "南京东路2号线", + "0A:69:6C:0C:53:7F": "陆家嘴2号线", + "0A:69:6C:0C:58:3B": "东昌路2号线", + "06:69:6C:0C:58:3B": "东昌路2号线", + "06:69:6C:0C:5A:3F": "东昌路2号线", + "0A:69:6C:BB:23:1C": "世纪大道2号线", + "2A:69:6C:32:B1:D3": "世纪大道2号线", + "0A:69:6C:BB:23:1D": "世纪大道2号线", + "06:69:6C:BB:23:1D": "世纪大道2号线", + "06:69:6C:B9:81:04": "世纪大道2号线", + "CA:69:6C:32:B0:DD": "世纪大道2号线", + "DA:69:6C:32:B0:DE": "世纪大道2号线", + "0A:69:6C:B9:81:03": "世纪大道2号线", + "06:69:6C:0C:53:D3": "上海科技馆2号线", + "06:69:6C:0C:53:23": "上海科技馆2号线", + "0A:69:6C:0C:53:D3": "上海科技馆2号线", + "06:69:6C:0C:52:FE": "上海科技馆2号线", + "06:69:6C:0C:53:DF": "上海科技馆2号线", + "06:69:6C:0C:5A:C2": "世纪公园2号线", + "CA:69:6C:31:F3:8D": "花木路7号线", + "4A:69:6C:32:01:B5": "花木路7号线", + "06:14:4B:70:8F:A4": "花木路7号线", + "0A:14:4B:6D:16:BF": "花木路7号线", + "06:14:4B:70:8F:A3": "花木路7号线", + "0A:14:4B:6D:16:C0": "花木路7号线", + "BA:69:6C:31:F3:8C": "花木路7号线", + "06:14:4B:6D:16:BF": "花木路7号线", + "0A:14:4B:70:8F:84": "花木路7号线", + "06:14:4B:6D:16:C0": "花木路7号线", + "0A:14:4B:70:8F:83": "花木路7号线", + "0A:14:4B:6D:1D:AC": "花木路7号线", + "DA:69:6C:31:FE:AE": "花木路7号线", + "0A:14:4B:6D:1D:AB": "花木路7号线", + "5A:69:6C:32:01:B6": "花木路7号线", + "0A:14:4B:6D:14:A4": "花木路7号线", + "06:14:4B:70:8F:84": "花木路7号线", + "0A:14:4B:70:8F:A4": "花木路7号线", + "06:14:4B:70:8F:83": "花木路7号线", + "06:14:4B:6D:1D:AC": "花木路7号线", + "0A:14:4B:70:8F:A3": "花木路7号线", + "EA:69:6C:31:FE:AF": "花木路7号线", + "06:14:4B:6D:1D:AB": "花木路7号线", + "06:14:4B:6D:14:A4": "花木路7号线", + "06:14:4B:6D:14:A3": "花木路7号线", + "AA:69:6C:32:66:DB": "龙阳路7号线", + "AA:69:6C:32:63:BB": "龙阳路7号线", + "06:14:4B:6D:1D:83": "龙阳路7号线", + "0A:14:4B:6D:1D:84": "龙阳路7号线", + "06:14:4B:6D:1D:84": "龙阳路7号线", + "7A:69:6C:32:64:28": "龙阳路7号线", + "0A:14:4B:6D:1D:87": "龙阳路7号线", + "06:14:4B:6D:1D:87": "龙阳路7号线", + "0A:14:4B:6D:1D:88": "龙阳路7号线", + "8A:69:6C:32:64:29": "龙阳路7号线", + "06:14:4B:6D:1D:88": "龙阳路7号线", + "0A:14:4B:6D:1D:83": "龙阳路7号线", + "0A:14:4B:6D:1D:47": "龙阳路7号线", + "0A:14:4B:6D:1D:C7": "龙阳路7号线", + "06:14:4B:6D:1D:47": "龙阳路7号线", + "06:14:4B:6D:1D:C7": "龙阳路7号线", + "0A:14:4B:6D:1D:C8": "龙阳路7号线", + "0A:69:6C:20:DD:51": "龙阳路7号线", + "06:14:4B:70:8F:77": "龙阳路7号线", + "0A:14:4B:70:8F:78": "龙阳路7号线", + "06:14:4B:6D:1D:C8": "龙阳路7号线", + "06:14:4B:70:8F:78": "龙阳路7号线", + "0A:14:4B:70:8F:77": "龙阳路7号线", + "9A:69:6C:32:63:BA": "龙阳路7号线", + "14:9D:09:C3:AD:C8": "龙阳路7号线", + "06:14:4B:71:32:48": "昌平路7号线", + "0A:14:4B:71:32:47": "昌平路7号线", + "06:14:4B:71:31:83": "昌平路7号线", + "3A:69:6C:32:63:74": "昌平路7号线", + "B8:BC:1B:32:12:79": "昌平路7号线", + "06:14:4B:71:31:8C": "昌平路7号线", + "DA:69:6C:32:62:EE": "昌平路7号线", + "0A:14:4B:71:31:8C": "昌平路7号线", + "6A:69:6C:31:F4:27": "昌平路7号线", + "06:14:4B:71:31:EB": "昌平路7号线", + "06:14:4B:71:31:8B": "昌平路7号线", + "3A:69:6C:32:6B:A4": "昌平路7号线", + "CA:69:6C:32:62:ED": "昌平路7号线", + "06:14:4B:71:31:A8": "昌平路7号线", + "06:14:4B:71:31:84": "昌平路7号线", + "0A:14:4B:71:31:A8": "昌平路7号线", + "1A:69:6C:32:6A:72": "昌平路7号线", + "AA:69:6C:31:F4:EB": "昌平路7号线", + "06:14:4B:71:31:A7": "昌平路7号线", + "06:14:4B:71:32:47": "昌平路7号线", + "0A:14:4B:71:31:A7": "昌平路7号线", + "0A:14:4B:71:32:48": "昌平路7号线", + "06:14:4B:6D:12:68": "昌平路7号线", + "06:14:4B:71:31:87": "昌平路7号线", + "0A:14:4B:71:31:88": "昌平路7号线", + "0A:14:4B:6D:12:68": "昌平路7号线", + "BA:69:6C:32:72:AC": "昌平路7号线", + "06:14:4B:6D:12:67": "昌平路7号线", + "0A:14:4B:6D:12:67": "昌平路7号线", + "06:14:4B:71:31:88": "昌平路7号线", + "8A:69:6C:32:65:A9": "昌平路7号线", + "3A:69:6C:34:59:94": "岚皋路7号线", + "06:14:4B:6D:17:8B": "岚皋路7号线", + "0A:14:4B:6D:17:8C": "岚皋路7号线", + "06:14:4B:6D:17:8C": "岚皋路7号线", + "0A:14:4B:6D:16:C7": "岚皋路7号线", + "06:14:4B:6D:16:C7": "岚皋路7号线", + "0A:14:4B:6D:13:1F": "岚皋路7号线", + "0A:14:4B:6D:16:C8": "岚皋路7号线", + "06:14:4B:6D:13:1F": "岚皋路7号线", + "0A:14:4B:6D:13:20": "岚皋路7号线", + "06:14:4B:6D:13:20": "岚皋路7号线", + "0A:14:4B:71:32:50": "镇坪路7号线", + "06:14:4B:71:31:B8": "镇坪路7号线", + "06:14:4B:71:32:4F": "镇坪路7号线", + "0A:14:4B:71:31:B8": "镇坪路7号线", + "0A:14:4B:71:32:4F": "镇坪路7号线", + "06:14:4B:71:3A:34": "长寿路7号线", + "0A:14:4B:71:3A:34": "长寿路7号线", + "0A:69:6C:BB:0F:60": "静安寺7号线", + "06:69:6C:BB:0F:61": "静安寺7号线", + "06:14:4B:71:32:30": "静安寺7号线", + "2A:69:6C:31:FA:B3": "静安寺7号线", + "0A:14:4B:71:32:30": "静安寺7号线", + "DA:69:6C:32:01:3E": "静安寺7号线", + "8A:69:6C:31:F6:49": "静安寺7号线", + "06:14:4B:71:32:3F": "静安寺7号线", + "0A:14:4B:71:32:3F": "静安寺7号线", + "06:14:4B:71:32:40": "静安寺7号线", + "06:14:4B:71:2F:5C": "静安寺7号线", + "0A:14:4B:71:32:40": "静安寺7号线", + "0A:14:4B:71:2F:67": "静安寺7号线", + "0A:14:4B:71:2F:68": "静安寺7号线", + "06:14:4B:71:2F:67": "静安寺7号线", + "06:14:4B:71:2F:68": "静安寺7号线", + "0A:14:4B:71:2F:5B": "静安寺7号线", + "0A:14:4B:71:2F:5C": "静安寺7号线", + "06:14:4B:71:2F:5B": "静安寺7号线", + "06:14:4B:6D:15:6C": "行知路7号线 ", + "06:14:4B:6D:16:4F": "行知路7号线 ", + "0A:14:4B:6D:16:50": "行知路7号线 ", + "06:14:4B:6D:16:50": "行知路7号线 ", + "CA:69:6C:32:67:0D": "行知路7号线 ", + "0A:14:4B:6D:15:6B": "行知路7号线 ", + "06:14:4B:6D:15:6B": "行知路7号线 ", + "0A:14:4B:6D:15:6C": "行知路7号线 ", + "0A:14:4B:6D:16:4F": "行知路7号线 ", + "4A:69:6C:32:62:D5": "行知路7号线 ", + "06:14:4B:79:0A:28": "行知路7号线 ", + "06:14:4B:78:FF:97": "行知路7号线 ", + "06:14:4B:78:FF:98": "行知路7号线 ", + "1A:69:6C:32:72:42": "行知路7号线 ", + "0A:14:4B:6D:16:FB": "行知路7号线 ", + "0A:14:4B:6D:16:FC": "行知路7号线 ", + "06:14:4B:6D:16:FC": "行知路7号线 " } \ No newline at end of file