Non-blocking code help - timeline is attached

all in all you are asking for a pretty complex functionality.
The code to do all this requires:

  • a lot of programming-knowledge and a lot of programming-experience
    if you want to have the code in the most compact and most elegant way

  • a lot of lines of code
    if you want to have the code written in a way that is easy to understand

I have thrown out the struct because I'm using long but self-explaining names
With the long variable-names adding

mySolenoids.

in front of each variable makes things much less readable = much less understandable
It would only become "readable" again with shorter names.
But as long as you only have a little programming-experience the sjort names make it hard to understand the code

conclusion: => not using a struct

For easier understanding I still added a lot of comments

So here is a first rough sketch of how it can be coded with this approach
I have to do other work now

If you want to learn more about non-blocking timing, whic is used here a lot
read this tutorial

and to learn about step-chains ( state-machines) you can read here

#define NUM_SOLENOIDS 10 // using one more array-element to start indexes with 1 instead of zero
const byte BTN_PIN = 10;
const unsigned long PULSE_LENGTH = 50;

// declaring the timing-variables
// assigning values is MISSING and must be added
unsigned long PulsingStarts[NUM_SOLENOIDS];        // The time this solenoid is first active
unsigned long PulsingStops[NUM_SOLENOIDS];         // The time this solenoid becomes inactive
unsigned long PulsingStartsPausing[NUM_SOLENOIDS]; // The time this solenoid shall START the pausing
unsigned long PulsingResumes[NUM_SOLENOIDS];       // The time this solenoid shall resume pulsing again

unsigned long minWaitTimeBeforeNextPulse[NUM_SOLENOIDS];        // The minimum time the next pulse is generated
unsigned long maxWaitTimeBeforeNextPulse[NUM_SOLENOIDS];        // The maximum time the next pulse is generated
unsigned long WaitingIntervalUntilNextPulse[NUM_SOLENOIDS];     // The time that has to pass by until next pulse is created
unsigned long ShortPulseTimer[NUM_SOLENOIDS];                   // variable used for creating the short pulses with non-blocking timing
unsigned long NextPulseTimer[NUM_SOLENOIDS];                    // variable used for non-blocking timing of when the next short-pulse shall be created
uint8_t pin[NUM_SOLENOIDS];


const byte sc_idling          = 0;

const byte sc_Sol1_pulsing    = 1;
const byte sc_Sol12_pulsing   = 2;
const byte sc_Sol1To3_pulsing = 3;
const byte sc_Sol1To4_pulsing = 4;
const byte sc_Sol1To5_pulsing = 5;
const byte sc_Sol1To6_pulsing = 6;
const byte sc_Sol1To7_pulsing = 7;
const byte sc_Sol1To8_pulsing = 8;
const byte sc_Sol1To9_pulsing = 9;

byte stepNo = sc_idling;

unsigned long SequenceStarted;

unsigned long PulsingStarted[NUM_SOLENOIDS];
unsigned long PausingStarted[NUM_SOLENOIDS];
unsigned long PulsingResumes[NUM_SOLENOIDS];
unsigned long PulsingStops[NUM_SOLENOIDS];

unsigned long TimeToStartOperationMode[NUM_SOLENOIDS];

// easy to use helper-function for non-blocking timing
boolean TimePeriodIsOver (unsigned long &startOfPeriod, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - startOfPeriod >= TimePeriod ) {
    // more time than TimePeriod has elapsed since last time if-condition was true
    startOfPeriod = currentMillis; // a new period starts right here so set new starttime
    return true;
  }
  else return false;            // actual TimePeriod is NOT yet over
}


void mySequenceStepChain() {

  static unsigned long CurrentMillis = millis();

  switch (StepNo) {

    case sc_idling:
      if (digitalRead(BTN_PIN) == pressed) {
        SequenceStarted   = CurrentMillis;  // store snapshot of time when sequence BEGINS
        PulsingStarts[1]  = CurrentMillis;  // store snapshot of time when pulsing of solenoid 1 starts
        NextPulseTimer[1] = CurrentMillis;  // initialise Timer-variable that is used for non-blocking timing to create the short pulses with actual time
        digitalWrite(pin[1], HIGH); //
        ShortPulseTimer[1] = CurrentMillis; // store snapshot of time when IO-pin is switched to HIGH

        // create randomised waiting time
        WaitingIntervalUntilNextPulse[1] = random(minWaitTimeBeforeNextPulse[1], maxWaitTimeBeforeNextPulse[1]);
        StepNo = sc_Sol1_pulsing;
      }
      break; // immidiately jump down to END-OF-SWITCH


    case sc_Sol1_pulsing:

      if ( TimePeriodIsOver(NextPulseTimer[1], WaitingIntervalUntilNextPulse[1]) ) {
        // if that amount of milliseconds HAVE passed by that shall be waited until
        // the next short-pulse shall be created
        digitalWrite(pin[1], HIGH); // create next short-pulse
        ShortPulseTimer[1] = CurrentMillis;      // store snapshot of time when IO-pin is switched to HIGH
        WaitingIntervalUntilNextPulse[1] = random(minWaitTimeBeforeNextPulse[1], maxWaitTimeBeforeNextPulse[1]);
      }

      if ( TimePeriodIsOver(ShortPulseTimer[1], PULSE_LENGTH) ) { // check if it is time to finish the short pulse
        // if it is time to finish short-pulse
        digitalWrite(mySolenoids[1].pin, LOW); // switch IO-pin to LOW
        // create next WaitingInterval that shall be waited until the next short pulse is created
        WaitingIntervalUntilNextPulse[1] = random(minWaitTimeBeforeNextPulse[1], maxWaitTimeBeforeNextPulse[1]);
      }

      // check if it is time to switch to the next mode of operation
      if ( CurrentMillis - SequenceStarted >= TimeToStartOperationMode[2] ) {
        StepNo = sc_Sol12_pulsing; 
      }
      break; // immidiately jump down to END-OF-SWITCH

    case :
      break; // immidiately jump down to END-OF-SWITCH

    case :
      break; // immidiately jump down to END-OF-SWITCH

    case :
      break; // immidiately jump down to END-OF-SWITCH

    case :
      break; // immidiately jump down to END-OF-SWITCH

    case :
      break; // immidiately jump down to END-OF-SWITCH

    case :
      break; // immidiately jump down to END-OF-SWITCH

    case :
      break; // immidiately jump down to END-OF-SWITCH

    case :
      break; // immidiately jump down to END-OF-SWITCH

  }
}
void setup() {
  Serial.begin(115200);
  pinMode( BTN_PIN, INPUT_PULLUP );
  for (uint8_t i = 0; i < NUM_SOLENOIDS; i++) {
    pinMode(mySolenoids[i].pin, OUTPUT);
  }
}

void loop() {
  mySequenceStepChain();
}

best regards Stefan