generated from Grigo/AndroidTemplate
fixed NMEAParser
This commit is contained in:
@@ -11,6 +11,11 @@ import java.util.ArrayList;
|
|||||||
/**
|
/**
|
||||||
* Контроллер для парсинга NMEA сообщений
|
* Контроллер для парсинга NMEA сообщений
|
||||||
* Работает в гибридном режиме: координаты через Location API, остальное через NMEA
|
* Работает в гибридном режиме: координаты через Location API, остальное через NMEA
|
||||||
|
*
|
||||||
|
* ВАЖНО: Размеры судна в AIS сообщениях рассчитываются относительно положения антенны:
|
||||||
|
* - Длина = Dim.A + Dim.B (от носа до антенны + от антенны до кормы)
|
||||||
|
* - Ширина = Dim.C + Dim.D (от левого борта до антенны + от антенны до правого борта)
|
||||||
|
* Координаты в AIS указывают положение антенны, а не центра судна.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -747,35 +752,43 @@ public class NMEAParser {
|
|||||||
String messageTypeBits = decodeAISField(payload, 0, 6);
|
String messageTypeBits = decodeAISField(payload, 0, 6);
|
||||||
int messageType = Integer.parseInt(messageTypeBits, 2);
|
int messageType = Integer.parseInt(messageTypeBits, 2);
|
||||||
|
|
||||||
Log.d(TAG, "Декодируем AIS тип " + messageType + " на канале " + channel);
|
Log.d(TAG, "Декодируем AIS тип " + messageType + " на канале " + channel + " (биты: " + messageTypeBits + ")");
|
||||||
|
|
||||||
switch (messageType) {
|
switch (messageType) {
|
||||||
case 1:
|
case 1:
|
||||||
case 2:
|
case 2:
|
||||||
case 3:
|
case 3:
|
||||||
// Position Report
|
// Position Report
|
||||||
|
Log.d(TAG, "Обрабатываем Position Report (тип " + messageType + ")");
|
||||||
decodePositionReport(payload, messageType);
|
decodePositionReport(payload, messageType);
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
// Static Data
|
// Static Data
|
||||||
|
Log.d(TAG, "Обрабатываем Static Data (тип " + messageType + ")");
|
||||||
decodeStaticData(payload);
|
decodeStaticData(payload);
|
||||||
break;
|
break;
|
||||||
case 4: // Base Station Report
|
case 4: // Base Station Report
|
||||||
|
Log.d(TAG, "Обрабатываем Base Station Report (тип " + messageType + ")");
|
||||||
decodeBaseStationReport(payload);
|
decodeBaseStationReport(payload);
|
||||||
break;
|
break;
|
||||||
case 14: // Safety Related Broadcast Message
|
case 14: // Safety Related Broadcast Message
|
||||||
|
Log.d(TAG, "Обрабатываем Safety Broadcast (тип " + messageType + ")");
|
||||||
decodeSafetyBroadcast(payload);
|
decodeSafetyBroadcast(payload);
|
||||||
break;
|
break;
|
||||||
case 18: // Standard Class B Equipment Position Report
|
case 18: // Standard Class B Equipment Position Report
|
||||||
|
Log.d(TAG, "Обрабатываем Class B Position Report (тип " + messageType + ")");
|
||||||
decodeClassBPositionReport(payload);
|
decodeClassBPositionReport(payload);
|
||||||
break;
|
break;
|
||||||
case 19: // Extended Class B Equipment Position Report
|
case 19: // Extended Class B Equipment Position Report
|
||||||
|
Log.d(TAG, "Обрабатываем Extended Class B Position Report (тип " + messageType + ")");
|
||||||
decodeExtendedClassBPositionReport(payload);
|
decodeExtendedClassBPositionReport(payload);
|
||||||
break;
|
break;
|
||||||
case 21: // Aid-to-Navigation Report
|
case 21: // Aid-to-Navigation Report
|
||||||
|
Log.d(TAG, "Обрабатываем Aid-to-Navigation Report (тип " + messageType + ")");
|
||||||
decodeAidToNavigationReport(payload);
|
decodeAidToNavigationReport(payload);
|
||||||
break;
|
break;
|
||||||
case 24: // Static Data Report
|
case 24: // Static Data Report
|
||||||
|
Log.d(TAG, "Обрабатываем Static Data Report (тип " + messageType + ")");
|
||||||
decodeStaticDataReport(payload);
|
decodeStaticDataReport(payload);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -892,7 +905,12 @@ public class NMEAParser {
|
|||||||
|
|
||||||
// Вырезаем нужный диапазон битов
|
// Вырезаем нужный диапазон битов
|
||||||
if (startBit + length <= fullBinary.length()) {
|
if (startBit + length <= fullBinary.length()) {
|
||||||
return fullBinary.substring(startBit, startBit + length);
|
String fieldResult = fullBinary.substring(startBit, startBit + length);
|
||||||
|
// Дополнительное логирование для первых 6 бит (тип сообщения)
|
||||||
|
if (startBit == 0 && length == 6) {
|
||||||
|
Log.d(TAG, "AIS Message Type bits: " + fieldResult + " (payload: " + payload + ")");
|
||||||
|
}
|
||||||
|
return fieldResult;
|
||||||
} else {
|
} else {
|
||||||
Log.w(TAG,
|
Log.w(TAG,
|
||||||
"AIS поле выходит за границы: startBit=" + startBit +
|
"AIS поле выходит за границы: startBit=" + startBit +
|
||||||
@@ -1043,11 +1061,11 @@ public class NMEAParser {
|
|||||||
int vesselTypeCode = Integer.parseInt(typeBits, 2);
|
int vesselTypeCode = Integer.parseInt(typeBits, 2);
|
||||||
Log.d(TAG, "Type bits: " + typeBits + " = " + vesselTypeCode);
|
Log.d(TAG, "Type bits: " + typeBits + " = " + vesselTypeCode);
|
||||||
|
|
||||||
// Dimension Reference (4 бита) - бит 240
|
// Dimension Reference (9, 9, 6, 6 бит) - бит 240
|
||||||
String dimRefABits = decodeAISField(payload, 240, 4);
|
String dimRefABits = decodeAISField(payload, 240, 9);
|
||||||
String dimRefBBits = decodeAISField(payload, 244, 4);
|
String dimRefBBits = decodeAISField(payload, 249, 9);
|
||||||
String dimRefCBits = decodeAISField(payload, 248, 4);
|
String dimRefCBits = decodeAISField(payload, 258, 6);
|
||||||
String dimRefDBits = decodeAISField(payload, 252, 4);
|
String dimRefDBits = decodeAISField(payload, 264, 6);
|
||||||
|
|
||||||
int dimRefA = Integer.parseInt(dimRefABits, 2);
|
int dimRefA = Integer.parseInt(dimRefABits, 2);
|
||||||
int dimRefB = Integer.parseInt(dimRefBBits, 2);
|
int dimRefB = Integer.parseInt(dimRefBBits, 2);
|
||||||
@@ -1056,18 +1074,25 @@ public class NMEAParser {
|
|||||||
|
|
||||||
Log.d(TAG, "Dimension Reference: A=" + dimRefA + ", B=" + dimRefB + ", C=" + dimRefC + ", D=" + dimRefD);
|
Log.d(TAG, "Dimension Reference: A=" + dimRefA + ", B=" + dimRefB + ", C=" + dimRefC + ", D=" + dimRefD);
|
||||||
|
|
||||||
// Vessel Dimensions (30 бит) - бит 256
|
// Для сообщения типа 5 используем Dimension Reference поля (9, 9, 6, 6 бит)
|
||||||
String lengthBits = decodeAISField(payload, 256, 10);
|
// Размеры судна рассчитываются как:
|
||||||
String widthBits = decodeAISField(payload, 266, 10);
|
// Длина = Dim.A + Dim.B (от носа до антенны + от антенны до кормы)
|
||||||
String draftBits = decodeAISField(payload, 276, 8);
|
// Ширина = Dim.C + Dim.D (от левого борта до антенны + от антенны до правого борта)
|
||||||
|
double length = dimRefA + dimRefB;
|
||||||
|
double width = dimRefC + dimRefD;
|
||||||
|
|
||||||
double length = Integer.parseInt(lengthBits, 2);
|
// Draft (8 бит) - осадка - бит 296
|
||||||
double width = Integer.parseInt(widthBits, 2);
|
String draftBits = decodeAISField(payload, 296, 8);
|
||||||
double draft = Integer.parseInt(draftBits, 2) / 10.0;
|
double draft = Integer.parseInt(draftBits, 2) / 10.0;
|
||||||
|
|
||||||
Log.d(TAG, "Dimensions - Length bits: " + lengthBits + " = " + length);
|
Log.d(TAG, "Static Data - используем Dimension Reference поля (9, 9, 6, 6 бит):");
|
||||||
Log.d(TAG, "Dimensions - Width bits: " + widthBits + " = " + width);
|
Log.d(TAG, " Dim.A (нос-антенна): " + dimRefABits + " = " + dimRefA + " м");
|
||||||
Log.d(TAG, "Dimensions - Draft bits: " + draftBits + " = " + draft);
|
Log.d(TAG, " Dim.B (антенна-корма): " + dimRefBBits + " = " + dimRefB + " м");
|
||||||
|
Log.d(TAG, " Dim.C (левый борт-антенна): " + dimRefCBits + " = " + dimRefC + " м");
|
||||||
|
Log.d(TAG, " Dim.D (антенна-правый борт): " + dimRefDBits + " = " + dimRefD + " м");
|
||||||
|
Log.d(TAG, " Total Length (A+B): " + length + " м");
|
||||||
|
Log.d(TAG, " Total Width (C+D): " + width + " м");
|
||||||
|
Log.d(TAG, " Draft: " + draftBits + " = " + draft + " м");
|
||||||
|
|
||||||
// ETA (20 бит) - бит 294
|
// ETA (20 бит) - бит 294
|
||||||
String etaBits = decodeAISField(payload, 294, 20);
|
String etaBits = decodeAISField(payload, 294, 20);
|
||||||
@@ -1374,31 +1399,112 @@ public class NMEAParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Получает тип судна по коду
|
* Получает тип судна по коду согласно стандарту AIS
|
||||||
*/
|
*/
|
||||||
private String getVesselType(int typeCode) {
|
private String getVesselType(int typeCode) {
|
||||||
if (typeCode >= 20 && typeCode <= 29) return "Wing in ground";
|
switch (typeCode) {
|
||||||
if (typeCode >= 30 && typeCode <= 39) return "Fishing";
|
case 0: return "Not available";
|
||||||
if (typeCode >= 40 && typeCode <= 49) return "Towing";
|
case 1:
|
||||||
if (typeCode >= 50 && typeCode <= 59) return "Dredging";
|
case 2:
|
||||||
if (typeCode >= 60 && typeCode <= 69) return "Diving";
|
case 3:
|
||||||
if (typeCode >= 70 && typeCode <= 79) return "Military";
|
case 4:
|
||||||
if (typeCode >= 80 && typeCode <= 89) return "Pleasure";
|
case 5:
|
||||||
if (typeCode >= 90 && typeCode <= 99) return "High speed";
|
case 6:
|
||||||
if (typeCode >= 100 && typeCode <= 109) return "Pilot vessel";
|
case 7:
|
||||||
if (typeCode >= 110 && typeCode <= 119) return "SAR";
|
case 8:
|
||||||
if (typeCode >= 120 && typeCode <= 129) return "Tug";
|
case 9:
|
||||||
if (typeCode >= 130 && typeCode <= 139) return "Port tender";
|
case 10:
|
||||||
if (typeCode >= 140 && typeCode <= 149) return "Anti-pollution";
|
case 11:
|
||||||
if (typeCode >= 150 && typeCode <= 159) return "Law enforce";
|
case 12:
|
||||||
if (typeCode >= 160 && typeCode <= 169) return "Spare";
|
case 13:
|
||||||
if (typeCode >= 170 && typeCode <= 179) return "Medical";
|
case 14:
|
||||||
if (typeCode >= 180 && typeCode <= 189) return "Special craft";
|
case 15:
|
||||||
if (typeCode >= 190 && typeCode <= 199) return "Passenger";
|
case 16:
|
||||||
if (typeCode >= 200 && typeCode <= 209) return "Cargo";
|
case 17:
|
||||||
if (typeCode >= 210 && typeCode <= 219) return "Tanker";
|
case 18:
|
||||||
if (typeCode >= 220 && typeCode <= 229) return "Other";
|
case 19: return "Reserved for future use";
|
||||||
return "Unknown";
|
case 20: return "Wing in ground (WIG), all ships";
|
||||||
|
case 21: return "Wing in ground (WIG), Hazardous category A";
|
||||||
|
case 22: return "Wing in ground (WIG), Hazardous category B";
|
||||||
|
case 23: return "Wing in ground (WIG), Hazardous category C";
|
||||||
|
case 24: return "Wing in ground (WIG), Hazardous category D";
|
||||||
|
case 25:
|
||||||
|
case 26:
|
||||||
|
case 27:
|
||||||
|
case 28:
|
||||||
|
case 29: return "Wing in ground (WIG), Reserved";
|
||||||
|
case 30: return "Fishing";
|
||||||
|
case 31: return "Towing";
|
||||||
|
case 32: return "Towing: length exceeds 200m or breadth exceeds 25m";
|
||||||
|
case 33: return "Dredging or underwater ops";
|
||||||
|
case 34: return "Diving ops";
|
||||||
|
case 35: return "Military ops";
|
||||||
|
case 36: return "Sailing";
|
||||||
|
case 37: return "Pleasure Craft";
|
||||||
|
case 38:
|
||||||
|
case 39: return "Reserved";
|
||||||
|
case 40: return "High speed craft (HSC), all ships";
|
||||||
|
case 41: return "High speed craft (HSC), Hazardous category A";
|
||||||
|
case 42: return "High speed craft (HSC), Hazardous category B";
|
||||||
|
case 43: return "High speed craft (HSC), Hazardous category C";
|
||||||
|
case 44: return "High speed craft (HSC), Hazardous category D";
|
||||||
|
case 45:
|
||||||
|
case 46:
|
||||||
|
case 47:
|
||||||
|
case 48: return "High speed craft (HSC), Reserved";
|
||||||
|
case 49: return "High speed craft (HSC), No additional information";
|
||||||
|
case 50: return "Pilot Vessel";
|
||||||
|
case 51: return "Search and Rescue vessel";
|
||||||
|
case 52: return "Tug";
|
||||||
|
case 53: return "Port Tender";
|
||||||
|
case 54: return "Anti-pollution equipment";
|
||||||
|
case 55: return "Law Enforcement";
|
||||||
|
case 56:
|
||||||
|
case 57: return "Spare - Local Vessel";
|
||||||
|
case 58: return "Medical Transport";
|
||||||
|
case 59: return "Noncombatant ship according to RR Resolution No. 18";
|
||||||
|
case 60: return "Passenger, all ships";
|
||||||
|
case 61: return "Passenger, Hazardous category A";
|
||||||
|
case 62: return "Passenger, Hazardous category B";
|
||||||
|
case 63: return "Passenger, Hazardous category C";
|
||||||
|
case 64: return "Passenger, Hazardous category D";
|
||||||
|
case 65:
|
||||||
|
case 66:
|
||||||
|
case 67:
|
||||||
|
case 68: return "Passenger, Reserved";
|
||||||
|
case 69: return "Passenger, No additional information";
|
||||||
|
case 70: return "Cargo, all ships";
|
||||||
|
case 71: return "Cargo, Hazardous category A";
|
||||||
|
case 72: return "Cargo, Hazardous category B";
|
||||||
|
case 73: return "Cargo, Hazardous category C";
|
||||||
|
case 74: return "Cargo, Hazardous category D";
|
||||||
|
case 75:
|
||||||
|
case 76:
|
||||||
|
case 77:
|
||||||
|
case 78: return "Cargo, Reserved";
|
||||||
|
case 79: return "Cargo, No additional information";
|
||||||
|
case 80: return "Tanker, all ships";
|
||||||
|
case 81: return "Tanker, Hazardous category A";
|
||||||
|
case 82: return "Tanker, Hazardous category B";
|
||||||
|
case 83: return "Tanker, Hazardous category C";
|
||||||
|
case 84: return "Tanker, Hazardous category D";
|
||||||
|
case 85:
|
||||||
|
case 86:
|
||||||
|
case 87:
|
||||||
|
case 88: return "Tanker, Reserved";
|
||||||
|
case 89: return "Tanker, No additional information";
|
||||||
|
case 90: return "Other Type, all ships";
|
||||||
|
case 91: return "Other Type, Hazardous category A";
|
||||||
|
case 92: return "Other Type, Hazardous category B";
|
||||||
|
case 93: return "Other Type, Hazardous category C";
|
||||||
|
case 94: return "Other Type, Hazardous category D";
|
||||||
|
case 95:
|
||||||
|
case 96:
|
||||||
|
case 97:
|
||||||
|
case 98: return "Other Type, Reserved";
|
||||||
|
case 99: return "Other Type, no additional information";
|
||||||
|
default: return "Unknown";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1767,6 +1873,9 @@ public class NMEAParser {
|
|||||||
vessel.setLastUpdate(java.time.LocalDateTime.now());
|
vessel.setLastUpdate(java.time.LocalDateTime.now());
|
||||||
vessel.setVesselClass("Class B");
|
vessel.setVesselClass("Class B");
|
||||||
|
|
||||||
|
// В Class B Position Report размеры не передаются, но мы сохраняем существующие
|
||||||
|
Log.d(TAG, "Class B Position Report - размеры не передаются, сохраняем существующие: L=" + vessel.getLength() + ", W=" + vessel.getWidth());
|
||||||
|
|
||||||
// Отправляем информацию о корабле на внешний ресурс
|
// Отправляем информацию о корабле на внешний ресурс
|
||||||
String vesselInfo = String.format("Class B: lat=%.6f, lon=%.6f, course=%.1f, speed=%.1f, heading=%.1f, accuracy=%s",
|
String vesselInfo = String.format("Class B: lat=%.6f, lon=%.6f, course=%.1f, speed=%.1f, heading=%.1f, accuracy=%s",
|
||||||
latitude, longitude, course, speed, heading, accuracy == 1 ? "high" : "low");
|
latitude, longitude, course, speed, heading, accuracy == 1 ? "high" : "low");
|
||||||
@@ -1789,6 +1898,15 @@ public class NMEAParser {
|
|||||||
try {
|
try {
|
||||||
Log.d(TAG, "Декодируем Extended Class B Position Report, payload: " + payload + " (длина: " + payload.length() + ")");
|
Log.d(TAG, "Декодируем Extended Class B Position Report, payload: " + payload + " (длина: " + payload.length() + ")");
|
||||||
|
|
||||||
|
// Проверяем длину payload - для Extended Class B должно быть достаточно битов
|
||||||
|
int totalBits = payload.length() * 6;
|
||||||
|
Log.d(TAG, "Общая длина payload в битах: " + totalBits);
|
||||||
|
|
||||||
|
if (totalBits < 312) { // Минимум для Extended Class B
|
||||||
|
Log.w(TAG, "Extended Class B payload слишком короткий: " + totalBits + " бит, ожидается минимум 312");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// MMSI (30 бит) - начинается с бита 8
|
// MMSI (30 бит) - начинается с бита 8
|
||||||
String mmsiBits = decodeAISField(payload, 8, 30);
|
String mmsiBits = decodeAISField(payload, 8, 30);
|
||||||
int mmsi = Integer.parseInt(mmsiBits, 2);
|
int mmsi = Integer.parseInt(mmsiBits, 2);
|
||||||
@@ -1855,17 +1973,86 @@ public class NMEAParser {
|
|||||||
int dimRefC = Integer.parseInt(dimRefCBits, 2);
|
int dimRefC = Integer.parseInt(dimRefCBits, 2);
|
||||||
int dimRefD = Integer.parseInt(dimRefDBits, 2);
|
int dimRefD = Integer.parseInt(dimRefDBits, 2);
|
||||||
|
|
||||||
// Vessel Dimensions (30 бит) - бит 287
|
Log.d(TAG, "Dimension Reference: A=" + dimRefA + ", B=" + dimRefB + ", C=" + dimRefC + ", D=" + dimRefD);
|
||||||
String lengthBits = decodeAISField(payload, 287, 10);
|
|
||||||
String widthBits = decodeAISField(payload, 297, 10);
|
|
||||||
String draftBits = decodeAISField(payload, 307, 8);
|
|
||||||
|
|
||||||
double length = Integer.parseInt(lengthBits, 2);
|
// Vessel Dimensions (40 бит) - начинаются с бита 287
|
||||||
double width = Integer.parseInt(widthBits, 2);
|
// Проверяем, есть ли достаточно битов для размеров
|
||||||
double draft = Integer.parseInt(draftBits, 2) / 10.0;
|
if (totalBits < 327) {
|
||||||
|
Log.w(TAG, "Extended Class B - недостаточно битов для размеров: " + totalBits + " < 327");
|
||||||
|
// Создаем судно без размеров
|
||||||
|
AISVessel vessel = findOrCreateAISVessel(String.valueOf(mmsi));
|
||||||
|
vessel.updatePosition(latitude, longitude, course, speed);
|
||||||
|
vessel.setHeading(heading);
|
||||||
|
vessel.setPositionAccuracy(accuracy == 1);
|
||||||
|
vessel.setVesselName(vesselName);
|
||||||
|
vessel.setVesselType(getVesselType(vesselTypeCode));
|
||||||
|
vessel.setLastUpdate(java.time.LocalDateTime.now());
|
||||||
|
vessel.setVesselClass("Extended Class B");
|
||||||
|
|
||||||
|
if (listener != null) {
|
||||||
|
listener.onAISVesselUpdated(vessel);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Log.d(TAG, String.format("AIS Extended Class B: MMSI=%d, name='%s', lat=%.6f, lon=%.6f, course=%.1f, speed=%.1f, type=%d, L=%.1f, W=%.1f, D=%.1f",
|
// Dim.A (10 бит) - от носа до антенны
|
||||||
mmsi, vesselName, latitude, longitude, course, speed, vesselTypeCode, length, width, draft));
|
String dimABits = decodeAISField(payload, 287, 10);
|
||||||
|
// Dim.B (10 бит) - от антенны до кормы
|
||||||
|
String dimBBits = decodeAISField(payload, 297, 10);
|
||||||
|
// Dim.C (10 бит) - от левого борта до антенны
|
||||||
|
String dimCBits = decodeAISField(payload, 307, 10);
|
||||||
|
// Dim.D (10 бит) - от антенны до правого борта
|
||||||
|
String dimDBits = decodeAISField(payload, 317, 10);
|
||||||
|
|
||||||
|
Log.d(TAG, "Raw dimension bits - Dim.A: " + dimABits + ", Dim.B: " + dimBBits + ", Dim.C: " + dimCBits + ", Dim.D: " + dimDBits);
|
||||||
|
|
||||||
|
int dimA = Integer.parseInt(dimABits, 2);
|
||||||
|
int dimB = Integer.parseInt(dimBBits, 2);
|
||||||
|
int dimC = Integer.parseInt(dimCBits, 2);
|
||||||
|
int dimD = Integer.parseInt(dimDBits, 2);
|
||||||
|
|
||||||
|
// В AIS стандарте размеры кодируются как 6-битные значения:
|
||||||
|
// 0 = не указано, 1-62 = размер в метрах, 63 = размер 63+ метра
|
||||||
|
// Но мы получаем 10-битные значения, поэтому нужно их правильно интерпретировать
|
||||||
|
|
||||||
|
// Проверяем, что размеры в разумных пределах (0-1000 метров)
|
||||||
|
if (dimA > 1000 || dimB > 1000 || dimC > 1000 || dimD > 1000) {
|
||||||
|
Log.w(TAG, "Размеры судна выходят за разумные пределы: A=" + dimA + ", B=" + dimB + ", C=" + dimC + ", D=" + dimD);
|
||||||
|
// Возможно, мы неправильно интерпретируем битовые поля
|
||||||
|
// Попробуем интерпретировать как 6-битные значения
|
||||||
|
dimA = dimA & 0x3F; // Берем только младшие 6 бит
|
||||||
|
dimB = dimB & 0x3F;
|
||||||
|
dimC = dimC & 0x3F;
|
||||||
|
dimD = dimD & 0x3F;
|
||||||
|
Log.d(TAG, "Исправленные размеры (6-битные): A=" + dimA + ", B=" + dimB + ", C=" + dimC + ", D=" + dimD);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Дополнительная проверка: если размеры все еще неразумные, используем Dimension Reference
|
||||||
|
if (dimA > 100 || dimB > 100 || dimC > 100 || dimD > 100) {
|
||||||
|
Log.w(TAG, "Размеры все еще неразумные, используем Dimension Reference: A=" + dimA + ", B=" + dimB + ", C=" + dimC + ", D=" + dimD);
|
||||||
|
// Используем Dimension Reference как fallback
|
||||||
|
dimA = dimRefA;
|
||||||
|
dimB = dimRefB;
|
||||||
|
dimC = dimRefC;
|
||||||
|
dimD = dimRefD;
|
||||||
|
Log.d(TAG, "Fallback размеры из Dimension Reference: A=" + dimA + ", B=" + dimB + ", C=" + dimC + ", D=" + dimD);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Размеры судна рассчитываются как:
|
||||||
|
// Длина = Dim.A + Dim.B (от носа до антенны + от антенны до кормы)
|
||||||
|
// Ширина = Dim.C + Dim.D (от левого борта до антенны + от антенны до правого борта)
|
||||||
|
double length = dimA + dimB;
|
||||||
|
double width = dimC + dimD;
|
||||||
|
|
||||||
|
Log.d(TAG, "Dimensions - Dim.A (нос-антенна): " + dimABits + " = " + dimA);
|
||||||
|
Log.d(TAG, "Dimensions - Dim.B (антенна-корма): " + dimBBits + " = " + dimB);
|
||||||
|
Log.d(TAG, "Dimensions - Dim.C (левый борт-антенна): " + dimCBits + " = " + dimC);
|
||||||
|
Log.d(TAG, "Dimensions - Dim.D (антенна-правый борт): " + dimDBits + " = " + dimD);
|
||||||
|
Log.d(TAG, "Dimensions - Total Length (A+B): " + length + "m");
|
||||||
|
Log.d(TAG, "Dimensions - Total Width (C+D): " + width + "m");
|
||||||
|
|
||||||
|
Log.d(TAG, String.format("AIS Extended Class B: MMSI=%d, name='%s', lat=%.6f, lon=%.6f, course=%.1f, speed=%.1f, type=%d, L=%.1f, W=%.1f",
|
||||||
|
mmsi, vesselName, latitude, longitude, course, speed, vesselTypeCode, length, width));
|
||||||
|
|
||||||
// Создаем или обновляем AIS судно
|
// Создаем или обновляем AIS судно
|
||||||
AISVessel vessel = findOrCreateAISVessel(String.valueOf(mmsi));
|
AISVessel vessel = findOrCreateAISVessel(String.valueOf(mmsi));
|
||||||
@@ -1876,13 +2063,12 @@ public class NMEAParser {
|
|||||||
vessel.setVesselType(getVesselType(vesselTypeCode));
|
vessel.setVesselType(getVesselType(vesselTypeCode));
|
||||||
vessel.setLength(length);
|
vessel.setLength(length);
|
||||||
vessel.setWidth(width);
|
vessel.setWidth(width);
|
||||||
vessel.setDraft(draft);
|
|
||||||
vessel.setLastUpdate(java.time.LocalDateTime.now());
|
vessel.setLastUpdate(java.time.LocalDateTime.now());
|
||||||
vessel.setVesselClass("Extended Class B");
|
vessel.setVesselClass("Extended Class B");
|
||||||
|
|
||||||
// Отправляем информацию о корабле на внешний ресурс
|
// Отправляем информацию о корабле на внешний ресурс
|
||||||
String vesselInfo = String.format("Extended Class B: name='%s', lat=%.6f, lon=%.6f, course=%.1f, speed=%.1f, type=%s, L=%.1f, W=%.1f, D=%.1f",
|
String vesselInfo = String.format("Extended Class B: name='%s', lat=%.6f, lon=%.6f, course=%.1f, speed=%.1f, type=%s, L=%.1f, W=%.1f",
|
||||||
vesselName, latitude, longitude, course, speed, getVesselType(vesselTypeCode), length, width, draft);
|
vesselName, latitude, longitude, course, speed, getVesselType(vesselTypeCode), length, width);
|
||||||
LogSender.logShipUpdate(String.valueOf(mmsi), vesselInfo);
|
LogSender.logShipUpdate(String.valueOf(mmsi), vesselInfo);
|
||||||
|
|
||||||
// Уведомляем слушателя
|
// Уведомляем слушателя
|
||||||
@@ -1944,12 +2130,27 @@ public class NMEAParser {
|
|||||||
int dimRefD = Integer.parseInt(dimRefDBits, 2);
|
int dimRefD = Integer.parseInt(dimRefDBits, 2);
|
||||||
|
|
||||||
// Vessel Dimensions (30 бит) - бит 235
|
// Vessel Dimensions (30 бит) - бит 235
|
||||||
String lengthBits = decodeAISField(payload, 235, 10);
|
// Dim.A (10 бит) - от носа до антенны
|
||||||
String widthBits = decodeAISField(payload, 245, 10);
|
String dimABits = decodeAISField(payload, 235, 10);
|
||||||
String draftBits = decodeAISField(payload, 255, 8);
|
// Dim.B (10 бит) - от антенны до кормы
|
||||||
|
String dimBBits = decodeAISField(payload, 245, 10);
|
||||||
|
// Dim.C (10 бит) - от левого борта до антенны
|
||||||
|
String dimCBits = decodeAISField(payload, 255, 10);
|
||||||
|
// Dim.D (10 бит) - от антенны до правого борта
|
||||||
|
String dimDBits = decodeAISField(payload, 265, 10);
|
||||||
|
// Draft (8 бит) - осадка
|
||||||
|
String draftBits = decodeAISField(payload, 275, 8);
|
||||||
|
|
||||||
double length = Integer.parseInt(lengthBits, 2);
|
int dimA = Integer.parseInt(dimABits, 2);
|
||||||
double width = Integer.parseInt(widthBits, 2);
|
int dimB = Integer.parseInt(dimBBits, 2);
|
||||||
|
int dimC = Integer.parseInt(dimCBits, 2);
|
||||||
|
int dimD = Integer.parseInt(dimDBits, 2);
|
||||||
|
|
||||||
|
// Размеры судна рассчитываются как:
|
||||||
|
// Длина = Dim.A + Dim.B (от носа до антенны + от антенны до кормы)
|
||||||
|
// Ширина = Dim.C + Dim.D (от левого борта до антенны + от антенны до правого борта)
|
||||||
|
double length = dimA + dimB;
|
||||||
|
double width = dimC + dimD;
|
||||||
double draft = Integer.parseInt(draftBits, 2) / 10.0;
|
double draft = Integer.parseInt(draftBits, 2) / 10.0;
|
||||||
|
|
||||||
Log.d(TAG, String.format("AIS Aid-to-Navigation: MMSI=%d, type=%d, name='%s', lat=%.6f, lon=%.6f, L=%.1f, W=%.1f, D=%.1f",
|
Log.d(TAG, String.format("AIS Aid-to-Navigation: MMSI=%d, type=%d, name='%s', lat=%.6f, lon=%.6f, L=%.1f, W=%.1f, D=%.1f",
|
||||||
@@ -2027,25 +2228,59 @@ public class NMEAParser {
|
|||||||
String callSign = decodeAISString(callSignBits);
|
String callSign = decodeAISString(callSignBits);
|
||||||
Log.d(TAG, "Call Sign bits: " + callSignBits + " = '" + callSign + "'");
|
Log.d(TAG, "Call Sign bits: " + callSignBits + " = '" + callSign + "'");
|
||||||
|
|
||||||
// Dimension Reference (4 бита) - бит 132
|
// Dimension Reference (6 бит каждое) - бит 132
|
||||||
String dimRefABits = decodeAISField(payload, 132, 4);
|
// Согласно онлайн декодеру, размеры находятся в других позициях
|
||||||
String dimRefBBits = decodeAISField(payload, 136, 4);
|
// Попробуем позиции, которые соответствуют онлайн декодеру
|
||||||
String dimRefCBits = decodeAISField(payload, 140, 4);
|
String dimRefABits = decodeAISField(payload, 132, 9);
|
||||||
String dimRefDBits = decodeAISField(payload, 144, 4);
|
String dimRefBBits = decodeAISField(payload, 141, 9);
|
||||||
|
String dimRefCBits = decodeAISField(payload, 150, 6);
|
||||||
|
String dimRefDBits = decodeAISField(payload, 156, 6);
|
||||||
|
|
||||||
int dimRefA = Integer.parseInt(dimRefABits, 2);
|
int dimRefA = Integer.parseInt(dimRefABits, 2);
|
||||||
int dimRefB = Integer.parseInt(dimRefBBits, 2);
|
int dimRefB = Integer.parseInt(dimRefBBits, 2);
|
||||||
int dimRefC = Integer.parseInt(dimRefCBits, 2);
|
int dimRefC = Integer.parseInt(dimRefCBits, 2);
|
||||||
int dimRefD = Integer.parseInt(dimRefDBits, 2);
|
int dimRefD = Integer.parseInt(dimRefDBits, 2);
|
||||||
|
|
||||||
// Vessel Dimensions (30 бит) - бит 148
|
Log.d(TAG, "Dimension Reference bits - A: " + dimRefABits + " = " + dimRefA);
|
||||||
String lengthBits = decodeAISField(payload, 148, 10);
|
Log.d(TAG, "Dimension Reference bits - B: " + dimRefBBits + " = " + dimRefB);
|
||||||
String widthBits = decodeAISField(payload, 158, 10);
|
Log.d(TAG, "Dimension Reference bits - C: " + dimRefCBits + " = " + dimRefC);
|
||||||
String draftBits = decodeAISField(payload, 168, 8);
|
Log.d(TAG, "Dimension Reference bits - D: " + dimRefDBits + " = " + dimRefD);
|
||||||
|
|
||||||
double length = Integer.parseInt(lengthBits, 2);
|
// Проверяем, есть ли достаточно битов для размеров
|
||||||
double width = Integer.parseInt(widthBits, 2);
|
int totalBits = payload.length() * 6;
|
||||||
double draft = Integer.parseInt(draftBits, 2) / 10.0;
|
Log.d(TAG, "Static Data Part B - общая длина payload в битах: " + totalBits);
|
||||||
|
|
||||||
|
double length = 0.0;
|
||||||
|
double width = 0.0;
|
||||||
|
double draft = 0.0;
|
||||||
|
|
||||||
|
// Для коротких сообщений типа 24 Part B (168 бит) используем Dimension Reference
|
||||||
|
// В коротких сообщениях размеры кодируются в Dimension Reference полях
|
||||||
|
if (totalBits >= 168) {
|
||||||
|
// В сообщениях типа 24 Part B для Class B судов
|
||||||
|
// размеры кодируются в полях Dimension Reference (биты 132-147)
|
||||||
|
// где каждое поле - 4 бита и представляет размер в метрах
|
||||||
|
// Эти поля уже правильно декодированы выше
|
||||||
|
|
||||||
|
// Размеры судна рассчитываются как:
|
||||||
|
// Длина = Dim.A + Dim.B (от носа до антенны + от антенны до кормы)
|
||||||
|
// Ширина = Dim.C + Dim.D (от левого борта до антенны + от антенны до правого борта)
|
||||||
|
length = dimRefA + dimRefB;
|
||||||
|
width = dimRefC + dimRefD;
|
||||||
|
|
||||||
|
Log.d(TAG, "Static Data Part B - используем Dimension Reference:");
|
||||||
|
Log.d(TAG, " Dim.A (нос-антенна): " + dimRefA + " м");
|
||||||
|
Log.d(TAG, " Dim.B (антенна-корма): " + dimRefB + " м");
|
||||||
|
Log.d(TAG, " Dim.C (левый борт-антенна): " + dimRefC + " м");
|
||||||
|
Log.d(TAG, " Dim.D (антенна-правый борт): " + dimRefD + " м");
|
||||||
|
Log.d(TAG, "Static Data Part B - итоговые размеры: L=" + length + ", W=" + width);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "Static Data Part B - недостаточно битов для размеров: " + totalBits + " < 168");
|
||||||
|
// Используем нулевые размеры
|
||||||
|
length = 0.0;
|
||||||
|
width = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
Log.d(TAG, String.format("AIS Static Data Part B: MMSI=%d, type=%d, vendor='%s', callSign='%s', L=%.1f, W=%.1f, D=%.1f",
|
Log.d(TAG, String.format("AIS Static Data Part B: MMSI=%d, type=%d, vendor='%s', callSign='%s', L=%.1f, W=%.1f, D=%.1f",
|
||||||
mmsi, vesselTypeCode, vendorId, callSign, length, width, draft));
|
mmsi, vesselTypeCode, vendorId, callSign, length, width, draft));
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ public class YandexMarkerWrapper extends MarkerWrapper {
|
|||||||
|
|
||||||
private void createMarker() {
|
private void createMarker() {
|
||||||
try {
|
try {
|
||||||
double lat = isOwnVessel ? vessel.getLatitude() : aisVessel.getLongitude();
|
double lat = isOwnVessel ? vessel.getLatitude() : aisVessel.getLatitude();
|
||||||
double lon = isOwnVessel ? vessel.getLongitude() : aisVessel.getLongitude();
|
double lon = isOwnVessel ? vessel.getLongitude() : aisVessel.getLongitude();
|
||||||
|
|
||||||
// Сначала создаем иконку
|
// Сначала создаем иконку
|
||||||
|
|||||||
@@ -58,11 +58,55 @@ public class LogSender {
|
|||||||
message += " | " + vesselInfo;
|
message += " | " + vesselInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Извлекаем тип судна из vesselInfo и генерируем цвет
|
||||||
|
// Генерируем уникальный цвет для корабля на основе MMSI
|
||||||
|
String vesselColor = generateVesselColor(mmsi);
|
||||||
|
|
||||||
String encodedMessage = encodeForURL(message);
|
String encodedMessage = encodeForURL(message);
|
||||||
String url = BASE_URL + "?ships=" + encodedMessage + "&color=green";
|
String encodedColor = encodeColorForURL(vesselColor);
|
||||||
|
String url = BASE_URL + "?ships=" + encodedMessage + "&color=" + encodedColor;
|
||||||
|
|
||||||
sendGetRequest(url);
|
sendGetRequest(url);
|
||||||
Log.d(TAG, "Ship update лог отправлен: " + message);
|
Log.d(TAG, "Ship update лог отправлен: " + message + " ( " + ", цвет: " + vesselColor + ")");
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Ошибка отправки ship update лога: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Отправляет лог обновления информации о корабле с заданным цветом
|
||||||
|
* @param mmsi MMSI корабля
|
||||||
|
* @param vesselInfo Информация о корабле
|
||||||
|
* @param color Цвет в формате HEX (#RRGGBB) или имя цвета
|
||||||
|
*/
|
||||||
|
public static void logShipUpdate(String mmsi, String vesselInfo, String color) {
|
||||||
|
if (mmsi == null || mmsi.trim().isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
executor.execute(() -> {
|
||||||
|
try {
|
||||||
|
String message = "MMSI: " + mmsi;
|
||||||
|
if (vesselInfo != null && !vesselInfo.trim().isEmpty()) {
|
||||||
|
message += " | " + vesselInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Используем переданный цвет или генерируем на основе типа судна
|
||||||
|
String vesselColor;
|
||||||
|
if (color != null && !color.trim().isEmpty()) {
|
||||||
|
vesselColor = color;
|
||||||
|
} else {
|
||||||
|
// Генерируем уникальный цвет для корабля на основе MMSI
|
||||||
|
vesselColor = generateVesselColor(mmsi);
|
||||||
|
}
|
||||||
|
|
||||||
|
String encodedMessage = encodeForURL(message);
|
||||||
|
String encodedColor = encodeColorForURL(vesselColor);
|
||||||
|
String url = BASE_URL + "?ships=" + encodedMessage + "&color=" + encodedColor;
|
||||||
|
|
||||||
|
sendGetRequest(url);
|
||||||
|
Log.d(TAG, "Ship update лог отправлен: " + message + " (цвет: " + vesselColor + ")");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e(TAG, "Ошибка отправки ship update лога: " + e.getMessage(), e);
|
Log.e(TAG, "Ошибка отправки ship update лога: " + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
@@ -97,6 +141,160 @@ public class LogSender {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Генерирует уникальный цвет для корабля на основе MMSI (устаревший метод)
|
||||||
|
* @param mmsi MMSI корабля
|
||||||
|
* @return HEX цвет в формате #RRGGBB
|
||||||
|
*/
|
||||||
|
private static String generateVesselColor(String mmsi) {
|
||||||
|
try {
|
||||||
|
// Преобразуем MMSI в число для хеширования
|
||||||
|
long mmsiValue = Long.parseLong(mmsi);
|
||||||
|
|
||||||
|
// Используем хеш-функцию для получения равномерного распределения
|
||||||
|
int hash = Long.hashCode(mmsiValue);
|
||||||
|
|
||||||
|
// Извлекаем RGB компоненты из хеша
|
||||||
|
int r = (hash & 0xFF0000) >> 16;
|
||||||
|
int g = (hash & 0x00FF00) >> 8;
|
||||||
|
int b = hash & 0x0000FF;
|
||||||
|
|
||||||
|
// Проверяем, не слишком ли темный цвет (чтобы избежать черного)
|
||||||
|
int brightness = (r + g + b) / 3;
|
||||||
|
if (brightness < 100) {
|
||||||
|
// Если цвет слишком темный, осветляем его
|
||||||
|
r = Math.min(255, r + 120);
|
||||||
|
g = Math.min(255, g + 120);
|
||||||
|
b = Math.min(255, b + 120);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверяем, не слишком ли светлый цвет (чтобы избежать белого)
|
||||||
|
if (brightness > 220) {
|
||||||
|
// Если цвет слишком светлый, затемняем его
|
||||||
|
r = Math.max(0, r - 60);
|
||||||
|
g = Math.max(0, g - 60);
|
||||||
|
b = Math.max(0, b - 60);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Форматируем в HEX
|
||||||
|
String color = String.format("#%02X%02X%02X", r, g, b);
|
||||||
|
|
||||||
|
Log.d(TAG, "Сгенерирован цвет для MMSI " + mmsi + ": " + color + " (RGB: " + r + "," + g + "," + b + ")");
|
||||||
|
|
||||||
|
return color;
|
||||||
|
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
Log.w(TAG, "Не удалось распарсить MMSI как число: " + mmsi + ", используем цвет по умолчанию");
|
||||||
|
return "#00AA00"; // Зеленый по умолчанию
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Ошибка генерации цвета для MMSI " + mmsi + ": " + e.getMessage(), e);
|
||||||
|
return "#00AA00"; // Зеленый по умолчанию
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Определяет тип судна по MMSI
|
||||||
|
* Использует более точную логику на основе стандартных диапазонов MMSI
|
||||||
|
* @param mmsi MMSI судна
|
||||||
|
* @return Тип судна
|
||||||
|
*/
|
||||||
|
private static String getVesselTypeByMMSI(long mmsi) {
|
||||||
|
// Стандартные диапазоны MMSI для разных типов судов
|
||||||
|
if (mmsi >= 100000000 && mmsi <= 199999999) {
|
||||||
|
return "COASTAL"; // Прибрежные суда
|
||||||
|
} else if (mmsi >= 200000000 && mmsi <= 299999999) {
|
||||||
|
return "FISHING"; // Рыболовные суда
|
||||||
|
} else if (mmsi >= 300000000 && mmsi <= 399999999) {
|
||||||
|
return "CARGO"; // Грузовые суда
|
||||||
|
} else if (mmsi >= 400000000 && mmsi <= 499999999) {
|
||||||
|
return "TANKER"; // Танкеры
|
||||||
|
} else if (mmsi >= 500000000 && mmsi <= 599999999) {
|
||||||
|
return "PASSENGER"; // Пассажирские суда
|
||||||
|
} else if (mmsi >= 600000000 && mmsi <= 699999999) {
|
||||||
|
return "MILITARY"; // Военные корабли
|
||||||
|
} else if (mmsi >= 700000000 && mmsi <= 799999999) {
|
||||||
|
return "PILOT"; // Лоцманские суда
|
||||||
|
} else if (mmsi >= 800000000 && mmsi <= 899999999) {
|
||||||
|
return "PILOT"; // Лоцманские суда (дополнительный диапазон)
|
||||||
|
} else if (mmsi >= 900000000 && mmsi <= 999999999) {
|
||||||
|
return "MILITARY"; // Военные корабли (дополнительный диапазон)
|
||||||
|
} else if (mmsi >= 1000000000 && mmsi <= 1099999999) {
|
||||||
|
return "SAR"; // Спасательные суда
|
||||||
|
} else if (mmsi >= 1100000000 && mmsi <= 1199999999) {
|
||||||
|
return "TUG"; // Буксиры
|
||||||
|
} else if (mmsi >= 1200000000 && mmsi <= 1299999999) {
|
||||||
|
return "PORT_TENDER"; // Портовые суда
|
||||||
|
} else if (mmsi >= 1300000000 && mmsi <= 1399999999) {
|
||||||
|
return "ANTI_POLLUTION"; // Антизагрязнительные суда
|
||||||
|
} else if (mmsi >= 1400000000 && mmsi <= 1499999999) {
|
||||||
|
return "LAW_ENFORCEMENT"; // Правоохранительные суда
|
||||||
|
} else if (mmsi >= 1500000000 && mmsi <= 1599999999) {
|
||||||
|
return "MEDICAL"; // Медицинские суда
|
||||||
|
} else if (mmsi >= 1600000000 && mmsi <= 1699999999) {
|
||||||
|
return "SPECIAL_CRAFT"; // Специальные суда
|
||||||
|
} else if (mmsi >= 1700000000 && mmsi <= 1799999999) {
|
||||||
|
return "PASSENGER"; // Пассажирские суда (дополнительный диапазон)
|
||||||
|
} else if (mmsi >= 1800000000 && mmsi <= 1899999999) {
|
||||||
|
return "CARGO"; // Грузовые суда (дополнительный диапазон)
|
||||||
|
} else if (mmsi >= 1900000000 && mmsi <= 1999999999) {
|
||||||
|
return "TANKER"; // Танкеры (дополнительный диапазон)
|
||||||
|
} else if (mmsi >= 2000000000 && mmsi <= 2099999999) {
|
||||||
|
return "OTHER"; // Другие типы судов
|
||||||
|
} else if (mmsi >= 2100000000L && mmsi <= 2199999999L) {
|
||||||
|
return "OTHER"; // Другие типы судов (дополнительный диапазон)
|
||||||
|
} else if (mmsi >= 2200000000L && mmsi <= 2299999999L) {
|
||||||
|
return "OTHER"; // Другие типы судов (дополнительный диапазон)
|
||||||
|
} else if (mmsi >= 2300000000L && mmsi <= 2399999999L) {
|
||||||
|
return "OTHER"; // Другие типы судов (дополнительный диапазон)
|
||||||
|
} else if (mmsi >= 2400000000L && mmsi <= 2499999999L) {
|
||||||
|
return "OTHER"; // Другие типы судов (дополнительный диапазон)
|
||||||
|
} else if (mmsi >= 2500000000L && mmsi <= 2599999999L) {
|
||||||
|
return "OTHER"; // Другие типы судов (дополнительный диапазон)
|
||||||
|
} else if (mmsi >= 2600000000L && mmsi <= 2699999999L) {
|
||||||
|
return "OTHER"; // Другие типы судов (дополнительный диапазон)
|
||||||
|
} else if (mmsi >= 2700000000L && mmsi <= 2799999999L) {
|
||||||
|
return "OTHER"; // Другие типы судов (дополнительный диапазон)
|
||||||
|
} else if (mmsi >= 2800000000L && mmsi <= 2899999999L) {
|
||||||
|
return "OTHER"; // Другие типы судов (дополнительный диапазон)
|
||||||
|
} else if (mmsi >= 2900000000L && mmsi <= 2999999999L) {
|
||||||
|
return "OTHER"; // Другие типы судов (дополнительный диапазон)
|
||||||
|
} else {
|
||||||
|
return "UNKNOWN"; // Неизвестный тип
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Кодирует цвет для безопасного использования в URL
|
||||||
|
* Специально обрабатывает HEX цвета, заменяя # на %23
|
||||||
|
* @param color Цвет в формате HEX (#RRGGBB) или имя цвета
|
||||||
|
* @return Закодированный цвет
|
||||||
|
*/
|
||||||
|
private static String encodeColorForURL(String color) {
|
||||||
|
if (color == null || color.trim().isEmpty()) {
|
||||||
|
return "green"; // Цвет по умолчанию
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Если цвет начинается с #, заменяем его на %23
|
||||||
|
if (color.startsWith("#")) {
|
||||||
|
String encoded = "%23" + color.substring(1);
|
||||||
|
Log.d(TAG, "Закодирован HEX цвет: " + color + " -> " + encoded);
|
||||||
|
return encoded;
|
||||||
|
} else {
|
||||||
|
// Для именованных цветов используем стандартное кодирование
|
||||||
|
String encoded = URLEncoder.encode(color, StandardCharsets.UTF_8.toString());
|
||||||
|
Log.d(TAG, "Закодирован именованный цвет: " + color + " -> " + encoded);
|
||||||
|
return encoded;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Ошибка кодирования цвета: " + e.getMessage(), e);
|
||||||
|
return "green"; // Цвет по умолчанию
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Кодирует строку для безопасного использования в URL
|
* Кодирует строку для безопасного использования в URL
|
||||||
* Дополнительно экранирует символы, которые могут вызывать проблемы
|
* Дополнительно экранирует символы, которые могут вызывать проблемы
|
||||||
@@ -109,12 +307,13 @@ public class LogSender {
|
|||||||
String encoded = URLEncoder.encode(message, StandardCharsets.UTF_8.toString());
|
String encoded = URLEncoder.encode(message, StandardCharsets.UTF_8.toString());
|
||||||
|
|
||||||
// Дополнительно экранируем символы, которые могут вызывать проблемы
|
// Дополнительно экранируем символы, которые могут вызывать проблемы
|
||||||
// Заменяем < на %3C, > на %3E, & на %26, " на %22, ' на %27
|
// Заменяем < на %3C, > на %3E, & на %26, " на %22, ' на %27, # на %23
|
||||||
encoded = encoded.replace("<", "%3C")
|
encoded = encoded.replace("<", "%3C")
|
||||||
.replace(">", "%3E")
|
.replace(">", "%3E")
|
||||||
.replace("&", "%26")
|
.replace("&", "%26")
|
||||||
.replace("\"", "%22")
|
.replace("\"", "%22")
|
||||||
.replace("'", "%27");
|
.replace("'", "%27")
|
||||||
|
.replace("#", "%23");
|
||||||
|
|
||||||
// Логируем для отладки
|
// Логируем для отладки
|
||||||
Log.d(TAG, "Исходное сообщение: " + message);
|
Log.d(TAG, "Исходное сообщение: " + message);
|
||||||
@@ -129,6 +328,7 @@ public class LogSender {
|
|||||||
.replace("&", "%26")
|
.replace("&", "%26")
|
||||||
.replace("\"", "%22")
|
.replace("\"", "%22")
|
||||||
.replace("'", "%27")
|
.replace("'", "%27")
|
||||||
|
.replace("#", "%23")
|
||||||
.replace(" ", "%20");
|
.replace(" ", "%20");
|
||||||
Log.d(TAG, "Fallback кодирование: " + fallback);
|
Log.d(TAG, "Fallback кодирование: " + fallback);
|
||||||
return fallback;
|
return fallback;
|
||||||
|
|||||||
Reference in New Issue
Block a user