Retrieves raw data from eDNA history for multiple tags, merging them into
    a single DataFrame, and resampling the data according to the specified
    sampling_rate.

    :param tag_list: a list of fully-qualified (site.service.tag) eDNA tags
    :param start_time: must be in format mm/dd/yy hh:mm:ss
    :param end_time: must be in format mm/dd/yy hh:mm:ss
    :param sampling_rate: in units of seconds
    :param fill_limit: in units of data points
    :param verify_time: verify that the time is not before or after the query
    :param desc_as_label: use the tag description as the column name instead
        of the full tag
    :param utc: if True, use the integer time format instead of DateTime
    :return: a pandas DataFrame with timestamp and values

def GetMultipleTags(tag_list, start_time, end_time, sampling_rate=None,
                    fill_limit=99999, verify_time=False, desc_as_label=False,
                    utc=False):
    """
    Retrieves raw data from eDNA history for multiple tags, merging them into
    a single DataFrame, and resampling the data according to the specified
    sampling_rate.

    :param tag_list: a list of fully-qualified (site.service.tag) eDNA tags
    :param start_time: must be in format mm/dd/yy hh:mm:ss
    :param end_time: must be in format mm/dd/yy hh:mm:ss
    :param sampling_rate: in units of seconds
    :param fill_limit: in units of data points
    :param verify_time: verify that the time is not before or after the query
    :param desc_as_label: use the tag description as the column name instead
        of the full tag
    :param utc: if True, use the integer time format instead of DateTime
    :return: a pandas DataFrame with timestamp and values
    """
    # Since we are pulling data from multiple tags, let's iterate over each
    # one. For this case, we only want to pull data using the "raw" method,
    # which will obtain all data as it is actually stored in the historian.
    dfs = []
    columns_names = []
    for tag in tag_list:
        df = GetHist(tag, start_time, end_time, utc=utc)
        if not df.empty:
            # Sometimes a duplicate index/value pair is retrieved from
            # eDNA, which will cause the concat to fail if not removed
            # df.drop_duplicates(inplace=True)
            df = df[~df.index.duplicated(keep='first')]
            # If the user wants to use descriptions as labels, we need to
            # ensure that only unique labels are used
            label = tag
            if desc_as_label:
                orig_label = _GetLabel(tag)
                label = orig_label
                rename_number = 2
                while label in columns_names:
                    label = orig_label + str(rename_number)
                    rename_number += 1
                columns_names.append(label)
                df.rename(columns={tag: label}, inplace=True)
            # Add the DataFrame to the list, to be concatenated later
            dfs.append(pd.DataFrame(df[label]))

    # Next, we concatenate all the DataFrames using an outer join (default).
    # Verify integrity is slow, but it ensures that the concatenation
    # worked correctly.
    if dfs:
        merged_df = pd.concat(dfs, axis=1, verify_integrity=True)
        merged_df = merged_df.fillna(method="ffill", limit=fill_limit)
    else:
        warnings.warn('WARNING- No data retrieved for any tags. ' +
                      'Check eDNA connection, ensure that the start time is ' +
                      'not later than the end time, verify that the ' +
                      'DateTime formatting matches eDNA requirements, and ' +
                      'check that data exists in the query time period.')
        return pd.DataFrame()

    # eDNA sometimes pulls data too early or too far- let's filter out all
    # the data that is not within our original criteria.
    if verify_time:
        start_np = pd.to_datetime(start_time)
        end_np = pd.to_datetime(end_time)
        mask = (merged_df.index > start_np) & (merged_df.index <= end_np)
        merged_df = merged_df.loc[mask]

    # Finally, we resample the data at the rate requested by the user.
    if sampling_rate:
        sampling_string = str(sampling_rate) + "S"
        merged_df = merged_df.resample(sampling_string).fillna(
            method="ffill", limit=fill_limit)
    return merged_df