This commit is contained in:
2025-12-07 12:28:10 +00:00
parent 493fe2e4d8
commit 9e1c17b2f6

View File

@@ -418,49 +418,99 @@ class GwtRpcProvider(BaseProvider):
return [] return []
def _extract_outages(self, data_list): def _extract_outages(self, data_list):
"""
Decodes a GWT-RPC payload to extract detailed point outage information.
This is a more robust implementation that replaces the previous heuristic-based coordinate search.
"""
results = [] results = []
if not self.transformer: return [] try:
processed = set() # 1. Separate Stream and String Table
stride = 2 string_table = next((item for item in data_list if isinstance(item, list)), None)
if not string_table: return []
for i in range(len(data_list) - stride):
val1 = data_list[i] stream_raw = [item for item in data_list if not isinstance(item, list)]
val2 = data_list[i+stride]
# 2. Normalize the Stream
stream = [int(token) for token in stream_raw if isinstance(token, (int, float))]
# 3. Define Signatures and Helper
# The signature can vary (e.g., cc.nisc... vs coop.nisc...).
# We search for a common, unique substring.
OUTAGE_SIG_KEYWORD = ".pojo.Outage/"
if (isinstance(val1, (int, float)) and isinstance(val2, (int, float)) and outage_sig_full = next((s for s in string_table if OUTAGE_SIG_KEYWORD in s), None)
abs(val1) > 100000 and abs(val2) > 100000):
if not outage_sig_full:
lat, lon = None, None logger.error(f"Outage type signature not found for {self.name}.")
try: return []
res_lon, res_lat = self.transformer.transform(val2, val1)
if self._is_valid(res_lat, res_lon): lat, lon = res_lat, res_lon # Get the 1-based index of the found signature
except: pass outage_type_id = string_table.index(outage_sig_full) + 1
if not lat:
# 4. Decode Logic
i = 0
stream_len = len(stream)
while i < stream_len:
if stream[i] == outage_type_id:
try: try:
res_lon, res_lat = self.transformer.transform(val1, val2) p = i + 1
if self._is_valid(res_lat, res_lon): lat, lon = res_lat, res_lon
except: pass # Field extraction based on observed GWT stream structure
outagen = stream[p] if p < stream_len else 0; p += 1
crew_status_idx = stream[p] if p < stream_len else 0; p += 1
cause_idx = stream[p] if p < stream_len else 0; p += 1
etr_high = stream[p] if p < stream_len else 0; p += 1
etr_low = stream[p] if p < stream_len else 0; p += 1
p += 1 # Skip long type ID
start_high = stream[p] if p < stream_len else 0; p += 1
start_low = stream[p] if p < stream_len else 0; p += 1
p += 1 # Skip long type ID
if lat and lon: coord_x = stream[p] if p < stream_len else 0; p += 1
k = f"{lat:.4f},{lon:.4f}" coord_y = stream[p] if p < stream_len else 0; p += 1
if k in processed: continue
processed.add(k) # Process coordinates
lat, lon = None, None
oid = str(abs(hash(k))) if self.transformer and coord_x and coord_y:
for o in range(1, 15): try:
idx = i - o lon, lat = self.transformer.transform(coord_x, coord_y)
if idx >= 0 and isinstance(data_list[idx], str): if not self._is_valid(lat, lon): lat, lon = None, None
s = data_list[idx] except: pass
if len(s) < 20 and "java" not in s and "http" not in s: oid = s; break
results.append({ if lat and lon:
'incidentid': oid, 'utility': self.name, # Process timestamps (GWT sends 64-bit longs as two 32-bit integers)
'lat': lat, 'lon': lon, 'pointgeom': k, 'areageom': None, start_ms = (start_high << 32) | start_low
'start': datetime.now(timezone.utc), 'etr': None, 'outagen': 1, etr_ms = (etr_high << 32) | etr_low
'cause': "Unknown", 'crew_status': "Unknown", 'active': True, start_time = datetime.fromtimestamp(start_ms / 1000, tz=timezone.utc) if start_ms > 0 else None
'last_change': datetime.now(timezone.utc) etr_time = datetime.fromtimestamp(etr_ms / 1000, tz=timezone.utc) if etr_ms > 0 else None
})
return results # Resolve strings from table
cause = string_table[cause_idx - 1].strip() if 0 < cause_idx <= len(string_table) else "Unknown"
crew_status = string_table[crew_status_idx - 1].strip() if 0 < crew_status_idx <= len(string_table) else "Unknown"
results.append({
'incidentid': f"{self.name}-{lat:.5f}-{lon:.5f}",
'utility': self.name,
'lat': lat, 'lon': lon,
'pointgeom': f"{lat:.5f},{lon:.5f}",
'areageom': None,
'start': start_time,
'etr': etr_time,
'outagen': outagen,
'cause': cause,
'crew_status': crew_status,
'active': True,
'last_change': datetime.now(timezone.utc)
})
except (IndexError, TypeError):
pass # Move to the next potential object
i += 1
return results
except Exception as e:
logger.error(f"Could not parse point outages for {self.name}: {e}")
return []
def _is_valid(self, lat, lon): def _is_valid(self, lat, lon):
if not self.state_filter: return True if not self.state_filter: return True