fix2
This commit is contained in:
106
newpower2.py
106
newpower2.py
@@ -418,49 +418,99 @@ class GwtRpcProvider(BaseProvider):
|
||||
return []
|
||||
|
||||
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 = []
|
||||
if not self.transformer: return []
|
||||
processed = set()
|
||||
stride = 2
|
||||
try:
|
||||
# 1. Separate Stream and String Table
|
||||
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]
|
||||
val2 = data_list[i+stride]
|
||||
stream_raw = [item for item in data_list if not isinstance(item, list)]
|
||||
|
||||
if (isinstance(val1, (int, float)) and isinstance(val2, (int, float)) and
|
||||
abs(val1) > 100000 and abs(val2) > 100000):
|
||||
# 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/"
|
||||
|
||||
outage_sig_full = next((s for s in string_table if OUTAGE_SIG_KEYWORD in s), None)
|
||||
|
||||
if not outage_sig_full:
|
||||
logger.error(f"Outage type signature not found for {self.name}.")
|
||||
return []
|
||||
|
||||
# Get the 1-based index of the found signature
|
||||
outage_type_id = string_table.index(outage_sig_full) + 1
|
||||
|
||||
# 4. Decode Logic
|
||||
i = 0
|
||||
stream_len = len(stream)
|
||||
while i < stream_len:
|
||||
if stream[i] == outage_type_id:
|
||||
try:
|
||||
p = i + 1
|
||||
|
||||
# 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
|
||||
|
||||
coord_x = stream[p] if p < stream_len else 0; p += 1
|
||||
coord_y = stream[p] if p < stream_len else 0; p += 1
|
||||
|
||||
# Process coordinates
|
||||
lat, lon = None, None
|
||||
if self.transformer and coord_x and coord_y:
|
||||
try:
|
||||
res_lon, res_lat = self.transformer.transform(val2, val1)
|
||||
if self._is_valid(res_lat, res_lon): lat, lon = res_lat, res_lon
|
||||
except: pass
|
||||
if not lat:
|
||||
try:
|
||||
res_lon, res_lat = self.transformer.transform(val1, val2)
|
||||
if self._is_valid(res_lat, res_lon): lat, lon = res_lat, res_lon
|
||||
lon, lat = self.transformer.transform(coord_x, coord_y)
|
||||
if not self._is_valid(lat, lon): lat, lon = None, None
|
||||
except: pass
|
||||
|
||||
if lat and lon:
|
||||
k = f"{lat:.4f},{lon:.4f}"
|
||||
if k in processed: continue
|
||||
processed.add(k)
|
||||
# Process timestamps (GWT sends 64-bit longs as two 32-bit integers)
|
||||
start_ms = (start_high << 32) | start_low
|
||||
etr_ms = (etr_high << 32) | etr_low
|
||||
start_time = datetime.fromtimestamp(start_ms / 1000, tz=timezone.utc) if start_ms > 0 else None
|
||||
etr_time = datetime.fromtimestamp(etr_ms / 1000, tz=timezone.utc) if etr_ms > 0 else None
|
||||
|
||||
oid = str(abs(hash(k)))
|
||||
for o in range(1, 15):
|
||||
idx = i - o
|
||||
if idx >= 0 and isinstance(data_list[idx], str):
|
||||
s = data_list[idx]
|
||||
if len(s) < 20 and "java" not in s and "http" not in s: oid = s; break
|
||||
# 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': oid, 'utility': self.name,
|
||||
'lat': lat, 'lon': lon, 'pointgeom': k, 'areageom': None,
|
||||
'start': datetime.now(timezone.utc), 'etr': None, 'outagen': 1,
|
||||
'cause': "Unknown", 'crew_status': "Unknown", 'active': True,
|
||||
'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):
|
||||
if not self.state_filter: return True
|
||||
|
||||
Reference in New Issue
Block a user