Merge legacy dataforge and migrate to git

This commit is contained in:
2021-05-29 13:45:33 +03:00
commit c4164f2681
1087 changed files with 117263 additions and 0 deletions

View 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')
}

File diff suppressed because one or more lines are too long

View 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>&nbsp;</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'>&nbsp;</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 &quot;SerialNumber&quot;</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 &quot;AppName&quot; &quot;Version&quot;</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>&quot;On/Off&quot;</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>&nbsp;</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 -&gt; OK -&gt; ON </span>или<span
lang=EN-US> COOL-DOWN -&gt; OK -&gt; 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 &quot;MeasurementName&quot; &quot;FilterMode&quot;
&quot;0..8&quot; &quot;0&quot; &quot;0&quot; &quot;0&quot;</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 &quot;Mass&quot;</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'>&quot;</span><span lang=EN-US
style='background:yellow'>MassIndex&quot; &quot;NewMass&quot;</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'>Заменяет массу с индексом &quot;
MassIndex&quot; на новое значение</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 &quot;Analog1</span><span
lang=EN-US style='background:yellow'>&quot;</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'>&nbsp;</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
&quot;Barchart1&quot;</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 &quot;MeasurementName&quot;</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 &quot;NumScans&quot;</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>&nbsp;</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 &quot;ZeroMass&quot;</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 &quot;NumScans&quot;</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 &quot;</span><span
lang=EN-US style='background:yellow'>NumScans&quot;</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'>Перезапускает сканер с самого
начала для &quot;</span><span lang=EN-US style='background:yellow'>NumScans</span><span
style='background:yellow'>&quot; измерений </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
&quot;MeasurementName&quot;</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>&nbsp;</p>
</div>
</body>
</html>

View File

@@ -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")}
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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()
}
}
}

View File

@@ -0,0 +1 @@
inr.numass.control.msp.MspDeviceFactory

View 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>

View 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>

View File

@@ -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));
}
}