digraph {
    // Node definitions
    sim [label="sim [Simulation]"];
    driver [label="driver [LeaderDriver]"];
    leader [label="leader [CarDynamic]"];
    car1 [label="car1 [CarDynamic]"];
    car2 [label="car2 [CarDynamic]"];
    car3 [label="car3 [CarDynamic]"];
    cacc1 [label="cacc1 [CACC]"];
    cacc2 [label="cacc2 [CACC]"];
    cacc3 [label="cacc3 [CACC]"];
    net0_d [label="net0_d [DownLinkMedium]"];
    net1_d [label="net1_d [DownLinkMedium]"];
    net2_d [label="net2_d [DownLinkMedium]"];
    net3_d [label="net3_d [DownLinkMedium]"];
    net1_u [label="net1_u [UpLinkMedium]"];
    net2_u [label="net2_u [UpLinkMedium]"];
    net3_u [label="net3_u [UpLinkMedium]"];

    // Edges based on channel synchronization (cacc)
    sim -> cacc1 [label="cacc"];
    sim -> cacc2 [label="cacc"];
    sim -> cacc3 [label="cacc"];

    // Data flow edges
    driver -> leader [label="a_ref[0]"];
    leader -> net0_d [label="a, v, x"];
    net0_d -> cacc1 [label="a_n[0], v_n[0]"];
    net0_d -> cacc2 [label="a_n[0], v_n[0]"];
    net0_d -> cacc3 [label="a_n[0], v_n[0]"];
    leader -> net1_d [label="x_p[0]"];
    car1 -> net1_d [label="a, v, x"];
    net1_d -> cacc1 [label="a_n[1], v_n[1]"];
    net1_d -> cacc2 [label="a_n[1], v_n[1]"];
    car1 -> net2_d [label="x_p[1]"];
    car2 -> net2_d [label="a, v, x"];
    net2_d -> cacc2 [label="a_n[2], v_n[2]"];
    net2_d -> cacc3 [label="a_n[2], v_n[2]"];
    car2 -> net3_d [label="x_p[2]"];
    car3 -> net3_d [label="a, v, x"];
    net3_d -> cacc3 [label="a_n[3], v_n[3]"];
    cacc1 -> net1_u [label="a_ref[1]"];
    net1_u -> car1 [label="a_ref_n[1]"];
    cacc2 -> net2_u [label="a_ref[2]"];
    net2_u -> car2 [label="a_ref_n[2]"];
    cacc3 -> net3_u [label="a_ref[3]"];
    net3_u -> car3 [label="a_ref_n[3]"];

    // Simulation to CarDynamic via crashed flags
    sim -> leader [label="crashed[0]"];
    sim -> car1 [label="crashed[1]"];
    sim -> car2 [label="crashed[2]"];
    sim -> car3 [label="crashed[3]"];
}
