{ Виртуальный масс-спектрометр Базовый объект - виток масс-спектрометра Содержит основные функции 1) доступа к оборудованию, 2) хранения данных, 3) управления } {--------------------------------------------------------------------------- The control program for mass-spectrometer MI1201-AGM (c) Copyright Aleksandrov O.E., 2001 Molecular Physics department, USTU, Ekaterinsburg, K-2, 620002, RUSSIA phone 75-47-15 E-mail: aleks@dpt.ustu.ru Программа управления масс-спектрометром МИ1201-АГМ (c) Собственность Александрова О.Е., 2001 620002, Екатеринбург, К-2, УГТУ, Кафедра молекулярной физики тел. 75-47-15 E-mail: aleks@dpt.ustu.ru ----------------------------------------------------------------------------} unit MCAD_MI1201_Thread0; interface uses Windows, Classes, SysUtils, SyncObjs, Registry, TMultiThreadObjectMethodsList, xSystem, xRegistry, MMTimer, MultiThreadList, MiscFunc, MCAD_MI1201_FileOutput, MCAD_MI1201_Range, MCAD_MI1201_XCommands, c_ISSB, MassClbr, c_Mi1201, MITypes, MCAD_MI1201_Registry, MCAD_Event, McadUserTypes, MCAD_MI1201_TChartSeries, MCAD_MI1201_Thread_Types; const cMainTreadName='MI1201_Thread'; cDefaultTimeOut=500; {мс} // Идентификатор сообщения масс-спектрометра - используется при обращении к окну (форме) // процедурой Notify CM_MI1201Notify = $9FFE; const cChannels:array[tSeries] of tChannel=(PNC1,PNC2,PNC3,PNC4,PNC5,PNC_SEM,IonCounter,PNC1); type // массив серий (данных каналов измерений) tDataSeries=array[tSeries] of tMSSeries; // управляюшие флаги получения сигнала tSignalsGetFlag=( sgfAuto, // Автоматически (в соответствии с глобальными параметрами) sgfDirect, // Запуск измерения и возврат данных с прибора sgfInterpolate // ); tSignalsGetFlags=set of tSignalsGetFlag; TUpDownDirection=(updNone, updUp, updDown); tMNIParameters=(mni_X, mni_dM); tCounter=integer; // Переопределение имени типа для модуля управления МИ-1201 АГМ tMsSp=c_Mi1201.tCtrl; tMsSpPtr=^tMsSp; // Внутренние флаги tFlag=(fDataAllocated,// данные успешно размещены fNoMemory, // недостаточно памяти fStopMeasure, // останов фоновой развертки fError, // ошибка fDifferentPointsNumberInSeries, // серии имеют разное число точек fPointProcessed, // точка обработана фоновой разверткой fDataValid, // данные доступны fCumulativeMeasure, // аккумулирование данных в серии с весом времени интегрирования, иначе замена fSourceTimerFails, // не удалось запустить таймер автозакрытия клапана fVoltmeterTimerFails, // не удалось запустить таймер автосчитывания вольтметра fMassLimit, fMassLimitLo, fMassLimitUp, // достигнут предел развертки по шкале масс fMeasureCycleContinued, // цикл фоновой развертки не прерывался fDataChanged // данные в хранилище изменены с момента последнего сохранения ); tFlags=set of tFlag; const // флаги, требующие реакции в витке (запускающие какую-либо процедуру) cDoFlags=[fDoChangeMass..fDoSetIntegrationTime]; type // Ошибки витка tError=( ecOK, // все хорошо ecHardwareError, // ошибка оборудования, см. MsSp.ErrorCode ecNoMemory, // недостаточно памяти ecUnexpectedError, // неожиданная и неподдерживаемая ошибка ecDataSeriesWriteAutoError // ошибка автосохранения данных ); // Результаты размещения диапазона tSetRangeResult=(rOK, rMin, rMax, rStep, rCantSetup); // Состояние фонового измерения tMeasureState=(msUnknown, // непонятное состояние msErrorStopped, // остановлено из-за ошибки msNotAvailableStopped, // остановлено из-за недоступности измерения - выключен прибор msSuspended, // приостановлен виток msStopped, // остановлено по требованию пользователя msPaused, // измеряем стоя на месте msScroll // Развертка ); // Направление развертки tScrollDirection=(sdUnknown, sdNoScroll, sdUp, sdDown); const // флаги недопускающие фоновое измерение cDoNotMeasureFlags=[fNoMemory, fStopMeasure, fError]; // флаги допускающие фоновое измерение cDoMeasureFlags=[fDataAllocated]; // номер серии для записи времени интегрирования cFullIntegrationTime=Succ(Ord(High(tChannel))); type // оповещатель tNotifyList=class(tMultiThreadList) public procedure Add(var NotifyHanler:tMI1201_Thread_NotifyData); function Remove(var NotifyHanler:tMI1201_Thread_NotifyData):Integer; procedure NotifyByList(Sender:TObject; Event:tMI1201_Thread_Event); end; // оповещатель окна tNotifyWindowsList=class(tMultiThreadList) public procedure Add(WindowsHanler:HWND); function Remove(WindowsHanler:HWND): Integer; procedure NotifyByList(Sender:TObject; Event:tMI1201_Thread_Event); end; // данные по массе для фоновой развертки tMassData=record Step:tMass; StepMultiplicator:integer; LastStep:tMass; end; // данные статистики фонового измерения tPointTimeData=record // Время одного замера (запуск-получение данных- запись в хранилище) TimeInterval:cardinal; LastTime,MinTime,MaxTime:cardinal; FullTime:int64; Number:cardinal; end; tIdleTimeData=record // Время ожидания окончания замера LastTime,MinTime,MaxTime:cardinal; FullTime:int64; Number:cardinal; end; tCycleTimeData=record // Время цикла замера (запуск-запуск следующего) Start:cardinal; LastTime,MinTime,MaxTime:cardinal; FullTime:int64; Number:cardinal; end; tSpecterDateTime=record StartDateTime,EndDateTime:TDateTime; end; // Статистические данные tStatisticalData=record // Данные по времени на считывание точки (статистические данные) PointTimeData:tPointTimeData; // Данные по времени на цикл считывание точки (статистические данные) CycleTimeData:tCycleTimeData; // Данные по времени пустого ожидания за цикл считывание точки (статистические данные) IdleTimeData:tIdleTimeData; // Данные по времени на снятие спектра DateTime:tSpecterDateTime; end; type tVoltmeterData=record Timer:tMMTimer; Channels:tVoltageChannels; Time:cardinal; Data:array[tVoltageChannel] of tMkVolts; end; tSourcesData=record AutoCloseTimes:array[tSource] of cardinal; Timer:tMMTimer; OneSecondTickTimer:tMMTimer; StartDateTime:TDateTime; StartTime:tTimeInterval; OpenTime:tTimeInterval; end; tPNCCalibrationParameter=(pncAll, pncK, pncU0, pncZeroLevel); tDataGetAndStoreControlFlag=(dgsfAccumulate, dgsfDoNotStoreData, dgsfUseGlobalIntegrationTime, dgsfCalcStatistic); tDataGetAndStoreControlFlags=set of tDataGetAndStoreControlFlag; tSignalsVEx=record Data:tSignalsV; ControlFlags:tDataGetAndStoreControlFlags; end; tMsThread = class(TThread) private // Ожидание завершения, по истечении предпринимаются решительные действия; prTerminateTimeOut:cardinal; // Доступ к реестру prLocalRegistry:tRegistry; // Данные по шкале масс prMass:tMassData; // Код ошибки prError:tError; // Данные для блокировки prDataLock{,prMsSpLock}:TMultiReadExclusiveWriteSynchronizer; prDataWriteLockCount,prDataReadLockCount:integer; // Контроль завершения потока prEventRunning:TEvent; prEventNotExecuted:TEvent; prFinished:boolean; // Внутренние флаги prFlags:tFlags; // Внутренние переключатели, относящиеся к данному потоку prSwitches:tMSSwitches; // Фоновое переключение prSwitchesOnX:tMSSwitches; prSwitchesOffX:tMSSwitches; // Начальная емкость данных для спектра prDefaultCapacity:integer; // Приблизительный начальный размер данных prInitialDataSize:cardinal; // Последний считанный сигнал prSignalsV:tSignalsVEx; // Статистические данные prStatistic:tStatisticalData; // Список оповещения prNotifyList:tNotifyList; prNotifyWindowsList:tNotifyWindowsList; // Имя файла конфигурации prConfigFileName:string; // Данные автообновления вольтметра prVoltmeter:tVoltmeterData; // Данные автообновления вольтметра prSourcesData:tSourcesData; // Данные спектра prDataSeries:tDataSeries; // Размещения спектра prAssignedDataSeries:tSeriesSet; // Данные по массе для фоновой установки массы prBackgroundMass:tMass; // Данные по счетчику для фоновой установки счетчика prBackgroundCounter:tCounter; // Главный канал измерения prMainChannel:tSeries; // Формат текстового файла prFileFormat:tMsDataFileFormat; // Набор каналов ПНЧ для фоновой калибровки prPNCCalibrateChannelSet:tChannels; // Набор активных (размещенных в памяти) серий (данных измерения для каналов) prActiveSeries:tSeriesSet; // Время интегрирования prIntegrationTime:cardinal; // для быстрого доступа prIntegrationTimeX:cardinal; // для фоновой установки // список диапазонов prRanges:tRangeList; // активный диапазон prActiveRange:TRange; // номер активного диапазона prActiveRangeIndex:integer; // таймер проверки флагов опасности для оборудования prEmergencyCheckTimer:tMMTimer; // последнее значение флагов опасности для оборудования считанное таймером prCtrlFlags:tCtrlFlags; // предыдущее значение флагов опасности для оборудования считанное таймером prPredCtrlFlags:tCtrlFlags; // список устройств для установки значений prDevicesToReset:tDevices; prDevicesToSet:tDevices; prDevicesValuesToSet:array[tDevice] of integer; // указатель на процедуру фонового измерения prDoMeasure:procedure of object; // ограничители частоты вызовов оповещения (для уменьшения частоты перерисовки) prMassChangeTimeInterval:tTimeInterval; prNewPointTimeInterval:tTimeInterval; prPointsNumberChangedTimeInterval:tTimeInterval; // Значение различия показаний по счетчику и ИМЧ, // при превышении которого шкала масс синхронизуется автоманически prAutoMassScaleSyncWithIMChValue:tMass; // Число сделанных автосинхронизаций prAutoMassScaleSyncWithIMChCount:cardinal; prAutoSaveTimer:tMMTimer; // Имя файла данных в который записываются-читаются серии функцией // DataSeriesWriteDataToFileX, DataSeriesReadDataFromFileX prDataFileName:string; prExtendedCommandsList,prExecutingCommandsList:TMultiThreadMethodsList; prDataAutoSaveCount:cardinal; // размещение и инициализация внутренних данных function LoadOldDataOnInit:boolean; procedure Init; // деинициализация внутренних данных procedure Done; // установка-очистка внутренних флагов procedure FlagSet(f:tFlag); procedure FlagClear(f:tFlag); procedure FlagState(f:tFlag; state:boolean); // установка-очистка флагов фоновых команд procedure XCmdSet(c:tXCommand); procedure XCmdClear(c:tXCommand); // установка начального объема хранилища данных procedure InitialCapacitySet(ic:integer); // управление клапанами procedure ValveSetOpen(AValve:tSource); function ValveGetOpen:tSource; function ValveAutoCloseTimeGet:cardinal; procedure ValveAutoCloseTimeSet(dT_ms:cardinal); function ValveAutoCloseTimesGet(AValve:tSource):cardinal; procedure ValveAutoCloseTimesSet(AValve:tSource; dT_ms:cardinal); function ValveAutoCloseEnabledGet:boolean; function ValveAutoCloseActivateEx(AValve:tSource):boolean; function ValveAutoCloseActivate:boolean; procedure ValveAutoCloseActivateTiks; procedure ValveTerminateTimers; procedure ValveDoAutoClose(Sender:tObject); procedure ValveOneSecondTimerTick(Sender:tObject); function ValveAutoCloseTimeLeftGet:cardinal; function ValveOpenedTimeGet:cardinal; // процедуры оповещения procedure DoEvMassChanged; procedure DoEvNewPoint; procedure DoEvPointsNumberChanged; // Операция проверки флагов опасности для оборудования таймером procedure DoEmergencyCheck(Sender:tObject); // управление временем интегрирования function IntegrationTimeGet:cardinal; procedure IntegrationTimeSet(Time:cardinal); procedure IntegrationTimeSetInternal(Time:word); procedure IntegrationTimeSetX(Time:cardinal); // готовность к фоновому измерению function MeasureAvailableEx:boolean; // управление переключателями function SwitchesGet:tMSSwitches; procedure SwitchesSet(ASwitches:tMSSwitches); // автоизмерение напряжений function VoltmeterGet(Channel:tVoltageChannel):tMkVolts; function VoltmeterCurrentGet:tMkVolts; function VoltmeterCurrentChannelGet:tVoltageChannel; procedure VoltmeterCurrentChannelSet(Channel:tVoltageChannel); function VoltmeterUpDateGet(Channel:tVoltageChannel):tMkVolts; procedure VoltmeterUpdateTimeSet(dT:cardinal); procedure VoltmeterUpdateChannelsSet(Channels:tVoltageChannels); procedure VoltmeterInitTimer; procedure VoltmeterDoAutoUpdate(Sender:TObject); // отработка изменений диапазона procedure HandleRangesChanged(Sender:TObject; ARangeIndex:integer; AItem:tRangeItem); // отработка изменения списка диапазонов procedure HandleRangeListChanged(Sender:TObject); // запуск-останов развертки диапазонов procedure RangesProcessStartStop; // развертка диапазона ARange function RangeScroll(ARange:tRange):boolean; // изменение массы при развертке диапазона ARange function RangeChangeMass(ARange:tRange):BOOLEAN; // получение сигнала при развертке диапазона ARange procedure RangeDataGet(ARange:tRange); // Сохранение данных по сигналу при развертке диапазона ARange // procedure RangeDataStore(ARange:tRange); // Активный диапазон function RangeActiveGet:tRange; // Следующий диапазон function RangeNext:tRange; // Установка номера активного диапазона procedure ActiveRangeIndexSet(AIndex:integer); // Установка величины границы автосинхронизации с ИМЧ procedure AutoMassScaleSyncWithIMChValueSet(AValue:tMass); // Выполнение процедуры автосинхронизации с ИМЧ procedure DoAutoMassScaleSyncWithIMCh; procedure FileWriteFormattedStrings(AFileFormat:tMsDataFileFormat; i0,i1:integer); procedure FileWriteFormattedColumns(AFileFormat:tMsDataFileFormat; i0,i1:integer); procedure DoSwitchesX; function MainChannelNameGet:string; function ChannelNameGet(AChannel:tSeries):string; // Автосохранение данных измерения procedure DataSeriesWriteDataAuto(Sender:tObject); procedure DataAutoSaveSet(Enabled:boolean); function DataAutoSaveGet:boolean; procedure DataAutoSaveIntervalSet(Interval:word); function DataAutoSaveIntervalGet:word; protected // Драйвер масс-спектрометра prMsSp:tMsSp; prXCommands:tXCommands; // Завершение развертки дипазонов procedure DoOnRangesScrollComplete; virtual; procedure DoOnRangeScrollComplete(ARange:TRange); // Установка кода ошибки procedure ErrorSet(e:tError); // Текстовое сообщение об ошибке function ErrorMsgGet:string; // Код ошибки драйвера function HardwareErrorGet:word; // Запуск измерения procedure Start; // Время ожидания завершения операции function TimeOutGet:cardinal; // Очистка хранилища данных procedure ClearAll; // Частичная очистка хранилища данных, указанной ASeries procedure ClearSer(ASeries:tSeries); // Процедура фонового измерения procedure DoMeasure; // Процедура ПРОСТОГО измерения procedure DoMeasureSimple; // Процедура измерения ДИАПАЗОНОВ procedure DoMeasureRange; // Процедура изменения массы для ПРОСТОГО измерения function NewMass:tMass; // Процедуры получения сигнала { Процедура получения и запоминания данных сигнала работают так: 1) при вызове запускается измерение на приборе; 2) пока идет измерение данные предыдущего измерения заносятся в глобальное хранилище; 3) новые данные помещаются в Signals, НО НЕ ЗАНОСЯТСЯ В ХРАНИЛИЩЕ } function DataGetAndStoreEx(AMass:tMass; Accumulate:boolean):boolean; function DataGetAndStoreAdv(AMass:tMass; AIntegrationTime:word; AControlFlags:tDataGetAndStoreControlFlags):boolean; // Принудительная запись данных последнего измерения в хранилище function DataStore:boolean; function DataDirtyGet:boolean; procedure DataDirtySet(AState:boolean); property DataDirty:boolean read DataDirtyGet write DataDirtySet; // Процедура записи сигнала в хранилище procedure DataStoreEx(const ASignals:tSignalsV; Accumulate:boolean); // Полный интервал времени развертки function DeltaDateTimeGet:tDateTime; // Установка умножителя шага procedure StepMultiplicatorSet(SMult:integer); // Включение автоподстройки procedure AutoTuningSet(x:boolean); function AutoTuningGet:boolean; // Получение-установка данных по калибровке шкалы масс function CalibrationMGet:tMass; procedure CalibrationMSet(Mass:tMass); function CalibrationKGet:tMass; procedure CalibrationKSet(Koef:tMass); function Calibration_dCGet:tMass; // Получение-установка данных по калибровке ИМЧ function IMChCalibrationMGet:tMass; procedure IMChCalibrationMSet(M0:tMass); function IMChCalibrationKGet:tMass; procedure IMChCalibrationKSet(K:tMass); function IMCh_MassGet:tMass; // Получение данных по флагам оборудования function EqupmentFlagsGet:tEmergencyFlags; // Основная процедура MsThread procedure Execute; override; // Процедура фонового выполнения команд MsThread procedure ExecuteXCommands; // Процедура выполнения команд MsThread procedure ExecuteXCommandsEx(f:tXCommands); virtual; // Процедура РАСШИРЕННОГО выполнения команд MsThread procedure DoExtendedCommands; // Код ошибки MsThread property ErrorCode:tError read prError write ErrorSet; // Процедуры оповещения о событиях MsThread procedure Notify(Event:tMI1201_Thread_EventID); procedure NotifyEx(Event:tMI1201_Thread_EventID; parByte:byte; parWord:word); procedure NotifyA(Event:tMI1201_Thread_Event); // Доступ к реестру function Registry:tRegistry; // Установка-чтение имени файла конфигурации procedure ConfigFileNameSet(NewName:string); function ConfigFileNameGet:string; // Запись/чтение конфигурации MsThread в реестр procedure ConfigSave; virtual; procedure ConfigRead; virtual; // Восстановление (актуализация) конфигурации после включения procedure ConfigRestore; virtual; // Запись/чтение конфигурации драйвера в файл function DriverConfigSave:boolean; function DriverConfigRead:boolean; // Установка-чтение счетчика procedure CounterSet(c:tCounter); procedure CounterXSet(c:tCounter); function CounterGet:tCounter; // Установка-чтение массы procedure MassSet0(m:tMass); // без ожидания procedure MassSet(m:tMass); procedure MassXSet(m:tMass); function MassGet:tMass; // Установка-чтение шага по шкале масс procedure MassStepSet(m:tMass); function MassMinStepGet:tMass; function MassStepAbsoluteMinGet:tMass; // Чтение признака аппаратного предела function MassLimitGet:boolean; function MassLimitLowGet:boolean; function MassLimitUpGet:boolean; // Сброс признака аппаратного предела procedure MassLimitClear; // Установка-чтение калибровки ПНЧ function PNCCalibrationGet(cl:tChannel; par:tPNCCalibrationParameter):double; procedure PNCCalibrationSet(cl:tChannel; par:tPNCCalibrationParameter; Value:double); // Чтение состояния фонового измерения function MeasureStateGet:tMeasureState; // Чтение направления развертки function ScrollDirectionGet:tScrollDirection; function DataSeriesGet(s:tSeries):tMSSeries; function AssignedDataSeriesGet:tSeriesSet; function DataPointsCountGet:cardinal; function DataSeriesFirstIndex(Mass:tMass):integer; function DataSeriesLastIndex(Mass:tMass):integer; // Очистка хранилища - одной серии ASeries function ClearSeries(ASeries:tSeries):boolean; procedure DataMassShift(dM:tMass); procedure DataMassCalibrationChange(const OldCalibration:tMassCalibration); procedure DataMassToCounter; procedure DataCounterToMass(CounterShift:integer); procedure MainChannelSet(AChannel:tSeries); function DataSeriesActiveGet:tSeriesSet; procedure DataSeriesActiveSet(ActiveSeries:tSeriesSet); procedure DataSeriesDrawLock(Sender:tObject); procedure DataSeriesDrawUnLock(Sender:tObject); procedure DataDeleteAllXRangeByValues(AMin,AMax:tMAss); procedure DataDeleteXRangeByValues(ASeriesSet:TSeriesSet; AMin,AMax:tMAss); procedure DataSeriesWrite(AStream:TStream); procedure DataSeriesRead(AStream:TStream); // Блокировка доступа к данным измерения procedure DataBeginWrite; procedure DataEndWrite; procedure DataBeginRead; procedure DataEndRead; property DataReadLockCount:integer read prDataReadLockCount; property DataWriteLockCount:integer read prDataWriteLockCount; // Запись автокомментария в текстовый файл procedure FileWriteAutoComment(AFileFormat:tMsDataFileFormat); // Установка формата текстового файла procedure FileFormatSet(AFileFormat:tMsDataFileFormat); function RegistryOpenRootKey(ForWrite:boolean):boolean; function RegistryOpenKey(const SubKey:string; ForWrite:boolean):boolean; // Состояние ЛОКАЛЬНЫХ переключателей property ThreadSwitches:tMSSwitches read prSwitches; public // Объект блокировки доступа к MsThread Ready:tMCADThreadEvent; constructor Create(const Name:string); destructor Destroy; override; // Ожидание запуска витка function WaitForStart(TimeOut:cardinal):tWaitResult; // Остановка (завершение) витка MsThread procedure Terminate; function TerminateAndWait:tWaitResult; function WaitForTerminate(TimeOut:cardinal):tWaitResult; property Finished:boolean read prFinished; property InitialCapacity:integer read prDefaultCapacity write InitialCapacitySet; property InitialDataSize:cardinal read prInitialDataSize; // Включение-выключение оборудования (без остановки витка) function SetON:boolean; function SetOff:boolean; procedure SetONX; procedure SetOffX; function IsON:boolean; // Проверка наличия подключения прибора к сети function IsPowerON:boolean; // Состояние витка MsThread property MeasureState:tMeasureState read MeasureStateGet; // готовность к фоновому измерению function MeasureAvailable:boolean; // Направление развертки property ScrollDirection:tScrollDirection read ScrollDirectionGet; // Включение-выключение фонового измерения procedure Stop; // ВЫКЛючение развертки и измерения procedure Resume; // ВКЛЮчение развертки и измерения procedure Pause; // выключение развертки (StepMultiplicator:=0); function ResumeThreadIfSuspended:boolean; // Возобновление исполнения витка // Прерывание операции драйвера procedure Abort; // Доступ к MsThread с ожиданием готовности function WaitForLimitedTime(ATimeOut:cardinal):TWaitResult; function Wait:TWaitResult; function WaitReady:boolean; function SetReady:boolean; // Текущее время ожидания готовности property TimeOut:cardinal read TimeOutGet; // Флаги состояния витка MsThread property Flags:tFlags read prFlags; // Флаги состояния оборудования property EmergencyFlags:tCtrlFlags read prCtrlFlags; // Установка параметров устройства Device в значение value procedure DeviceSet(Device:tDevice; value:integer); procedure DevicesDoSet; procedure DeviceSetX(Device:tDevice; value:integer); function DeviceSetEx(Device:tDevice; value:integer):boolean; function DeviceIndicatorGet(Device:tDevice):integer; // Хранилище данных property DataSeries[series:tSeries]:tMSSeries read DataSeriesGet; procedure DataSeriesOff(Owner:tObject); procedure DataSeriesAllOff; function DataSeriesCount:integer; // число серий function DataValidSeriesCount(ASeriesSet:tSeriesSet):integer; // число реально существующих в хранилище серий из набора ASeriesSet property DataPointsCount:cardinal read DataPointsCountGet; // максимальное число точек во всех сериях function DataPointCount(ASeriesSet:TSeriesSet):integer; // максимальное число точек в сериях из набора ASeriesSe // активные (отображающиеся) серии property DataSeriesActive:tSeriesSet read DataSeriesActiveGet write DataSeriesActiveSet; property AssignedDataSeries:tSeriesSet read AssignedDataSeriesGet; // число реально размещенных серий // Получение сигнала для массы AMass с канала AChannel function DataSignalGet(AMass:tMass; AChannel:tSeries; var ASignal:tChannelSignal):boolean; function DataSignalGetLInterp(AMass:tMass; AChannel:tSeries; var ASignal:tChannelSignal):boolean; function DataSignalGetEx(AMass:tMass; AChannel:tSeries; ControlFlags:tSignalsGetFlags; var ASignal:tChannelSignal):boolean; function SignalGet(AMass:tMass; AChannel:tSeries):tChannelSignal; function SignalGetDirect(AMass:tMass; AChannel:tSeries):tChannelSignal; function SignalGetEx(AMass:tMass; AChannel:tSeries; ControlFlags:tSignalsGetFlags; var ASignal:tChannelSignal):boolean; // Запись (чтение) данных хранилища в (из) файл (файла) procedure DataSeriesWriteDataToFile(const AFileName:string); procedure DataSeriesReadDataFromFile(const AFileName:string); procedure DataSeriesWriteData; procedure DataSeriesReadData; procedure DataSeriesWriteDataToFileX(const AFileName:string); procedure DataSeriesReadDataFromFileX(const AFileName:string); // Очистка хранилища function Clear:boolean; // Основной канал - на нем проводятся одноканальные операции property MainChannel:tSeries read prMainChannel write MainChannelSet; property MainChannelName:string read MainChannelNameGet; // Число точек (максимальное) в диапазоне [AMin,AMax] для набора ASeriesSet function DataRangePointsCount(ASeriesSet:tSeriesSet; AMin,AMax:tMass):integer; // Заполнение массива MathCAD точеками из диапазона [AMin,AMax] для набора ASeriesSet function DataArrayGet(const AMcadArray:ComplexArray; ASeriesSet:tSeriesSet; AMin,AMax:tMass):boolean; function DataPointGet(AMass:tMass; const ASignals:ComplexArray):boolean; // Управление ошибками property Error:tError read prError; // код ошибки property ErrorMsg:string read ErrorMsgGet; // текст-сообщение об ошибке property HardwareError:word read HardwareErrorGet; // код ошибки драйвера procedure ResetError; // сброс ошибки function IsError:boolean; // TRUE - состояние ошибки function IsNoError:boolean; // TRUE - состояние БЕЗ ошибки function DriverErrorGetFirstCtrlWith:tController; function DriverErrorCodeFromSubCtrl(c:tController):word; // Текущая установленная масса для масс-спектрометра (присваивание устанавливает новую массу) property Mass:tMass read MassGet write MassSet; // Текущая установленная фоновая масса для масс-спектрометра // параметры и функции "имя_X" делают то же самое, что и функции "имя", // но в витке MsThread, т.е. в 'фоновом' режиме. Возврат из // функций "имя_X" происходит немедленно, а выполнение операции, // заданной функцией "имя_X" или присваиванием параметру "имя_X" происходит // после завершения текущей операции. // Возврат из функции "имя" или присваиванием параметру "имя_X" // происходит по завершению операции. property MassX:tMass read prBackgroundMass write MassXSet; // Текущая установленное значение счетчика для масс-спектрометра (присваивание устанавливает новое значение счетчика) property Counter:tCounter read CounterGet write CounterSet; // Фоновое значение счетчика для масс-спектрометра (присваивание устанавливает новое значение счетчика) property CounterX:tCounter read prBackgroundCounter write CounterXSet; // Округление массы до ближайшего доступного значения function MassAjust(AMass:tMass):tMass; // Текущий установленный шаг развертки по шкале масс property MassStep:tMass read prMass.Step write MassStepSet; // Текущий минимально возможный шаг развертки по шкале масс property MassStepAbsoluteMin:tMass read MassStepAbsoluteMinGet; // Минимально возможный шаг развертки по шкале масс для массы AMass function MassStepAbsoluteMinCalculate(AMass:tMass):tMass; // Реальный шаг развертки по шкале масс с которым выполняется развертка, // это значение не меньше MassStepAbsoluteMin и не больше MassStep property MassStepMin:tMass read MassMinStepGet; // Реальный шаг развертки по шкале масс с которым выполнялась быя развертка, // для массы AMass и шага AStep function MassStepMinCalculate(AMass,AStep:tMass):tMass; // Значение последнего выполненого шага авторазвертки property MassStepLast:tMass read prMass.LastStep; // Достигнут аппаратный предел на шкале масс property MassLimit:boolean read MassLimitGet; property MassLimitLow:boolean read MassLimitLowGet; property MassLimitUp:boolean read MassLimitUpGet; // Установить ноль по аппартному пределу procedure MassZeroSetByHardware; procedure MassZeroSetByHardwareX; // Установить текущую массу БЕЗ изменения магнитного поля // (изменение калибровки шкалы масс) procedure RefineMass(m:tMass); procedure RefineMassEx(m:tMass; DoShiftData:boolean); // Установить текущий счетчик БЕЗ изменения магнитного поля // (изменение калибровки шкалы масс) procedure RefineCounter(c:integer); // Установить текущую калибровку шкалы масс function CalibrationSet(Mass0,Coefficient:tMass):boolean; // Прочитать текущую калибровку шкалы масс procedure CalibrationGet(var Mass0,Coefficient:tMass); property K:tMass read CalibrationKGet write CalibrationKSet; property M0:tMass read CalibrationMGet write CalibrationMSet; property dC:tMass read Calibration_dCGet; // Преобразовать счетчик -> массу function Counter2Mass(c:integer):tMass; // Преобразовать массу -> счетчик function Mass2Counter(m:tMass):integer; // Значение сигналов (последнее полученное) property Signals:tSignalsV read prSignalsV.Data; // Статистические данные property PointTimeData:tPointTimeData read prStatistic.PointTimeData; property CycleTimeData:tCycleTimeData read prStatistic.CycleTimeData; property IdleTimeData:tIdleTimeData read prStatistic.IdleTimeData; // очистка статистических данных по времени на измерение точки procedure StatisticDataClear; procedure PointTimeDataClear; procedure CycleTimeDataClear; procedure IdleTimeDataClear; // Калибровка ПНЧ - преобразование числа импульсов в секунду к вольтам property PNCCalibration[cl:tChannel; par:tPNCCalibrationParameter]:double read PNCCalibrationGet write PNCCalibrationSet; // Выполнить калибровку ПНЧ procedure PNCCalibrate; procedure PNCCalibrateX; // Выполнить калибровку ПНЧ для каналов ChannelSet procedure PNCCalibrateEx(ChannelSet:tChannels); procedure PNCCalibrateExX(ChannelSet:tChannels); // Калибровка ИМЧ (индикатора массовых чисел) function IMChCalibrationSet(Mass0,Coefficient:tMass):boolean; procedure IMChCalibrationGet(var Mass0,Coefficient:tMass); property IMCh_M0:tMass read IMChCalibrationMGet write IMChCalibrationMSet; property IMCh_K:tMass read IMChCalibrationKGet write IMChCalibrationKSet; // Вычислить калибровку ИМЧ на основании текущей шкалы масс procedure IMChCalibrationCalculate; procedure IMChCalibrationRefineOrigin; // Масса по ИМЧ на основании текущей калибровки property IMCh_Mass:tMass read IMCh_MassGet; // Проверка ошибки оборудования - // если ошибка есть - возвращается TRUE и устанавливается состояние ошибки // ecHardwareError для MsThread function CheckHardwareError:boolean; // Чтение значения параметра устройства Device function DeviceGet(Device:tDevice):integer; property Device[d:tDevice]:integer read DeviceGet write DeviceSet; property DeviceX[d:tDevice]:integer read DeviceGet write DeviceSetX; property DeviceIndicators[d:tDevice]:integer read DeviceIndicatorGet; // Чтение значения шага параметра устройства Device function DeviceStepGet(Device:tDevice):cardinal; property DeviceStep[d:tDevice]:cardinal read DeviceStepGet; // Чтение максимального числа шагов параметра устройства Device function DeviceMaxStepCount(Device:tDevice):cardinal; // Переустановка текущего значения параметра устройства Device function DeviceReset(d:tDevice):boolean; procedure DeviceResetX(d:tDevice); // Переустановка текущего значения параметров набора устройств ADevices function DevicesReset(ADevices:tDevices):tDevices; // Время, потребное на выполнение DeviceReset function DeviceOperationTime_ms(ADevice:tDevice; AStep:integer):integer; function DeviceOperationTime_msToSetValue(ADevice:tDevice; AValue:integer):integer; // Выполнить один шаг изменения значения параметра устройства Device // в напрвлении Direction function DeviceDoStep(d:tDevice; Direction:TUpDownDirection):boolean; // Набор устройств для которых значения параметров установлены достоверно // - это связано с отсутствием в приборе обратной связи по некоторым устройствам function DevicesValidated:tDevices; // Аппаратные флаги оборудования property EqupmentFlags:tEmergencyFlags read EqupmentFlagsGet; // Умножитель шага по шкале масс для авторазвертки. Равный 0 - останавливает развертку. property StepMultiplicator:integer read prMass.StepMultiplicator write StepMultiplicatorSet; // TRUE, если cl правильный и доступный номер канала измерений function ChannelValid(cl:byte):boolean; // Время запуска развертки property StartDateTime:tDateTime read prStatistic.DateTime.StartDateTime; // Время остановки развертки property EndDateTime:tDateTime read prStatistic.DateTime.EndDateTime; // Полный интервал времени развертки property DeltaDateTime:tDateTime read DeltaDateTimeGEt; // Открытый клапан property Valve:tSource read ValveGetOpen write ValveSetOpen; // Время открытия клапана property ValveOpenDateTime:tDateTime read prSourcesData.StartDateTime; // Интервал времени, в течение которого клапан открыт property ValveOpenedTime:cardinal read ValveOpenedTimeGet; // Интервалы времени, после которого клапан Valve будет автоматически закрыт property ValveAutoCloseTimes[Valve:tSource]:cardinal read ValveAutoCloseTimesGet write ValveAutoCloseTimesSet; // Интервалы времени, после которого открытый клапан будет автоматически закрыт property ValveAutoCloseTime:cardinal read ValveAutoCloseTimeGet write ValveAutoCloseTimeSet; // Авозакрытие клапана включено - TRUE property ValveAutoCloseEnabled:boolean read ValveAutoCloseEnabledGet; // Интервалы времени, оставшийся до автозакрытия открытого клапана property ValveAutoCloseTimeLeft:cardinal read ValveAutoCloseTimeLeftGet; // Текущие показания вольтметра property VoltmeterCurrent:tMkVolts read VoltmeterCurrentGet; // Текущий канал вольтметра property VoltmeterCurrentChannel: tVoltageChannel read VoltmeterCurrentChannelGet write VoltmeterCurrentChannelSet; // Показания вольтметра для канала MeasurePoint property Voltmeter[MeasurePoint:tVoltageChannel]:tMkVolts read VoltmeterGet; // Период автообновления данных по вольтметру для канала MeasurePoint property VoltmeterUpdateTime:cardinal read prVoltmeter.Time write VoltmeterUpdateTimeSet; // Каналы автообновления property VoltmeterUpdateChannels:tVoltageChannels read prVoltmeter.Channels write VoltmeterUpdateChannelsSet; // Данные автообновления для канала MeasurePoint property VoltmeterUpdateData[MeasurePoint:tVoltageChannel]:tMkVolts read VoltmeterUpDateGet; // Время интегрирования property IntegrationTime:cardinal read IntegrationTimeGet write IntegrationTimeSet; property IntegrationTimeX:cardinal read IntegrationTimeGet write IntegrationTimeSetX; // Автоподстройка property AutoTuning:boolean read AutoTuningGet write AutoTuningSet; // Состояние переключателей property Switches:tMSSwitches read SwitchesGet write SwitchesSet; // Включить function SwitchON(s:tMSSwitch):boolean; procedure SwitchOnX(s:tMSSwitch); // Выключить function SwitchOFF(s:tMSSwitch):boolean; procedure SwitchOffX(s:tMSSwitch); // Добавить процедуру оповещения об изменениях состояния MsThread procedure NotifyAdd(var NotifyHanler:tMI1201_Thread_NotifyData); // Удалить процедуру оповещения об изменениях состояния MsThread procedure NotifyDel(var NotifyHanler:tMI1201_Thread_NotifyData); // Имя файла сохранения-восстановления конфигурации драйвера. property ConfigFileName:string read prConfigFileName write ConfigFileNameSet; // Имя файла сохранения-восстановления данных по масс-спектру. // Генерируется так DataFileName:=ConfigFileName+'.dat'. function DataFileName:string; // Описание формата текстового файла данных, используемого по умолчанию property FileFormat:tMsDataFileFormat read prFileFormat write FileFormatSet; // Запись текстового файла данных с форматом AFileFormat procedure FileWriteFormatted(AFileFormat:tMsDataFileFormat); // Запись текстового файла данных с форматом по умолчанию procedure FileWrite; // Фоновая запись текстового файла данных с форматом по умолчанию procedure FileWriteX; property Ranges:tRangeList read prRanges; property RangeActive:TRange read prActiveRange; property RangeActiveIndex:integer read prActiveRangeIndex write ActiveRangeIndexSet; function RangeActiveSet(AIndex:integer; AState:boolean):boolean; function RangeIsActive(AIndex:integer):boolean; property AutoMassScaleSyncWithIMChValue:tMass read prAutoMassScaleSyncWithIMChValue write AutoMassScaleSyncWithIMChValueSet; property AutoMassScaleSyncWithIMChCount:cardinal read prAutoMassScaleSyncWithIMChCount; property TerminateTimeOut:cardinal read prTerminateTimeOut write prTerminateTimeOut; function ExtendedCommand(ACommand:tNotifyEvent):integer; // Установка калибровки шкалы масс function MassCalibrationGet:tMassCalibration; function MassCalibrationSet(const AMassCalibration:tMassCalibration):boolean; procedure MassCalibrationSetP(const AMassCalibration:tMassCalibration); property MassCalibration:tMassCalibration read MassCalibrationGet write MassCalibrationSetP; property DataAutoSave:boolean read DataAutoSaveGet write DataAutoSaveSet; property DataAutoSaveInterval:word { минуты } read DataAutoSaveIntervalGet write DataAutoSaveIntervalSet; property DataAutoSaveCount:cardinal read prDataAutoSaveCount; end; function SwitchesToStr(ASwitches:tMSSwitches; Sep:string):string; implementation Uses TeEngine,MmSystem, MCAD_MiscFuncs; function SwitchesToStr(ASwitches:tMSSwitches; Sep:string):string; var s:tMSSwitch; begin if Sep='' then Sep:='; '; for s:=Low(s) to High(s) do begin if not (s in cInActiveSwitches) then begin Result:=Result+cMSSwitchesName[s]+': '; if s in ASwitches then begin Result:=Result+'1'; end else begin Result:=Result+'0'; end; if s<>High(s) then Result:=Result+Sep; end; end; end; function tMsThread.RangeActiveSet(AIndex:integer; AState:boolean):boolean; begin Result:=(0<=AIndex) and (AIndex0) then Result:=RangeNext else Stop; end; end; procedure tMsThread.ActiveRangeIndexSet(AIndex:integer); var r:tRange; begin prActiveRangeIndex:=AIndex; r:=RangeActiveGet; if Assigned(r) then begin try prActiveRange.Assign(r); if prActiveRange.IntegrationTime=0 then prActiveRange.IntegrationTime:=IntegrationTime; if prActiveRange.Step=0 then begin prActiveRange.Step:=MassStep; end; prActiveRange.ResetMass(StepMultiplicator); prActiveRange.Mass:=MassAjust(prActiveRange.Mass); prActiveRange.PointProcessed:=FALSE; if (prActiveRange.ClearOnStart) then begin try DataDeleteAllXRangeByValues(prActiveRange.Min,prActiveRange.Max); except prActiveRange.Max:=prActiveRange.Max; end; end; prActiveRange.Enabled:=(mstUseRanges in prSwitches); except end; end; end; function tMsThread.RangeActiveGet:tRange; begin if not (mstUseRanges in prSwitches) then begin Result:=NIL; end else if (RangeActiveIndex>=0) and (RangeActiveIndex[]) then begin FlagClear(fMeasureCycleContinued); ExecuteXCommands; end else if (fError in Flags) then begin FlagClear(fMeasureCycleContinued); if CheckHardwareError then begin Suspend; end; end else if (fStopMeasure in Flags) then begin FlagClear(fMeasureCycleContinued); prActiveRange.Enabled:=FALSE; prStatistic.DateTime.EndDateTime:=Now(); Notify(evStop); Suspend; end else if MeasureAvailableEx then begin DoMeasure; end else begin FlagClear(fMeasureCycleContinued); Suspend; end; Priority:=tpNormal; except end; end; Priority:=tpHighest; Done; except end; Notify(evTerminate); prEventNotExecuted.SetEvent; prEventRunning.ResetEvent; prFinished:=TRUE; Suspend; end; procedure tMsThread.ExecuteXCommandsEx(f:tXCommands); begin if (fDoFileWrite in f) then begin FileWrite; end; if (fDoSetOn in f) then begin SetOn; end; if (fDoSetOff in f) then begin SetOff; end; if (fDoChangeMass in f) then begin Mass:=MassX; end else if (fDoChangeCounter in f) then begin Counter:=CounterX; end; if (fDoSetSwitches in f) then begin DoSwitchesX; end; if (fDoPNCCalibration in f) then begin if (fDoPNCCalibrationEx in f) then begin PNCCalibrateEx(prPNCCalibrateChannelSet); end else begin PNCCalibrate; end; end; if (fDoResetDevice in f) then begin DevicesReset(prDevicesToReset); end; if (fDoSetIntegrationTime in f) then begin {$IFNdef DBG} временное исправление {$EndIF} IntegrationTime:=prIntegrationTimeX; end; if (fDoDataFileRead in f) then begin DataSeriesReadDataFromFile(prDataFileName); end; if (fDoDataFileWrite in f) then begin DataSeriesWriteDataToFile(prDataFileName); end; if (fDoMassZeroSetByHardware in f) then begin MassZeroSetByHardware; end; if (fDoSetDevice in f) then begin DevicesDoSet; end; if (fDoExtendedCommands in f) then begin DoExtendedCommands; end; end; procedure tMsThread.ExecuteXCommands; var f:tXCommands; OldPr:TThreadPriority; begin f:=prXCommands; prXCommands:=prXCommands-f; {$IFNdef DBG} временное исправление {$EndIF} OldPr:=Priority; // Priority:=tpLower; ExecuteXCommandsEx(f); Priority:=OldPr; end; procedure tMsThread.DoMeasureSimple; begin DataGetAndStoreEx(NewMass,(mstAccumulation in prSwitches)); end; function tMsThread.RangeChangeMass(ARange:tRange):boolean; begin Result:=TRUE; if ARange.PointProcessed then begin ARange.Mass:=MassAjust(ARange.Mass+MassStepMinCalculate(ARange.Mass,ARange.Step)); if ARange.InRange(MassStepAbsoluteMinCalculate(ARange.Mass)) then begin ARange.PointProcessed:=FALSE; end else begin ARange.Enabled:=FALSE; DoOnRangeScrollComplete(ARange); Result:=ARange.Enabled; end; end; end; function tMsThread.RangeScroll(ARange:tRange):boolean; var DoDataGet:boolean; begin Result:=true; if not ARange.Enabled then begin Result:=FALSE; end else begin if RangeChangeMass(ARange) then begin DoDataGet:= not ARange.UseExistingPoints or (prDataSeries[sIntegrationTime].xValues.Locate(ARange.Mass)=-1); if DoDataGet then begin { MsSpBeginWrite; try} // MassSet0(ARange.Mass); RangeDataGet(ARange); { finally MsSpEndWrite; end;} end else begin ARange.PointProcessed:=TRUE; end; end else begin Result:=true; end; end; end; procedure tMsThread.DoMeasureRange; begin if (StepMultiplicator<>0) then begin if not prActiveRange.Enabled then RangeNext; RangeScroll(prActiveRange); end else begin DoMeasureSimple; end; end; procedure tMsThread.DoMeasure; begin if WaitReady then begin try prDoMeasure; except ErrorSet(ecUnexpectedError); end; SetReady; end; end; (*procedure tMsThread.ChangeMass; var m,dm:tMass; begin if (fPointProcessed in prFlags) then begin dm:=MassStepMin; if dm<>0 then begin m:=Mass; MassSet0(m+dm); prMass.LastStep:=Mass-m; end; FlagClear(fPointProcessed); end; end; *) function tMsThread.NewMass:tMass; // var dm:tMass; begin Result:=Mass; if (fPointProcessed in prFlags) then begin Result:=Result+MassStepMin; FlagClear(fPointProcessed); end; end; function tMsThread.DataGetAndStoreEx(AMass:tMass; Accumulate:boolean):boolean; var AControlFlags:tDataGetAndStoreControlFlags; begin AControlFlags:=[dgsfUseGlobalIntegrationTime]; if Accumulate then Include(AControlFlags,dgsfAccumulate); if mstCalculateStatisticalData in prSwitches then Include(AControlFlags,dgsfCalcStatistic); result:=DataGetAndStoreAdv(AMass, 0, AControlFlags); end; (*function tMsThread.DataGetAndStoreExEx(AMass:tMass; AControlFlags:tDataGetAndStoreControlFlags):boolean; begin Result:=DataGetAndStoreAdv(AMass, 0, AControlFlags+[dgsfUseGlobalIntegrationTime]); Result:=FALSE; CalcSatistic:=dgsfCalcStatistic in AControlFlags; try if CalcSatistic then begin timeBeginPeriod(1); end; m:=prMsSp.Mass; prMsSp.exJumpToMass(AMass); prMsSp.exSignalsStart; // запуск измерения if CalcSatistic then begin StartTime:=timeGetTime; end; MassLimitGet; m:=prMsSp.Mass-m; if (m<>0) then begin prMass.LastStep:=m; DoEvMassChanged; end; OldPiority:=Priority; if (fDataValid in Flags) then begin if CalcSatistic then begin // Вычисление статистических данных по внутренним операциям IdleStartTime:=timeGetTime; // Вычисление статистических данных по циклу try if fMeasureCycleContinued in Flags then begin Time:=TimeSpentTime(prStatistic.CycleTimeData.Start); if Time>=Signals.Time then begin Dec(Time,Signals.Time); prStatistic.CycleTimeData.LastTime:=Time; if Time>prStatistic.CycleTimeData.MaxTime then prStatistic.CycleTimeData.MaxTime:=Time; if TimeprStatistic.IdleTimeData.MaxTime then prStatistic.IdleTimeData.MaxTime:=Time; if Time=Signals.Time) then begin Dec(Time,Signals.Time); prStatistic.PointTimeData.LastTime:=Time; if Time>prStatistic.PointTimeData.MaxTime then prStatistic.PointTimeData.MaxTime:=Time; if Time0) then begin prMass.LastStep:=m; DoEvMassChanged; end; OldPiority:=Priority; if (fDataValid in Flags) then begin if CalcSatistic then begin // Вычисление статистических данных по внутренним операциям IdleStartTime:=timeGetTime; // Вычисление статистических данных по циклу try if fMeasureCycleContinued in Flags then begin Time:=TimeSpentTime(prStatistic.CycleTimeData.Start); if Time>=Signals.Time then begin Dec(Time,Signals.Time); prStatistic.CycleTimeData.LastTime:=Time; if Time>prStatistic.CycleTimeData.MaxTime then prStatistic.CycleTimeData.MaxTime:=Time; if TimeprStatistic.IdleTimeData.MaxTime then prStatistic.IdleTimeData.MaxTime:=Time; if Time=Signals.Time) then begin Dec(Time,Signals.Time); prStatistic.PointTimeData.LastTime:=Time; if Time>prStatistic.PointTimeData.MaxTime then prStatistic.PointTimeData.MaxTime:=Time; if Time'') then begin prMsSp.RestoreFromFile(PChar(ConfigFileName)); end; prIntegrationTime:=IntegrationTime; ConfigRead; for ds:=Low(ds) to High(ds) do begin if ds in prActiveSeries then begin s:=tMSSeries.CreateEx(NIL,InitialCapacity); prDataSeries[ds]:=s; if Assigned(s) then begin s.Title:=cMSSeriesTitles[ds]; if s.Name='' then s.Name:=cMSSeriesNames[ds]; s.BeforeDrawValues:=DataSeriesDrawLock; s.AfterDrawValues:=DataSeriesDrawUnLock; end; end; end; if RegistryOpenKey('DataSeries',FALSE) then begin for ds:=Low(ds) to High(ds) do begin s:=prDataSeries[ds]; If Assigned(s) then try s.RestoreParameters(Registry); except end; end; end; if LoadOldDataOnInit {mstAutoSaveData in prSwitches} then try DataSeriesReadData; except end; prSourcesData.StartTime.Start(0); prSourcesData.OpenTime.Start(0); try prEmergencyCheckTimer:=tMMTimer.Create; prEmergencyCheckTimer.Enabled:=FALSE; prEmergencyCheckTimer.TimerType:=ttPeriodic; prEmergencyCheckTimer.Resolution:=1000; prEmergencyCheckTimer.Interval:=5000; prEmergencyCheckTimer.OnTimer:=DoEmergencyCheck; except end; Inc(prInitialDataSize,GetHeapStatus.TotalAllocated-Mem); finally DataEndWrite; end; finally SetReady; end; end; procedure tMsThread.Done; begin DataSeriesAllOff; SetOff; ConfigSave; if DataDirty and ( (mstAutoSaveData in prSwitches) or DataAutoSave) then try DataSeriesWriteData; except end; end; destructor tMsThread.Destroy; var ds:tSeries; begin Notify(evDestroy); TerminateAndWait; Suspend; prEmergencyCheckTimer.Enabled:=False; prSourcesData.OneSecondTickTimer.Enabled:=False; prSourcesData.Timer.Enabled:=False; prVoltmeter.Timer.Enabled:=False; prAutoSaveTimer.Enabled:=False; // Блокировка данных перед уничтожением WaitReady; DataBeginWrite; // SeriesOff; DataSeriesAllOff; for ds:=Low(ds) to High(ds) do begin FreeAndNil(prDataSeries[ds]); end; FreeAndNil(prSourcesData.OneSecondTickTimer); FreeAndNil(prSourcesData.Timer); FreeAndNil(prVoltmeter.Timer); FreeAndNil(prEmergencyCheckTimer); FreeAndNil(prNotifyList); FreeAndNil(prNotifyWindowsList); FreeAndNil(prLocalRegistry); FreeAndNil(prFileFormat); FreeAndNil(prRanges); FreeAndNil(prActiveRange); FreeAndNil(prExtendedCommandsList); FreeAndNil(prExecutingCommandsList); FreeAndNil(prAutoSaveTimer); prMsSp.Done; FreeAndNil(prDataLock); FreeAndNil(Ready); FreeAndNil(prEventNotExecuted); FreeAndNil(prEventRunning); Inherited Destroy; end; function tMsThread.Wait; begin Result:=Ready.WaitFor(TimeOut); end; function tMsThread.WaitReady; begin Result:=Ready.WaitFor(TimeOut)=wrSignaled; end; function tMsThread.SetReady; begin Result:=Ready.Exit; end; function tMsThread.TimeOutGet; begin Result:=cDefaultTimeOut+prMsSp.OperationTime; end; function tMsThread.CheckHardwareError:boolean; begin Result:=prMsSp.Error; if Result then begin Ready.WaitForever:=False; Ready.SetEventAnyway; ErrorSet(ecHardwareError); end else if ErrorCode=ecHardwareError then begin ErrorSet(ecOK); end; end; function tMsThread.HardwareErrorGet:word; begin Result:=prMsSp.ErrorCode; end; procedure tMsThread.ErrorSet(e:tError); begin Notify(evError); if e=ecOK then begin prError:=ecOK; FlagClear(fError); prMsSp.SetNoError; Notify(evClearError); // Resume; end else if prError=ecOK then begin prError:=e; FlagSet(fError); Notify(evSetError); Stop; end; end; function tMsThread.ErrorMsgGet:string; begin if prMsSp.Error then Result:=prMsSp.ErrorMessageCurrent else case ErrorCode of ecOK: Result:='Все в порядке'; ecHardwareError: Result:='Ошибка оборудования'; ecNoMemory: Result:='Недостаточно памяти'; ecUnexpectedError: Result:='Неожиданная и неизвестная ошибка'; else Result:='Описание ошибки недоступно'; end; end; procedure tMsThread.ClearAll; var s:tSeries; begin DataBeginWrite; try for s:=Low(s) to High(s) do begin ClearSeries(s); end; Notify(evPointsNumberChanged); finally DataEndWrite; end; end; procedure tMsThread.ClearSer(ASeries:tSeries); var sr:tMSSeries; begin DataBeginWrite; try sr:=DataSeries[ASeries]; if Assigned(sr) then begin sr.Clear; FlagSet(fDataChanged); end; finally DataEndWrite; end; end; function tMsThread.Clear; begin try ClearAll; Result:=TRUE; except Result:=FALSE; end; end; function tMsThread.ClearSeries(ASeries:tSeries):boolean; begin try ClearSer(ASeries); Result:=TRUE; except Result:=FALSE; end; end; function tMsThread.CalibrationSet; var c0:tCounter; OldM0,OldK:tMass; begin Result:=false; if WaitReady then begin try prMsSp.MassCalibrationGet(OldM0,OldK); if (OldM0<>Mass0) or (OldK<>Coefficient) then begin DataBeginWrite; try DataMassToCounter; c0:=Counter; prMsSp.MassCalibrationSet(Mass0,Coefficient); DataCounterToMass(Counter-c0); except end; DataEndWrite; Notify(evMassCalibrationChanged); end; Result:=TRUE; finally SetReady; end; end; end; procedure tMsThread.DataMassToCounter; var i:tSeries; s:tMSSeries; MC:tMassCalibration; begin DataBeginWrite; try prMsSp.MassCalibrationGetEx(MC); prSignalsV.Data.Mass:=MC.Mass2Counter(prSignalsV.Data.Mass); for i:=Low(i) to High(i) do begin s:=prDataSeries[i]; if Assigned(s) then begin s.ConvertXValuesToCounter(MC); end; end; finally DataEndWrite; end; end; procedure tMsThread.DataCounterToMass(CounterShift:integer); var i:tSeries; s:tMSSeries; MC:tMassCalibration; begin DataBeginWrite; try prMsSp.MassCalibrationGetEx(MC); prSignalsV.Data.Mass:=MC.Counter2Mass(Round(prSignalsV.Data.Mass)+CounterShift); for i:=Low(i) to High(i) do begin s:=prDataSeries[i]; if Assigned(s) then begin s.ConvertXValuesToMass(MC,CounterShift); end; end; finally DataEndWrite; end; end; procedure tMsThread.DataMassCalibrationChange(const OldCalibration:tMassCalibration); var i:tSeries; s:tMSSeries; NewMC:tMassCalibration; begin DataBeginWrite; try prMsSp.MassCalibrationGetEx(NewMC); prSignalsV.Data.Mass:=NewMC.Convert(prSignalsV.Data.Mass,OldCalibration); prBackgroundMass:=NewMC.Convert(prBackgroundMass,OldCalibration); for i:=Low(i) to High(i) do begin s:=prDataSeries[i]; if Assigned(s) then begin s.RecalculateXValues(OldCalibration,NewMC); end; end; except end; DataEndWrite; end; procedure tMsThread.CalibrationGet; begin try prMsSp.MassCalibrationGet(Mass0,Coefficient); except Mass0:=-1; Coefficient:=-1; end; end; function tMsThread.MassCalibrationGet:tMassCalibration; begin try prMsSp.MassCalibrationGetEx(Result); except end; end; function tMsThread.MassCalibrationSet(const AMassCalibration:tMassCalibration):boolean; var OldClbr:tMassCalibration; begin Result:=FALSE; if WaitReady then begin try if not MassCalibration.Equal(AMassCalibration) then begin OldClbr:=MassCalibration; Result:=prMsSp.MassCalibrationSetEx(AMassCalibration); if Result then begin DataMassCalibrationChange(OldClbr); end; Notify(evMassCalibrationChanged); end; Result:=TRUE; except end; SetReady; end; end; procedure tMsThread.MassCalibrationSetP(const AMassCalibration:tMassCalibration); begin MassCalibrationSet(AMassCalibration); end; function tMsThread.CalibrationMGet:tMass; var x:tMass; begin CalibrationGet(Result,x); end; procedure tMsThread.CalibrationMSet; begin CalibrationSet(Mass,K); end; function tMsThread.CalibrationKGet:tMass; var x:tMass; begin CalibrationGet(x,Result); end; procedure tMsThread.CalibrationKSet; begin CalibrationSet(M0,Koef); end; function tMsThread.Calibration_dCGet:tMass; begin Result:=prMsSp.MassCalibrationPtr^.Get_dC; end; function tMsThread.IMChCalibrationSet; begin Result:=false; if WaitReady then begin try prMsSp.IMChCalibrationSet(Mass0,Coefficient); Notify(exMNICalibrationChanged); Result:=TRUE; except end; SetReady; end; end; procedure tMsThread.IMChCalibrationCalculate; begin if WaitReady then begin try prMsSp.exIMChCalibrationCalculateDefault; except end; SetReady; end; end; procedure tMsThread.IMChCalibrationRefineOrigin; begin if WaitReady then begin try prMsSp.exIMChCalibrationRefineOrigin; except end; SetReady; end; end; procedure tMsThread.IMChCalibrationGet; begin try prMsSp.IMChCalibrationGet(Mass0,Coefficient); except Mass0:=-1; Coefficient:=-1; end; end; function tMsThread.IMChCalibrationMGet:tMass; var x:tMass; begin IMChCalibrationGet(Result,x); end; procedure tMsThread.IMChCalibrationMSet; begin IMChCalibrationSet(M0,IMCh_K); end; function tMsThread.IMChCalibrationKGet:tMass; var x:tMass; begin IMChCalibrationGet(x,Result); end; procedure tMsThread.IMChCalibrationKSet; begin IMChCalibrationSet(IMCh_M0,K); end; function CheckDataPoint(var dp:ComplexArray):boolean; begin Result:=(dp.hReal<>NIL) and (dp.Rows>=(Ord(High(tChannel))+2)) and (dp.Cols>=1); end; procedure tMsThread.StepMultiplicatorSet; const cMaxMul=1000; begin try if SMult>cMaxMul then SMult:=cMaxMul else if SMult<-cMaxMul then SMult:=-cMaxMul; prMass.StepMultiplicator:=SMult; Notify(evStepMultiplicatorChanged); if SMult=0 then begin Notify(evMassChanged); Notify(evNewPoint); end; except end; end; procedure tMsThread.Start; begin prActiveRange.Enabled:=FALSE; FlagClear(fPointProcessed); prStatistic.DateTime.StartDateTime:=Now(); prStatistic.DateTime.EndDateTime:=prStatistic.DateTime.StartDateTime; Notify(evStart); Resume; end; procedure tMsThread.Stop; begin FlagSet(fStopMeasure); end; procedure tMsThread.Resume; // var Resumed:boolean; begin FlagClear(fStopMeasure); if ResumeThreadIfSuspended then Notify(evResume); // Resumed:=Suspended; // Inherited Resume; // if Resumed then // Notify(evResume); end; function tMsThread.ResumeThreadIfSuspended:boolean; begin Result:=Suspended; if Result then Inherited Resume; end; procedure tMsThread.Pause; begin StepMultiplicator:=0; Resume; end; procedure tMsThread.ValveSetOpen; var curValve:tSource; begin curValve:=Valve; try ValveTerminateTimers; if not IsON then Exit; if not prMsSp.exSwitchIsON(fValvesControl) then prMsSp.exSwitchTurnON(fValvesControl); if prMsSp.NoError then begin prMsSp.exSourceSet(AValve); end else begin prMsSp.ctrlPanel.SetNoError; prMsSp.ctrlPanel.exSourceSet(AValve); end; if Valve<>curValve then prSourcesData.OpenTime.Start(0); ValveAutoCloseActivateEx(AValve); if Valve<>curValve then prSourcesData.StartDateTime:=Now(); except ErrorSet(ecUnexpectedError); end; CheckHardwareError; if Valve<>curValve then Notify(evSourceChanged); end; procedure tMsThread.ValveTerminateTimers; begin // if Assigned(prSourcesData.Timer) then prSourcesData.Timer.Enabled:=False; // if Assigned(prSourcesData.OneSecondTickTimer) then prSourcesData.OneSecondTickTimer.Enabled:=False; end; function tMsThread.ValveAutoCloseActivate:boolean; begin Result:=ValveAutoCloseActivateEx(Valve); end; function tMsThread.ValveAutoCloseActivateEx(AValve:tSource):boolean; begin Result:=FALSE; ValveTerminateTimers; if (AValve<>sCloseAll) and (ValveAutoCloseTimes[AValve]>0) then begin if not Assigned(prSourcesData.Timer) then begin try prSourcesData.Timer:=tMMTimer.Create; prSourcesData.Timer.Enabled:=FALSE; prSourcesData.Timer.Resolution:=1; prSourcesData.Timer.TimerType:=ttOnce; prSourcesData.Timer.OnTimer:=ValveDoAutoCLose; except FreeAndNil(prSourcesData.Timer); end; end; if Assigned(prSourcesData.Timer) then begin Exclude(prFlags,fSourceTimerFails); prSourcesData.Timer.Interval:=ValveAutoCloseTimes[AValve]; prSourcesData.Timer.Enabled:=True; if not prSourcesData.Timer.Enabled then begin Include(prFlags,fSourceTimerFails); end else begin prSourcesData.StartTime.Start(ValveAutoCloseTimes[AValve]); Result:=TRUE; ValveAutoCloseActivateTiks; end; end else begin Include(prFlags,fSourceTimerFails); end; end; end; function tMsThread.ValveAutoCloseTimesGet(AValve:tSource):cardinal; begin if AValve in [sCloseAll,sBad] then Result:=0 else Result:=prSourcesData.AutoCloseTimes[AValve]; end; function tMsThread.ValveAutoCloseTimeGet:cardinal; begin Result:=ValveAutoCloseTimes[Valve]; end; procedure tMsThread.ValveAutoCloseTimesSet(AValve:tSource; dT_ms:cardinal); begin if prSourcesData.AutoCloseTimes[AValve]<>dT_ms then begin prSourcesData.AutoCloseTimes[AValve]:=dT_ms; Notify(evSourceAutoCloseTimeChanged); end; if AValve=Valve then ValveAutoCloseActivateEx(AValve); end; procedure tMsThread.ValveAutoCloseTimeSet(dT_ms:cardinal); begin ValveAutoCloseTimes[Valve]:=dT_ms; end; procedure tMsThread.ValveDoAutoCLose; begin if ValveAutoCloseEnabled then begin Valve:=sCloseAll; end; end; function tMsThread.ValveAutoCloseEnabledGet:boolean; begin Result:=(ValveAutoCloseTime>0) and Assigned(prSourcesData.Timer) and prSourcesData.Timer.Enabled; end; function tMsThread.ValveAutoCloseTimeLeftGet:cardinal; begin if ValveAutoCloseEnabled then Result:=prSourcesData.StartTime.TimeLeft else Result:=High(Result); end; function tMsThread.ValveOpenedTimeGet:cardinal; begin Result:=prSourcesData.OpenTime.TimeSpendSinceStarted; end; procedure tMsThread.ValveAutoCloseActivateTiks; begin prSourcesData.OneSecondTickTimer.Enabled:=False; if Assigned(prSourcesData.Timer) and (prSourcesData.Timer.Interval>1000) then begin if not Assigned(prSourcesData.OneSecondTickTimer) then begin try prSourcesData.OneSecondTickTimer:=tMMTimer.Create; prSourcesData.OneSecondTickTimer.Enabled:=FALSE; prSourcesData.OneSecondTickTimer.Resolution:=100; prSourcesData.OneSecondTickTimer.TimerType:=ttPeriodic; prSourcesData.OneSecondTickTimer.OnTimer:=ValveOneSecondTimerTick; except FreeAndNil(prSourcesData.OneSecondTickTimer); end; end; if Assigned(prSourcesData.OneSecondTickTimer) then prSourcesData.OneSecondTickTimer.Enabled:=TRUE; end; end; procedure tMsThread.ValveOneSecondTimerTick; begin Notify(evValveOneSecondTimerTick); end; function tMsThread.ValveGetOpen; begin if not IsOn then begin Result:=sBad; end else begin try Result:=prMsSp.exSource; except Result:=sBad; CheckHardwareError; end; end; end; function tMsThread.DeviceStepGet(Device:tDevice):cardinal; begin if Device in [IonizationVoltage..SEM_Voltage] then begin try Result:=prMsSp.DeviceUStep(Device); except Result:=0; end; end else begin Result:=0; end; end; function tMsThread.DeviceMaxStepCount(Device:tDevice):cardinal; begin if Device in [IonizationVoltage..SEM_Voltage] then begin try Result:=prMsSp.DeviceCounterMax(Device); except Result:=0; end; end else begin Result:=0; end; end; function tMsThread.DeviceOperationTime_ms(ADevice:tDevice; AStep:integer):integer; begin if ADevice in [IonizationVoltage..SEM_Voltage] then begin try Result:=prMsSp.DeviceCounterMax(ADevice); if Result>Abs(AStep) then Result:=Abs(AStep); Result:=Result*prMsSp.ctrlISSB.CurStepDelay; except Result:=0; end; end else begin Result:=0; end; end; function tMsThread.DeviceOperationTime_msToSetValue(ADevice:tDevice; AValue:integer):integer; begin Result:=DeviceOperationTime_ms(ADevice, (AValue-Device[ADevice])); end; function tMsThread.DeviceDoStep(d:tDevice; Direction:TUpDownDirection):boolean; var c:word; begin Result:=FALSE; if d in [IonizationVoltage..SEM_Voltage] then begin try c:=prMsSp.DeviceCounter(d); case Direction of updUp: prMsSp.ctrlISSB.exStepUp(c_ISSB.tUnit(d)); updDown: prMsSp.ctrlISSB.exStepDown(c_ISSB.tUnit(d)); else end; CheckHardwareError; Result:=(prMsSp.DeviceCounter(d)<>c ); if Result then begin NotifyEx(evDevice,Ord(d),0); end; except end; end; end; function tMsThread.DeviceSetEx(Device:tDevice; value:integer):boolean; var DoSet:boolean; begin Result:=False; if not IsON then Exit; try { MsSpBeginRead; try} DoSet:=(prMsSp.DeviceUMin(Device)<=Value) and (Value<=prMsSp.DeviceUMax(Device)) and (prMsSp.DeviceU(Device)<>Value); { finally MsSpEndRead; end;} if DoSet then begin { MsSpBeginWrite; try} prMsSp.exDeviceUSet(Device,Value); { finally MsSpEndWrite; end;} CheckHardwareError; Result:=IsNoError; NotifyEx(evDevice,Ord(Device),0); end; except CheckHardwareError; end; end; procedure tMsThread.DevicesDoSet; var d:tDevice; ds:tDevices; begin ds:=prDevicesToSet; prDevicesToSet:=prDevicesToSet-ds; for d:=Low(d) to High(d) do if (d in ds) then begin DeviceSet(d,prDevicesValuesToSet[d]); end; end; procedure tMsThread.DeviceSetX(Device:tDevice; value:integer); begin prDevicesValuesToSet[Device]:=value; Include(prDevicesToSet,Device); XCmdSet(fDoSetDevice); end; procedure tMsThread.DeviceResetX(d:tDevice); begin if d in [IonizationVoltage..CorrectionZ] then begin Include(prDevicesToReset,d); XCmdSet(fDoResetDevice); end; end; function tMsThread.DevicesReset(ADevices:tDevices):tDevices; var d:tDevice; begin for d:=Low(d) to High(d) do if (d in ADevices) then DeviceReset(d); end; function tMsThread.DeviceReset(d:tDevice):boolean; begin if d in [IonizationVoltage..CorrectionZ] then begin try { MsSpBeginWrite; try} prMsSp.exDeviceReset(d); { finally MsSpEndWrite; end;} CheckHardwareError; NotifyEx(evDevice,Ord(d),0); except CheckHardwareError; end; Result:=(ErrorCode=ecOK); end else begin Result:=TRUE; end; end; procedure tMsThread.DeviceSet(Device:tDevice; value:integer); begin DeviceSetEx(Device,value); end; function tMsThread.DeviceGet; begin Result:=Low(Result); if Device in [IonizationVoltage..SEM_Voltage] then begin try if IsON then Result:=prMsSp.DeviceU(Device) else Result:=0; except CheckHardwareError; end; end; end; function tMsThread.DevicesValidated:tDevices; begin prMsSp.ctrlISSB.CheckedUnitsGet(c_ISSB.TUnits(Result)); end; function tMsThread.Mass2Counter(m:tMass):integer; begin Result:=prMsSp.Mass2Counter(m); end; function tMsThread.Counter2Mass(c:integer):tMass; begin Result:=prMsSp.Counter2Mass(c); end; function tMsThread.IntegrationTimeGet; begin Result:=prMsSp.IntegrationTime; prIntegrationTime:=Result; end; procedure tMsThread.IntegrationTimeSet; begin if Time=0 then Time:=1 else if Time>prMsSp.IntegrationTimeMax then Time:=prMsSp.IntegrationTimeMax; if Time=IntegrationTime then Exit; if WaitReady then begin try IntegrationTimeSetInternal(Time); NotifyEx(evIntegrationTimeChanged,0,Time); except end; end; SetReady; end; procedure tMsThread.IntegrationTimeSetInternal(Time:word); begin { MsSpBeginWrite; try} prMsSp.IntegrationTimeSet(Time); prIntegrationTime:=Time; { finally MsSpEndWrite; end;} end; procedure tMsThread.IntegrationTimeSetX; begin if prIntegrationTime=Time then Exit; prIntegrationTimeX:=Time; XCmdSet(fDoSetIntegrationTime); ResumeThreadIfSuspended; // inherited Resume; end; procedure tMsThread.StatisticDataClear; begin PointTimeDataClear; CycleTimeDataClear; IdleTimeDataClear; end; procedure tMsThread.PointTimeDataClear; begin ZeroMemory(@prStatistic.PointTimeData,SizeOf(prStatistic.PointTimeData)); prStatistic.PointTimeData.MinTime:=High(prStatistic.PointTimeData.MinTime); end; procedure tMsThread.CycleTimeDataClear; begin FlagClear(fMeasureCycleContinued); ZeroMemory(@prStatistic.CycleTimeData,SizeOf(prStatistic.CycleTimeData)); prStatistic.CycleTimeData.MinTime:=High(prStatistic.CycleTimeData.MinTime); end; procedure tMsThread.IdleTimeDataClear; begin FlagClear(fMeasureCycleContinued); ZeroMemory(@prStatistic.IdleTimeData,SizeOf(prStatistic.IdleTimeData)); prStatistic.IdleTimeData.MinTime:=High(prStatistic.IdleTimeData.MinTime); end; procedure tMsThread.Abort; begin prMsSp.SetErrorCode(word(ecAbort)); Ready.WaitForever:=False; CheckHardwareError; Notify(evAbort); end; function tMsThread.EqupmentFlagsGet; begin prMsSp.exEmergencyFlagsGet(Result); end; function tMsThread.MeasureAvailable:boolean; begin Result:=MeasureAvailableEx; end; function tMsThread.MeasureAvailableEx:boolean; begin Result:=IsON and IsNoError and not (fStopMeasure in Flags); end; procedure tMsThread.AutoTuningSet(x:boolean); begin if AutoTuningGet<>x then begin if WaitReady then begin prMsSp.ctrlRoll.exAutoTuning(x); SetReady; end; end; end; function tMsThread.AutoTuningGet:boolean; begin Result:=prMsSp.ctrlRoll.AutoTuning; end; function tMsThread.DeltaDateTimeGet:tDateTime; begin Result:=EndDateTime-StartDateTime; end; function tMsThread.ChannelValid; begin Result:={(Ord(Low(tChannel))<=cl) and} (cl<=Byte(High(tChannel))); end; procedure tMsThread.ResetError; begin ErrorCode:=ecOK; end; procedure tMsThread.NotifyAdd(var NotifyHanler:tMI1201_Thread_NotifyData); begin if not Assigned(prNotifyList) then Exit; prNotifyList.Add(NotifyHanler); end; procedure tMsThread.NotifyDel(var NotifyHanler:tMI1201_Thread_NotifyData); begin if not Assigned(Self) or not Assigned(prNotifyList) then Exit; try prNotifyList.Remove(NotifyHanler); except end; end; procedure tMsThread.Notify(Event:tMI1201_Thread_EventID); var Ev:tMI1201_Thread_Event; begin Ev.EventID:=Event; Ev.ParameterB:=0; Ev.ParameterW:=0; NotifyA(Ev) end; procedure tMsThread.NotifyEx(Event:tMI1201_Thread_EventID; parByte:byte; parWord:word); var Ev:tMI1201_Thread_Event; begin Ev.EventID:=Event; Ev.ParameterB:=ParByte; Ev.ParameterW:=ParWord; NotifyA(Ev); end; procedure tMsThread.NotifyA(Event:tMI1201_Thread_Event); begin if Terminated and not (Event.EventID in [evDestroy, evTerminate]) then Exit; // Сообщение через Windows-message окну prNotifyWindowsList.NotifyByList(Self,Event); // Прямой вызов-уведомление prNotifyList.NotifyByList(Self,Event); end; procedure tNotifyList.Add(var NotifyHanler:tMI1201_Thread_NotifyData); resourcestring AlreadyExist='Указатель %h уже присутствует в списке.'; Unassigned='Указатель %h = NIL.'; begin if not (Assigned(Self)) then Exit; if not Assigned(Addr(NotifyHanler)) then begin Error(Unassigned, Integer(NotifyHanler)); end else if (IndexOf(Addr(NotifyHanler))<>-1) then begin Error(AlreadyExist, cardinal(Addr(NotifyHanler))); end else begin Inherited Add(Addr(NotifyHanler)); end; end; function tNotifyList.Remove(var NotifyHanler:tMI1201_Thread_NotifyData):Integer; begin if (Assigned(Self)) then Result:=Inherited Remove(Addr(NotifyHanler)) else Result:=-1; end; procedure tNotifyList.NotifyByList(Sender:TObject; Event:tMI1201_Thread_Event); var i:integer; p:^tMI1201_Thread_NotifyData; begin if not (Assigned(Self)) then Exit; MultiReadExclusiveWriteSynchronizer.BeginRead; for i:=0 to Pred(Count) do begin p:=Items[i]; If Assigned(p) then begin try p^.Notify(Sender,Event); except Delete(i); end; end else begin Delete(i); end; end; MultiReadExclusiveWriteSynchronizer.EndRead; end; procedure tNotifyWindowsList.Add(WindowsHanler:HWND); resourcestring AlreadyExist='Windows handle %h уже присутствует в списке.'; Unassigned='Windows handle %h = 0.'; begin if not (Assigned(Self)) then Exit; if WindowsHanler=0 then begin Error(Unassigned, cardinal(WindowsHanler)); end else if (IndexOf(Pointer(WindowsHanler))<>-1) then begin Error(AlreadyExist, cardinal(WindowsHanler)); end else begin Inherited Add(Pointer(WindowsHanler)); end; end; function tNotifyWindowsList.Remove(WindowsHanler:HWND): Integer; resourcestring Unassigned='Windows handle %h = 0.'; begin if (Assigned(Self)) then begin if WindowsHanler=0 then begin Result:=-1; Error(Unassigned, cardinal(WindowsHanler)); end else begin Result:=Inherited Remove(Pointer(WindowsHanler)); end; end else begin Result:=-1; end; end; procedure tNotifyWindowsList.NotifyByList(Sender:TObject; Event:tMI1201_Thread_Event); var i:integer; h:HWND; begin if not (Assigned(Self)) then Exit; MultiReadExclusiveWriteSynchronizer.BeginRead; for i:=0 to Pred(Count) do begin try h:=HWND(Items[i]); If h<>0 then begin PostMessage(h, CM_MI1201Notify, Longint(Event), Longint(Sender) ); end else begin Delete(i); end; except end; end; MultiReadExclusiveWriteSynchronizer.EndRead; end; function tMsThread.DriverErrorGetFirstCtrlWith:tController; begin Result:=prMsSp.ErrorGetFirstCtrlWith; end; function tMsThread.DriverErrorCodeFromSubCtrl(c:tController):word; begin Result:=prMsSp.ErrorCodeFromSubCtrl(c); end; procedure AddFeatures(var s:tMSSwitches; f:tFeatures); begin // if ffRollFastExInit in f then Include(s,XsffRollFastExInit); if ffSkipValvesExInit in f then Include(s,sffSkipValvesExInit); if ffSkipValvesExDone in f then Include(s,sffSkipValvesExDone); if ffSkipExDoneAtAll in f then Include(s,sffSkipExDoneAtAll); if ffRestoreBlocksState in f then Include(s,sffRestoreBlocksStateOnExInit); if ffUseEmulator in f then Include(s,sffUseEmulator); end; function GetFeatures(s:tMSSwitches):tFeatures; begin Result:=[]; // if XsffRollFastExInit in s then Include(Result,ffRollFastExInit); if sffSkipValvesExInit in s then Include(Result,ffSkipValvesExInit); if sffSkipValvesExDone in s then Include(Result,ffSkipValvesExDone); if sffSkipExDoneAtAll in s then Include(Result,ffSkipExDoneAtAll); if sffRestoreBlocksStateOnExInit in s then Include(Result,ffRestoreBlocksState); if sffUseEmulator in s then Include(Result,ffUseEmulator); end; procedure AddControllersOff(var s:tMSSwitches; f:tControllers); begin ; if Bus in f then Include(s,sBus); if CVF in f then Include(s,sCVF); ; if Roll in f then Include(s,sMagnet); if Volts in f then Include(s,sVoltmeter); ; if Panel in f then Include(s,sPanel); ; if ISSB in f then Include(s,sBPGI); if Count in f then Include(s,sCounter); end; function GetControllersOff(s:tMSSwitches):tControllers; begin Result:=[]; if sBus in s then Include(Result,Bus); if sCVF in s then Include(Result,CVF); if sMagnet in s then Include(Result,Roll); if sVoltmeter in s then Include(Result,Volts); if sPanel in s then Include(Result,Panel); if sBPGI in s then Include(Result,ISSB); if sCounter in s then Include(Result,Count); end; procedure AddSwitches(var s:tMSSwitches; f:tSwitches); begin if fCVF_Beam in f then Include(s,sfCVF_Beam); if fCVF_Base0 in f then Include(s,sfCVF_Base0); if fCVF_InputEMU in f then Include(s,sfCVF_InputEMU); if fCVF_DoNotInvertInput in f then Include(s,sfCVF_DoNotInvertInput); if fBPGI in f then Include(s,sfBPGI); if f10kV in f then Include(s,sf10kV); if fSEM in f then Include(s,sfSEM); if fValvesControl in f then Include(s,sfValvesControl); // if fAllowHighVoltageAndSEM in f then Include(s,sfAllowHighVoltageAndSEM); end; function GetSwitches(s:tMSSwitches):tSwitches; begin Result:=[]; if sfCVF_Beam in s then Include(Result,fCVF_Beam); if sfCVF_Base0 in s then Include(Result,fCVF_Base0); if sfCVF_InputEMU in s then Include(Result,fCVF_InputEMU); if sfCVF_DoNotInvertInput in s then Include(Result,fCVF_DoNotInvertInput); if sfBPGI in s then Include(Result,fBPGI); if sf10kV in s then Include(Result,f10kV); if sfSEM in s then Include(Result,fSEM); if sfValvesControl in s then Include(Result,fValvesControl); // if sfAllowHighVoltageAndSEM in s then Include(Result,fAllowHighVoltageAndSEM); end; function tMsThread.SwitchesGet:tMSSwitches; var f:tFeatures; c:tControllers; s:tSwitches; begin Result:=[]; if not Assigned(Self) then Exit; prMsSp.SkipMaskGetForExInit(c); AddControllersOff(Result,c); prMsSp.SpecialFeaturesGet(f); AddFeatures(Result,f); if IsOn then begin prMsSp.exSwitchesGet(s); AddSwitches(Result,s); end; Result:=Result+prSwitches; end; procedure tMsThread.SwitchesSet(ASwitches:tMSSwitches); var CurSwitches:tMSSwitches; begin if not Assigned(Self) then Exit; CurSwitches:=Switches; if ASwitches<>CurSwitches then begin if cMSSwitchesRequiredSynchonization*ASwitches=cMSSwitchesRequiredSynchonization*CurSwitches then begin prMsSp.SpecialFeaturesSet(GetFeatures(ASwitches)); prMsSp.SkipMaskSetForExInit(GetControllersOff(ASwitches)); if prMsSp.CompletelyInitiated then begin prMsSp.exSwitchesSet(GetSwitches(ASwitches)); end; end else if WaitReady then begin try prMsSp.SpecialFeaturesSet(GetFeatures(ASwitches)); prMsSp.SkipMaskSetForExInit(GetControllersOff(ASwitches)); if prMsSp.CompletelyInitiated then begin prMsSp.exSwitchesSet(GetSwitches(ASwitches)); end; finally SetReady; end; CheckHardwareError; end; prSwitches:=ASwitches*cMSTSwitches; if (mstCalculateStatisticalData in CurSwitches)<>(mstCalculateStatisticalData in ASwitches) then FlagClear(fMeasureCycleContinued); if (mstUseRanges in CurSwitches)<>(mstUseRanges in ASwitches) then RangesProcessStartStop; DataAutoSave:=mstAutosaveDataOnInterval in prSwitches; end; Notify(evSwitchesChanged); end; function tMsThread.SwitchON(s:tMSSwitch):boolean; begin SwitchesSet(SwitchesGet+[s]); Result:=s in SwitchesGet; end; function tMsThread.SwitchOFF(s:tMSSwitch):boolean; begin SwitchesSet(SwitchesGet-[s]); Result:=not (s in SwitchesGet); end; procedure tMsThread.SwitchONX(s:tMSSwitch); begin if s in Switches then begin Notify(evSwitchesChanged); end else if (s in cFastSwitches) or not IsON then begin SwitchON(s); end else begin Include(prSwitchesOnX,s); XCmdSet(fDoSetSwitches); ResumeThreadIfSuspended; // Inherited Resume; end; end; procedure tMsThread.SwitchOFFX(s:tMSSwitch); begin if not (s in Switches) then begin Notify(evSwitchesChanged); end else if (s in cFastSwitches) or not IsON then begin SwitchOFF(s); end else begin Include(prSwitchesOffX,s); XCmdSet(fDoSetSwitches); ResumeThreadIfSuspended; // Inherited Resume; end; end; procedure tMsThread.DoSwitchesX(); var SwitchesOff,SwitchesOn:tMSSwitches; begin SwitchesOn:=tMSSwitches(InterlockedExchange(Integer(prSwitchesOnX),0)); SwitchesOff:=tMSSwitches(InterlockedExchange(Integer(prSwitchesOffX),0)); Switches:=Switches-SwitchesOff+SwitchesOn; end; procedure tMsThread.Terminate; begin Inherited Terminate; Stop; Priority:=tpHighest; if Not prFinished {and Suspended} then begin ResumeThreadIfSuspended; // inherited Resume; end; end; function tMsThread.TerminateAndWait:tWaitResult; begin Terminate; Result:=WaitForTerminate(TerminateTimeOut+TimeOut); end; function tMsThread.WaitForTerminate(TimeOut:cardinal):tWaitResult; procedure WaitSuspended; var ti:tTimeInterval; begin ti.Start(2000); while (not Suspended) and ti.NotIntervalEnd do Sleep(10); end; begin if GetCurrentThreadID=ThreadID then begin Result:=wrAbandoned; end else if prFinished then begin Result:=wrSignaled; WaitSuspended; end else begin Result:=prEventNotExecuted.WaitFor(TimeOut); if Result=wrSignaled then begin WaitSuspended; end; end; end; function tMsThread.WaitForStart(TimeOut:cardinal):tWaitResult; begin if GetCurrentThreadID=ThreadID then begin Result:=wrAbandoned; end else begin Result:=prEventRunning.WaitFor(TimeOut); end; end; procedure tMsThread.ConfigFileNameSet(NewName:string); var ok:boolean; rk:HKEY; begin if not Assigned(Self) then Exit; NewName:=ExpandFileName(NewName); if UpperCase(ConfigFileName)=UpperCase(NewName) then Exit; ok:=TRUE; { if FileExists(NewName) then begin end;} if Ok then begin try Registry.CloseKey; Registry.Access:=KEY_WRITE; rk:=Registry.RootKey; Registry.RootKey:=HKEY_LOCAL_MACHINE; if Registry.OpenKey(cRegistryKeyRoot,TRUE) then begin try Registry.WriteString('ConfigFileName',NewName); prConfigFileName:=NewName; Notify(evConfigFileNameChanged); finally Registry.CloseKey; Registry.RootKey:=rk; end; end; except end; end; end; function tMsThread.ConfigFileNameGet:string; var rk:HKEY; begin if not Assigned(Self) then Exit; Result:=''; try Registry.CloseKey; rk:=Registry.RootKey; Registry.RootKey:=HKEY_LOCAL_MACHINE; if Registry.OpenKeyReadOnly(cRegistryKeyRoot) then begin try Result:=Registry.ReadString('ConfigFileName'); finally Registry.CloseKey; Registry.RootKey:=rk; end; end; except Result:=''; end; if Result='' then begin Result:=ExtractFilePath(CurrentModuleFileName)+'MI-1201 AGM.cfg'; end; end; function tMsThread.Registry:tRegistry; begin if not Assigned(prLocalRegistry) then begin prLocalRegistry:=tRegistry.Create(KEY_READ); end; Result:=prLocalRegistry; end; function tMsThread.DriverConfigSave:boolean; begin Result:=FALSE; if (ConfigFileName<>'') then begin if WaitReady then begin try prMsSp.SaveToFile(PChar(ConfigFileName)); Result:=TRUE; except end; SetReady; end; end; end; function tMsThread.DriverConfigRead:boolean; begin Result:=(ConfigFileName<>''); if Result then begin Result:=WaitReady; if Result then begin try prMsSp.RestoreFromFile(PChar(ConfigFileName)); Result:=prMsSp.NoError; finally SetReady; end; end; end; end; function tMsThread.VoltmeterCurrentGet:tMkVolts; begin If IsON then begin prMsSp.exVoltageSelectChannel; Result:=prMsSp.exVoltage; prVoltmeter.Data[prMsSp.VoltageChannel]:=Result; Notify(evVoltmeterNewMeasure); end else begin Result:=0; end; end; function tMsThread.VoltmeterGet(Channel:tVoltageChannel):tMkVolts; begin If IsON then begin prMsSp.VoltageChannelSet(Channel); Result:=VoltmeterCurrentGet; end else begin Result:=0; end; end; procedure tMsThread.VoltmeterUpdateTimeSet(dT:cardinal); begin if prVoltmeter.Time<>dT then begin prVoltmeter.Time:=dT; Notify(evVoltmeterAutoUpdateTimeChanged); end; VoltmeterInitTimer; end; procedure tMsThread.VoltmeterUpdateChannelsSet(Channels:tVoltageChannels); begin if prVoltmeter.Channels<>Channels then begin prVoltmeter.Channels:=Channels; Notify(evVoltmeterAutoUpdateChannelsChanged); end; VoltmeterInitTimer; end; procedure tMsThread.VoltmeterInitTimer; begin If not IsON then Exit; if (VoltmeterUpdateChannels<>[]) and (VoltmeterUpdateTime>0) then begin try if not Assigned(prVoltmeter.Timer) then begin prVoltmeter.Timer:=tMMTimer.Create; prVoltmeter.Timer.Resolution:=1; prVoltmeter.Timer.TimerType:=ttPeriodic; prVoltmeter.Timer.OnTimer:=VoltmeterDoAutoUpdate; end; except end; Exclude(prFlags,fVoltmeterTimerFails); if Assigned(prVoltmeter.Timer) then begin prVoltmeter.Timer.Enabled:=True; prVoltmeter.Timer.Interval:=VoltmeterUpdateTime; if not prVoltmeter.Timer.Enabled then Include(prFlags,fVoltmeterTimerFails); end else begin Include(prFlags,fVoltmeterTimerFails); end; end else begin if Assigned(prVoltmeter.Timer) then prVoltmeter.Timer.Enabled:=False; end; end; procedure tMsThread.VoltmeterDoAutoUpdate; var vuc:tVoltageChannels; c0,c:tVoltageChannel; begin If not IsON then Exit; vuc:=VoltmeterUpdateChannels; SetThreadPriority(GetCurrentThreadID,THREAD_PRIORITY_BELOW_NORMAL); if (vuc=[]) then Exit; try c0:=prMsSp.VoltageChannel; for c:=Low(c) to High(c) do if c in vuc then begin prMsSp.VoltageChannelSet(C); prMsSp.exVoltageSelectChannel; prVoltmeter.Data[c]:=prMsSp.exVoltage; end; prMsSp.VoltageChannelSet(c0); prMsSp.exVoltageSelectChannel; Notify(evVoltmeterAutoUpdate); except end; end; function tMsThread.VoltmeterUpDateGet(Channel:tVoltageChannel):tMkVolts; begin if Assigned(Self) then Result:=prVoltmeter.Data[Channel] else Result:=0; end; function tMsThread.VoltmeterCurrentChannelGet:tVoltageChannel; begin Result:=prMsSp.VoltageChannel end; procedure tMsThread.VoltmeterCurrentChannelSet(Channel:tVoltageChannel); begin If not IsON then Exit; prMsSp.VoltageChannelSet(Channel); Notify(evVoltmeterAutoUpdateChannelsChanged); end; function tMsThread.MassAjust(AMass:tMass):tMass; begin Result:=Counter2Mass(Mass2Counter(AMass)); end; procedure tMsThread.MassSet0(m:tMass); var OldCounter:tCounter; LargeChange:boolean; begin if m<>Mass then try Notify(evMassChange); LargeChange:=ABS(prMsSp.Mass2Counter(m)-prMsSp.Counter)>10000; if LargeChange then Notify(evMassAboutChangeForLargeValue); OldCounter:=Counter; prMsSp.exJumpToMass(m); CheckHardwareError; if LargeChange then Notify(evMassAboutChangeForLargeValueEnd); if (Counter<>OldCounter) then begin Notify(evMassChanged); if (fMassLimit in Flags) then MassLimit; end else if not (fMassLimit in Flags) then begin MassLimit; end; except CheckHardwareError; end; end; procedure tMsThread.MassSet(m:tMass); begin if m=Mass then Exit; if WaitReady then begin MassSet0(m); SetReady; end; end; procedure tMsThread.MassXSet(m:tMass); begin prBackgroundMass:=m; XCmdSet(fDoChangeMass); ResumeThreadIfSuspended; // Inherited Resume; end; procedure tMsThread.CounterXSet(c:tCounter); begin prBackgroundCounter:=c; XCmdSet(fDoChangeCounter); ResumeThreadIfSuspended; // Inherited Resume; end; procedure tMsThread.MassStepSet(m:tMass); begin if MassStep=m then Exit; if WaitReady then begin try prMass.Step:=m; Notify(evMassStepChanged); except end; SetReady; end; end; function tMsThread.MassStepAbsoluteMinCalculate(AMass:tMass):tMass; var c:tCounter; begin c:=Mass2Counter(AMass); Result:=(prMsSp.Counter2Mass(Succ(c))-prMsSp.Counter2Mass(c))+(1E-14); end; function tMsThread.MassStepAbsoluteMinGet:tMass; begin Result:=(prMsSp.Counter2Mass(Succ(prMsSp.Counter))-prMsSp.Mass)+(1E-14); end; function tMsThread.MassStepMinCalculate(AMass,AStep:tMass):tMass; var dm:tMass; begin Result:=AStep*StepMultiplicator; if Result=0 then Exit; dm:=MassStepAbsoluteMinCalculate(AMass); if Result<0 then begin dm:=-dm; if Result>dm then Result:=dm; end else begin if Resultdm then Result:=dm; end else begin dm:=(prMsSp.Counter2Mass(Succ(c))-dm); if ResultMass then begin if DoShiftData then begin DataBeginWrite; try DataMassToCounter; C0:=Counter; prMsSp.MassCalibrationSetCurrentMass(m); DataCounterToMass(Counter-C0); except end; DataEndWrite; end else begin prMsSp.MassCalibrationSetCurrentMass(m); end; Notification:=TRUE; end; except end; SetReady; if Notification then begin Notify(evMassCalibrationChanged); Notify(evMassChanged); Notify(evNewPoint); end; end; end; procedure tMsThread.RefineCounter(c:integer); var C0:tCounter; begin if WaitReady then begin try if c<>Counter then begin if not (mstDoNotShiftDataOnRefineMass in Switches) then begin DataBeginWrite; try DataMassToCounter; C0:=Counter; prMsSp.MassCalibrationSetCurrentCounter(c); DataCounterToMass(Counter-C0); except; end; DataEndWrite; end else begin prMsSp.MassCalibrationSetCurrentCounter(c); end; Notify(evMassCalibrationChanged); Notify(evMassChanged); Notify(evNewPoint); end; except end; SetReady; end; end; procedure tMsThread.DataMassShift(dM:tMass); var i:tSeries; s:tMSSeries; begin DataBeginWrite; try prSignalsV.Data.Mass:=Counter2Mass(Mass2Counter(prSignalsV.Data.Mass+dM)); for i:=Low(i) to High(i) do begin s:=prDataSeries[i]; if Assigned(s) then s.ShiftXValues(dM); end; finally DataEndWrite; end; end; function tMsThread.IsError:boolean; begin Result:=Error<>ecOK; end; function tMsThread.IsNoError:boolean; begin Result:=Error=ecOK; end; function tMsThread.WaitForLimitedTime(ATimeOut:cardinal):TWaitResult; begin Result:=Ready.WaitForLimitedTime(ATimeOut); end; procedure tMsThread.FlagSet(f:tFlag); begin Include(prFlags,f); end; procedure tMsThread.FlagClear(f:tFlag); begin Exclude(prFlags,f); end; procedure tMsThread.FlagState(f:tFlag; state:boolean); begin if state then Include(prFlags,f) else Exclude(prFlags,f); end; procedure tMsThread.XCmdSet(c:tXCommand); begin Include(prXCommands,c); end; procedure tMsThread.XCmdClear(c:tXCommand); begin Exclude(prXCommands,c); end; function tMsThread.PNCCalibrationGet(cl:tChannel; par:tPNCCalibrationParameter):double; var K:double; begin if par=pncZeroLevel then begin Result:=prMsSp.SignalZeroLevelGet(tChannel(cl)) end else begin if cl in [PNC1..PNC5] then begin prMsSp.ctrlCVF.CalibrationDataGetV(Succ(Ord(cl)),K); end else if cl=PNC_SEM then begin prMsSp.ctrlCVF.CalibrationDataGetV(9,K); end else begin K:=0; end; case par of pncK: Result:=K; else Result:=0; end; end; end; procedure tMsThread.PNCCalibrationSet(cl:tChannel; par:tPNCCalibrationParameter; Value:double); var K:double; begin if par=pncZeroLevel then begin if prMsSp.SignalZeroLevelGet(cl)=Value then Exit; prMsSp.SignalZeroLevelSet(cl,Value); end else begin if cl in [PNC1..PNC5] then begin prMsSp.ctrlCVF.CalibrationDataGetV(Succ(Ord(cl)),K); end else if cl=PNC_SEM then begin prMsSp.ctrlCVF.CalibrationDataGetV(9,K); end else begin K:=0; end; case par of pncK: begin if K=Value then Exit; K:=Value; end; else ; end; if cl in [PNC1..PNC5] then begin prMsSp.ctrlCVF.CalibrationDataSetV(Succ(Ord(cl)),K); end else if cl=PNC_SEM then begin prMsSp.ctrlCVF.CalibrationDataSetV(9,K); end; NotifyEx(evPNCCalibrationChanged,Ord(cl),Ord(par)); end; end; procedure tMsThread.PNCCalibrate; var Notification:boolean; begin if WaitReady then begin Notification:=FALSE; try prMsSp.exCalibrate; CheckHardwareError; Notification:=TRUE; except end; SetReady; if Notification then NotifyEx(evPNCCalibrationChanged,$FF,Ord(pncAll)); end; end; procedure tMsThread.PNCCalibrateX; begin xCmdSet(fDoPNCCalibration); ResumeThreadIfSuspended; // Inherited Resume; end; procedure tMsThread.PNCCalibrateEx(ChannelSet:tChannels); var Notification:boolean; begin if WaitReady then begin Notification:=FALSE; try xCmdClear(fDoPNCCalibrationEx); prMsSp.exCalibrateEx(ChannelSet); CheckHardwareError; Notification:=TRUE; except end; SetReady; if Notification then NotifyEx(evPNCCalibrationChanged,Byte(ChannelSet),Ord(pncAll)); end; end; procedure tMsThread.PNCCalibrateExX(ChannelSet:tChannels); begin prPNCCalibrateChannelSet:=ChannelSet; if ChannelSet<>[] then begin xCmdSet(fDoPNCCalibrationEx); xCmdSet(fDoPNCCalibration); ResumeThreadIfSuspended; // Inherited Resume; end else begin xCmdClear(fDoPNCCalibration); xCmdClear(fDoPNCCalibrationEx); end; end; function tMsThread.MeasureStateGet:tMeasureState; begin if not IsNoError then begin Result:=msErrorStopped; end else if (fStopMeasure in Flags) then begin Result:=msStopped; end else if (not MeasureAvailableEx) then begin Result:=msNotAvailableStopped; end else if Suspended then begin Result:=msSuspended; end else if MassStepMin=0 then begin Result:=msPaused; end else begin Result:=msScroll; end; end; function tMsThread.ScrollDirectionGet:tScrollDirection; var m:tMass; begin m:=MassStepMin; if m=0 then begin Result:=sdNoScroll; end else if m>0 then begin Result:=sdUp; end else begin Result:=sdDown; end; end; function tMsThread.RegistryOpenRootKey(ForWrite:boolean):boolean; begin if ForWrite then begin Registry.CloseKey; Registry.Access:=KEY_WRITE; Result:=Registry.OpenKey(cRegistryKeyRoot,TRUE) end else Result:=Registry.OpenKeyReadOnly(cRegistryKeyRoot); end; function tMsThread.RegistryOpenKey(const SubKey:string; ForWrite:boolean):boolean; begin Registry.CloseKey; if ForWrite then begin Registry.Access:=KEY_WRITE; Result:=Registry.OpenKey(cRegistryKeyRoot+'\'+SubKey,TRUE) end else Result:=Registry.OpenKeyReadOnly(cRegistryKeyRoot+'\'+SubKey); end; procedure tMsThread.ConfigSave; var ds:TSeries; begin if not Assigned(Self) then Exit; if RegistryOpenRootKey(TRUE) then begin try Registry.WriteInteger('InitialCapacity',InitialCapacity); except end; try Registry.WriteBinaryData('Switches',prSwitches,SizeOf(prSwitches)); except end; try Registry.WriteBinaryData('ActiveSeries',prActiveSeries,SizeOf(prActiveSeries)); except end; try Registry.WriteFloat('MassStep',prMass.Step); except end; try Registry.WriteFloat('AutoMassScaleSyncWithIMChValue',prAutoMassScaleSyncWithIMChValue); except end; try Registry.WriteBinaryData('SourceAutoCloseTime', prSourcesData.AutoCloseTimes, SizeOF(prSourcesData.AutoCloseTimes)); except end; try Registry.WriteBinaryData('Signals', prSignalsV, SizeOF(prSignalsV)); except end; try Registry.WriteInteger('MainChannel',Ord(MainChannel)); except end; try Registry.WriteInteger('AutoSaveTimer.Interval',DataAutoSaveInterval); Registry.WriteBool('AutoSaveTimer.Enabled',DataAutoSave); except end; if Registry.OpenKey('DataSeries',TRUE) then for ds:=Low(ds) to High(ds) do begin If Assigned(prDataSeries[ds]) then try prDataSeries[ds].SaveParameters(Registry); except end; end; if RegistryOpenKey('FileFormat',TRUE) then try FileFormat.WriteToRegistry(Registry); except end; if prRanges.Changed then begin if RegistryOpenKey('Ranges',TRUE) then try prRanges.WriteToRegistry(Registry); except end; end; end; Registry.CloseKey; end; procedure tMsThread.ConfigRead; begin if not Assigned(Self) then Exit; try if RegistryOpenRootKey(FALSE) then begin try InitialCapacity:=Registry.ReadInteger('InitialCapacity'); except end; try prAutoMassScaleSyncWithIMChValue:=Registry.ReadFloat('AutoMassScaleSyncWithIMChValue'); except end; try Registry.ReadBinaryData('Switches',prSwitches,SizeOf(prSwitches)); prSwitches:=prSwitches*cMSTSwitches-[mstCalculateStatisticalData]; except end; try Registry.ReadBinaryData('ActiveSeries',prActiveSeries,SizeOf(prActiveSeries)); prActiveSeries:=prActiveSeries*[Low(tSeries)..High(tSeries)]; except end; try prMass.Step:=Registry.ReadFloat('MassStep'); except end; try Registry.ReadBinaryData('SourceAutoCloseTime', prSourcesData.AutoCloseTimes, SizeOF(prSourcesData.AutoCloseTimes)); except end; try Registry.ReadBinaryData('Signals', prSignalsV, SizeOF(prSignalsV)); except end; try MainChannel:=tSeries(Registry.ReadInteger('MainChannel')); except end; DataAutoSaveInterval:=Registry.ReadIntegerDef('AutoSaveTimer.Interval',DataAutoSaveInterval); DataAutoSave:=Registry.ReadBoolDef('AutoSaveTimer.Enabled',DataAutoSave); if RegistryOpenKey('FileFormat',FALSE) then try FileFormat.ReadFromRegistry(Registry); except end; if RegistryOpenKey('Ranges',FALSE) then try prRanges.ReadFromRegistry(Registry); prRanges.Changed:=FALSE; except end; end; except end; Registry.CloseKey; end; procedure tMsThread.ConfigRestore; var v:tSource; begin if not Assigned(Self) then Exit; for v:=Low(v) to High(v) do begin ValveAutoCloseTimes[v]:=prSourcesData.AutoCloseTimes[v]; end; RangesProcessStartStop; end; function tMsThread.IMCh_MassGet:tMass; begin try Result:=prMsSp.exIMCh_Mass; CheckHardwareError; except Result:=0; ErrorSet(ecUnexpectedError); end; end; function tMsThread.DataSeriesGet(s:tSeries):tMSSeries; begin DataBeginRead; try Result:=prDataSeries[s]; except Result:=NIL; end; DataEndRead; end; procedure tMsThread.DataSeriesAllOff; var s:tSeries; ms:tMSSeries; begin DataBeginRead; try for s:=Low(s) to High(s) do begin ms:=prDataSeries[s]; if Assigned(ms) then begin // ms.Active:=FALSE; ms.ParentChart:=NIL; end; end; except end; DataEndRead; end; procedure tMsThread.DataSeriesOff(Owner:tObject); var s:tSeries; ms:tMSSeries; begin DataBeginRead; try for s:=Low(s) to High(s) do begin ms:=prDataSeries[s]; if Assigned(ms) then begin if ms.ParentChart=Owner then begin // ms.Active:=FALSE; ms.ParentChart:=NIL; end; end; end; except end; DataEndRead; end; function tMsThread.DataSeriesCount:integer; begin Result:=Ord(High(tSeries)); end; function tMsThread.AssignedDataSeriesGet:tSeriesSet; var s:tSeries; begin Result:=prAssignedDataSeries; if Result<>[] then Exit; DataBeginRead; try for s:=Low(s) to High(s) do if Assigned(prDataSeries[s]) then Include(Result,s); finally DataEndRead; end; end; function tMsThread.DataPointsCountGet:cardinal; begin Result:=DataPointCount([Low(TSeries)..High(TSeries)]); end; function tMsThread.DataPointCount(ASeriesSet:TSeriesSet):integer; var s:tSeries; i:integer; begin if (fDifferentPointsNumberInSeries in Flags) then begin Result:=0; DataBeginRead; try for s:=Low(s) to High(s) do if s in ASeriesSet then begin i:=prDataSeries[s].Count; if i>Result then Result:=i; end; finally DataEndRead; end; end else begin DataBeginRead; try Result:=prDataSeries[sIntegrationTime].Count; finally DataEndRead; end; end; end; procedure tMsThread.DataDeleteXRangeByValues(ASeriesSet:TSeriesSet; AMin,AMax:tMAss); var s:tSeries; ms:tMSSeries; acv:boolean; begin ASeriesSet:=ASeriesSet*AssignedDataSeries; DataBeginWrite; try for s:=Low(s) to High(s) do if s in ASeriesSet then begin ms:=prDataSeries[s]; if Assigned(ms) then begin acv:=ms.Active; ms.Active:=FALSE; ms.RangeXDeleteByValues(AMin,AMax); ms.Active:=acv; ms.Repaint end; end; finally DataEndWrite; end; end; procedure tMsThread.DataDeleteAllXRangeByValues(AMin,AMax:tMAss); begin DataDeleteXRangeByValues([Low(TSeries)..High(TSeries)],AMin,AMax); end; procedure tMsThread.MassZeroSetByHardware; begin if WaitReady then begin try { MsSpBeginWrite; try} Notify(evMassAboutChangeForLargeValue); prMsSp.ctrlRoll.exScrollToAbsoluteLowLimit; CheckHardwareError; Notify(evMassAboutChangeForLargeValueEnd); { finally MsSpEndWrite; end;} if IsNoError then RefineMass(0); except ErrorSet(ecUnexpectedError); end; SetReady; end; end; procedure tMsThread.MassZeroSetByHardwareX; begin xCmdSet(fDoMassZeroSetByHardware); end; procedure tMsThread.InitialCapacitySet(ic:integer); begin if ic<0 then ic:=0 else if ic>1000000 then ic:=1000000; prDefaultCapacity:=ic; Notify(evInitialCapacityChanged); end; procedure tMsThread.DataBeginWrite; begin prDataLock.BeginWrite; InterlockedIncrement(prDataWriteLockCount); end; procedure tMsThread.DataEndWrite; begin prDataLock.EndWrite; InterlockedDecrement(prDataWriteLockCount); end; procedure tMsThread.DataBeginRead; begin prDataLock.BeginRead; InterlockedIncrement(prDataReadLockCount); end; procedure tMsThread.DataEndRead; begin prDataLock.EndRead; InterlockedDecrement(prDataReadLockCount); end; {procedure tMsThread.MsSpBeginWrite; begin prMsSpLock.BeginWrite; end; procedure tMsThread.MsSpEndWrite; begin prMsSpLock.EndWrite; end; procedure tMsThread.MsSpBeginRead; begin prMsSpLock.BeginRead; end; procedure tMsThread.MsSpEndRead; begin prMsSpLock.EndRead; end;} procedure tMsThread.MainChannelSet(AChannel:tSeries); begin if Byte(AChannel)>Byte(High(AChannel)) then AChannel:=High(AChannel); prMainChannel:=AChannel; Notify(evMainChannelChanged); end; procedure tMsThread.MassLimitClear; begin if Flags*[fMassLimitLo,fMassLimitUp,fMassLimit]=[] then Exit; prFlags:=prFlags-[fMassLimitLo,fMassLimitUp,fMassLimit]; Notify(evMassLimit); end; function tMsThread.MassLimitGet:boolean; begin Result:=IsON and prMsSp.ctrlRoll.LimitReached; if Result then begin if not (fMassLimit in Flags) then begin FlagSet(fMassLimit); Notify(evMassLimit); end; end else if (fMassLimit in Flags) then begin MassLimitClear; end; end; function tMsThread.MassLimitLowGet:boolean; begin Result:=MassLimit; if Result then begin Result:=prMsSp.ctrlRoll.LowLimit; if Result and not (fMassLimitLo in Flags) then begin FlagSet(fMassLimitLo); end; end; end; function tMsThread.MassLimitUpGet:boolean; begin Result:=MassLimit; if Result then begin Result:=prMsSp.ctrlRoll.UpLimit; if Result and not (fMassLimitUp in Flags) then begin FlagSet(fMassLimitUp); end; end; end; procedure WriteFormattedValue(ss:TStringStream; x:double; ATextFormat:tMsDataTextFileFormat); var xs:string; i,di:integer; begin xs:=FloatToStrF(X,ATextFormat.Format,ATextFormat.Precision,ATextFormat.Digits); if ATextFormat.NumberSeparator<>'' then xs:=xs+ATextFormat.NumberSeparator else xs:=xs+' '; i:=Length(xs); if iAVList.Count then i1:=AVList.Count; ss.Position:=0; for i:=i0 to Pred(i1) do begin if ss.Position>=cMaxBufLen then begin AFileFormat.WriteString(ss.DataString); ss.Position:=0; end; WriteFormattedValue(ss, AVList[i], AFileFormat.TextFile); end; ss.Position:=ss.Position-dPos; ss.WriteString(cEOL); AFileFormat.WriteString(ss.DataString); end; procedure tMsThread.FileWriteFormattedStrings(AFileFormat:tMsDataFileFormat; i0,i1:integer); var s:tSeries; ms:tMSSeries; ss:TStringStream; dPos:cardinal; begin ss:=TStringStream.Create(''); try dPos:=Length(AFileFormat.TextFile.NumberSeparator); if dPos=0 then Inc(dPos); for s:=Low(s) to High(s) do begin if (s in AFileFormat.Series) then begin ms:=prDataSeries[s]; if s in AFileFormat.WriteMassSeries then begin WriteValueList(ms.XValues, ss, i0,i1,dPos, AFileFormat); end; WriteValueList(ms.YValues, ss, i0,i1,dPos, AFileFormat); end; end; finally ss.Free; end; end; procedure tMsThread.FileWriteFormattedColumns(AFileFormat:tMsDataFileFormat; i0,i1:integer); var i:integer; s:tSeries; ms:tMSSeries; XY:tXY; ss:TStringStream; dPos:integer; begin ss:=TStringStream.Create(''); try dPos:=Length(AFileFormat.TextFile.NumberSeparator); if dPos=0 then Inc(dPos); for i:=i0 to Pred(i1) do begin ss.Position:=0; for s:=Low(s) to High(s) do begin if (s in AFileFormat.Series) then begin ms:=prDataSeries[s]; if (ms.Count>i) then begin if s in AFileFormat.WriteMassSeries then XY.X:=ms.XValues[i]; XY.Y:=ms.YValues[i]; end else begin if s in AFileFormat.WriteMassSeries then XY.X:=ms.XValues[Pred(ms.Count)]; XY.Y:=0; end; if s in AFileFormat.WriteMassSeries then WriteFormattedValue(ss, XY.x, AFileFormat.TextFile); WriteFormattedValue(ss, XY.Y, AFileFormat.TextFile); end; end; ss.Position:=ss.Position-dPos; ss.WriteString(cEOL); AFileFormat.WriteString(ss.DataString); end; finally ss.Free; end; end; procedure tMsThread.FileWriteFormatted(AFileFormat:tMsDataFileFormat); var i0,i1:integer; s:tSeries; begin AFileFormat.Open; AFileFormat.Series:=AFileFormat.Series*AssignedDataSeries; if not (fDifferentPointsNumberInSeries in Flags) then begin AFileFormat.WriteMassSeries:=[]; if AFileFormat.Series<>[] then begin s:=Low(s); while not (s in AFileFormat.Series) and (s<>High(s)) do begin s:=Succ(s); end; Include(AFileFormat.WriteMassSeries,s); end; end else begin AFileFormat.WriteMassSeries:=AFileFormat.Series; end; if (msdffAddComment in AFileFormat.Flags) and (msdffCommentAtBegin in AFileFormat.Flags) then begin AFileFormat.WriteComment; if (msdffAddAutoComment in AFileFormat.Flags) then FileWriteAutoComment(AFileFormat); end; if AFileFormat.TextFile.Precision>15 then AFileFormat.TextFile.Precision:=15; DataBeginWrite; try if AFileFormat.FullMassRange then begin i0:=0; i1:=DataPointCount(AFileFormat.Series); end else begin i0:=DataSeriesFirstIndex(AFileFormat.MassMin); i1:=DataSeriesLastIndex(AFileFormat.MassMax); if (i0<0) or (i1<0) then begin i0:=0; i1:=0; end; end; if (msdffWriteStrings in AFileFormat.Flags) then FileWriteFormattedStrings(AFileFormat, i0,i1) else FileWriteFormattedColumns(AFileFormat, i0,i1); if (msdffAddComment in AFileFormat.Flags) and not (msdffCommentAtBegin in AFileFormat.Flags) then begin if (msdffAddAutoComment in AFileFormat.Flags) then FileWriteAutoComment(AFileFormat); AFileFormat.WriteComment; end; finally DataEndWrite; AFileFormat.Close; end; end; procedure tMsThread.FileWriteAutoComment(AFileFormat:tMsDataFileFormat); var sw:tMSSwitch; sws:tMSSwitches; d:tDevice; xs:string; begin AFileFormat.WriteCommentBegin; if not (msdffCommentAtBegin in AFileFormat.Flags) then begin AFileFormat.WriteSeriesTitleLine; end else begin AFileFormat.Writeln; end; AFileFormat.WritelnCommentString('АВТОКОММЕНТАРИЙ'); AFileFormat.Writeln; AFileFormat.WritelnCommentString('Дата записи: '+DateTimeToStr(Now())); AFileFormat.WriteCommentString('Состояние масс-спектрометра: '); AFileFormat.WriteON_OFF(IsOn); AFileFormat.Writeln; AFileFormat.Writeln; AFileFormat.WritelnCommentString('ВНИМАНИЕ! Параметры могут не соответствовать состоянию прибора в момент записи спектра.'); AFileFormat.Writeln; AFileFormat.WritelnCommentString(' Время интегрирования, мс: '+IntToStr(IntegrationTime)); sws:=Switches; AFileFormat.Writeln; if IsOn and (not (sVoltmeter in sws)) then begin AFileFormat.WritelnCommentString('Показания вольтметра:'); AFileFormat.WritelnCommentString(' Ускоряющее напряжение, мкВ: '+IntToStr(Voltmeter[Acceleration])); AFileFormat.WritelnCommentString(' ВЭУ, мкВ: '+IntToStr(Voltmeter[Mutiplicator])); AFileFormat.WritelnCommentString(' Антидинатрон, мкВ: '+IntToStr(Voltmeter[AntiDinatron])); // AFileFormat.WritelnCommentString(' Линза, мкВ: '+IntToStr(Voltmeter[Lens])); end; AFileFormat.Writeln; AFileFormat.WritelnCommentString('Параметры устройств:'); for d:=Low(d) to Pred(High(d)) do begin AFileFormat.WritelnCommentString(' '+cDeviceName[d]+': '+IntToStr(Device[d])); end; AFileFormat.Writeln; AFileFormat.WritelnCommentString('Состояние переключателей: '); for sw:=sffUseEmulator to sfValvesControl do begin AFileFormat.WriteString(' '+cMSSwitchesName[sw]+': '); AFileFormat.WriteON_OFF(sw in sws); AFileFormat.Writeln; end; AFileFormat.Writeln; AFileFormat.WritelnCommentString('Состояние клапанов: '); AFileFormat.WritelnCommentString(' Открыт: '+cSourceNames[Valve]); if Valve<>sCloseAll then begin DateTimeToString(xs,'dd.MM.yy hh:mm:ss',ValveOpenDateTime); AFileFormat.WritelnCommentString(' Время открытия: '+xs); AFileFormat.WritelnCommentString(' Период открытия, мс: '+IntToStr(ValveOpenedTime)); end; AFileFormat.Writeln; if (msdffCommentAtBegin in AFileFormat.Flags) then begin AFileFormat.WriteSeriesTitleLine; end; AFileFormat.WriteCommentEnd; end; procedure tMsThread.FileWrite; begin xCmdClear(fDoFileWrite); try FileWriteFormatted(FileFormat); except Notify(evFileWriteFail); end; end; procedure tMsThread.FileWriteX; begin xCmdSet(fDoFileWrite); ResumeThreadIfSuspended; // inherited Resume; end; procedure tMsThread.DataSeriesDrawLock(Sender:tObject); begin DataBeginRead; end; procedure tMsThread.DataSeriesDrawUnLock(Sender:tObject); begin DataEndRead; end; procedure tMsThread.DataSeriesWriteDataToFile(const AFileName:string); var fs:tFileStream; begin fs:=tFileStream.Create(AFileName,fmCreate or fmShareDenyWrite); try DataSeriesWrite(fs); finally fs.Free; end; end; procedure tMsThread.DataSeriesWrite(AStream:TStream); var ds:tSeries; s:tMsSeries; begin DataBeginRead; try for ds:=Low(ds) to High(ds) do begin s:=prDataSeries[ds]; if Assigned(s) then begin s.WriteData(AStream); end; end; finally DataEndRead; end; end; procedure tMsThread.DataSeriesReadDataFromFile(const AFileName:string); var fs:tFileStream; begin if not FileExists(AFileName) then Exit; fs:=tFileStream.Create(AFileName,fmOpenRead or fmShareDenyWrite); try DataSeriesRead(fs); finally fs.Free; end; end; function tMsThread.DataSeriesActiveGet:tSeriesSet; var ds:tSeries; s:tMsSeries; begin Result:=[]; DataBeginRead; try for ds:=Low(ds) to High(ds) do begin s:=prDataSeries[ds]; if Assigned(s) then begin if s.Active then Include(Result,ds); end; end; except end; DataEndRead; end; procedure tMsThread.DataSeriesActiveSet(ActiveSeries:tSeriesSet); var ds:tSeries; s:tMsSeries; begin DataBeginWrite; try for ds:=Low(ds) to High(ds) do begin s:=prDataSeries[ds]; if Assigned(s) then begin s.Active:=ds in ActiveSeries; end; end; except end; DataEndWrite; end; procedure tMsThread.DataSeriesRead(AStream:TStream); var ds:tSeries; s:tMsSeries; ActiveS:tSeriesSet; begin DataBeginWrite; try ActiveS:=DataSeriesActive; DataSeriesActive:=[]; Clear; for ds:=Low(ds) to High(ds) do begin s:=prDataSeries[ds]; if Assigned(s) then begin s.ReadData(AStream); end; end; finally DataSeriesActive:=ActiveS; DataEndWrite; end; end; procedure tMsThread.DataSeriesWriteData; begin if DataFileName<>'' then begin DataSeriesWriteDataToFile(DataFileName); DataDirty:=FALSE; end; end; procedure tMsThread.DataSeriesReadData; begin if DataFileName<>'' then begin DataSeriesReadDataFromFile(DataFileName); DataDirty:=FALSE; end; end; procedure tMsThread.DataSeriesWriteDataAuto(Sender:tObject); begin if DataDirty then try DataSeriesWriteData; Inc(prDataAutoSaveCount); NotifyEx(evDataAutoSave,2,0); except ErrorSet(ecDataSeriesWriteAutoError); end; end; procedure tMsThread.DataAutoSaveSet(Enabled:boolean); begin if prAutoSaveTimer.Enabled<>Enabled then begin prAutoSaveTimer.Enabled:=Enabled; if DataAutoSave then Include(prSwitches,mstAutosaveDataOnInterval) else Exclude(prSwitches,mstAutosaveDataOnInterval); NotifyEx(evDataAutoSave,0,0); end; end; function tMsThread.DataAutoSaveGet:boolean; begin Result:=prAutoSaveTimer.Enabled; end; procedure tMsThread.DataAutoSaveIntervalSet(Interval:word); var i:uint; begin i:=Interval * 60000; {минуты} if prAutoSaveTimer.Interval<>i then begin prAutoSaveTimer.Interval:=i; NotifyEx(evDataAutoSave,1,0); end; end; function tMsThread.DataAutoSaveIntervalGet:word; var i:uint; begin i:=prAutoSaveTimer.Interval div 60000; {минуты} if i>High(Result) then i:=High(Result); Result:=i; end; function tMsThread.DataDirtyGet:boolean; begin Result:=fDataChanged in Flags; end; procedure tMsThread.DataDirtySet(AState:boolean); begin FlagState(fDataChanged,AState); end; function tMsThread.DataFileName:string; begin if ConfigFileName<>'' then Result:=ConfigFileName+'.dat' else Result:=''; end; procedure tMsThread.FileFormatSet(AFileFormat:tMsDataFileFormat); begin prFileFormat.Assign(AFileFormat); end; function tMsThread.DataSeriesFirstIndex(Mass:tMass):integer; begin Result:=prDataSeries[sPNC1].FindFirstXAboveEqual(Mass); end; function tMsThread.DataSeriesLastIndex(Mass:tMass):integer; begin Result:=prDataSeries[sPNC1].FindLastXBelowEqual(Mass); end; procedure tMsThread.DoEmergencyCheck(Sender:tObject); begin if IsON then begin Exit; {$IFNdef DBG} временное исправление {$EndIF} // Проверка аварийных сигналов if prMsSp.ctrlISSB.NoError then begin prMsSp.ctrlISSB.exCurFlags(prCtrlFlags); if (fBPGI_ON in prCtrlFlags) and (prCtrlFlags*[fCatodOK, fOverload]<>[fCatodOK]) then begin if prMsSp.ctrlISSB.NoError then Notify(evEmergency) else CheckHardwareError; end; // Проверка переключения блоков ручным выключателем с панели управления if (prCtrlFlags<>prPredCtrlFlags) and prMsSp.ctrlISSB.NoError then begin Notify(evSwitchesChanged); prPredCtrlFlags:=prCtrlFlags; end; end; end; end; procedure tMsThread.HandleRangesChanged(Sender:TObject; ARangeIndex:integer; AItem:tRangeItem); begin case AItem of riEnabled: NotifyEx(evRangeChanged,Ord(AItem),ARangeIndex); end; end; procedure tMsThread.HandleRangeListChanged(Sender:TObject); begin Notify(evRangeListChanged); end; function tMsThread.DataValidSeriesCount(ASeriesSet:tSeriesSet):integer; var s:tSeries; begin Result:=0; if ASeriesSet=[] then Exit; for s:=Low(s) to High(s) do begin if s in ASeriesSet then if Assigned(prDataSeries[s]) then Inc(Result); end; end; function tMsThread.DataRangePointsCount(ASeriesSet:tSeriesSet; AMin,AMax:tMass):integer; var s:tSeries; i:integer; ms:tMsSeries; begin Result:=0; if ASeriesSet=[] then Exit; DataBeginRead; try for s:=Low(s) to High(s) do begin if s in ASeriesSet then begin ms:=prDataSeries[s]; if Assigned(ms) then try i:=ms.RangePointsCount(AMin,AMax); if i>Result then Result:=i; except end; end; end; finally DataEndRead; end; end; function tMsThread.DataArrayGet(const AMcadArray:ComplexArray; ASeriesSet:tSeriesSet; AMin,AMax:tMass):boolean; var s:tSeries; i,n:integer; ms:tMsSeries; begin Result:=ASeriesSet<>[]; if not Result then Exit; s:=Low(s); i:=0; Result:=AMcadArray.cols>=2; if not Result then Exit; DataBeginRead; try n:=AMcadArray.cols; if n>Succ(Ord(High(s))) then n:=Succ(Ord(High(s))); repeat if s in ASeriesSet then begin ms:=prDataSeries[s]; if Assigned(ms) then begin try if i=0 then begin Inc(i); ms.RangeXYPointsGet(AMcadArray.hReal[0]^,AMcadArray.hReal[1]^,AMcadArray.rows,AMin,AMax); end else begin ms.RangeYPointsGet(AMcadArray.hReal[i]^,AMcadArray.rows,AMin,AMax); end; except // ColumnClear(AMcadArray.hReal[i]^, AMcadArray.rows); end; end else begin // ColumnClear(AMcadArray.hReal[i]^, AMcadArray.rows); end; Inc(i); end; if (s<>High(s)) then s:=Succ(s) else n:=i; until (i>=n); finally DataEndRead; end; end; function tMsThread.DataSignalGetEx(AMass:tMass; AChannel:tSeries; ControlFlags:tSignalsGetFlags; var ASignal:tChannelSignal):boolean; begin if not (sgfInterpolate in ControlFlags) then begin Result:=DataSignalGet(AMass, AChannel, ASignal); end else begin Result:=DataSignalGetLInterp(AMass, AChannel, ASignal); end; end; function tMsThread.DataSignalGet(AMass:tMass; AChannel:tSeries; var ASignal:tChannelSignal):boolean; var i:integer; s:tMSSeries; begin s:=prDataSeries[AChannel]; Result:=Assigned(s); if Result then begin AMass:=MassAjust(AMass); DataBeginRead; try i:=s.XValues.Locate(AMass); if i>=0 then begin ASignal.Mass:=AMass; ASignal.Signal:=s.YValues[i]; s:=prDataSeries[sIntegrationTime]; if Assigned(s) then ASignal.Time:=s.YValues[i] else ASignal.Time:=0; end else Result:=FALSE; except Result:=FALSE; end; DataEndRead; end; end; function tMsThread.DataSignalGetLInterp(AMass:tMass; AChannel:tSeries; var ASignal:tChannelSignal):boolean; var i1,i2:integer; s:tMSSeries; m1,m2,dm,s1,s2,t1,t2:tMass; DataLocked:boolean; begin s:=prDataSeries[AChannel]; Result:=Assigned(s); if Result then begin AMass:=MassAjust(AMass); DataBeginRead; DataLocked:=TRUE; try s.FindNeighbours(AMass,i1,i2); if i1>=0 then begin ASignal.Mass:=AMass; m1:=s.XValues[i1]; m2:=s.XValues[i2]; s1:=s.YValues[i1]; s2:=s.YValues[i2]; s:=prDataSeries[sIntegrationTime]; if Assigned(s) then begin t1:=s.YValues[i1]; t2:=s.YValues[i2]; end else begin t1:=0; t2:=0; end; DataEndRead; DataLocked:=FALSE; if i1=i2 then begin ASignal.Signal:=s1; ASignal.Time:=t1; end else begin try dm:=m2-m1; m2:=(m2-AMass)/dm; m1:=(AMass-m1)/dm; ASignal.Signal:=s1*m2+s2*m1; ASignal.Time:=t1*m2+t2*m1; except ASignal.Signal:=s1; ASignal.Time:=t1; end; end; end else begin Result:=FALSE; end; except Result:=FALSE; end; if DataLocked then DataEndRead; end; end; function tMsThread.SignalGetEx(AMass:tMass; AChannel:tSeries; ControlFlags:tSignalsGetFlags; var ASignal:tChannelSignal):boolean; begin if (sgfAuto in ControlFlags) then begin Result:=DataSignalGetEx(AMass,AChannel,ControlFlags,ASignal); if not Result then begin ASignal:=SignalGetDirect(AMass, AChannel); Result:=IsNoError; end; end else if (sgfDirect in ControlFlags) then begin ASignal:=SignalGetDirect(AMass, AChannel); Result:=IsNoError; end else begin Result:=DataSignalGetEx(AMass,AChannel,ControlFlags,ASignal); end; end; function tMsThread.SignalGet(AMass:tMass; AChannel:tSeries):tChannelSignal; begin if not DataSignalGet(AMass, AChannel, Result) then begin Result:=SignalGetDirect(AMass, AChannel); end; end; function tMsThread.SignalGetDirect(AMass:tMass; AChannel:tSeries):tChannelSignal; begin if WaitReady then begin try DataGetAndStoreEx(AMass,(mstAccumulation in prSwitches)); if IsNoError then begin Result.Time:=Signals.Time; Result.Mass:=Signals.Mass; if AChannel<>sIntegrationTime then begin Result.Signal:=Signals.Signals[tChannel(AChannel)]; end else Result.Signal:=Signals.Time; end else begin Result.Mass:=0; end; except end; SetReady; end else begin Result:=cInvalidChannelSignal; end; end; procedure SetSignalsToComplexArray(const Signals:tSignalsV; const AComplexArray:ComplexArray); var i:integer; begin if AComplexArray.Cols>0 then begin if AComplexArray.Rows>0 then begin AComplexArray.hReal[0,0]:=Signals.Mass; if AComplexArray.Rows>1 then begin if AComplexArray.Rows>Signals.Count then i:=Signals.Count else i:=AComplexArray.Rows; for i:=0 to Pred(i) do begin AComplexArray.hReal[0,Succ(i)]:=Signals.Signals[tChannel(i)]; end; if AComplexArray.Rows>Succ(Signals.Count) then begin AComplexArray.hReal[0,Succ(Signals.Count)]:=Signals.Time; end; end; end; end; end; function tMsThread.DataPointGet(AMass:tMass; const ASignals:ComplexArray):boolean; var s:tSignalsV; begin Result:=FALSE; if WaitReady then begin try DataGetAndStoreEx(AMass,(mstAccumulation in prSwitches)); Result:=IsNoError; s:=Signals; except Result:=FALSE; end; SetReady; if Result then begin SetSignalsToComplexArray(s,ASignals); end; end; end; procedure tMsThread.AutoMassScaleSyncWithIMChValueSet(AValue:tMass); begin AValue:=Abs(AValue); if AValue<>AutoMassScaleSyncWithIMChValue then begin prAutoMassScaleSyncWithIMChValue:=AValue; Notify(evAutoMassScaleSyncWithIMChValueChanged); end; end; procedure tMsThread.DoAutoMassScaleSyncWithIMCh; var mIMCh:tMass; Sync:boolean; begin if (mstAutoMassScaleSyncWithIMCh in prSwitches) and (AutoMassScaleSyncWithIMChValue>0) then begin if WaitReady then begin Sync:=FALSE; try mIMCh:=IMCh_Mass; if (mIMCh>0) and (Abs(mIMCh-Mass)>=AutoMassScaleSyncWithIMChValue) then begin RefineMassEx(mIMCh,FALSE); Inc(prAutoMassScaleSyncWithIMChCount); Sync:=TRUE; end; except end; SetReady; if Sync then Notify(evAutoMassScaleSyncWithIMChDone); end; end; end; procedure tMsThread.DataSeriesWriteDataToFileX(const AFileName:string); begin xCmdClear(fDoDataFileRead); prDataFileName:=AFileName; xCmdSet(fDoDataFileWrite); ResumeThreadIfSuspended; // inherited Resume; end; procedure tMsThread.DataSeriesReadDataFromFileX(const AFileName:string); begin xCmdClear(fDoDataFileWrite); prDataFileName:=AFileName; xCmdSet(fDoDataFileRead); ResumeThreadIfSuspended; // inherited Resume; end; procedure tMsThread.DoOnRangesScrollComplete; begin end; procedure tMsThread.DoOnRangeScrollComplete(ARange:TRange); begin end; function tMsThread.DeviceIndicatorGet(Device:tDevice):integer; begin Result:=prMsSp.DeviceIndicator(Device); end; procedure tMsThread.DoExtendedCommands; var i:integer; method:tNotifyEvent; begin if prExtendedCommandsList.Count>0 then begin prExecutingCommandsList.BeginWrite; try prExecutingCommandsList.Count:=0; prExtendedCommandsList.BeginWrite; try prExecutingCommandsList.Assign(prExtendedCommandsList); prExtendedCommandsList.Count:=0; except end; prExtendedCommandsList.EndWrite; for i:=0 to Pred(prExecutingCommandsList.Count) do begin method:=prExecutingCommandsList[i]; if Assigned(method) then try method(Self); except end; end; prExecutingCommandsList.Count:=0; except end; prExecutingCommandsList.EndWrite; end; end; function tMsThread.ExtendedCommand(ACommand:tNotifyEvent):integer; begin if Assigned(ACommand) then begin try Result:=prExtendedCommandsList.Add(ACommand); if Result<>-1 then XCmdSet(fDoExtendedCommands); ResumeThreadIfSuspended; // Inherited Resume; except Result:=-1; end; end else begin Result:=-1; end; end; function tMsThread.MainChannelNameGet:string; begin Result:=ChannelNameGet(MainChannel); end; function tMsThread.ChannelNameGet(AChannel:tSeries):string; begin Result:=cMSChannelTitles[AChannel]; end; function tMsThread.IsPowerON:boolean; begin try Result:=prMsSp.exIsPowerON; except Result:=TRUE; end; end; END.