Files
2025-04-21 21:38:58 +03:00

238 lines
9.6 KiB
Python

import json
import os
from server import PromptServer
from aiohttp import web
# Global dictionary to store current weights for each node
CURRENT_WEIGHTS = {}
# Path for persistent storage
STORAGE_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "storage")
WEIGHTS_FILE = os.path.join(STORAGE_DIR, "radar_weights_weights.json")
# Ensure storage directory exists
os.makedirs(STORAGE_DIR, exist_ok=True)
# Load saved weights if they exist
def load_saved_weights():
global CURRENT_WEIGHTS
if os.path.exists(WEIGHTS_FILE):
try:
with open(WEIGHTS_FILE, 'r') as f:
CURRENT_WEIGHTS = json.load(f)
print(f"[Radar Weights] Loaded {len(CURRENT_WEIGHTS)} saved weights from {WEIGHTS_FILE}")
except Exception as e:
print(f"[Radar Weights] Error loading saved weights: {e}")
CURRENT_WEIGHTS = {}
# Save weights to file
def save_weights():
try:
with open(WEIGHTS_FILE, 'w') as f:
json.dump(CURRENT_WEIGHTS, f)
print(f"[Radar Weights] Saved {len(CURRENT_WEIGHTS)} weights to {WEIGHTS_FILE}")
except Exception as e:
print(f"[Radar Weights] Error saving weights: {e}")
# Load saved weights on module import
load_saved_weights()
class RadarWeightsNode:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"axes_count": ("INT", {
"default": 5,
"min": 3,
"max": 10,
"step": 1,
"display": "number"
})
},
"hidden": {
"_weights_sync": ("STRING", {"default": ""}),
"unique_id": "UNIQUE_ID"
}
}
# Define dynamic RETURN_TYPES and RETURN_NAMES based on max possible axes
RETURN_TYPES = tuple(["FLOAT"] * 10) # Support up to 10 axes
RETURN_NAMES = tuple([str(i+1) for i in range(10)]) # Name them "1" through "10"
FUNCTION = "generate"
CATEGORY = "Spider/Widgets"
def generate(self, axes_count, **kwargs):
# Get node ID from kwargs
node_id = kwargs.get('unique_id')
_weights_sync = kwargs.get('_weights_sync', "") # Keep for logging/debugging
print(f"[Radar Weights] Generate called for node {node_id}")
# print(f"[Spider Widget] Full kwargs received: {kwargs}") # Optional: uncomment for deep debug
print(f"[Radar Weights] Current weights in global state: {CURRENT_WEIGHTS}")
# print(f"[Spider Widget] Sync widget value received: {_weights_sync}") # Optional: uncomment for deep debug
weights_string = None
node_id_str = str(node_id) if node_id else None
# Primarily rely on the global state updated by the API
if node_id_str and node_id_str in CURRENT_WEIGHTS:
weights_string = CURRENT_WEIGHTS[node_id_str]
print(f"[Radar Weights] Using weights from global state for node {node_id_str}: {weights_string}")
else:
# If no weights found for this node, use defaults
print(f"[Radar Weights] No weights found for node {node_id_str}. Using default weights.")
# Create default weights based on axes_count
default_weights = [1.0] * axes_count
weights_string = ",".join(["{:.2f}".format(w) for w in default_weights])
# Store these default weights in the global state
if node_id_str:
CURRENT_WEIGHTS[node_id_str] = weights_string
print(f"[Radar Weights] Stored default weights for node {node_id_str}: {weights_string}")
# Save weights to persistent storage
save_weights()
# Parse weights from string
try:
current_weights = [float(w.strip()) for w in weights_string.split(',') if w.strip()]
# Ensure the list matches axes_count (pad or truncate)
if len(current_weights) != axes_count:
print(f"[Radar Weights] Adjusting weights length ({len(current_weights)}) to match axes_count ({axes_count}) for node {node_id_str}.")
if len(current_weights) < axes_count:
current_weights.extend([1.0] * (axes_count - len(current_weights)))
else:
current_weights = current_weights[:axes_count]
except Exception as e:
print(f"[Error] RadarWeightsNode: Failed to parse weights for node {node_id_str}. Error: {e}. Using defaults.")
current_weights = [1.0] * axes_count
# Format weights for potential storage update (though API handles primary update)
# We format for storage/API, but round for output
formatted_weights = ["{:.2f}".format(w) for w in current_weights]
updated_weights_string = ",".join(formatted_weights)
# Round the weights for output to two decimal places
rounded_weights = [round(w, 2) for w in current_weights]
print(f"[Radar Weights] Returning rounded weights for node {node_id_str}: {rounded_weights}")
# Pad with zeros to match max number of outputs (10)
padded_weights = rounded_weights + [0.0] * (10 - len(rounded_weights))
return tuple(padded_weights)
@classmethod
def IS_CHANGED(cls, **kwargs):
# sync_val = kwargs.get('_weights_sync', '') # No longer rely on this potentially stale value
unique_id = kwargs.get('unique_id', '')
axes_count = kwargs.get('axes_count', 5)
# The hash should depend only on the user-controlled input (axes_count)
# and the actual state stored in the backend (CURRENT_WEIGHTS retrieved via unique_id).
node_id_str = str(unique_id) if unique_id else "-1"
current_weights_str = CURRENT_WEIGHTS.get(node_id_str, "") # Get the ground truth from global state
print(f"[Radar Weights] IS_CHANGED called for node {node_id_str}. Axes: {axes_count}, Current Backend Weights: {current_weights_str}")
# Create a hash based *only* on reliable data
return f"{unique_id}-{axes_count}-{current_weights_str}"
# API endpoint to handle weight updates
@PromptServer.instance.routes.post("/radar_weights/update_weights")
async def update_weights_route(request):
try:
data = await request.json()
node_id = data.get('node_id')
weights = data.get('weights')
if not node_id or not weights:
return web.json_response({"error": "Missing node_id or weights"}, status=400)
print(f"[Radar Weights] API received update for node {node_id}: {weights}")
CURRENT_WEIGHTS[str(node_id)] = weights
print(f"[Radar Weights] Updated weights for node {node_id}: {weights}")
print(f"[Radar Weights] Current global state: {CURRENT_WEIGHTS}")
# Save weights to persistent storage
save_weights()
return web.json_response({
"status": "success",
"node_id": node_id,
"weights": weights
})
except Exception as e:
print(f"[Radar Weights] Error in update_weights_route: {str(e)}")
return web.json_response({"error": str(e)}, status=500)
# Add a route to get the current weights for a node
@PromptServer.instance.routes.get("/radar_weights/get_weights/{node_id}")
async def get_weights_route(request):
try:
node_id = request.match_info.get('node_id')
if not node_id:
return web.json_response({"error": "Missing node_id"}, status=400)
weights = CURRENT_WEIGHTS.get(str(node_id), "")
print(f"[Radar Weights] Retrieved weights for node {node_id}: {weights}")
return web.json_response({
"status": "success",
"node_id": node_id,
"weights": weights
})
except Exception as e:
print(f"[Radar Weights] Error in get_weights_route: {str(e)}")
return web.json_response({"error": str(e)}, status=500)
# Add a route to get the most recent weights
@PromptServer.instance.routes.get("/radar_weights/get_latest_weights")
async def get_latest_weights_route(request):
try:
if not CURRENT_WEIGHTS:
return web.json_response({
"status": "success",
"weights": ""
})
# Get the last key-value pair from the dictionary
last_node_id, last_weights = list(CURRENT_WEIGHTS.items())[-1]
print(f"[Radar Weights] Retrieved latest weights from node {last_node_id}: {last_weights}")
return web.json_response({
"status": "success",
"node_id": last_node_id,
"weights": last_weights
})
except Exception as e:
print(f"[Radar Weights] Error in get_latest_weights_route: {str(e)}")
return web.json_response({"error": str(e)}, status=500)
# Add a route to get all saved weights
@PromptServer.instance.routes.get("/radar_weights/get_all_weights")
async def get_all_weights_route(request):
try:
return web.json_response({
"status": "success",
"weights": CURRENT_WEIGHTS
})
except Exception as e:
print(f"[Radar Weights] Error in get_all_weights_route: {str(e)}")
return web.json_response({"error": str(e)}, status=500)
# It's good practice to update the NODE_CLASS_MAPPINGS if this file defines it
# Assuming it might be in __init__.py or similar, but adding here for completeness if stand-alone
NODE_CLASS_MAPPINGS = {
"RadarWeightsNode": RadarWeightsNode
}
# Optionally, update display name mapping
NODE_DISPLAY_NAME_MAPPINGS = {
"RadarWeightsNode": "Radar Weights Node"
}
# If web directory registration exists, update it (less common in single file nodes)
# WEB_DIRECTORY = "./js"