<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def compile_sequence(cycles, program_or_profile='program', unit_converter=None):
 """ Makes the command list for a move sequence. Constructs the list of commands to execute the given sequence of motion. Program/command line commands or profile commands can be generated depending on the value of `program_or_profile` so that the commands can be used to construct a program or profile later. Types of motion supported (see Notes for how to specify) are moves from one position to another (the motion will always come to a stop before doing the next motion), waiting a given interval of time till starting the next move, and looping over a sequence of moves. Parameters cycles : iterable of dicts The iterable of cycles of motion to do one after another. See Notes for format. program_or_profile : {'program', 'profile'}, optional Whether program or profile motion commands should be used. Anything other than these two values implies the default. unit_converter : UnitConverter, optional ``GeminiMotorDrive.utilities.UnitConverter`` to use to convert the units in `cycles` to motor units. ``None`` indicates that they are already in motor units. Returns ------- commands : list of str ``list`` of ``str`` commands making up the move sequence. Notes ----- `cycles` is an iterable of individual cycles of motion. Each cycle is a ``dict`` that represents a sequence of moves that could possibly be looped over. The field ``'iterations'`` gives how many times the sequence of moves should be done (a value > 1 implies a loop). Then the field ``'moves'`` is an iterable of the individual moves. Each individual move is a ``dict`` with the acceleration (``'A'``), deceleration (``'AD'`` with 0 meaning the value of the acceleration is used), velocity (``'V'``), and the distance/position (``'D'``). Back in the cycle, the field ``'wait_times'`` is an iterable of numbers giving the time in seconds to wait after each move before going onto the next. See Also -------- get_sequence_time convert_sequence_to_motor_units GeminiMotorDrive.utilities.UnitConverter Examples -------- Simple program style two motions with a pause in between. ['A100', 'AD0', 'V100', 'D-1000', 'GO1', 'WAIT(AS.1=b0)', 'T1', 'A90', 'GO1', 'WAIT(AS.1=b0)'] The same motion but in profile style commands ['A100', 'AD100', 'V100', 'D-1000', 'VF0', 'GOBUF1', 'GOWHEN(T=1000)', 'A90', 'AD90', 'VF0', 'GOBUF1'] Another motion with a back and forth loop (100 iterations) in the middle, done in program style commands. ['A100', 'AD0', 'V100', 'D-1000', 'GO1', 'WAIT(AS.1=b0)', 'T1', 'L100', 'A50', 'AD40', 'V30', 'D-1000', 'GO1', 'WAIT(AS.1=b0)', 'D~', 'GO1', 'WAIT(AS.1=b0)', 'LN', 'A100', 'AD0', 'V100', 'GO1', 'WAIT(AS.1=b0)'] """

    # If needed, cycles needs to be converted to motor units.
    if unit_converter is None:
        cv_cycles = cycles
    else:
        cv_cycles = convert_sequence_to_motor_units(cycles, \
            unit_converter=unit_converter)

    # Initially, we have no commands in our command list.
    commands = []

    # The A, AD, D, and V parameters of the previous motion should be
    # kept track of because if they don't change from one motion to the
    # next, the commands to set them don't need to be included. They
    # will be started blank since there are no previous motions yet.
    previous_motion = {'A': [], 'AD': [], 'D': [], 'V': []}

    # Construct each cycle one by one.
    for cycle in cv_cycles:
        # If more than one iteration is being done, a loop needs to be
        # setup. It will be either 'L' or 'PLOOP' with the number of
        # iterations attached if it is a program or a profile
        # respectively. Since it will be tough to keep track of what
        # motion changed from the end of a loop to the beginning of it,
        # it is easier to just forget all previous motion values and set
        # them all at the beginning of the loop (clear previous_motion).
        iterations = int(cycle['iterations'])
        if iterations > 1:
            previous_motion = {'A': [], 'AD': [], 'D': [], 'V': []}
            if program_or_profile != 'profile':
                commands.append('L' + str(iterations))
            else:
                commands.append('PLOOP' + str(iterations))

        # Construct each individual move in the cycle.
        for i in range(0, len(cycle['moves'])):
            # Grab the motion indicated by the current move.
            new_motion = cycle['moves'][i]

            # If we are doing a profile, AD must be set explicitly
            # to A if it is 0.
            if program_or_profile == 'profile' \
                    and new_motion['AD'] == 0.0:
                new_motion['AD'] = new_motion['A']

            # Set A, AD, and V if they have changed.
            for k in ('A', 'AD', 'V'):
                if previous_motion[k] != new_motion[k]:
                    # Grab it and round it to 4 places after the decimal
                    # point because that is the most that is
                    # supported. Then, if it is an integer value,
                    # convert it to an integer because that is what the
                    # drive will send back if requested (makes
                    # comparisons easier). Then add the command.
                    val = round(float(new_motion[k]), 4)
                    if val == int(val):
                        val = int(val)
                    commands.append(k + str(val))

            # If the sign of D has flipped, we just need to issue a 'D~'
            # command. If the value has changed in another way, it needs
            # to be reset.
            if previous_motion['D'] != new_motion['D']:
                if previous_motion['D'] == -new_motion['D']:
                    commands.append('D~')
                else:
                    commands.append('D'
                                    + str(int(new_motion['D'])))

            # Grab the amount of time that should be waited after the
            # move is done.
            wait_time = cycle['wait_times'][i]

            # Give the motion command (GO or GOBUF), tell the drive to
            # wait till the motor has stopped (a WAIT command if it is a
            # program and a VF0 command if it is a profile), and make it
            # wait the period of time wait_time (T and GOWHEN commands).
            if program_or_profile != 'profile':
                commands.append('GO1')
                commands.append('WAIT(AS.1=b0)')
                if wait_time != 0:
                    # The wait time needs to be rounded to 3 places
                    # after the decimal. If it is an integer, it should
                    # be converted to an int so that the drive will send
                    # back what we send (makes compairisons easier).
                    wait_time = round(float(wait_time), 3)
                    if wait_time == int(wait_time):
                        wait_time = int(wait_time)
                    commands.append('T' + str(wait_time))
            else:
                commands.append('VF0')
                commands.append('GOBUF1')
                if wait_time != 0:
                    commands.append('GOWHEN(T='
                                    + str(int(1000*wait_time))
                                    + ')')

            # Before going onto the next move, previous_motion needs to
            # be set to the one just done.
            previous_motion = new_motion

        # Done with all the moves of the cycle. If we are looping, the
        # loop end needs to be put in.
        if iterations > 1:
            if program_or_profile != 'profile':
                commands.append('LN')
            else:
                commands.append('PLN')

    # Done constructing the command list.
    return commands