src/core/SubModel.c
McxStatus OrderedNodesDecoupleConnections(OrderedNodes * orderedNodes, ObjectContainer * comps) {
    size_t i; size_t k;

    size_t numNodes = 0;
    size_t numGroups = orderedNodes->size;
    NodeGroup * group = NULL;

    for (i = 0; i < numGroups; i++) {
        group = orderedNodes->groups[i];
        numNodes += group->nodes.size;
    }

    if (comps->Size(comps) != numNodes) {
        mcx_log(LOG_ERROR, "Model: Calculated %d nodes, expected %d", numNodes, comps->Size(comps));
        return RETURN_ERROR;
    }

    // get edges to decouple
    for (i = 0; i < numGroups; i++) {
        group = orderedNodes->groups[i];
        if (group->isLoop) {
            size_t decoupleFrom = SIZE_T_ERROR, decoupleTo = SIZE_T_ERROR;
            int decouplePriority = -1;
            size_t ii = 0, jj = 0;

            for (ii = 0; ii < group->nodes.size && decouplePriority < INT_MAX; ii++) {
                for (jj = 0; jj < group->nodes.size && decouplePriority < INT_MAX; jj++) {
                    int localDecouplePriority = -1;
                    ObjectContainer * connections = NULL;
                    Component * fromComp = (Component *) comps->At(comps, group->nodes.values[ii]);
                    Component * toComp = (Component *) comps->At(comps, group->nodes.values[jj]);

                    // consistency check
                    if (fromComp->GetID(fromComp) != group->nodes.values[ii] || toComp->GetID(toComp) != group->nodes.values[jj]) {
                        mcx_log(LOG_ERROR, "Model: Elements need to be ordered by ID");
                        return RETURN_ERROR;
                    }

                    connections = fromComp->GetConnections(fromComp, toComp);
                    if (!connections) {
                        mcx_log(LOG_ERROR, "Model: Could not get connections from %s to %s", fromComp->GetName(fromComp), toComp->GetName(toComp));
                        return RETURN_ERROR;
                    } else if (0 == connections->Size(connections)) {
                        mcx_log(LOG_DEBUG, "no connection %s -> %s", fromComp->GetName(fromComp), toComp->GetName(toComp));
                    }

                    // check if all connections are decoupleable and get priority
                    for (k = 0; k < connections->Size(connections); k++) {
                        Connection * conn = (Connection *) connections->At(connections, k);
                        ConnectionInfo    * info = conn->GetInfo(conn);

                        if (info->IsDecoupled(info)) {
                            continue;
                        }

                        if (fromComp->GetSequenceNumber(fromComp) > toComp->GetSequenceNumber(toComp)) {
                            localDecouplePriority = INT_MAX; // ordering by components takes priority
                            break;
                        } else if (info->GetDecoupleType(info) == DECOUPLE_IFNEEDED) {
                            if (info->GetDecouplePriority(info) > localDecouplePriority) {
                                localDecouplePriority = info->GetDecouplePriority(info);
                            }
                        } else if (info->GetDecoupleType(info) == DECOUPLE_NEVER) {
                            // if a connection in this bundle set to never decouple, discard this bundle
                            localDecouplePriority = -1;
                            break;
                        }
                    }

                    if (localDecouplePriority > decouplePriority) {
                        decoupleFrom = ii; decoupleTo = jj;
                        decouplePriority = localDecouplePriority;
                    } else {
                        // not highest priority
                    }

                    object_destroy(connections);
                }
            }

            // decouple connection with highest priority
            if (decoupleFrom != SIZE_T_ERROR && decoupleTo != SIZE_T_ERROR) {
                ObjectContainer * connections = NULL;
                Component * fromComp = (Component *) comps->At(comps, group->nodes.values[decoupleFrom]);
                Component * toComp = (Component *) comps->At(comps, group->nodes.values[decoupleTo]);

                // consistency check
                if (fromComp->GetID(fromComp) != group->nodes.values[decoupleFrom] || toComp->GetID(toComp) != group->nodes.values[decoupleTo]) {
                    mcx_log(LOG_ERROR, "Model: Elements need to be ordered by ID");
                    return RETURN_ERROR;
                }

                connections = fromComp->GetConnections(fromComp, toComp);
                if (!connections) {
                    mcx_log(LOG_ERROR, "Model: Could not get connections from %s to %s", fromComp->GetName(fromComp), toComp->GetName(toComp));
                    return RETURN_ERROR;
                }
                if (0 == connections->Size(connections)) {
                    mcx_log(LOG_ERROR, "Model: No connection from %s to %s", fromComp->GetName(fromComp), toComp->GetName(toComp));
                    return RETURN_ERROR;
                }

                // set all connections decoupled
                for (k = 0; k < connections->Size(connections); k++) {
                    Connection * conn = (Connection *) connections->At(connections, k);
                    ConnectionInfo * info = conn->GetInfo(conn);

                    char * connStr = info->ConnectionString(info);
                    mcx_log(LOG_INFO, "Decoupling connection %s", connStr);
                    mcx_free(connStr);

                    info->SetDecoupled(info);
                }

                object_destroy(connections);
            } else {
                mcx_log(LOG_ERROR, "Model: No connection can be decoupled");
                // in this group, no bundle can be decoupled
                return RETURN_ERROR;
            }
        }
    }
    return RETURN_OK;
}
// only need to consider DECOUPLE_IFNEEDED since DECOUPLE_ALWAYS is handled at connection->Setup()
only need to consider DECOUPLE_IFNEEDED since DECOUPLE_ALWAYS is handled at connection->Setup()
only need to consider DECOUPLE_IFNEEDED since DECOUPLE_ALWAYS is handled at connection - > Setup ()
McxStatus OrderedNodesDecoupleConnections(OrderedNodes * orderedNodes, ObjectContainer * comps) {
    size_t i; size_t k;
    size_t numNodes = 0;
    size_t numGroups = orderedNodes->size;
    NodeGroup * group = NULL;
    for (i = 0; i < numGroups; i++) {
        group = orderedNodes->groups[i];
        numNodes += group->nodes.size;
    }
    if (comps->Size(comps) != numNodes) {
        mcx_log(LOG_ERROR, "Model: Calculated %d nodes, expected %d", numNodes, comps->Size(comps));
        return RETURN_ERROR;
    }
    for (i = 0; i < numGroups; i++) {
        group = orderedNodes->groups[i];
        if (group->isLoop) {
            size_t decoupleFrom = SIZE_T_ERROR, decoupleTo = SIZE_T_ERROR;
            int decouplePriority = -1;
            size_t ii = 0, jj = 0;
            for (ii = 0; ii < group->nodes.size && decouplePriority < INT_MAX; ii++) {
                for (jj = 0; jj < group->nodes.size && decouplePriority < INT_MAX; jj++) {
                    int localDecouplePriority = -1;
                    ObjectContainer * connections = NULL;
                    Component * fromComp = (Component *) comps->At(comps, group->nodes.values[ii]);
                    Component * toComp = (Component *) comps->At(comps, group->nodes.values[jj]);
                    if (fromComp->GetID(fromComp) != group->nodes.values[ii] || toComp->GetID(toComp) != group->nodes.values[jj]) {
                        mcx_log(LOG_ERROR, "Model: Elements need to be ordered by ID");
                        return RETURN_ERROR;
                    }
                    connections = fromComp->GetConnections(fromComp, toComp);
                    if (!connections) {
                        mcx_log(LOG_ERROR, "Model: Could not get connections from %s to %s", fromComp->GetName(fromComp), toComp->GetName(toComp));
                        return RETURN_ERROR;
                    } else if (0 == connections->Size(connections)) {
                        mcx_log(LOG_DEBUG, "no connection %s -> %s", fromComp->GetName(fromComp), toComp->GetName(toComp));
                    }
                    for (k = 0; k < connections->Size(connections); k++) {
                        Connection * conn = (Connection *) connections->At(connections, k);
                        ConnectionInfo    * info = conn->GetInfo(conn);
                        if (info->IsDecoupled(info)) {
                            continue;
                        }
                        if (fromComp->GetSequenceNumber(fromComp) > toComp->GetSequenceNumber(toComp)) {
                            localDecouplePriority = INT_MAX; 
                            break;
                        } else if (info->GetDecoupleType(info) == DECOUPLE_IFNEEDED) {
                            if (info->GetDecouplePriority(info) > localDecouplePriority) {
                                localDecouplePriority = info->GetDecouplePriority(info);
                            }
                        } else if (info->GetDecoupleType(info) == DECOUPLE_NEVER) {
                            localDecouplePriority = -1;
                            break;
                        }
                    }
                    if (localDecouplePriority > decouplePriority) {
                        decoupleFrom = ii; decoupleTo = jj;
                        decouplePriority = localDecouplePriority;
                    } else {
                    }
                    object_destroy(connections);
                }
            }
            if (decoupleFrom != SIZE_T_ERROR && decoupleTo != SIZE_T_ERROR) {
                ObjectContainer * connections = NULL;
                Component * fromComp = (Component *) comps->At(comps, group->nodes.values[decoupleFrom]);
                Component * toComp = (Component *) comps->At(comps, group->nodes.values[decoupleTo]);
                if (fromComp->GetID(fromComp) != group->nodes.values[decoupleFrom] || toComp->GetID(toComp) != group->nodes.values[decoupleTo]) {
                    mcx_log(LOG_ERROR, "Model: Elements need to be ordered by ID");
                    return RETURN_ERROR;
                }
                connections = fromComp->GetConnections(fromComp, toComp);
                if (!connections) {
                    mcx_log(LOG_ERROR, "Model: Could not get connections from %s to %s", fromComp->GetName(fromComp), toComp->GetName(toComp));
                    return RETURN_ERROR;
                }
                if (0 == connections->Size(connections)) {
                    mcx_log(LOG_ERROR, "Model: No connection from %s to %s", fromComp->GetName(fromComp), toComp->GetName(toComp));
                    return RETURN_ERROR;
                }
                for (k = 0; k < connections->Size(connections); k++) {
                    Connection * conn = (Connection *) connections->At(connections, k);
                    ConnectionInfo * info = conn->GetInfo(conn);
                    char * connStr = info->ConnectionString(info);
                    mcx_log(LOG_INFO, "Decoupling connection %s", connStr);
                    mcx_free(connStr);
                    info->SetDecoupled(info);
                }
                object_destroy(connections);
            } else {
                mcx_log(LOG_ERROR, "Model: No connection can be decoupled");
                return RETURN_ERROR;
            }
        }
    }
    return RETURN_OK;
}
McxStatus OrderedNodesDecoupleConnections ( OrderedNodes * orderedNodes , ObjectContainer * comps ) { size_t i ; size_t k ; size_t numNodes = 0 ; size_t numGroups = orderedNodes -> size ; NodeGroup * group = NULL ; for ( i = 0 ; i < numGroups ; i ++ ) { group = orderedNodes -> groups [ i ] ; numNodes += group -> nodes . size ; } if ( comps -> Size ( comps ) != numNodes ) { mcx_log ( LOG_ERROR , " " , numNodes , comps -> Size ( comps ) ) ; return RETURN_ERROR ; } for ( i = 0 ; i < numGroups ; i ++ ) { group = orderedNodes -> groups [ i ] ; if ( group -> isLoop ) { size_t decoupleFrom = SIZE_T_ERROR , decoupleTo = SIZE_T_ERROR ; int decouplePriority = -1 ; size_t ii = 0 , jj = 0 ; for ( ii = 0 ; ii < group -> nodes . size && decouplePriority < INT_MAX ; ii ++ ) { for ( jj = 0 ; jj < group -> nodes . size && decouplePriority < INT_MAX ; jj ++ ) { int localDecouplePriority = -1 ; ObjectContainer * connections = NULL ; Component * fromComp = ( Component * ) comps -> At ( comps , group -> nodes . values [ ii ] ) ; Component * toComp = ( Component * ) comps -> At ( comps , group -> nodes . values [ jj ] ) ; if ( fromComp -> GetID ( fromComp ) != group -> nodes . values [ ii ] || toComp -> GetID ( toComp ) != group -> nodes . values [ jj ] ) { mcx_log ( LOG_ERROR , " " ) ; return RETURN_ERROR ; } connections = fromComp -> GetConnections ( fromComp , toComp ) ; if ( ! connections ) { mcx_log ( LOG_ERROR , " " , fromComp -> GetName ( fromComp ) , toComp -> GetName ( toComp ) ) ; return RETURN_ERROR ; } else if ( 0 == connections -> Size ( connections ) ) { mcx_log ( LOG_DEBUG , " " , fromComp -> GetName ( fromComp ) , toComp -> GetName ( toComp ) ) ; } for ( k = 0 ; k < connections -> Size ( connections ) ; k ++ ) { Connection * conn = ( Connection * ) connections -> At ( connections , k ) ; ConnectionInfo * info = conn -> GetInfo ( conn ) ; if ( info -> IsDecoupled ( info ) ) { continue ; } if ( fromComp -> GetSequenceNumber ( fromComp ) > toComp -> GetSequenceNumber ( toComp ) ) { localDecouplePriority = INT_MAX ; break ; } else if ( info -> GetDecoupleType ( info ) == DECOUPLE_IFNEEDED ) { if ( info -> GetDecouplePriority ( info ) > localDecouplePriority ) { localDecouplePriority = info -> GetDecouplePriority ( info ) ; } } else if ( info -> GetDecoupleType ( info ) == DECOUPLE_NEVER ) { localDecouplePriority = -1 ; break ; } } if ( localDecouplePriority > decouplePriority ) { decoupleFrom = ii ; decoupleTo = jj ; decouplePriority = localDecouplePriority ; } else { } object_destroy ( connections ) ; } } if ( decoupleFrom != SIZE_T_ERROR && decoupleTo != SIZE_T_ERROR ) { ObjectContainer * connections = NULL ; Component * fromComp = ( Component * ) comps -> At ( comps , group -> nodes . values [ decoupleFrom ] ) ; Component * toComp = ( Component * ) comps -> At ( comps , group -> nodes . values [ decoupleTo ] ) ; if ( fromComp -> GetID ( fromComp ) != group -> nodes . values [ decoupleFrom ] || toComp -> GetID ( toComp ) != group -> nodes . values [ decoupleTo ] ) { mcx_log ( LOG_ERROR , " " ) ; return RETURN_ERROR ; } connections = fromComp -> GetConnections ( fromComp , toComp ) ; if ( ! connections ) { mcx_log ( LOG_ERROR , " " , fromComp -> GetName ( fromComp ) , toComp -> GetName ( toComp ) ) ; return RETURN_ERROR ; } if ( 0 == connections -> Size ( connections ) ) { mcx_log ( LOG_ERROR , " " , fromComp -> GetName ( fromComp ) , toComp -> GetName ( toComp ) ) ; return RETURN_ERROR ; } for ( k = 0 ; k < connections -> Size ( connections ) ; k ++ ) { Connection * conn = ( Connection * ) connections -> At ( connections , k ) ; ConnectionInfo * info = conn -> GetInfo ( conn ) ; char * connStr = info -> ConnectionString ( info ) ; mcx_log ( LOG_INFO , " " , connStr ) ; mcx_free ( connStr ) ; info -> SetDecoupled ( info ) ; } object_destroy ( connections ) ; } else { mcx_log ( LOG_ERROR , " " ) ; return RETURN_ERROR ; } } } return RETURN_OK ; }
only need to consider DECOUPLE_IFNEEDED since DECOUPLE_ALWAYS is handled at connection->Setup()
only need to consider DECOUPLE_IFNEEDED since DECOUPLE_ALWAYS is handled at connection - > Setup ()
// get edges to decouple // consistency check // check if all connections are decoupleable and get priority // ordering by components takes priority // if a connection in this bundle set to never decouple, discard this bundle // not highest priority // decouple connection with highest priority // consistency check // set all connections decoupled // in this group, no bundle can be decoupled