#!/usr/bin/env bash
#
# Integration Infiniband test using villas-node.
#
# Author: Dennis Potter <dennis@dennispotter.eu>
# SPDX-FileCopyrightText: 2014-2023 Institute for Automation of Complex Power Systems, RWTH Aachen University
# SPDX-License-Identifier: Apache-2.0

set -e

# Settings

# ${NUM_VALUES}, ${RATE_SAMPLES}, and ${IB_MODES} may be a list.

NUM_VALUES=(8)
RATE_SAMPLES=(10)
TIME_TO_RUN=5
IB_MODES=("RC")

. ${SRCDIR}/tools/villas-helper.sh

# Check if user is superuser. SU is used for namespace
if [[ "$EUID" -ne 0 ]]; then
    echo "Please run as root"
    exit 99
fi

# Check whether cpuset cset command is availble
if [[ ! $(command -v cset) ]]; then
    echo "Cset is not availble for root. Please install it: https://github.com/lpechacek/cpuset"
    exit 99
fi

# Check if Infiniband card is present
if [[ ! $(lspci | grep Infiniband) ]]; then
    echo "Did not find any Infiniband cards in system"
    exit 99
fi

# Create list of configs and check whether they are valid
OIFS=$IFS; IFS=$'\n'; CONFIG_FILES=($(ls configs | sed -e "s/.conf//")); IFS=$OIFS;

NODETYPES=()

if [[ ! $1 ]]; then
    echo "Please define for which node-type to run the script"
    exit 1
elif [[ $1 == all ]]; then
    echo "Benchmarking the following nodes:"
    for NODETYPE in "${CONFIG_FILES[@]}"
    do
        echo ${NODETYPE}
        NODETYPES+=(${NODETYPE})
    done

else
    FOUND=0

    for NODETYPE in "${CONFIG_FILES[@]}"
    do
        if [[ $1 == ${NODETYPE} ]]; then
            NODETYPES=$1
            FOUND=1
            break
        fi
    done

    if [[ ${FOUND} == 0 ]]; then
        echo "Please define a valid node-type for which a config file is present in ./configs!"
        exit 1
    fi
fi

# SET PATHS

# Declare location of config files
CONFIG=$(mktemp /tmp/nodetype-benchmark-config-XXXX.conf)
CONFIG_TARGET=$(mktemp /tmp/nodetype-benchmark-config-target-XXXX.conf)
CONFIG_SOURCE=$(mktemp /tmp/nodetype-benchmark-config-source-XXXX.conf)

# Initialize counter
COUNT=0

# START OF LOOPS THROUGH CONFIGS

echo ${CONFIG_FILES[0]}
echo ${CONFIG_FILES[1]}

for NODETYPE in "${NODETYPES[@]}"
do

    # CREATE PATH CONFIG FILES

    # Set target and source config file, which is the same for both runs
cat > ${CONFIG_SOURCE} <<EOF
@include "${CONFIG//\/tmp\/}"

paths = (
    {
        in = "siggen",
        out = ("source_node", "results_in"),
    }
)
EOF

cat > ${CONFIG_TARGET} <<EOF
@include "${CONFIG//\/tmp\/}"

paths = (
    {
        in = "target_node",
        out = "results_out",

        original_sequence_no = true
    }
)
EOF
    # SPECIAL TREATMENT FOR SOME NODES

    # Some nodes require special treatment:
    #   * loopback node: target_node is identical to source_node
    #   * infiniband node: one node must be executed in a namespace

    # loopback
    if [ "${NODETYPE}" == "loopback" ]; then
cat > ${CONFIG_TARGET} <<EOF
@include "${CONFIG//\/tmp\/}"

paths = (
    {
        in = "siggen",
        out = ("source_node", "results_in"),
    },
    {
        in = "source_node",
        out = "results_out",

        original_sequence_no = true
    }
)
EOF
    fi

    # infiniband
    if [ "${NODETYPE}" == "infiniband" ]; then
        NAMESPACE_CMD='ip netns exec namespace0'
    else
        NAMESPACE_CMD=''
    fi


    # RUN THROUGH MODES
    for IB_MODE in "${IB_MODES[@]}"
    do
        LOG_DIR=$(date +%Y%m%d_%H-%M-%S)_benchmark_${NODETYPE}_${IB_MODE}

        for NUM_VALUE in "${NUM_VALUES[@]}"
        do
            for RATE_SAMPLE in "${RATE_SAMPLES[@]}"
            do
                NUM_SAMPLE=$((${RATE_SAMPLE} * ${TIME_TO_RUN}))
                #TIME_TO_RUN=$((${NUM_SAMPLE} / ${RATE_SAMPLE}))

                echo "#####"
                echo "## START ${IB_MODE}"
                echo "## NUM_VALUES: ${NUM_VALUE}"
                echo "## RATE_SAMPLES: ${RATE_SAMPLE}"
                echo "## NUM_SAMPLES: ${NUM_SAMPLE}"

                # Set wrapper of config file
cat > ${CONFIG} <<EOF
logging = {
    level = 0,
    facilities = "all",
}

http = {
    enabled = false,
},

nodes = {
    siggen = {
        type = "signal",

        signal = "mixed",
        values = ${NUM_VALUE},
        frequency = 3,
        rate = ${RATE_SAMPLE},
        limit = ${NUM_SAMPLE},

        monitor_missed = false
    },

    results_in = {
        type = "file",

        format = "csv",
        uri = "${LOG_DIR}/$(printf "%03d" ${COUNT})_${IB_MODE}-${NUM_VALUE}-${RATE_SAMPLE}-${NUM_SAMPLE}_input.csv",

        buffer_size = 500000000
    },

    results_out = {
        type = "file",

        format = "csv",
        uri = "${LOG_DIR}/$(printf "%03d" ${COUNT})_${IB_MODE}-${NUM_VALUE}-${RATE_SAMPLE}-${NUM_SAMPLE}_output.csv",

        buffer_size = 500000000
    },
EOF

                cat configs/${NODETYPE}.conf | sed -e "s/\${NUM_VALUE}/${NUM_VALUE}/" -e "s/\${IB_MODE}/${IB_MODE}/" >> ${CONFIG}

cat >> ${CONFIG} <<EOF
}
EOF

                # Start receiving node
                VILLAS_LOG_PREFIX=$(colorize "[target] ") \
                cset proc --set=real-time-0 --exec ../../build/src/villas-node -- ${CONFIG_TARGET} &
                target_node_proc=$!

                # Wait for node to complete init
                sleep 2

                if [ ! "${NODETYPE}" == "loopback" ]; then
                    # Start sending pipe
                    VILLAS_LOG_PREFIX=$(colorize "[source] ") \
                    ${NAMESPACE_CMD} cset proc --set=real-time-1 --exec ../../build/src/villas-node -- ${CONFIG_SOURCE} &
                    source_node_proc=$!
                fi

                sleep $((${TIME_TO_RUN} + 5))

                # Stop node
                kill $target_node_proc

                sleep 1

                echo "## STOP ${IB_MODE}-${NUM_VALUE}-${RATE_SAMPLE}-${NUM_SAMPLE}"
                echo ""

                ((COUNT++))

                sleep 1
            done
        done

        # Since this script will be executed as sudo we should chmod the
        # log dir 777. Otherwise too many unnecessary 'sudo rm -rf' will be invoked.
        chmod -R 777 ${LOG_DIR}
    done
done

rm ${CONFIG} ${CONFIG_TARGET} ${CONFIG_SOURCE}

exit 0
