using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Threading;
using SharpZipLib.Zip;
using Technekon.VDECommon;
namespace Technekon.VDESrv
{
sealed class TransferInspectionDataToServerOperation : BaseOperation
{
/// <summary> системый объект для обслуживания асинхронной операции </summary>
private AsyncOperation m_AsyncOp;
// Отработчики
private SendOrPostCallback m_OnCompletedDelegate;
// Прогресс
private const int TRANSFER_INSP_PROGRESS_BEGIN = 0;
private const int STORE_INSP_PROGRESS_BEGIN = 30;
private const int CALC_INSP_PROGRESS_BEGIN = 70;
private String m_FirstFileName;
private String m_SecondFileName;
private readonly Boolean m_IsHaveManualData;
/// <summary> Событие что передача данных окончена </summary>
private readonly ManualResetEvent m_ReceiveDoneEvent;
/// <summary> Операция передачи файла на сервер</summary>
private TransferFileToServer m_TransferOperation;
private readonly List<Guid> m_MachineEventsIDList = new List<Guid>();
public TransferInspectionDataToServerOperation(BaseOperation parentOperation, DataControlObject contextObject, Boolean isHaveManualData)
: base(parentOperation, contextObject)
{
m_ActivityName = ResourceLoaderStr.GetString("OpTransferInspectionData");
m_OperationName = ResourceLoaderStr.GetString("OpTransferInspectionData");
m_IsHaveManualData = isHaveManualData;
m_ReceiveDoneEvent = new ManualResetEvent(false);
m_OperationAction = OperationActionType.DataWrite;
m_CurrentHandler.RequestedBlockState = BlockState.BlockForWrite;
}
#region StoreInspection child operation
private ManualResetEvent m_StoreInspectionDataEvent;
private OperationAsyncCompletedEventArgs m_StoreInspectionDataCompletedEventArgs;
private void OnStoreInspectionDataCompleted(object sender, OperationAsyncCompletedEventArgs e)
{
// Запоминаем информацию о выполнении
m_StoreInspectionDataCompletedEventArgs = e;
// Освобождаем основной поток
m_StoreInspectionDataEvent.Set();
}
private void StoreInspectionDataToDataBaseOperation(String fileName, String manualDataFileName,
out List<Guid> machineEventsIDList, out Boolean bResult)
{
StoreInspectionDataOperation operation = null;
machineEventsIDList = null;
bResult = true;
try
{
// Создаем операцию
operation = new StoreInspectionDataOperation(this, m_ContextObject, fileName,
manualDataFileName, STORE_INSP_PROGRESS_BEGIN, CALC_INSP_PROGRESS_BEGIN);
// Подписываемся на события
operation.OperationCompleted += new OperationCompletedEventHandler(OnStoreInspectionDataCompleted);
operation.ProgressChanged += new ProgressChangedEventHandler(ReportProgress);
// Инициализируем
operation.Initialize(m_Dispatcher, CurrentClientHandler);
// Событие, сигнализирующее об окончании операции
m_StoreInspectionDataEvent = new ManualResetEvent(false);
// Запустить
operation.Start(null);
// Выполнение
m_StoreInspectionDataEvent.WaitOne();
// Если операция завершилась исключением - проталкиваем это исключение дальше
if (m_StoreInspectionDataCompletedEventArgs.Error != null)
throw new VDSrvException(m_StoreInspectionDataCompletedEventArgs.Error);
// Читаем результат, если операция не была отменена
if (!m_StoreInspectionDataCompletedEventArgs.Cancelled)
{
Object objRes;
operation.GetResult(out objRes);
StoreInspectionDataOperationResult res = (StoreInspectionDataOperationResult)objRes;
bResult = !res.ErrorExists;
}
}
catch (Exception ex)
{
VDException.Report(ex,
m_CurrentHandler.CurrentClientHandler.ClientName,
m_ContextObject.DBName);
throw ex;
}
finally
{
// Отписываемся от событий
if (operation != null)
{
operation.OperationCompleted -= new OperationCompletedEventHandler(OnStoreInspectionDataCompleted);
operation.ProgressChanged -= new ProgressChangedEventHandler(ReportProgress);
Object result;
if (operation.GetResult(out result))
machineEventsIDList = ((StoreInspectionDataOperationResult)result).MachineEventIDList;
// Удаляем операцию
operation.Dispose();
}
}
}
#endregion
#region CalculateInspection child operation
private ManualResetEvent m_CalcInspectionDataEvent;
private OperationAsyncCompletedEventArgs m_CalcInspectionDataCompletedEventArgs;
private void OnCalcInspectionDataCompleted(object sender, OperationAsyncCompletedEventArgs e)
{
// Запоминаем информацию о выполнении
m_CalcInspectionDataCompletedEventArgs = e;
// Освобождаем основной поток
m_CalcInspectionDataEvent.Set();
}
private void CalcInspectionData(List<Guid> machineEventIDList, out Boolean bResult)
{
CalcInspectionDataOperation operation = null;
bResult = false;
try
{
if (machineEventIDList.Count > 0)
{
// Создаем объект операции
operation = new CalcInspectionDataOperation(this, m_ContextObject, machineEventIDList, true);
// Подписываемся на события
operation.OperationCompleted += new OperationCompletedEventHandler(OnCalcInspectionDataCompleted);
operation.ProgressChanged += new ProgressChangedEventHandler(ReportProgressForCalcInspection);
// Инициализируем
operation.Initialize(m_Dispatcher, CurrentClientHandler);
// Событие, сигнализирующее об окончании операции
m_CalcInspectionDataEvent = new ManualResetEvent(false);
// Вперед
operation.Start(null);
// Ожидаем окончания операции, оглядываясь на возможную отмену
while (!m_CalcInspectionDataEvent.WaitOne(250, true))
{
if (IsStopped())
operation.Cancel();
}
// Если операция завершилась исключением - проталкиваем это исключение дальше
if (m_CalcInspectionDataCompletedEventArgs.Error != null)
throw new VDSrvException(m_CalcInspectionDataCompletedEventArgs.Error);
// Читаем результат, если операция не была отменена
if (!m_CalcInspectionDataCompletedEventArgs.Cancelled)
{
Object objRes;
operation.GetResult(out objRes);
bResult = (bool)objRes;
}
}
}
catch (Exception ex)
{
VDException.Report(ex, m_CurrentHandler.CurrentClientHandler.ClientName, m_ContextObject.DBName);
throw ex;
}
finally
{
if (operation != null)
{
// Отписываемся от событий
operation.OperationCompleted -= new OperationCompletedEventHandler(OnCalcInspectionDataCompleted);
operation.ProgressChanged -= new ProgressChangedEventHandler(ReportProgressForCalcInspection);
// Удаляем операцию
operation.Dispose();
}
}
}
#endregion
// Получить блок данных
public override Boolean NextData(Object userData)
{
FileTransferBlock block = (FileTransferBlock)userData;
if (String.IsNullOrEmpty(m_FirstFileName))
{
m_FirstFileName = block.FileName;
}
else
{
if ((m_FirstFileName != block.FileName) && (String.IsNullOrEmpty(m_SecondFileName)))
{
m_SecondFileName = block.FileName;
}
}
return m_TransferOperation.NextData(userData);
}
/// <summary> Служит для перетрансляции индикатора прогресса выполнения
/// операции 'CalcInspection' из базового диапазона (0-100) в заданный </summary>
/// <param name="state">параметр, орисывающий состояние операции</param>
private void ReportProgressForCalcInspection(object state)
{
OperationProgressChangedEventArgs e = state as OperationProgressChangedEventArgs;
OperationProgressChangedEventArgs adjustedState;
if (e != null)
{
Single fNewProgress = Convert.ToSingle(CALC_INSP_PROGRESS_BEGIN) +
Convert.ToSingle(e.ProgressPercentage) * (100 - CALC_INSP_PROGRESS_BEGIN) / 100;
adjustedState = new OperationProgressChangedEventArgs(Convert.ToInt32(fNewProgress),
e.UserState, e.HaveData, e.ReadyForNext, e.Operation, e.ActivityName, e.ActivityFinalised);
adjustedState.Operation.OperationName = m_OperationName;
OnProgressChanged(adjustedState);
}
}
private void ReportProgress(object state)
{
if (state is OperationProgressChangedEventArgs)
{
OperationProgressChangedEventArgs e = state as OperationProgressChangedEventArgs;
e.Operation.OperationName = m_OperationName;
OnProgressChanged(e);
}
}
private void ReportOperationCompleted(object operationState)
{
OperationAsyncCompletedEventArgs e = operationState as OperationAsyncCompletedEventArgs;
OnOperationCompleted(e);
}
protected override void InitializeDelegates()
{
//m_OnProgressReportDelegate = new SendOrPostCallback(ReportProgress);
m_OnCompletedDelegate = new SendOrPostCallback(ReportOperationCompleted);
}
/// <summary> Проверяет, архив ли полученный файл. Если да - он распаковывается.
/// Считаем, что архив может быть только типа Zip </summary>
/// <param name="fileName">Имя предположительного файла-архива</param>
/// <param name="outEntries">Список имен разархивированных файлов. null, если файл не архив</param>
/// <returns>false, если не архив или ошибки во время распаковки</returns>
private bool CheckAndInflate(String fileName, out List<string> outEntries)
{
ZipFile inFileEntry = null;
bool bResult = true;
bool bArchive;
outEntries = null;
// Проверить - архив ли полученный файл
try
{
using (inFileEntry = new ZipFile(fileName))
{
bArchive = inFileEntry.TestArchive(false);
}
}
catch (Exception)
{
bArchive = false;
}
finally
{
if (inFileEntry != null)
inFileEntry.Close();
}
// Полученный файл не нуждается
// в обработке - выходим
if (!bArchive)
return false;
try
{
String inArcFilePath = Path.GetDirectoryName(fileName);
outEntries = new List<string>();
// Если мы здесь, значит имеем дело с архивом подлежащим распаковке
using (ZipInputStream zipStream = new ZipInputStream(File.OpenRead(fileName)))
{
ZipEntry theEntry;
int dataSize = 2048;
byte[] dataBuffer = new byte[dataSize];
while ((theEntry = zipStream.GetNextEntry()) != null)
{
String entryDirName = Path.Combine(inArcFilePath, Path.GetDirectoryName(theEntry.Name));
String entryFileName = Path.GetFileName(theEntry.Name);
String inflatedItemName = Path.Combine(entryDirName, entryFileName);
// create directory
if (entryDirName != String.Empty)
Directory.CreateDirectory(entryDirName);
if (inflatedItemName != String.Empty)
{
using (FileStream streamWriter = File.Create(inflatedItemName))
{
while ((dataSize = zipStream.Read(dataBuffer, 0, dataBuffer.Length)) > 0)
streamWriter.Write(dataBuffer, 0, dataSize);
}
}
// Список имен распакованных файлов
outEntries.Add(inflatedItemName);
}
}
}
catch (Exception)
{
bResult = false;
}
finally
{
inFileEntry.Close();
}
return bResult;
}
private void StoreInspectionFile(String fileName, String manualDataFileName)
{
bool bStoredOk;
List<Guid> machineEventsIDList;
List<string> outFiles;
if (CheckAndInflate(fileName, out outFiles))
{
if ((outFiles != null) && (outFiles.Count > 0))
{
File.Delete(fileName);
fileName = outFiles[0];
// Удаляем все распакованные из архива файлы, кроме первого
for (Int32 i = 1; i < outFiles.Count; i++)
File.Delete(outFiles[i]);
}
}
StoreInspectionDataToDataBaseOperation(fileName, manualDataFileName, out machineEventsIDList, out bStoredOk);
File.Delete(fileName);
File.Delete(manualDataFileName);
foreach (Guid eventID in machineEventsIDList)
{
if (!m_MachineEventsIDList.Contains(eventID))
{
m_MachineEventsIDList.Add(eventID);
}
}
}
protected override void DoOperation(int numberToCheck, AsyncOperation asyncOp)
{
m_AsyncOp = asyncOp;
Exception e = null;
try
{
SendProgress(1, m_AsyncOp, ResourceLoaderStr.GetString("OpTransferInspectionData"));
m_TransferOperation = new TransferFileToServer(this, m_ContextObject, Path.GetTempPath(), 1,
m_ReceiveDoneEvent, TRANSFER_INSP_PROGRESS_BEGIN, STORE_INSP_PROGRESS_BEGIN);
m_TransferOperation.ProgressChanged += ReportProgress;
m_TransferOperation.OnProgressReportDelegate = ReportProgress;
m_TransferOperation.Initialize(m_Dispatcher, CurrentClientHandler);
m_TransferOperation.Start(null);
while (!m_ReceiveDoneEvent.WaitOne(300, true))
{
if (IsStopped())
break;
}
m_TransferOperation.ProgressChanged -= ReportProgress;
if (m_TransferOperation.OperationResult.Error != null)
throw new VDSrvException(m_TransferOperation.OperationResult.Error);
#region Сохранение данных обследований в БД
if (m_IsHaveManualData)
{
StoreInspectionFile(m_FirstFileName, m_SecondFileName);
}
else
{
foreach (KeyValuePair<String, String> pair in m_TransferOperation.ReceivedFilesCorrespondence)
{
StoreInspectionFile(pair.Value, "");
}
}
#endregion
#region Перерасчет данных
bool bCalculatedOk;
CalcInspectionData(m_MachineEventsIDList, out bCalculatedOk);
#endregion
#region Old
//for (m_SpotUnitIdx = 0; m_SpotUnitIdx < m_InspectionsUnits.Length; ++m_SpotUnitIdx)
//{
// m_ActivityName = ResourceLoaderStr.GetString("OpTransferInspectionData");
// m_OperationName = String.Format(ResourceLoaderStr.GetString("ImportInspectionOperationCommentsWithFileName"),
// m_InspectionsUnits[m_SpotUnitIdx].DataFileName);
// // Объявляем о начале операции импорта текущей пары данных
// // обследования 'm_InspectionsUnits[m_SpotUnitIdx]'
// LogMessage(String.Format(ResourceLoaderStr.GetString("ImportInspectionDataOperation_BeginOperation"),
// m_InspectionsUnits[m_SpotUnitIdx].DataFileName));
// // Вычисляем количество шагов для переноса данных обследования
// DataFraction = Convert.ToSingle(m_InspectionsUnits[m_SpotUnitIdx].DataFileSize) /
// Convert.ToSingle(m_TransferBufferSize);
// // Если размер переносимого обследования меньше размера буффера - округляем
// if (DataFraction < 1)
// DataFraction = 1.0F;
// // Вычисляем количество шагов для переноса данных, введенных вручную
// FollowUpFraction = Convert.ToSingle(m_InspectionsUnits[m_SpotUnitIdx].DataFollowUpFileSize) /
// Convert.ToSingle(m_TransferBufferSize);
// // Если размер данных, введенных вручную, меньше размера буффера - округляем
// if (FollowUpFraction < 1)
// FollowUpFraction = 1.0F;
// // Вычисляем приращения прогресса для каждого шага по переносу данных
// m_fProgressStep = Convert.ToSingle(STORE_INSP_PROGRESS_BEGIN - TRANSFER_INSP_PROGRESS_BEGIN) /
// (DataFraction + FollowUpFraction);
// // Сбрасываем прогресс для каждой единицы импорта
// m_fProgress = Convert.ToSingle(TRANSFER_INSP_PROGRESS_BEGIN);
// // Объявляем о начале операции переноса данных
// LogMessage(ResourceLoaderStr.GetString("TransferInspectionDataOperation_Begin"));
// // Создаем и открываем на запись временные файлы для приема данных
// // текущей пары импорта (обследования и данных, введенных вручную)
// InitializeTmpStorages(ref m_InspectionsUnits[m_SpotUnitIdx]);
// // Подготовка к приему потока обследования
// m_nDataReceived = 0;
// m_nDataSize = m_InspectionsUnits[m_SpotUnitIdx].DataFileSize;
// m_SpotFileStream = m_InspectionsUnits[m_SpotUnitIdx].rcvdDataFileStream as FileStream;
// m_TransferInspectionDataEvent.Reset();
// // Запрашиваем данные обследования
// UpdateStatus((int)m_fProgress, m_ActivityName, true, false);
// // Ждем окончания приема
// m_TransferInspectionDataEvent.WaitOne();
// // Зачищаем после себя
// ((FileStream)m_InspectionsUnits[m_SpotUnitIdx].rcvdDataFileStream).Close();
// m_InspectionsUnits[m_SpotUnitIdx].rcvdDataFileStream = null;
// // Есть ли данные, введенные вручную?
// if (m_InspectionsUnits[m_SpotUnitIdx].DataFollowUpFileSize > 0)
// {
// // Подготовка к приему потока данных, введенных вручную
// m_nDataReceived = 0;
// m_nDataSize = m_InspectionsUnits[m_SpotUnitIdx].DataFollowUpFileSize;
// m_SpotFileStream = m_InspectionsUnits[m_SpotUnitIdx].rcvdDataFollowUpFileStream as FileStream;
// m_TransferInspectionDataEvent.Reset();
// // Запрашиваем данные, введенные вручную
// UpdateStatus((int)m_fProgress, m_ActivityName, true, false);
// // Ждем окончания приема
// m_TransferInspectionDataEvent.WaitOne();
// // Зачищаем после себя
// ((FileStream)m_InspectionsUnits[m_SpotUnitIdx].rcvdDataFollowUpFileStream).Close();
// m_InspectionsUnits[m_SpotUnitIdx].rcvdDataFollowUpFileStream = null;
// }
// // Сохраняем имя файла для возможности его удаления
// // в случае возникновения исключения при обработке
// spotFollowUpFileName = m_InspectionsUnits[m_SpotUnitIdx].rcvdDataFollowUpFileName;
// // Объявляем об окончании операции переноса данных
// LogMessage(String.Format(ResourceLoaderStr.GetString("TransferInspectionDataOperation_End"),
// m_InspectionsUnits[m_SpotUnitIdx].rcvdDataFileName));
// // Проверяем, архив ли поступивший файл. На данный
// // момент считается, что это может быть только 'Zip'
// // Сохраняем имя файла для возможности его удаления
// // в случае возникновения исключения при обработке
// spotInspFileName = m_InspectionsUnits[m_SpotUnitIdx].rcvdDataFileName;
// // Выполнить операцию размещения данных в базе
// StoreInspectionDataToDataBaseOperation(out machineEventsIDList, out bStoredOk);
// // Объявляем о начале операции импорта текущей пары данных
// // обследования 'm_InspectionsUnits[m_SpotUnitIdx]'
// LogMessage(String.Format(ResourceLoaderStr.GetString("ImportInspectionDataOperation_EndOperation"),
// m_InspectionsUnits[m_SpotUnitIdx].DataFileName));
// // Выполняем операцию перерасчета данных импортированного обследования
// // Операция сама информирует о начале и окончании перерасчета
// CalcInspectionData(ref machineEventsIDList, out bCalculatedOk);
// // Удаляем временный файл с данными обследования
// File.Delete(m_InspectionsUnits[m_SpotUnitIdx].rcvdDataFileName);
// // Удаляем временный файл дополнительных
// // данных, если таковые имели место
// if (m_InspectionsUnits[m_SpotUnitIdx].rcvdDataFollowUpFileName != String.Empty)
// File.Delete(m_InspectionsUnits[m_SpotUnitIdx].rcvdDataFollowUpFileName);
// // Подчищаем за собой
// spotInspFileName = String.Empty;
// spotFollowUpFileName = String.Empty;
//}
#endregion
}
catch (Exception ex)
{
e = ex;
VDException.Report(ex);
}
finally
{
// Отсылаем событие о завершении операции
OperationAsyncCompletedEventArgs oP;
lock (m_SyncThis)
{
oP = new OperationAsyncCompletedEventArgs(e, m_StoreInspectionDataCompletedEventArgs.Cancelled,
m_IsHaveResults, false, null, m_CurrentHandler);
}
asyncOp.Post(m_OnCompletedDelegate, oP);
Complete(e, asyncOp);
}
}
/// <summary>Запись сообщения в лог</summary>
private void LogMessage(String message)
{
LogMessage(message,
"",
"",
ResourceLoaderStr.GetString("ImportInspectionOperationComments"),
m_CurrentHandler.CurrentClientHandler,
DetailType.SIMPLE);
return;
}
/// <summary>Запись сообщения в лог</summary>
private void LogMessage(String message,
String reason,
String recommendation,
String source,
ClientHandler clientHandler,
DetailType detailType)
{
String clientName = (clientHandler == null) ? "" : clientHandler.ClientName;
VDSrvException ex = new VDSrvException(clientName,
m_ContextObject.DBName,
m_ActivityName,
MessageType.INFORMATION,
0,
source,
message,
reason,
recommendation,
ReportType.ALL,
detailType);
VDSrvException.Report(ex);
}
}
}