/// <summary> /// Объединение форматов двух результатов сканирования одного трека. У треков main и add будут изменены тайминги при работе этой функции! /// </summary> /// <param name="track"></param> /// <param name="main">Основной трек.</param> /// <param name="add">Добавляемый трек.</param> /// <param name="result"></param> /// <returns>true - объединение успешно, false - объединение не удалось по какой-то причине (невозможность синхронизации, несовместимость треков).</returns> public bool CombineFormats(int track, TrackFormat main, TrackFormat add, TrackFormat result) { if (add.Layout.Cnt == 0) { result.Assign(main); if (result.FormatName == TrackFormatName.Unscanned) { result.FormatName = TrackFormatName.NoHeaders; } return(true); } if (main.Layout.Cnt == 0 && add.Layout.Cnt > 0) { result.AssignLayout(add, track); return(true); } int indexM = 0; int indexA = 0; for (int i = 0; i < main.Layout.Cnt; i++) { for (int j = 0; j < add.Layout.Cnt; j++) { if (main.Layout.Data[i].Parameters == add.Layout.Data[j].Parameters) { indexM = i; indexA = j; goto outside; } } } return(false); outside: int iM = indexM; int iA = indexA; int iR = 0; scanFormatBuffer.Layout.EnsureCapacity(main.Layout.Cnt + add.Layout.Cnt); scanFormatBuffer.Layout.Cnt = 0; int totalPassedInMain = 0; double timeM = main.Layout.Data[(iM + 1) % main.Layout.Cnt].TimeMs; double timeA = add.Layout.Data[(iA + 1) % add.Layout.Cnt].TimeMs; while (totalPassedInMain < main.Layout.Cnt) { int iM1 = (iM + 1) % main.Layout.Cnt; int iA1 = (iA + 1) % add.Layout.Cnt; if (main.Layout.Data[iM1].TimeCalculated) { if (main.Layout.Data[iM1].Parameters == add.Layout.Data[iA1].Parameters) { iM = iM1; totalPassedInMain++; iA = iA1; scanFormatBuffer.Layout.Data[iR] = main.Layout.Data[iM]; scanFormatBuffer.Layout.Data[iR].TimeMs = add.Layout.Data[iA].TimeMs; scanFormatBuffer.Layout.Data[iR].TimeCalculated = false; iR++; } else { return(false); //totalPassedInMain++; //for (int t = 0, r = iM1, tlast = main.Layout.Cnt - totalPassedInMain; t < tlast; t++, totalPassedInMain++, r = r % main.Layout.Cnt) //{ // for (int y = 0; y < add.Layout.Cnt; y++) // { // if (main.Layout.Data[r].Parameters == add.Layout.Data[y].Parameters) // { // iM = r; // iA = y; // goto outOfLoop; // } // } //} //outOfLoop:; } } else if (Math.Abs(timeM - timeA) / timeM < 0.1) { iM = iM1; totalPassedInMain++; iA = iA1; if (main.Layout.Data[iM].Parameters != add.Layout.Data[iA].Parameters) { Log.Info?.Out($"Несовпадение параметров секторов при совмещении форматов. Основной сектор: {main[iM]} | Добавляемый сектор: {add[iA]}"); return(false); } scanFormatBuffer.Layout.Data[iR] = main.Layout.Data[iM]; iR++; timeM = main.Layout.Data[(iM + 1) % main.Layout.Cnt].TimeMs; timeA = add.Layout.Data[(iA + 1) % add.Layout.Cnt].TimeMs; } else if (timeA < timeM) { timeM -= timeA; main.Layout.Data[iM1].TimeMs -= timeA; iA = iA1; scanFormatBuffer.Layout.Data[iR] = add.Layout.Data[iA]; // Проверки ниже закомментированы, т.к. стало понятно что заголовки могут быть без данных и быть как следствие с более высокой плотностью. // Проверка выхода конца добавляемого сектора дальше начала следующего сектора в массиве main. if (timeM < (add.Layout.Data[iA].SizeBytes + TrackFormat.MinSectorHeaderSize) / add.BytesPerMs) { return(false); } // Проверка выхода начала добавляемого сектора влево от конца предыдущего сектора в массиве main. if (timeA < (main.Layout.Data[iM].SizeBytes + TrackFormat.MinSectorHeaderSize) / main.BytesPerMs) { return(false); } iR++; timeA = add.Layout.Data[(iA + 1) % add.Layout.Cnt].TimeMs; } else { timeA -= timeM; add.Layout.Data[iA1].TimeMs -= timeM; iM = iM1; totalPassedInMain++; scanFormatBuffer.Layout.Data[iR] = main.Layout.Data[iM]; iR++; timeM = main.Layout.Data[(iM + 1) % main.Layout.Cnt].TimeMs; } } scanFormatBuffer.Layout.Cnt = iR; result.AssignLayout(scanFormatBuffer, track); return(true); }
/// <summary> /// Сканирование трека. Сканировать может в двух режимах: по времени оборота диска и по зацикливанию потока секторов. /// При сканировании по времени оборота диска скорость диска должна быть в районе 300 об/мин, т.е. замедлять диск не надо, иначе будут ошибки. /// Если byLooping == true, то конец трека определяется по зацикливанию потока секторов, но сектора сканируются не менее 150 мс и не более 1000 мс. /// </summary> /// <param name="track"></param> /// <returns></returns> public unsafe bool ScanFormat(TrackFormat result, int track, bool byLooping) { int ilayoutCnt = 0; scanFormatBuffer.Layout.Cnt = 0; SectorInfo[] layout = scanFormatBuffer.Layout.Data; tagFD_READ_ID_PARAMS pars = new tagFD_READ_ID_PARAMS() { flags = Driver.FD_OPTION_MFM, head = (byte)(track & 1) }; tagFD_CMD_RESULT cmdResult = new tagFD_CMD_RESULT(); scanFormatStopwatch.Restart(); scanFormatTotalTime.Stop(); int firstIndex = 1; for (int i = 0; i < scanFormatBuffer.Layout.Capacity; i++) { if (Aborted) { return(false); } if (!Driver.ReadId(DriverHandle, pars, out cmdResult)) { int error = Marshal.GetLastWin32Error(); Log.Trace?.Out($"Функция ReadId вернула false. i={i}. LastError={error}"); if (Aborted) { return(false); } Make30HeadPositionings(error, track); if (scanFormatBuffer.Layout.Cnt == 0) { goto success; } return(false); } double timeMs = scanFormatStopwatch.ElapsedMs; scanFormatStopwatch.Restart(); if (!scanFormatTotalTime.IsRunning) { scanFormatTotalTime.Restart(); } if (!byLooping && scanFormatTotalTime.ElapsedMs > 209) { goto success; } layout[ilayoutCnt] = new SectorInfo() { Cylinder = cmdResult.cyl, Head = cmdResult.head, SectorNumber = cmdResult.sector, SizeCode = cmdResult.size, TimeMs = timeMs }; ilayoutCnt++; scanFormatBuffer.Layout.Cnt = ilayoutCnt; // Бывают случаи когда заголовок сектора читается на первом обороте и не читается на втором. // Из-за этого алгоритм, в котором повтор сектора определяется сравнением с первым прочитанным заголовком, иногда даёт сбои. // Поэтому ищем встечался ли ранее только что прочитанный сектор среди всех прочитанных заголовков, а не только сравниваем с первым. // По отношению к найденному сектору замеряем время вращения. Если оно больше 250 мс, значит этот сектор был пропущен на одном из оборотов // и такую последовательность брать нельзя. Если время меньше 150, значит на треке есть сектора с одинаковыми параметрами. if (byLooping) { for (int u = 0; u < ilayoutCnt - 1; u++) { if (layout[u].Cylinder == cmdResult.cyl && layout[u].Head == cmdResult.head && layout[u].SectorNumber == cmdResult.sector && layout[u].SizeCode == cmdResult.size) { double spinTime = 0; for (int p = u + 1; p < ilayoutCnt; p++) { spinTime += layout[p].TimeMs; } if (spinTime > 250 || spinTime < 150) { continue; } firstIndex = u + 1; goto success; } } if (scanFormatTotalTime.ElapsedMs > 1000) { Log.Trace?.Out($"Не удалось найти цикл в последовательности секторов из-за нестабильного чтения. Сканироваие прервано по таймауту."); return(false); } } } return(false); success: result.AssignLayout(scanFormatBuffer, track, cmdResult.sector, firstIndex); Log.Trace?.Out($"Время сканирования трека {track}: {GP.ToString(scanFormatTotalTime.ElapsedMs, 2)}"); return(true); }