///|
pub fn validate_graph(graph : FactoryGraph) -> Array[@types.ValidationIssue] {
let issues = []
// Check for dangling edges
let all_edges = graph.get_all_edges()
for edge in all_edges {
let src = edge.source_id()
let dst = edge.target_id()
if !graph.has_node(src) {
issues.push(
@types.ValidationIssue::new(
"RULE-DANGLING-SRC",
@types.ValidationSeverity::Critical,
edge.id(),
"Edge references non-existent source node: " + src,
suggested_fix="Create node " + src + " or remove edge " + edge.id(),
),
)
}
if !graph.has_node(dst) {
issues.push(
@types.ValidationIssue::new(
"RULE-DANGLING-DST",
@types.ValidationSeverity::Critical,
edge.id(),
"Edge references non-existent target node: " + dst,
suggested_fix="Create node " + dst + " or remove edge " + edge.id(),
),
)
}
}
// Check for isolated nodes (no in or out edges at all)
let all_nodes = graph.get_all_nodes()
for node in all_nodes {
let id = node.id()
let in_e = graph.get_in_edges(id)
let out_e = graph.get_out_edges(id)
if in_e.length() == 0 && out_e.length() == 0 {
issues.push(
@types.ValidationIssue::new(
"RULE-ISOLATED-NODE",
@types.ValidationSeverity::Warning,
id,
"Node " +
id +
" is completely isolated with 0 incoming or outgoing edges",
suggested_fix="Connect node " +
id +
" to power, control, or material flows",
),
)
}
}
// Check for machines/workstations without power connection
for node in all_nodes {
let nt = node.node_type()
if nt == @types.NodeType::Workstation ||
nt == @types.NodeType::Machine ||
nt == @types.NodeType::RobotArm {
let pwr_in = graph.get_layer_in_edges(
@types.FlowType::EnergyFlow,
node.id(),
)
if pwr_in.length() == 0 {
issues.push(
@types.ValidationIssue::new(
"RULE-UNPOWERED-ASSET",
@types.ValidationSeverity::Error,
node.id(),
"Operational asset " +
node.id() +
" has no incoming EnergyFlow connection",
suggested_fix="Connect a PowerSource to " + node.id(),
),
)
}
}
}
// Check for PLC controllers with zero control connections
for node in all_nodes {
if node.node_type() == @types.NodeType::PLCController {
let ctl_out = graph.get_layer_out_edges(
@types.FlowType::ControlFlow,
node.id(),
)
if ctl_out.length() == 0 {
issues.push(
@types.ValidationIssue::new(
"RULE-PLC-DISCONNECTED",
@types.ValidationSeverity::Warning,
node.id(),
"PLC Controller " +
node.id() +
" has no outgoing ControlFlow connections",
suggested_fix="Connect PLC to field workstations or actuators",
),
)
}
}
}
issues
}