fix2
This commit is contained in:
120
newpower2.py
120
newpower2.py
@@ -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):
|
stream_raw = [item for item in data_list if not isinstance(item, list)]
|
||||||
val1 = data_list[i]
|
|
||||||
val2 = data_list[i+stride]
|
|
||||||
|
|
||||||
if (isinstance(val1, (int, float)) and isinstance(val2, (int, float)) and
|
# 2. Normalize the Stream
|
||||||
abs(val1) > 100000 and abs(val2) > 100000):
|
stream = [int(token) for token in stream_raw if isinstance(token, (int, float))]
|
||||||
|
|
||||||
lat, lon = None, None
|
# 3. Define Signatures and Helper
|
||||||
try:
|
# The signature can vary (e.g., cc.nisc... vs coop.nisc...).
|
||||||
res_lon, res_lat = self.transformer.transform(val2, val1)
|
# We search for a common, unique substring.
|
||||||
if self._is_valid(res_lat, res_lon): lat, lon = res_lat, res_lon
|
OUTAGE_SIG_KEYWORD = ".pojo.Outage/"
|
||||||
except: pass
|
|
||||||
if not lat:
|
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:
|
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
|
|
||||||
|
|
||||||
if lat and lon:
|
# Field extraction based on observed GWT stream structure
|
||||||
k = f"{lat:.4f},{lon:.4f}"
|
outagen = stream[p] if p < stream_len else 0; p += 1
|
||||||
if k in processed: continue
|
crew_status_idx = stream[p] if p < stream_len else 0; p += 1
|
||||||
processed.add(k)
|
cause_idx = stream[p] if p < stream_len else 0; p += 1
|
||||||
|
|
||||||
oid = str(abs(hash(k)))
|
etr_high = stream[p] if p < stream_len else 0; p += 1
|
||||||
for o in range(1, 15):
|
etr_low = stream[p] if p < stream_len else 0; p += 1
|
||||||
idx = i - o
|
p += 1 # Skip long type ID
|
||||||
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
|
|
||||||
|
|
||||||
results.append({
|
start_high = stream[p] if p < stream_len else 0; p += 1
|
||||||
'incidentid': oid, 'utility': self.name,
|
start_low = stream[p] if p < stream_len else 0; p += 1
|
||||||
'lat': lat, 'lon': lon, 'pointgeom': k, 'areageom': None,
|
p += 1 # Skip long type ID
|
||||||
'start': datetime.now(timezone.utc), 'etr': None, 'outagen': 1,
|
|
||||||
'cause': "Unknown", 'crew_status': "Unknown", 'active': True,
|
coord_x = stream[p] if p < stream_len else 0; p += 1
|
||||||
'last_change': datetime.now(timezone.utc)
|
coord_y = stream[p] if p < stream_len else 0; p += 1
|
||||||
})
|
|
||||||
return results
|
# Process coordinates
|
||||||
|
lat, lon = None, None
|
||||||
|
if self.transformer and coord_x and coord_y:
|
||||||
|
try:
|
||||||
|
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:
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|||||||
Reference in New Issue
Block a user