unit MCAD_MI1201_PeakControl; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, MCAD_MI1201_Thread0, MCAD_MI1201_Thread1, MITypes, MCAD_MI1201_Thread_Types, MCAD_MI1201_XForm, MCAD_MI1201_Registry, ExtCtrls, Buttons, Grids, ComCtrls, Menus, ActnList; Const SaveDir:string = '.'; Heights :array[boolean]of longint = (104,494); cRegistryTableInfo = 'Table'; ColsCount = 5; ColNames: array[0..ColsCount-1,0..1] of string[30] = ( ('Центр', 'CaptionPeakCenter'), ('Сигнал', 'CaptionMaxSignal'), ('Ширина', 'CaptionWidth'), ('Левый край', 'CaptionLeftLimit'), ('Правый край','CaptionRightLimit') ); ShowMoreCap:array[boolean] of char = ('>','<'); cFloatDigit = 6; cFloatPrec = 6; type TFormPeakCentering = class(TXForm) Label4: TLabel; Label7: TLabel; Label8: TLabel; PanelAdditionalProperties: TPanel; StringGridSavedPeaks: TStringGrid; TimerRemeasurePeak: TTimer; GroupBoxTimerSetup: TGroupBox; ComboBoxRemeasureMass: TComboBox; ActionListPeakCentering: TActionList; PanelProperties: TPanel; Label1: TLabel; ComboBoxMass: TComboBox; BitBtnSetCurMass: TBitBtn; GroupBoxPeakParams: TGroupBox; Label2: TLabel; LabelRealMass: TLabel; Label10: TLabel; LabelMaximum: TLabel; LabelRightBound: TLabel; Label13: TLabel; LabelLeftBound: TLabel; Label9: TLabel; Label3: TLabel; LabelHalfWidth: TLabel; CheckBoxRemeasurePeak: TCheckBox; BitBtnFindPeak: TBitBtn; BitBtnShowMore: TBitBtn; EditRemeasureInterval: TEdit; UpDownTimerInterval: TUpDown; BitBtnTimerOnOff: TBitBtn; EditPeakLevel: TEdit; UpDownPeakLevel: TUpDown; SaveDialogResults: TSaveDialog; PopupMenuStringGrid: TPopupMenu; ActionSaveResults: TAction; ItemSaveResults: TMenuItem; EditMassRange: TEdit; UpDownMassRange: TUpDown; ComboBoxRealMass: TComboBox; Label5: TLabel; BitBtnSetMass: TBitBtn; Label6: TLabel; Label11: TLabel; procedure FindPeakButtonClick(Sender: TObject); procedure FormCreate(Sender: TObject); procedure ButtonSetMassCalibrationClick(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure BitBtnShowMoreClick(Sender: TObject); procedure CheckBoxTimerStateClick(Sender: TObject); procedure TimerReMeasurePeakTimer(Sender: TObject); procedure BitBtnSetCurMassClick(Sender: TObject); procedure StringGridSavedPeaksDblClick(Sender: TObject); procedure StringGridSavedPeaksClick(Sender: TObject); procedure StringGridSavedPeaksKeyPress(Sender: TObject; var Key: Char); procedure ComboBoxMassKeyPress(Sender: TObject; var Key: Char); procedure UpDownPeakLevelClick(Sender: TObject; Button: TUDBtnType); procedure UpDownMassRangeClick(Sender: TObject; Button: TUDBtnType); procedure EditPeakLevelChange(Sender: TObject); procedure EditMassRangeChange(Sender: TObject); procedure ActionSaveResultsExecute(Sender: TObject); private { Private declarations } ShowMore: boolean; TimerState: boolean; Measuring :boolean; // Флаг запущенного измерения RemeasureMass :tMass; // Перемеряемая масса CurRow :integer; procedure MainChannelCange; procedure FormStateChange; procedure RefreshPeakParams; function GetUpDownFloatPos(UpDown:TUpDown;Mn,Mx:double):double; function GetUpDownPos(UpDown:TUpDown;Mn,Vl,Mx:double):integer; function FloatToStr_X(F:extended):string; function AddNumberToStr(Const S:String; Number:double):string; procedure AddNumToList(Const S:TStrings; Number:double; posit:integer); procedure AddToComboBox(Cb:TComboBox; N:tMass); procedure SortToList(S:TStrings;st:openstring); procedure InsertPeakData(AData:tPeakData); procedure ErrorMessage(txt:string); procedure ErrorInObject(Obj:TWinControl); procedure RegistryWriteSettings; procedure RegistryReadDimensions; procedure TestChannelVisible; procedure WritePeaksToFile(Fn:string); procedure CallPeakCentering(Sender:TObject); procedure EndOfPC; procedure NotifyHandler(Sender:TObject; Event:tMI1201_Thread_Event); override; public { Public declarations } end; var FormPeakCentering: TFormPeakCentering; procedure Execute(Sender:tObject); implementation Uses MCAD_MI1201_FormSpectrum, MCAD_MI1201_TChartSeries; {$R *.DFM} const FBCaption : array[boolean] of string = ('Найти центр','Прервать'); Var m:tMass; r:boolean; Procedure Execute; begin if not Assigned(FormPeakCentering) then try FormPeakCentering := TFormPeakCentering.Create(FormSpectrum); except FormPeakCentering := nil; exit; end; FormPeakCentering.MainChannelCange; FormPeakCentering.Show; FormPeakCentering.TestChannelVisible; end; function TFormPeakCentering.FloatToStr_X(F:extended):string; begin Result := FloatToStrF(F,ffGeneral,cFloatPrec,cFloatDigit); end; procedure TFormPeakCentering.MainChannelCange; ResourceString cFormPeakCaption = 'Поиск центра пика - '; begin FormPeakCentering.Caption := cFormPeakCaption + cMSSeriesTitles[FormSpectrum.MassSpectrometer.MainChannel]; end; Procedure TFormPeakCentering.ErrorMessage(Txt:string); ResourceString cErrorCaption = 'Ошибка'; begin MessageBox(txt, cErrorCaption, MB_OK + MB_ICONEXCLAMATION); end; procedure TFormPeakCentering.ErrorInObject(Obj:TWinControl); begin if assigned(obj) then Obj.SetFocus; TimerState := False; TimerRemeasurePeak.Enabled := False; end; procedure TFormPeakCentering.FormStateChange; begin ClientHeight := PanelProperties.Height; if ShowMore then ClientHeight := ClientHeight + PanelAdditionalProperties.Height; BitBtnShowMore.Caption := ShowMoreCap[ShowMore]; PanelAdditionalProperties.Visible := ShowMore; end; function TFormPeakCentering.GetUpDownFloatPos(UpDown:TUpDown;Mn,Mx:double):double; begin with UpDown do Result := (Position /(Max - Min))*(Mx - Mn); end; function TFormPeakCentering.GetUpDownPos(UpDown:TUpDown;Mn,Vl,Mx:double):integer; begin with UpDown do Result := round((Vl /(Mx - Mn)) * (Max - Min)); end; procedure TFormPeakCentering.FormCreate(Sender: TObject); Procedure TimerSetup; begin TimerState := False; RemeasureMass := 0; // Возможно её нужно будет сохранять и восстанавливать ComboBoxRemeasureMass.Text := ''; ComboBoxRemeasureMass.Items.Clear; EditRemeasureInterval.Text := ''; TimerRemeasurePeak.Enabled := False; Measuring := False; end; Procedure FormAndMassSetup; Var i:integer; begin ShowMore := True; MassSpectrometer := FormSpectrum.MassSpectrometer; CheckBoxRemeasurePeak.Checked := False; FormStateChange; ComboBoxMass.Text := ''; ComboBoxMass.Items.Clear; ComboBoxRealMass.Text := ''; ComboBoxRealMass.Items.Clear; EditMassRange.Text := FloatToStr_X(MassSpectrometer.PeakDelta); EditPeakLevel.Text := FloatToStr_X(MassSpectrometer.PeakLevel); LabelRealMass.Caption := ''; LabelMaximum.Caption := ''; LabelHalfWidth.Caption := ''; LabelLeftBound.Caption := ''; LabelRightBound.Caption := ''; end; procedure GridSetup; Var i:integer; begin // Очищаем список и устанавливаем имена колонок with StringGridSavedPeaks do begin RowCount := 10; CurRow := 1; for i := 0 to ColsCount - 1 do begin Cols[i].Clear; Cells[i,0] := ColNames[i,0]; end; end; end; begin FormAndMassSetup; TimerSetup; GridSetup; RegistryReadDimensions; end; procedure TFormPeakCentering.NotifyHandler(Sender:TObject; Event:tMI1201_Thread_Event); begin Case Event.EventID of evMainChannelChanged: Synchronize(MainChannelCange); evDestroy, evTerminate: MassSpectrometerNIL; evPeakCenterEnd: EndOfPC; else end; //Case end; Procedure TFormPeakCentering.SortToList(S:TStrings;st:openstring); Var i:Longint; begin With S do begin Insert(0, St); for i := 1 to Count - 1 do if (Strings[0] = Strings[i]) then begin Delete(i); break; end; end; //With end; procedure TFormPeakCentering.InsertPeakData(AData:tPeakData); begin with StringGridSavedPeaks,AData do begin if CurRow >= RowCount then RowCount := RowCount + 1; Cells[0,CurRow] := FloatToStr_X(PeakCenter); Cells[1,CurRow] := FloatToStr_X(Height); Cells[2,CurRow] := FloatToStr_X(HalfWidth); Cells[3,CurRow] := FloatToStr_X(LeftBound); Cells[4,CurRow] := FloatToStr_X(RightBound); TopRow := CurRow; inc(CurRow); end; end; Procedure TFormPeakCentering.RefreshPeakParams; Const cErrorText = 'Ошибка!'; begin if not assigned(MassSpectrometer) then exit; with MassSpectrometer do with PeakData, DataSeries[MainChannel] do begin LabelRealMass.Caption := FloatToStr_X(PeakCenter); LabelMaximum.Caption := FloatToStr_X(YValues[IndexOfMaximum]); LabelHalfWidth.Caption := FloatToStr_X(HalfWidth); if not LeftLimit then LabelLeftBound.Caption := FloatToStr_X(XValues[IndexOfLeft]) else LabelLeftBound.Caption := cErrorText; if not RightLimit then LabelRightBound.Caption := FloatToStr_X(XValues[IndexOfRight]) else LabelRightBound.Caption := cErrorText; end; //with end; procedure TFormPeakCentering.CallPeakCentering(Sender:TObject); begin MassSpectrometer.CenterOfPeak(m,r); end; procedure TFormPeakCentering.FindPeakButtonClick(Sender: TObject); begin if not Assigned(MassSpectrometer) then // Возможно, это не нужно Exit; if Measuring then begin MassSpectrometer.PCFlag := pcBreak; Measuring := False; BitBtnFindPeak.Caption := FBCaption[Measuring]; Exit; end; With MassSpectrometer do begin // Задаём параметры try m := StrToFloat_MSG(ComboBoxMass.Text); r := CheckBoxRemeasurePeak.Checked; except ErrorInObject(ComboBoxMass); Exit; end; // Запускаем процедуру Measuring := True; BitBtnFindPeak.Caption := FBCaption[Measuring]; ProcessMessages; ExtendedCommand(CallPeakCentering); end; //with end; procedure TFormPeakCentering.EndOfPC; begin With MassSpectrometer do begin if PCFlag = pcFinished then begin RefreshPeakParams; InsertPeakData(PeakData); SortToList(ComboBoxMass.Items, ComboBoxMass.Text); ComboBoxMass.ItemIndex := 0; end; Measuring := False; BitBtnFindPeak.Caption := FBCaption[Measuring]; end; //With end; // Добавляем число к числу в строке function TFormPeakCentering.AddNumberToStr(Const S:String; Number:double):string; begin if Length(s) = 0 then Result := s else try Result := FloatToStr_X(StrToFloat_MSG(s) + Number); except ErrorInObject(Self); end; end; // Прибавляем число в списку чисел Procedure TFormPeakCentering.AddNumToList(Const S:TStrings; Number:double; posit:integer); Var i:longint; begin For i := posit to S.Count - 1 do S[i] := AddNumberToStr(S[i], Number); end; Procedure TFormPeakCentering.AddToComboBox(Cb:TComboBox; N:tMass); begin with CB do begin AddNumToList(Items, N, 0); ItemIndex := 0; Text := AddNumberToStr(Text, N); end; //with end; procedure TFormPeakCentering.ButtonSetMassCalibrationClick(Sender: TObject); Var RealMass :tMass; St :String; begin if not Assigned(MassSpectrometer) then Exit; try St := ComboBoxRealMass.Text; if Length(St) = 0 then exit; RealMass := StrToFloat_MSG(St); except ErrorInObject(ComboBoxRealMass); Exit; end; // Предполагаем, что в LabelRealMass корректное число, т.к. он ReadOnly if length(LabelRealMass.Caption) = 0 then Exit; RealMass := RealMass - StrToFloat(LabelRealMass.Caption); AddNumToList(StringGridSavedPeaks.Cols[0], RealMass, 1);{!} AddToComboBox(ComboBoxMass, RealMass); AddToComboBox(ComboBoxRealMass, RealMass); AddToComboBox(ComboBoxRemeasureMass, RealMass); LabelRealMass.Caption := AddNumberToStr(LabelRealMass.Caption, RealMass); if not MassSpectrometer.LimitOfRange then begin LabelLeftBound.Caption := AddNumberToStr(LabelLeftBound.Caption, RealMass); LabelRightBound.Caption := AddNumberToStr(LabelRightBound.Caption, RealMass); end; ProcessMessages; With MassSpectrometer do try RefineMass(Mass + RealMass); except end; end; function GetFileDir(FileName:string):string; begin Result := ExtractFilePath(ExpandFileName(FileName)); end; // ------------------ Операции с реестром ------------------------------------ procedure TFormPeakCentering.RegistryWriteSettings; Var i:integer; procedure RegistryWriteDimensions; begin try prRegistry.WriteInteger('Left',Left); prRegistry.WriteInteger('Top',Top); except end; end; procedure RegistryWriteComboBox(CB:TComboBox); begin try prRegistry.WriteString(CB.Name,CB.Items.Text); except end; end; procedure RegistryWriteColumns; Var i: integer; begin if RegistryOpenForWriteSubKey(cRegistryTableInfo) then try With StringGridSavedPeaks do For i := 0 to ColsCount - 1 do try prRegistry.WriteInteger(ColNames[i,1], ColWidths[i]); except end; RegistryWriteComboBox(ComboBoxRealMass); try prRegistry.WriteString('LastDir',GetFileDir(SaveDialogResults.FileName)); except end; finally prRegistry.CloseKey; end; end; procedure RegistryWritePeakParams; begin try prRegistry.WriteFloat('PeakCenteringLevel', StrToFloat(EditPeakLevel.Text)); except end; try prRegistry.WriteFloat('MassRange', StrToFloat(EditMassRange.Text)); except end; try prRegistry.WriteBool('Remeasuring', CheckBoxRemeasurePeak.Checked); except end; try prRegistry.WriteBool('ShowAdvancedPeakCentering', ShowMore); except end; RegistryWriteComboBox(ComboBoxRemeasureMass); end; begin RegistryWriteColumns; if RegistryOpenForWrite then try RegistryWriteDimensions; RegistryWritePeakParams; finally prRegistry.CloseKey; end; end; procedure TFormPeakCentering.RegistryReadDimensions; Var i:integer; procedure RegistryReadComboBox(CB:TComboBox); begin try CB.Items.Text := prRegistry.ReadString(CB.Name); except CB.Items.Text := ''; end; end; procedure RegistryReadDimensions; begin try Left := prRegistry.ReadInteger('Left'); if (Left < 0) then Left := 0; if ((Left + Width) > Screen.Width) then Left := Screen.Width - Width; except end; // try try Top := prRegistry.ReadInteger('Top'); if (Top < 0) then Top := 0; if ((Top + Height) > Screen.Height) then Top := Screen.Height - Height; except end; // try end; procedure RegistryReadColWidth; Var i: integer; begin if RegistryOpenForReadSubKey(cRegistryTableInfo) then try With StringGridSavedPeaks do for i := 0 to ColsCount - 1 do try ColWidths[i] := prRegistry.ReadInteger(ColNames[i,1]); except end; // try RegistryReadComboBox(ComboBoxRealMass); try SaveDialogResults.InitialDir := prRegistry.ReadString('LastDir'); except SaveDialogResults.InitialDir := '.'; end; finally prRegistry.CloseKey; end; end; procedure RegistryReadPeakParams; begin try EditPeakLevel.Text := FloatToStr_X(prRegistry.ReadFloat('PeakCenteringLevel')); except EditPeakLevel.Text := FloatToStr_X(DefaultPeakLevel); end; try EditMassRange.Text := FloatToStr_X(prRegistry.ReadFloat('MassRange')); except EditMassRange.Text := FloatToStr_X(DefaultPeakDelta); end; try CheckBoxRemeasurePeak.Checked := prRegistry.ReadBool('Remeasuring'); except CheckBoxRemeasurePeak.Checked := False; end; try ShowMore := prRegistry.ReadBool('ShowAdvancedPeakCentering'); except ShowMore := True; end; RegistryReadComboBox(ComboBoxRemeasureMass); end; begin RegistryReadColWidth; if RegistryOpenForRead then try RegistryReadDimensions; RegistryReadPeakParams; finally prRegistry.CloseKey; end; end; //------------------ Конец операций с реестром ------------------------------- procedure TFormPeakCentering.FormDestroy(Sender: TObject); begin RegistryWriteSettings; if Self = FormPeakCentering then begin FormPeakCentering := nil; MassSpectrometerNIL; end; end; procedure TFormPeakCentering.BitBtnShowMoreClick(Sender: TObject); begin ShowMore := not ShowMore; FormStateChange; end; procedure TFormPeakCentering.CheckBoxTimerStateClick(Sender: TObject); Const MinToMSec = 60 * 1000; ResourceString cTooSmallInterval = 'Задан слишком маленький интевал повторения!'#13+ 'Задайте больший интервал (в минутах).'; begin TimerState := not TimerState; if not Assigned(MassSpectrometer) then Exit; With TimerRemeasurePeak do begin if not TimerState then begin Enabled := False; exit; end; try Interval := Round(StrToFloat_MSG(EditRemeasureInterval.Text) * MinToMSec); except ErrorInObject(EditRemeasureInterval); Exit; end; if Interval < MinToMsec then begin ErrorMessage(cTooSmallInterval); ErrorInObject(EditRemeasureInterval); Exit; end; // Обновляем массу try RemeasureMass := StrToFloat_MSG(ComboBoxRemeasureMass.Text); except ErrorInObject(ComboBoxRemeasureMass); Exit; end; // Если новая, то вставляем в ComboBox SortToList(ComboBoxRemeasureMass.Items, FloatToStr_X(RemeasureMass)); // Перерисовываемся ProcessMessages; // Включаем таймер Enabled := True; end; //with end; procedure TFormPeakCentering.TimerReMeasurePeakTimer(Sender: TObject); begin // Если идёт измерение, то выходим if Measuring then exit; Measuring := True; // Ставим флаг измерения if not assigned(MassSpectrometer) then begin ErrorInObject(Self); Exit; end; try RemeasureMass := StrToFloat_MSG(ComboBoxRemeasureMass.Text); except ErrorInObject(ComboBoxRemeasureMass); Exit; end; ProcessMessages; RemeasureMass := MassSpectrometer.CenterOfPeak(RemeasureMass, True); InsertPeakData(MassSpectrometer.PeakData); ComboBoxRemeasureMass.Text := FloatToStr_X(RemeasureMass); Measuring := False; // Измерение закончено end; procedure TFormPeakCentering.BitBtnSetCurMassClick(Sender: TObject); begin with ComboBoxMass do begin Text := FloatToStr_X(MassSpectrometer.Mass); SetFocus; end; end; procedure TFormPeakCentering.StringGridSavedPeaksClick(Sender: TObject); begin With StringGridSavedPeaks,MassSpectrometer.PeakData do try PeakCenter := StrToFloat(Cells[0,Selection.Top]); Height := StrToFloat(Cells[1,Selection.Top]); HalfWidth := StrToFloat(Cells[2,Selection.Top]); LeftBound := StrToFloat(Cells[3,Selection.Top]); RightBound := StrToFloat(Cells[4,Selection.Top]); except Exit; end; RefreshPeakParams; end; procedure TFormPeakCentering.StringGridSavedPeaksDblClick(Sender: TObject); begin if not Assigned(MassSpectrometer) then Exit; StringGridSavedPeaksClick(Sender); ComboBoxMass.Text := FloatToStr_X(MassSpectrometer.PeakData.PeakCenter); end; procedure TFormPeakCentering.StringGridSavedPeaksKeyPress(Sender: TObject; var Key: Char); begin if Key = #13 then StringGridSavedPeaksDblClick(Sender); if Key = #27 then ; end; procedure TFormPeakCentering.ComboBoxMassKeyPress(Sender: TObject; var Key: Char); begin if Key = #13 then FindPeakButtonClick(Sender); end; procedure TFormPeakCentering.UpDownPeakLevelClick(Sender: TObject; Button: TUDBtnType); Var t:double; begin T := GetUpDownFloatPos(UpDownPeakLevel,0,1); MassSpectrometer.PeakLevel := T; EditPeakLevel.Text := FloatToStr_X(T); end; procedure TFormPeakCentering.UpDownMassRangeClick(Sender: TObject; Button: TUDBtnType); Var t:double; begin T := GetUpDownFloatPos(UpDownMassRange,1,10); MassSpectrometer.PeakDelta := T; EditMassRange.Text := FloatToStr_X(T); end; procedure TFormPeakCentering.TestChannelVisible; begin if assigned(MassSpectrometer) then With MassSpectrometer do if not (MainChannel in DataSeriesActive) then begin FormStyle := fsNormal; MessageBox('Главный канал не отбражается на графике!'#13#10+ 'Возможно вы забыли его сменить!', 'Предупреждение',MB_ICONEXCLAMATION+MB_OK); FormStyle := fsStayOnTop; end;{} end; procedure TFormPeakCentering.EditPeakLevelChange(Sender: TObject); begin if Sender.InheritsFrom(tEdit) then try UpDownPeakLevel.Position := GetUpDownPos(UpDownPeakLevel, 0, StrToFloat(EditPeakLevel.Text), 1); except end; end; procedure TFormPeakCentering.EditMassRangeChange(Sender: TObject); begin if Sender.InheritsFrom(tEdit) then try UpDownMassRange.Position := GetUpDownPos(UpDownMassRange, 1, StrToFloat(EditMassRange.Text), 10); except end; end; procedure TFormPeakCentering.WritePeaksToFile(Fn:string); Var f:TextFile; i,k:integer; s:string; begin FormStyle := fsNormal; AssignFile(f,Fn); Rewrite(f); with StringGridSavedPeaks do for i := 0 to RowCount - 1 do begin s := ''; for k := 0 to ColCount - 1 do s := s + #9 + Cells[k,i] + #9; Writeln(f,S); end; CloseFile(f); FormStyle := fsStayOnTop; end; procedure TFormPeakCentering.ActionSaveResultsExecute(Sender: TObject); begin with SaveDialogResults do if Execute then WritePeaksToFile(FileName); end; end.