Merge legacy dataforge and migrate to git
This commit is contained in:
13
numass-control/msp/build.gradle
Normal file
13
numass-control/msp/build.gradle
Normal file
@@ -0,0 +1,13 @@
|
||||
apply plugin: 'application'
|
||||
|
||||
version = "0.4.0"
|
||||
|
||||
if (!hasProperty('mainClass')) {
|
||||
ext.mainClass = 'inr.numass.control.msp.MspApp'
|
||||
}
|
||||
mainClassName = mainClass
|
||||
|
||||
|
||||
dependencies {
|
||||
compile project(':numass-control')
|
||||
}
|
||||
4933
numass-control/msp/docs/ASCII Protocol User Manual SP1040016.100.pdf
Normal file
4933
numass-control/msp/docs/ASCII Protocol User Manual SP1040016.100.pdf
Normal file
File diff suppressed because one or more lines are too long
489
numass-control/msp/docs/commands.htm
Normal file
489
numass-control/msp/docs/commands.htm
Normal file
@@ -0,0 +1,489 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta http-equiv=Content-Type content="text/html; charset=utf-8">
|
||||
<meta name=Generator content="Microsoft Word 15 (filtered)">
|
||||
<style>
|
||||
|
||||
</style>
|
||||
|
||||
</head>
|
||||
|
||||
<body lang=RU style='line-break:strict'>
|
||||
|
||||
<div class=WordSection1>
|
||||
|
||||
<table class=MsoNormalTable border=1 cellspacing=0 cellpadding=0
|
||||
style='margin-left:3.55pt;border-collapse:collapse;border:none'>
|
||||
<tr style='height:8.5pt'>
|
||||
<td width=181 valign=top style='width:136.0pt;border:solid windowtext 1.0pt;
|
||||
padding:2.75pt 2.75pt 2.75pt 2.75pt;height:8.5pt'>
|
||||
<p class=a1> </p>
|
||||
</td>
|
||||
<td width=246 valign=top style='width:184.5pt;border:solid windowtext 1.0pt;
|
||||
border-left:none;padding:2.75pt 2.75pt 2.75pt 2.75pt;height:8.5pt'>
|
||||
<p class=a1><span lang=EN-US>MKSRGA Multi</span></p>
|
||||
<p class=a1><span lang=EN-US>Protocol_Revision 1.1</span></p>
|
||||
<p class=a1><span lang=EN-US>Min_Compatibility 1.1</span></p>
|
||||
</td>
|
||||
<td width=215 valign=top style='width:161.0pt;border:solid windowtext 1.0pt;
|
||||
border-left:none;padding:2.75pt 2.75pt 2.75pt 2.75pt;height:8.5pt'>
|
||||
<p class=a1>Асинхронное сообщение о подключении к прибору по tcp-ip</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr style='height:8.5pt'>
|
||||
<td width=181 valign=top style='width:136.0pt;border:solid windowtext 1.0pt;
|
||||
border-top:none;padding:2.75pt 2.75pt 2.75pt 2.75pt;height:8.5pt'>
|
||||
<p class=a1><span style='background:yellow'> </span></p>
|
||||
</td>
|
||||
<td width=246 valign=top style='width:184.5pt;border-top:none;border-left:
|
||||
none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
|
||||
padding:2.75pt 2.75pt 2.75pt 2.75pt;height:8.5pt'>
|
||||
<p class=a1><span lang=EN-US style='background:yellow'>«command» ERROR </span></p>
|
||||
<p class=a1><span lang=EN-US style='background:yellow'>Number
|
||||
200 </span></p>
|
||||
<p class=a1><span lang=EN-US style='background:yellow'>Description «err
|
||||
description»* </span></p>
|
||||
</td>
|
||||
<td width=215 valign=top style='width:161.0pt;border-top:none;border-left:
|
||||
none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
|
||||
padding:2.75pt 2.75pt 2.75pt 2.75pt;height:8.5pt'>
|
||||
<p class=a1><span style='background:yellow'>В случае ошибки возвращается это
|
||||
сообщение</span></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width=181 valign=top style='width:136.0pt;border:solid windowtext 1.0pt;
|
||||
border-top:none;padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1>Sensors</p>
|
||||
</td>
|
||||
<td width=246 valign=top style='width:184.5pt;border-top:none;border-left:
|
||||
none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
|
||||
padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1><span lang=EN-US>Sensors OK </span></p>
|
||||
<p class=a1><span lang=EN-US>State SerialNumber Name</span></p>
|
||||
<p class=a1><span lang=EN-US>Ready LM70-00197021 “Chamber A”</span></p>
|
||||
</td>
|
||||
<td width=215 valign=top style='width:161.0pt;border-top:none;border-left:
|
||||
none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
|
||||
padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1>Выдает все сенсоры, которые могут быть использованы</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width=181 valign=top style='width:136.0pt;border:solid windowtext 1.0pt;
|
||||
border-top:none;padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1>Select "SerialNumber"</p>
|
||||
</td>
|
||||
<td width=246 valign=top style='width:184.5pt;border-top:none;border-left:
|
||||
none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
|
||||
padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1><span lang=EN-US>Select OK</span></p>
|
||||
<p class=a1><span lang=EN-US>SerialNumber LM70-00197021</span></p>
|
||||
<p class=a1><span lang=EN-US>State Ready </span></p>
|
||||
</td>
|
||||
<td width=215 valign=top style='width:161.0pt;border-top:none;border-left:
|
||||
none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
|
||||
padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1>Выбираем сенсор, с которым будем работать</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width=181 valign=top style='width:136.0pt;border:solid windowtext 1.0pt;
|
||||
border-top:none;padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1>Control "AppName" "Version"</p>
|
||||
</td>
|
||||
<td width=246 valign=top style='width:184.5pt;border-top:none;border-left:
|
||||
none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
|
||||
padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1>Control OK</p>
|
||||
<p class=a1>SerialNumber LM70-00197021</p>
|
||||
</td>
|
||||
<td width=215 valign=top style='width:161.0pt;border-top:none;border-left:
|
||||
none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
|
||||
padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1>Получаем контроль над сенсором</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width=181 rowspan=2 valign=top style='width:136.0pt;border:solid windowtext 1.0pt;
|
||||
border-top:none;padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1>FilamentControl </p>
|
||||
<p class=a1>"On/Off"</p>
|
||||
</td>
|
||||
<td width=246 valign=top style='width:184.5pt;border-top:none;border-left:
|
||||
none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
|
||||
padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1>FilamentControl OK </p>
|
||||
<p class=a1>State On</p>
|
||||
<p class=a1> </p>
|
||||
</td>
|
||||
<td width=215 valign=top style='width:161.0pt;border-top:none;border-left:
|
||||
none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
|
||||
padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1>Включение нити накала</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width=246 valign=top style='width:184.5pt;border-top:none;border-left:
|
||||
none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
|
||||
padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1><span lang=EN-US>FilamentStatus 1 ON/OFF/WARM-UP/COOL- DOWN</span></p>
|
||||
<p class=a1><span lang=EN-US>Trip None </span></p>
|
||||
<p class=a1><span lang=EN-US>Drive Off</span></p>
|
||||
<p class=a1><span lang=EN-US>EmissionTripState OK</span></p>
|
||||
<p class=a1>ExternalTripState OK </p>
|
||||
<p class=a1>RVCTripState OK</p>
|
||||
</td>
|
||||
<td width=215 valign=top style='width:161.0pt;border-top:none;border-left:
|
||||
none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
|
||||
padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1>Асинхронное сообщение о любом изменении состояния нити накала</p>
|
||||
<p class=a1>Последовательность<span lang=EN-US> : WARM-UP -> OK -> ON </span>или<span
|
||||
lang=EN-US> COOL-DOWN -> OK -> OFF</span></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width=181 valign=top style='width:136.0pt;border:solid windowtext 1.0pt;
|
||||
border-top:none;padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1>AddPeakJump "MeasurementName" "FilterMode"
|
||||
"0..8" "0" "0" "0"</p>
|
||||
</td>
|
||||
<td width=246 valign=top style='width:184.5pt;border-top:none;border-left:
|
||||
none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
|
||||
padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1><span lang=EN-US>AddPeakJump OK</span></p>
|
||||
<p class=a1><span lang=EN-US>Name PeakJump1</span></p>
|
||||
<p class=a1><span lang=EN-US>FilterMode
|
||||
PeakCenter/PeakMax/PeakAverage</span></p>
|
||||
<p class=a1>Accuracy 5</p>
|
||||
<p class=a1>EGainIndex 0 </p>
|
||||
<p class=a1>SourceIndex 0</p>
|
||||
<p class=a1>DetectorIndex 0 </p>
|
||||
</td>
|
||||
<td width=215 valign=top style='width:161.0pt;border-top:none;border-left:
|
||||
none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
|
||||
padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1>Создаем режим измерения PeakJump</p>
|
||||
<p class=a1>0..8 — точность измерений: 0 — меньшая точность, но большая
|
||||
скорость, 8 — наоборот </p>
|
||||
<p class=a1>Все остальное полагать 0</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width=181 valign=top style='width:136.0pt;border:solid windowtext 1.0pt;
|
||||
border-top:none;padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1>MeasurementAddMass "Mass"</p>
|
||||
</td>
|
||||
<td width=246 valign=top style='width:184.5pt;border-top:none;border-left:
|
||||
none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
|
||||
padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1>MeasurementAddMass OK</p>
|
||||
<p class=a1>Mass 10</p>
|
||||
</td>
|
||||
<td width=215 valign=top style='width:161.0pt;border-top:none;border-left:
|
||||
none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
|
||||
padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1>Добавляем массы в PeakJump, которые хотим измерить.</p>
|
||||
<p class=a1>Чтобы добавить новую массу, нужно повторно вызвать эту команду.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width=181 valign=top style='width:136.0pt;border:solid windowtext 1.0pt;
|
||||
border-top:none;padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1><span style='background:yellow'>MeasurementChangeMass </span></p>
|
||||
<p class=a1><span style='background:yellow'>"</span><span lang=EN-US
|
||||
style='background:yellow'>MassIndex" "NewMass"</span></p>
|
||||
</td>
|
||||
<td width=246 valign=top style='width:184.5pt;border-top:none;border-left:
|
||||
none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
|
||||
padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1><span style='background:yellow'>MeasurementChangeMass OK
|
||||
MassIndex 0 </span></p>
|
||||
<p class=a1><span style='background:yellow'>NewMass 6
|
||||
</span></p>
|
||||
</td>
|
||||
<td width=215 valign=top style='width:161.0pt;border-top:none;border-left:
|
||||
none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
|
||||
padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1><span style='background:yellow'>Заменяет массу с индексом "
|
||||
MassIndex" на новое значение</span></p>
|
||||
<p class=a1><span style='background:yellow'>(индексация с нуля)</span></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width=181 valign=top style='width:136.0pt;border:solid windowtext 1.0pt;
|
||||
border-top:none;padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1><span style='background:yellow'>MeasurementSelect "Analog1</span><span
|
||||
lang=EN-US style='background:yellow'>"</span></p>
|
||||
</td>
|
||||
<td width=246 valign=top style='width:184.5pt;border-top:none;border-left:
|
||||
none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
|
||||
padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1><span style='background:yellow'>MeasurementSelect OK</span></p>
|
||||
<p class=a1><span style='background:yellow'>Measurement
|
||||
Analog1 </span></p>
|
||||
</td>
|
||||
<td width=215 valign=top style='width:161.0pt;border-top:none;border-left:
|
||||
none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
|
||||
padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1><span style='background:yellow'>Определяет </span><span
|
||||
lang=EN-US style='background:yellow'>Measurement</span><span lang=EN-US
|
||||
style='background:yellow'> </span><span style='background:yellow'>по его
|
||||
имени, который будет использоваться в дальнейшем для </span><span lang=EN-US
|
||||
style='background:yellow'>MeasurementXXXX</span><span lang=EN-US
|
||||
style='background:yellow'> </span><span style='background:yellow'>команд </span></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width=181 valign=top style='width:136.0pt;border:solid windowtext 1.0pt;
|
||||
border-top:none;padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1><span style='background:yellow'>MeasurementRemoveAll</span></p>
|
||||
</td>
|
||||
<td width=246 valign=top style='width:184.5pt;border-top:none;border-left:
|
||||
none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
|
||||
padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1><span style='background:yellow'>MeasurementRemoveAll OK </span></p>
|
||||
</td>
|
||||
<td width=215 valign=top style='width:161.0pt;border-top:none;border-left:
|
||||
none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
|
||||
padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1><span style='background:yellow'>Удаляет все </span><span
|
||||
lang=EN-US style='background:yellow'>Measurements</span><span lang=EN-US
|
||||
style='background:yellow'> </span><span style='background:yellow'>из списка
|
||||
сканера</span></p>
|
||||
<p class=a1><span style='background:yellow'> </span></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width=181 valign=top style='width:136.0pt;border:solid windowtext 1.0pt;
|
||||
border-top:none;padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1><span style='background:yellow'>MeasurementRemove
|
||||
"Barchart1"</span></p>
|
||||
</td>
|
||||
<td width=246 valign=top style='width:184.5pt;border-top:none;border-left:
|
||||
none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
|
||||
padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1><span style='background:yellow'>MeasurementRemove OK
|
||||
Measurement Barchart1 </span></p>
|
||||
</td>
|
||||
<td width=215 valign=top style='width:161.0pt;border-top:none;border-left:
|
||||
none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
|
||||
padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1><span style='background:yellow'>Удаляет </span><span lang=EN-US
|
||||
style='background:yellow'>Measurement</span><span lang=EN-US
|
||||
style='background:yellow'> </span><span style='background:yellow'>с данным
|
||||
именем из списка сканера</span></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width=181 valign=top style='width:136.0pt;border:solid windowtext 1.0pt;
|
||||
border-top:none;padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1>ScanAdd "MeasurementName"</p>
|
||||
</td>
|
||||
<td width=246 valign=top style='width:184.5pt;border-top:none;border-left:
|
||||
none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
|
||||
padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1>ScanAdd OK</p>
|
||||
<p class=a1>Measurement PeakJump1</p>
|
||||
</td>
|
||||
<td width=215 valign=top style='width:161.0pt;border-top:none;border-left:
|
||||
none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
|
||||
padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1>Добавляем созданное измерение сканеру.</p>
|
||||
<p class=a1>Сканер НЕ должен быть запущен.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width=181 rowspan=5 valign=top style='width:136.0pt;border:solid windowtext 1.0pt;
|
||||
border-top:none;padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1>ScanStart "NumScans"</p>
|
||||
</td>
|
||||
<td width=246 valign=top style='width:184.5pt;border-top:none;border-left:
|
||||
none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
|
||||
padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1>ScanStart OK </p>
|
||||
</td>
|
||||
<td width=215 valign=top style='width:161.0pt;border-top:none;border-left:
|
||||
none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
|
||||
padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1>Запускаем сканер.</p>
|
||||
<p class=a1>Далее - асинхронные сообщения после каждого из <span lang=EN-US>NumScans</span>
|
||||
сканирования. </p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width=246 valign=top style='width:184.5pt;border-top:none;border-left:
|
||||
none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
|
||||
padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1>StartingScan 1 16858 0</p>
|
||||
<p class=a1> </p>
|
||||
</td>
|
||||
<td width=215 valign=top style='width:161.0pt;border-top:none;border-left:
|
||||
none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
|
||||
padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1>Сообщение о том, что закончилось некоторое измерение, указано
|
||||
время с момента первого измерения и оставшееся количество измерений до
|
||||
перезапуска</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width=246 valign=top style='width:184.5pt;border-top:none;border-left:
|
||||
none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
|
||||
padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1>StartingMeasurement PeakJump1</p>
|
||||
</td>
|
||||
<td width=215 valign=top style='width:161.0pt;border-top:none;border-left:
|
||||
none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
|
||||
padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1>Сообщение о том, какой Measurment сканера запущен (сканер может
|
||||
иметь несколько режимов измерений)</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width=246 valign=top style='width:184.5pt;border-top:none;border-left:
|
||||
none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
|
||||
padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1>ZeroReading 5.5 1.01e-8</p>
|
||||
</td>
|
||||
<td width=215 valign=top style='width:161.0pt;border-top:none;border-left:
|
||||
none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
|
||||
padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1>Нулевое значени давления ( 5.5 - это MassPosition)</p>
|
||||
<p class=a1>Изменяется командой: MeasurementZeroMass "ZeroMass"</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width=246 valign=top style='width:184.5pt;border-top:none;border-left:
|
||||
none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
|
||||
padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1>MassReading 1 2.9383e-5 </p>
|
||||
</td>
|
||||
<td width=215 valign=top style='width:161.0pt;border-top:none;border-left:
|
||||
none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
|
||||
padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1>Парциальное давление газа заданной массы в формате масса,
|
||||
давление (выводится весь список масс)</p>
|
||||
<p class=a1>Далее сканер остается запущенным в режиме ожидания</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width=181 valign=top style='width:136.0pt;border:solid windowtext 1.0pt;
|
||||
border-top:none;padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1>ScanResume "NumScans"</p>
|
||||
</td>
|
||||
<td width=246 valign=top style='width:184.5pt;border-top:none;border-left:
|
||||
none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
|
||||
padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1>ScanResume OK </p>
|
||||
</td>
|
||||
<td width=215 valign=top style='width:161.0pt;border-top:none;border-left:
|
||||
none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
|
||||
padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1>Продолжает работу сканера для повторного считывания данных с
|
||||
последующей цепочкой аналогичных сообщений</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width=181 valign=top style='width:136.0pt;border:solid windowtext 1.0pt;
|
||||
border-top:none;padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1><span style='background:yellow'>ScanRestart "</span><span
|
||||
lang=EN-US style='background:yellow'>NumScans"</span></p>
|
||||
</td>
|
||||
<td width=246 valign=top style='width:184.5pt;border-top:none;border-left:
|
||||
none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
|
||||
padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1><span style='background:yellow'>ScanRestart OK</span></p>
|
||||
</td>
|
||||
<td width=215 valign=top style='width:161.0pt;border-top:none;border-left:
|
||||
none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
|
||||
padding:2.75pt 2.75pt 2.75pt 2.75pt'>
|
||||
<p class=a1><span style='background:yellow'>Перезапускает сканер с самого
|
||||
начала для "</span><span lang=EN-US style='background:yellow'>NumScans</span><span
|
||||
style='background:yellow'>" измерений </span></p>
|
||||
<p class=a1><span style='background:yellow'>(полезно в случае сбоев
|
||||
сканирования, чтобы заново не переопределять параметры сенсора)</span></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr style='height:10.65pt'>
|
||||
<td width=181 valign=top style='width:136.0pt;border:solid windowtext 1.0pt;
|
||||
border-top:none;padding:2.75pt 2.75pt 2.75pt 2.75pt;height:10.65pt'>
|
||||
<p class=a1>ScanStop</p>
|
||||
</td>
|
||||
<td width=246 valign=top style='width:184.5pt;border-top:none;border-left:
|
||||
none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
|
||||
padding:2.75pt 2.75pt 2.75pt 2.75pt;height:10.65pt'>
|
||||
<p class=a1>ScanStop OK </p>
|
||||
</td>
|
||||
<td width=215 valign=top style='width:161.0pt;border-top:none;border-left:
|
||||
none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
|
||||
padding:2.75pt 2.75pt 2.75pt 2.75pt;height:10.65pt'>
|
||||
<p class=a1>Выключает сканер и сбрасывает с него все имеющиеся Measurements</p>
|
||||
<p class=a1>Для следующих измерений нужно снова ScanAdd
|
||||
"MeasurementName"</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr style='height:3.55pt'>
|
||||
<td width=181 valign=top style='width:136.0pt;border:solid windowtext 1.0pt;
|
||||
border-top:none;padding:2.75pt 2.75pt 2.75pt 2.75pt;height:3.55pt'>
|
||||
<p class=a1>Release</p>
|
||||
</td>
|
||||
<td width=246 valign=top style='width:184.5pt;border-top:none;border-left:
|
||||
none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
|
||||
padding:2.75pt 2.75pt 2.75pt 2.75pt;height:3.55pt'>
|
||||
<p class=a1>Release OK </p>
|
||||
</td>
|
||||
<td width=215 valign=top style='width:161.0pt;border-top:none;border-left:
|
||||
none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
|
||||
padding:2.75pt 2.75pt 2.75pt 2.75pt;height:3.55pt'>
|
||||
<p class=a1>Теряем контроль над сенсором</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p class=MsoNormal><span style='background:yellow'>* номера и описания ошибок:</span></p>
|
||||
|
||||
<p class=MsoNormal><span lang=EN-US style='background:yellow'>200 – </span><span
|
||||
style='background:yellow'>некорректная команда</span></p>
|
||||
|
||||
<p class=MsoNormal><span style='background:yellow'>201 — неверное количество
|
||||
параметров в команде</span></p>
|
||||
|
||||
<p class=MsoNormal><span lang=EN-US style='background:yellow'>202 – </span><span
|
||||
style='background:yellow'>ошибочно</span><span style='background:yellow'> </span><span
|
||||
style='background:yellow'>переданный</span><span style='background:yellow'> </span><span
|
||||
style='background:yellow'>параметр</span><span lang=EN-US style='background:
|
||||
yellow'> (Parameter 1 'State' could not be interpreted as on/off)</span></p>
|
||||
|
||||
<p class=MsoNormal><span lang=EN-US style='background:yellow'>203 – </span><span
|
||||
style='background:yellow'>ошибка</span><span style='background:yellow'> </span><span
|
||||
style='background:yellow'>действия</span><span lang=EN-US style='background:
|
||||
yellow'>, </span><span style='background:yellow'>подразумевающего</span><span
|
||||
style='background:yellow'> </span><span style='background:yellow'>корректное</span><span
|
||||
style='background:yellow'> </span><span style='background:yellow'>выполнение</span><span
|
||||
style='background:yellow'> </span><span style='background:yellow'>какого</span><span
|
||||
style='background:yellow'> </span><span style='background:yellow'>либо</span><span
|
||||
style='background:yellow'> </span><span style='background:yellow'>другого</span><span
|
||||
style='background:yellow'> </span><span style='background:yellow'>действия</span><span
|
||||
lang=EN-US style='background:yellow'> (No sensor selected/Must be in control
|
||||
of sensor/Not scanning)</span></p>
|
||||
|
||||
<p class=MsoNormal><span lang=EN-US style='background:yellow'>204 – </span><span
|
||||
style='background:yellow'>ошибка</span><span style='background:yellow'> </span><span
|
||||
style='background:yellow'>в</span><span style='background:yellow'> </span><span
|
||||
style='background:yellow'>параметрах</span><span lang=EN-US style='background:
|
||||
yellow'>, </span><span style='background:yellow'>связанных</span><span
|
||||
style='background:yellow'> </span><span style='background:yellow'>с</span><span
|
||||
style='background:yellow'> <span lang=EN-US>Measurement (Measurement with this
|
||||
name already exists/Bad SourceIndex/Invalid mass value)</span></span></p>
|
||||
|
||||
<p class=MsoNormal><span style='background:yellow'>300 – ошибка выбора сенсора
|
||||
(неверный серийный номер сенсора)</span></p>
|
||||
|
||||
<p class=MsoNormal> </p>
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2015 Alexander Nozik.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package inr.numass.control.msp
|
||||
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.MetaUtils
|
||||
import inr.numass.control.NumassControlApplication
|
||||
import javafx.stage.Stage
|
||||
|
||||
/**
|
||||
* @author darksnake
|
||||
*/
|
||||
class MspApp : NumassControlApplication<MspDevice>() {
|
||||
|
||||
override val deviceFactory = MspDeviceFactory()
|
||||
|
||||
|
||||
override fun setupStage(stage: Stage, device: MspDevice) {
|
||||
stage.title = "Numass mass-spectrometer view"
|
||||
stage.minHeight = 400.0
|
||||
stage.minWidth = 600.0
|
||||
}
|
||||
|
||||
override fun getDeviceMeta(config: Meta): Meta {
|
||||
return MetaUtils.findNode(config,"device"){it.getString("name") == "numass.msp"}.orElseThrow{RuntimeException("Mass-spectrometer configuration not found")}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,413 @@
|
||||
/*
|
||||
* Copyright 2015 Alexander Nozik.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package inr.numass.control.msp
|
||||
|
||||
import hep.dataforge.connections.NamedValueListener
|
||||
import hep.dataforge.connections.RoleDef
|
||||
import hep.dataforge.connections.RoleDefs
|
||||
import hep.dataforge.context.Context
|
||||
import hep.dataforge.control.collectors.RegularPointCollector
|
||||
import hep.dataforge.control.connections.Roles
|
||||
import hep.dataforge.control.devices.PortSensor
|
||||
import hep.dataforge.control.devices.notifyError
|
||||
import hep.dataforge.control.ports.GenericPortController
|
||||
import hep.dataforge.control.ports.Port
|
||||
import hep.dataforge.control.ports.PortFactory
|
||||
import hep.dataforge.description.ValueDef
|
||||
import hep.dataforge.exceptions.ControlException
|
||||
import hep.dataforge.exceptions.MeasurementException
|
||||
import hep.dataforge.exceptions.PortException
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.states.StateDef
|
||||
import hep.dataforge.states.StateDefs
|
||||
import hep.dataforge.states.valueState
|
||||
import hep.dataforge.storage.StorageConnection
|
||||
import hep.dataforge.tables.TableFormatBuilder
|
||||
import hep.dataforge.tables.ValuesListener
|
||||
import hep.dataforge.useMeta
|
||||
import hep.dataforge.values.ValueType
|
||||
import inr.numass.control.DeviceView
|
||||
import inr.numass.control.NumassStorageConnection
|
||||
import inr.numass.control.msp.MspDevice.Companion.SELECTED_STATE
|
||||
import java.time.Duration
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* @author Alexander Nozik
|
||||
*/
|
||||
@RoleDefs(
|
||||
RoleDef(name = Roles.STORAGE_ROLE, objectType = StorageConnection::class),
|
||||
RoleDef(name = Roles.VIEW_ROLE)
|
||||
)
|
||||
@StateDefs(
|
||||
StateDef(value = ValueDef(key = "controlled", info = "Connection with the device itself"), writable = true),
|
||||
StateDef(ValueDef(key = SELECTED_STATE)),
|
||||
StateDef(value = ValueDef(key = "storing", info = "Define if this device is currently writes to storage"), writable = true),
|
||||
StateDef(value = ValueDef(key = "filament", info = "The number of filament in use"), writable = true),
|
||||
StateDef(value = ValueDef(key = "filamentOn", info = "Mass-spectrometer filament on"), writable = true),
|
||||
StateDef(ValueDef(key = "filamentStatus", info = "Filament status")),
|
||||
StateDef(ValueDef(key = "peakJump.zero", type = [ValueType.NUMBER], info = "Peak jump zero reading"))
|
||||
)
|
||||
@DeviceView(MspDisplay::class)
|
||||
class MspDevice(context: Context, meta: Meta) : PortSensor(context, meta) {
|
||||
|
||||
// private var measurementDelegate: Consumer<MspResponse>? = null
|
||||
|
||||
//val selected: Boolean by valueState("selected").booleanDelegate
|
||||
|
||||
val controlled = valueState(CONTROLLED_STATE) { value ->
|
||||
runOnDeviceThread {
|
||||
val res = control(value.boolean)
|
||||
updateState(CONTROLLED_STATE, res)
|
||||
}
|
||||
}
|
||||
|
||||
val filament = valueState("filament") { value ->
|
||||
selectFilament(value.int)
|
||||
}
|
||||
|
||||
val filamentOn = valueState("filamentOn") { value ->
|
||||
setFilamentOn(value.boolean)
|
||||
}
|
||||
|
||||
var peakJumpZero: Double by valueState("peakJump.zero").doubleDelegate
|
||||
|
||||
private val averagingDuration: Duration = Duration.parse(meta.getString("averagingDuration", "PT30S"))
|
||||
|
||||
private var storageHelper: NumassStorageConnection? = null
|
||||
|
||||
private val collector = RegularPointCollector(averagingDuration) { res ->
|
||||
notifyResult(res)
|
||||
forEachConnection(ValuesListener::class.java) {
|
||||
it.accept(res)
|
||||
}
|
||||
}
|
||||
|
||||
override fun buildConnection(meta: Meta): GenericPortController {
|
||||
logger.info("Connecting to port {}", meta)
|
||||
val port: Port = PortFactory.build(meta)
|
||||
return GenericPortController(context, port, "\r\r").also {
|
||||
it.weakOnPhrase({ it.startsWith("FilamentStatus") }, this) {
|
||||
val response = MspResponse(it)
|
||||
val status = response[0, 2]
|
||||
updateState("filamentOn", status == "ON")
|
||||
updateState("filamentStatus", status)
|
||||
}
|
||||
logger.info("Connected to MKS mass-spectrometer on {}", it.port)
|
||||
}
|
||||
}
|
||||
|
||||
override fun init() {
|
||||
super.init()
|
||||
meta.useMeta("peakJump"){
|
||||
updateState(MEASUREMENT_META_STATE, it)
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(ControlException::class)
|
||||
override fun shutdown() {
|
||||
if (controlled.booleanValue) {
|
||||
setFilamentOn(false)
|
||||
}
|
||||
controlled.set(false)
|
||||
super.shutdown()
|
||||
}
|
||||
|
||||
override val type: String
|
||||
get() = MSP_DEVICE_TYPE
|
||||
|
||||
/**
|
||||
* Startup MSP: get available sensors, select sensor and control.
|
||||
*
|
||||
* @param on
|
||||
* @return
|
||||
* @throws hep.dataforge.exceptions.ControlException
|
||||
*/
|
||||
private fun control(on: Boolean): Boolean {
|
||||
if (on != this.controlled.booleanValue) {
|
||||
val sensorName: String
|
||||
if (on) {
|
||||
logger.info("Starting initialization sequence")
|
||||
//ensure device is connected
|
||||
connected.setAndWait(true)
|
||||
var response = commandAndWait("Sensors")
|
||||
if (response.isOK) {
|
||||
sensorName = response[2, 1]
|
||||
} else {
|
||||
notifyError(response.errorDescription, null)
|
||||
return false
|
||||
}
|
||||
//PENDING определеить в конфиге номер прибора
|
||||
|
||||
response = commandAndWait("Select", sensorName)
|
||||
if (response.isOK) {
|
||||
updateState("selected", true)
|
||||
} else {
|
||||
notifyError(response.errorDescription, null)
|
||||
return false
|
||||
}
|
||||
|
||||
response = commandAndWait("Control", "inr.numass.msp", "1.1")
|
||||
if (response.isOK) {
|
||||
controlled.update(true)
|
||||
} else {
|
||||
notifyError(response.errorDescription, null)
|
||||
return false
|
||||
}
|
||||
// connected = true;
|
||||
updateState(PortSensor.CONNECTED_STATE, true)
|
||||
return true
|
||||
} else {
|
||||
logger.info("Releasing device")
|
||||
return !commandAndWait("Release").isOK
|
||||
}
|
||||
} else {
|
||||
return on
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send request to the msp
|
||||
*
|
||||
* @param command
|
||||
* @param parameters
|
||||
* @throws PortException
|
||||
*/
|
||||
@Throws(PortException::class)
|
||||
private fun command(command: String, vararg parameters: Any) {
|
||||
send(buildCommand(command, *parameters))
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper method to builder msp command string
|
||||
*
|
||||
* @param command
|
||||
* @param parameters
|
||||
* @return
|
||||
*/
|
||||
private fun buildCommand(command: String, vararg parameters: Any): String {
|
||||
val builder = StringBuilder(command)
|
||||
for (par in parameters) {
|
||||
builder.append(String.format(" \"%s\"", par.toString()))
|
||||
}
|
||||
builder.append("\n")
|
||||
return builder.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Send specific command and wait for its results (the result must begin
|
||||
* with command name)
|
||||
*
|
||||
* @param commandName
|
||||
* @param parameters
|
||||
* @return
|
||||
* @throws PortException
|
||||
*/
|
||||
@Throws(PortException::class)
|
||||
private fun commandAndWait(commandName: String, vararg parameters: Any): MspResponse {
|
||||
val command = buildCommand(commandName, *parameters)
|
||||
if (debug) {
|
||||
logger.info("SEND: $command")
|
||||
}
|
||||
val response = connection.sendAndWait(command, TIMEOUT) { str: String -> str.trim { it <= ' ' }.startsWith(commandName) }
|
||||
if (debug) {
|
||||
logger.info("RECEIVE:\n$response")
|
||||
}
|
||||
return MspResponse(response)
|
||||
}
|
||||
|
||||
@Throws(PortException::class)
|
||||
private fun selectFilament(filament: Int) {
|
||||
runOnDeviceThread {
|
||||
val response = commandAndWait("FilamentSelect", filament)
|
||||
if (response.isOK) {
|
||||
this.filament.update(response[1, 1])
|
||||
} else {
|
||||
logger.error("Failed to set filament with error: {}", response.errorDescription)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn filament on or off
|
||||
*
|
||||
* @param filamentOn
|
||||
* @return
|
||||
* @throws hep.dataforge.exceptions.PortException
|
||||
*/
|
||||
@Throws(PortException::class)
|
||||
private fun setFilamentOn(filamentOn: Boolean): Boolean {
|
||||
return if (filamentOn) {
|
||||
commandAndWait("FilamentControl", "On").isOK
|
||||
} else {
|
||||
commandAndWait("FilamentControl", "Off").isOK
|
||||
}
|
||||
}
|
||||
|
||||
override fun stopMeasurement() {
|
||||
runOnDeviceThread {
|
||||
stopPeakJump()
|
||||
}
|
||||
super.stopMeasurement()
|
||||
}
|
||||
|
||||
override fun startMeasurement(oldMeta: Meta?, newMeta: Meta) {
|
||||
if (oldMeta != null) {
|
||||
stopMeasurement()
|
||||
}
|
||||
if (newMeta.getString("type", "peakJump") == "peakJump") {
|
||||
runOnDeviceThread {
|
||||
startPeakJump(newMeta)
|
||||
}
|
||||
} else {
|
||||
throw MeasurementException("Unknown measurement type")
|
||||
}
|
||||
}
|
||||
|
||||
private fun startPeakJump(meta: Meta) {
|
||||
notifyMeasurementState(MeasurementState.IN_PROGRESS)
|
||||
val measurementName = "peakJump"
|
||||
val filterMode = meta.getString("filterMode", "PeakAverage")
|
||||
val accuracy = meta.getInt("accuracy", 5)
|
||||
//PENDING вставить остальные параметры?
|
||||
sendAndWait("MeasurementRemoveAll", Duration.ofMillis(200))
|
||||
|
||||
// val peakMap: MutableMap<Int, String> = LinkedHashMap()
|
||||
|
||||
val builder = TableFormatBuilder().addTime("timestamp")
|
||||
|
||||
if (commandAndWait("AddPeakJump", measurementName, filterMode, accuracy, 0, 0, 0).isOK) {
|
||||
// peakMap.clear()
|
||||
for (peak in meta.getMetaList("peak")) {
|
||||
// peakMap[peak.getInt("mass")] = peak.getString("name", peak.getString("mass"))
|
||||
if (!commandAndWait("MeasurementAddMass", peak.getString("mass")).isOK) {
|
||||
throw ControlException("Can't add mass to measurement measurement for msp")
|
||||
}
|
||||
builder.addNumber(peak.getString("name", peak.getString("mass")))
|
||||
}
|
||||
} else {
|
||||
throw ControlException("Can't create measurement for msp")
|
||||
}
|
||||
|
||||
storageHelper = NumassStorageConnection("msp") { builder.build() }
|
||||
connect(storageHelper)
|
||||
|
||||
connection.onAnyPhrase(this) {
|
||||
val response = MspResponse(it)
|
||||
when (response.commandName) {
|
||||
"MassReading" -> {
|
||||
val mass = java.lang.Double.parseDouble(response[0, 1])
|
||||
val value = java.lang.Double.parseDouble(response[0, 2]) / 100.0
|
||||
val massName = Integer.toString(Math.floor(mass + 0.5).toInt())
|
||||
collector.put(massName, value)
|
||||
forEachConnection(Roles.VIEW_ROLE, NamedValueListener::class.java) { listener -> listener.pushValue(massName, value) }
|
||||
}
|
||||
"ZeroReading" -> {
|
||||
updateState("peakJump.zero", java.lang.Double.parseDouble(response[0, 2]) / 100.0)
|
||||
}
|
||||
"StartingScan" -> {
|
||||
val numScans = Integer.parseInt(response[0, 3])
|
||||
|
||||
if (numScans == 0) {
|
||||
try {
|
||||
command("ScanResume", 10)
|
||||
//FIXME обработать ошибку связи
|
||||
} catch (ex: PortException) {
|
||||
notifyError("Failed to resume scan", ex)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!filamentOn.booleanValue) {
|
||||
notifyError("Can't start measurement. Filament is not turned on.")
|
||||
}
|
||||
if (!commandAndWait("ScanAdd", measurementName).isOK) {
|
||||
notifyError("Failed to add scan")
|
||||
}
|
||||
|
||||
if (!commandAndWait("ScanStart", 2).isOK) {
|
||||
notifyError("Failed to start scan")
|
||||
}
|
||||
}
|
||||
|
||||
private fun stopPeakJump() {
|
||||
collector.stop()
|
||||
val stop = commandAndWait("ScanStop").isOK
|
||||
//Reset loaders in connections
|
||||
storageHelper?.let { disconnect(it) }
|
||||
notifyMeasurementState(MeasurementState.STOPPED)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The MKS response as two-dimensional array of strings
|
||||
*/
|
||||
class MspResponse(response: String) {
|
||||
|
||||
private val data = ArrayList<List<String>>()
|
||||
|
||||
val commandName: String
|
||||
get() = this[0, 0]
|
||||
|
||||
val isOK: Boolean
|
||||
get() = "OK" == this[0, 1]
|
||||
|
||||
init {
|
||||
val rx = "[^\"\\s]+|\"(\\\\.|[^\\\\\"])*\""
|
||||
val scanner = Scanner(response.trim { it <= ' ' })
|
||||
|
||||
while (scanner.hasNextLine()) {
|
||||
val line = ArrayList<String>()
|
||||
var next: String? = scanner.findWithinHorizon(rx, 0)
|
||||
while (next != null) {
|
||||
line.add(next)
|
||||
next = scanner.findInLine(rx)
|
||||
}
|
||||
data.add(line)
|
||||
}
|
||||
}
|
||||
|
||||
fun errorCode(): Int {
|
||||
return if (isOK) {
|
||||
-1
|
||||
} else {
|
||||
Integer.parseInt(get(1, 1))
|
||||
}
|
||||
}
|
||||
|
||||
val errorDescription: String
|
||||
get() {
|
||||
return if (isOK) {
|
||||
throw RuntimeException("Not a error")
|
||||
} else {
|
||||
get(2, 1)
|
||||
}
|
||||
}
|
||||
|
||||
operator fun get(lineNo: Int, columnNo: Int): String = data[lineNo][columnNo]
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val MSP_DEVICE_TYPE = "numass.msp"
|
||||
const val CONTROLLED_STATE = "controlled"
|
||||
const val SELECTED_STATE = "selected"
|
||||
|
||||
private val TIMEOUT = Duration.ofMillis(200)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package inr.numass.control.msp
|
||||
|
||||
import hep.dataforge.context.Context
|
||||
import hep.dataforge.control.devices.DeviceFactory
|
||||
import hep.dataforge.meta.Meta
|
||||
|
||||
/**
|
||||
* Created by darksnake on 09-May-17.
|
||||
*/
|
||||
class MspDeviceFactory : DeviceFactory {
|
||||
override val type = MspDevice.MSP_DEVICE_TYPE
|
||||
|
||||
override fun build(context: Context, config: Meta): MspDevice {
|
||||
return MspDevice(context, config)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* Copyright 2015 Alexander Nozik.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package inr.numass.control.msp
|
||||
|
||||
import hep.dataforge.connections.NamedValueListener
|
||||
import hep.dataforge.control.devices.PortSensor
|
||||
import hep.dataforge.control.devices.Sensor
|
||||
import hep.dataforge.fx.asBooleanProperty
|
||||
import hep.dataforge.fx.bindWindow
|
||||
import hep.dataforge.fx.fragments.LogFragment
|
||||
import hep.dataforge.fx.plots.PlotContainer
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.MetaBuilder
|
||||
import hep.dataforge.plots.PlotGroup
|
||||
import hep.dataforge.plots.PlotUtils
|
||||
import hep.dataforge.plots.data.TimePlot
|
||||
import hep.dataforge.plots.data.TimePlot.Companion.setMaxItems
|
||||
import hep.dataforge.plots.data.TimePlot.Companion.setPrefItems
|
||||
import hep.dataforge.plots.jfreechart.JFreeChartFrame
|
||||
import hep.dataforge.states.ValueState
|
||||
import hep.dataforge.values.Value
|
||||
import inr.numass.control.DeviceDisplayFX
|
||||
import inr.numass.control.deviceStateIndicator
|
||||
import inr.numass.control.deviceStateToggle
|
||||
import inr.numass.control.switch
|
||||
import javafx.beans.property.SimpleObjectProperty
|
||||
import javafx.collections.FXCollections
|
||||
import javafx.collections.MapChangeListener
|
||||
import javafx.geometry.Insets
|
||||
import javafx.geometry.Orientation
|
||||
import javafx.scene.Parent
|
||||
import javafx.scene.control.Alert
|
||||
import javafx.scene.layout.Priority
|
||||
import javafx.scene.layout.VBox
|
||||
import javafx.scene.paint.Paint
|
||||
import tornadofx.*
|
||||
|
||||
/**
|
||||
* FXML Controller class
|
||||
|
||||
* @author darksnake
|
||||
*/
|
||||
class MspDisplay() : DeviceDisplayFX<MspDevice>(), NamedValueListener {
|
||||
|
||||
private val table = FXCollections.observableHashMap<String, Value>()
|
||||
|
||||
override fun getBoardView(): Parent {
|
||||
return VBox().apply {
|
||||
this += super.getBoardView()
|
||||
}
|
||||
}
|
||||
|
||||
override fun buildView(device: MspDevice): View {
|
||||
return MspView()
|
||||
}
|
||||
|
||||
override fun pushValue(valueName: String, value: Value) {
|
||||
table[valueName] = value
|
||||
}
|
||||
|
||||
|
||||
inner class MspView : View("Numass mass-spectrometer measurement") {
|
||||
private val plotFrameMeta: Meta = device.meta.getMeta("plotConfig", device.meta)
|
||||
|
||||
private val plotFrame by lazy {
|
||||
val basePlotConfig = MetaBuilder("plotFrame")
|
||||
.setNode(MetaBuilder("yAxis")
|
||||
.setValue("type", "log")
|
||||
.setValue("title", "partial pressure")
|
||||
.setValue("units", "mbar")
|
||||
)
|
||||
.setValue("xAxis.type", "time")
|
||||
|
||||
|
||||
JFreeChartFrame().apply { configure(basePlotConfig) }.apply {
|
||||
PlotUtils.setXAxis(this, "timestamp", "", "time")
|
||||
configure(plotFrameMeta)
|
||||
}
|
||||
}
|
||||
val plottables = PlotGroup.typed<TimePlot>("peakJump").apply {
|
||||
setMaxItems(this, 1000)
|
||||
setPrefItems(this, 400)
|
||||
|
||||
if (plotFrameMeta.hasMeta("peakJump.peak")) {
|
||||
for (peakMeta in plotFrameMeta.getMetaList("peakJump.peak")) {
|
||||
val mass = peakMeta.getString("mass")
|
||||
get(mass) ?: TimePlot(mass, mass).also {
|
||||
it.configureValue("titleBase", peakMeta.getString("title", mass))
|
||||
add(it)
|
||||
}.configure(peakMeta)
|
||||
}
|
||||
} else {
|
||||
showError("No peaks defined in config")
|
||||
throw RuntimeException()
|
||||
}
|
||||
}
|
||||
|
||||
// private val logWindow = FragmentWindow(LogFragment().apply {
|
||||
// addLogHandler(device.logger)
|
||||
// })
|
||||
|
||||
private val filamentProperty = SimpleObjectProperty<Int>(this, "filament", 1).apply {
|
||||
addListener { _, oldValue, newValue ->
|
||||
if (newValue != oldValue) {
|
||||
runAsync {
|
||||
device.filament.set(newValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val root = borderpane {
|
||||
minHeight = 400.0
|
||||
minWidth = 600.0
|
||||
top {
|
||||
toolbar {
|
||||
deviceStateToggle(this@MspDisplay, MspDevice.CONTROLLED_STATE, "Connect")
|
||||
combobox(filamentProperty, listOf(1, 2)) {
|
||||
cellFormat {
|
||||
text = "Filament $it"
|
||||
}
|
||||
disableProperty().bind(booleanStateProperty(PortSensor.CONNECTED_STATE).not())
|
||||
}
|
||||
switch {
|
||||
padding = Insets(5.0, 0.0, 0.0, 0.0)
|
||||
disableProperty().bind(device.controlled.asBooleanProperty().not())
|
||||
device.filamentOn.asBooleanProperty().bindBidirectional(selectedProperty())
|
||||
}
|
||||
deviceStateIndicator(this@MspDisplay, "filamentStatus", false) {
|
||||
when (it.string) {
|
||||
"ON" -> Paint.valueOf("red")
|
||||
"OFF" -> Paint.valueOf("blue")
|
||||
"WARM-UP", "COOL-DOWN" -> Paint.valueOf("yellow")
|
||||
else -> Paint.valueOf("grey")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
togglebutton("Measure") {
|
||||
isSelected = false
|
||||
disableProperty().bind(booleanStateProperty(PortSensor.CONNECTED_STATE).not())
|
||||
device.measuring.asBooleanProperty().bindBidirectional(selectedProperty())
|
||||
}
|
||||
togglebutton("Store") {
|
||||
isSelected = false
|
||||
disableProperty().bind(booleanStateProperty(Sensor.MEASURING_STATE).not())
|
||||
device.states.getState<ValueState>("storing")?.asBooleanProperty()?.bindBidirectional(selectedProperty())
|
||||
}
|
||||
separator(Orientation.VERTICAL)
|
||||
pane {
|
||||
hgrow = Priority.ALWAYS
|
||||
}
|
||||
separator(Orientation.VERTICAL)
|
||||
|
||||
togglebutton("Log") {
|
||||
isSelected = false
|
||||
|
||||
LogFragment().apply {
|
||||
addLogHandler(device.logger)
|
||||
bindWindow(this@togglebutton, selectedProperty())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
center = PlotContainer(plotFrame).root
|
||||
}
|
||||
|
||||
init {
|
||||
table.addListener { change: MapChangeListener.Change<out String, out Value> ->
|
||||
if (change.wasAdded()) {
|
||||
val pl = plottables[change.key] as TimePlot?
|
||||
val value = change.valueAdded
|
||||
if (pl != null) {
|
||||
if (value.double > 0) {
|
||||
pl.put(value)
|
||||
} else {
|
||||
pl.put(Value.NULL)
|
||||
}
|
||||
val titleBase = pl.config.getString("titleBase")
|
||||
val title = String.format("%s (%.4g)", titleBase, value.double)
|
||||
pl.configureValue("title", title)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// override fun evaluateDeviceException(device: Device, message: String, exception: Throwable) {
|
||||
// Platform.runLater {
|
||||
// logFragment!!.appendLine("ERROR: " + message)
|
||||
// showError(message)
|
||||
// }
|
||||
// }
|
||||
|
||||
private fun showError(message: String) {
|
||||
val alert = Alert(Alert.AlertType.ERROR)
|
||||
alert.title = "Error!"
|
||||
alert.headerText = null
|
||||
alert.contentText = message
|
||||
|
||||
alert.showAndWait()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
inr.numass.control.msp.MspDeviceFactory
|
||||
35
numass-control/msp/src/main/resources/config/devices.xml
Normal file
35
numass-control/msp/src/main/resources/config/devices.xml
Normal file
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2015 Alexander Nozik.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<config>
|
||||
<storage path="file:///D:/temp/test" type = "numass"/>
|
||||
<device type="msp" name="numass.msp" debug = "true">
|
||||
<!--<connection ip="127.0.0.1" port="10014"/>-->
|
||||
<port type="tcp" ip="192.168.111.11" port="10014"/>
|
||||
<peakJump>
|
||||
<peak mass="2" title="hydrogen" color="black" thickness="4"/>
|
||||
<peak mass="3"/>
|
||||
<peak mass="4"/>
|
||||
<peak mass="6" title="tritium" thickness="4"/>
|
||||
<peak mass="12"/>
|
||||
<peak mass="14"/>
|
||||
<peak mass="18" title="water"/>
|
||||
<peak mass="28"/>
|
||||
<peak mass="32" title="oxygen"/>
|
||||
</peakJump>
|
||||
</device>
|
||||
</config>
|
||||
50
numass-control/msp/src/main/resources/fxml/MspView.fxml
Normal file
50
numass-control/msp/src/main/resources/fxml/MspView.fxml
Normal file
@@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!--
|
||||
Copyright 2015 Alexander Nozik.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.BorderPane?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.layout.Pane?>
|
||||
<?import javafx.scene.shape.Circle?>
|
||||
<?import org.controlsfx.control.ToggleSwitch?>
|
||||
<BorderPane fx:id="root" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="400.0" minWidth="600.0"
|
||||
xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1"
|
||||
fx:controller="inr.numass.control.msp.MspDisplay">
|
||||
<top>
|
||||
<ToolBar prefHeight="50.0" prefWidth="200.0">
|
||||
<ToggleButton fx:id="connectButton" mnemonicParsing="false" text="Connect"/>
|
||||
<ComboBox fx:id="filamentSelector" promptText="Fillament 1" visibleRowCount="2"/>
|
||||
<ToggleSwitch fx:id="filamentButton" prefHeight="40.0" prefWidth="35.0">
|
||||
<padding>
|
||||
<Insets top="11.0"/>
|
||||
</padding>
|
||||
</ToggleSwitch>
|
||||
<Circle fx:id="filamentIndicator" fill="GRAY" radius="10.0" stroke="BLACK" strokeType="INSIDE"/>
|
||||
<Separator orientation="VERTICAL" prefHeight="20.0"/>
|
||||
<ToggleButton fx:id="measureButton" mnemonicParsing="false" onAction="#onPlotToggle" text="Measure"/>
|
||||
<ToggleButton fx:id="storeButton" mnemonicParsing="false" onAction="#onStoreButtonClick" text="Store"/>
|
||||
<Separator orientation="VERTICAL" prefHeight="20.0"/>
|
||||
<Pane HBox.hgrow="ALWAYS"/>
|
||||
<ToggleButton fx:id="consoleButton" mnemonicParsing="false" text="Console"/>
|
||||
</ToolBar>
|
||||
</top>
|
||||
<center>
|
||||
<BorderPane fx:id="plotPane" minHeight="400.0" minWidth="600.0" prefHeight="200.0" prefWidth="200.0"/>
|
||||
</center>
|
||||
</BorderPane>
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2015 Alexander Nozik.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package inr.numass.control.msp;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexander Nozik
|
||||
*/
|
||||
public class TestScanner {
|
||||
|
||||
/**
|
||||
* @param args the command line arguments
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
MspDevice.MspResponse response = new MspDevice.MspResponse(
|
||||
"FilamentStatus 1 ON\n"
|
||||
+ "Trip None \n"
|
||||
+ "Drive Off\n"
|
||||
+ "EmissionTripState OK\n"
|
||||
+ "ExternalTripState OK \n"
|
||||
+ "RVCTripState OK");
|
||||
System.out.println(response.get(2, 1));
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user