<?xml version="1.0" encoding="utf-8"?>

<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
	<channel>
		<title>Форум программистов и сисадминов Киберфорум - Блоги - ФедосеевПавел</title>
		<link>https://www.cyberforum.ru/blogs/534277/</link>
		<description>КиберФорум - форум программистов, системных администраторов, администраторов баз данных, компьютерный форум, форум по электронике и бытовой технике, обсуждение софта. Бесплатная помощь в решении задач по программированию и наукам, решение проблем с компьютером, операционными системам</description>
		<language>ru</language>
		<lastBuildDate>Sat, 09 May 2026 15:55:25 GMT</lastBuildDate>
		<generator>vBulletin</generator>
		<ttl>60</ttl>
		<image>
			<url>https://www.cyberforum.ru//cyberstatic.net/images/misc/rss.jpg</url>
			<title>Форум программистов и сисадминов Киберфорум - Блоги - ФедосеевПавел</title>
			<link>https://www.cyberforum.ru/blogs/534277/</link>
		</image>
		<item>
			<title><![CDATA[[Owen Logic] Поддержание уровня воды в резервуаре количеством включённых насосов: моделирование и выбор регулятора]]></title>
			<link>https://www.cyberforum.ru/blogs/534277/10807.html</link>
			<pubDate>Sat, 14 Mar 2026 20:11:25 GMT</pubDate>
			<description><![CDATA[[Owen Logic] Поддержание уровня воды в резервуаре количеством включённых насосов: моделирование и...]]></description>
			<content:encoded><![CDATA[<div><h2>[Owen Logic] Поддержание уровня воды в резервуаре количеством включённых насосов: моделирование и выбор регулятора</h2><br />
<br />
<br />
<h3>ВВЕДЕНИЕ</h3><br />
<br />
Выполняя задание на управление насосной группой заполнения резервуара, потребовалось выбрать алгоритм регулятора каскада из нескольких вариантов.<br />
<br />
Выбор определил при помощи моделирования объекта регулирования с различными вариантами каскадного регулятора.<br />
<br />
О моделировании и результатах эта статья.<br />
<br />
<h3>1. ЗАДАНИЕ НА АВТОМАТИЗАЦИЮ</h3><br />
<br />
Имеются<ul><li>резервуар для воды</li>
<li>группа из пяти насосов, заполняющих резервуар</li>
<li>вторая насосная группа, одновременно опустошающая его</li>
</ul><br />
Уровень воды в резервуаре измеряется аналоговым датчиком.<br />
<br />
Расход воды через перекачивающие насосы периодически изменяется, но в целом длительное время сохраняется постоянным.<br />
<br />
Поддержание уровня воды в резервуаре осуществляется изменением количества одновременно работающих заполняющих насосов.<br />
<br />
При изменении количества работающих насосов следует стремиться выровнять их наработку — включать наименее работавший, отключать наиболее проработавший. Ротацию выполнять по мере выполнения запросов на изменение количества включённых насосов, ограничений длительности непрерывной работы конкретного насоса быть не должно.<br />
<br />
Каждый из заполняющих насосов может как участвовать в каскадном регулировании, так и быть выведенным из него (например, в ремонт). Т.е. количество принимающих участие в регулировании может варьироваться.<br />
<br />
<h3>2. ВОЗМОЖНЫЕ АЛГОРИТМЫ КАСКАДНОГО РЕГУЛЯТОРА</h3><br />
<br />
<h4>2.1 ПИД регулятор с распределением диапазона</h4><br />
<br />
Выход ПИД регулятора изменяется от 0% до 100%. Разделяем весь диапазон на 6 (5+1) частей и сопоставив каждую часть с количеством одновременно работающих насосов получим, казалось бы, работающее решение.<br />
<div class="codeblock"><table class="pascal"><thead><tr><td colspan="2" id="319996808"  class="head">Pascal</td></tr></thead><tbody><tr class="li1"><td><div id="319996808" style="height: 158px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
</pre></td><td class="de1"><pre class="de1">&nbsp; &nbsp; <span class="kw1">case</span> real_to_udint<span class="br0">&#40;</span>rPID<span class="br0">&#41;</span> <span class="kw1">of</span>
&nbsp; &nbsp; &nbsp; &nbsp;<span class="nu0">0</span><span class="sy1">..</span><span class="nu0">9</span><span class="sy1">:</span> &nbsp; nAmount <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="nu0">10</span><span class="sy1">..</span><span class="nu0">29</span><span class="sy1">:</span> &nbsp;nAmount <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">1</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="nu0">30</span><span class="sy1">..</span><span class="nu0">49</span><span class="sy1">:</span> &nbsp;nAmount <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">2</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="nu0">50</span><span class="sy1">..</span><span class="nu0">69</span><span class="sy1">:</span> &nbsp;nAmount <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">3</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="nu0">70</span><span class="sy1">..</span><span class="nu0">89</span><span class="sy1">:</span> &nbsp;nAmount <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">4</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="nu0">90</span><span class="sy1">..</span><span class="nu0">100</span><span class="sy1">:</span> nAmount <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">5</span><span class="sy1">;</span>
&nbsp; &nbsp; end_case</pre></td></tr></table></div></td></tr></tbody></table></div>Но способ имеет серьёзный недостаток — вероятен случай частых переключений при колебаниях значения выхода ПИД на границе двух диапазонов.<br />
<br />
Есть вариант, улучшающий, хотя и не полностью устраняющий проблемы — добавить разрывы в диапазонах переключения. При этом возникает неоднозначность при включении регулятора и значении ПИД, оказавшемся в разрыве диапазона.<br />
<div class="codeblock"><table class="pascal"><thead><tr><td colspan="2" id="294325432"  class="head">Pascal</td></tr></thead><tbody><tr class="li1"><td><div id="294325432" style="height: 158px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
</pre></td><td class="de1"><pre class="de1">&nbsp; &nbsp; <span class="kw1">case</span> real_to_udint<span class="br0">&#40;</span>rPID<span class="br0">&#41;</span> <span class="kw1">of</span>
&nbsp; &nbsp; &nbsp; &nbsp;<span class="nu0">0</span><span class="sy1">..</span><span class="nu0">13</span><span class="sy1">:</span> &nbsp;nAmount <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="nu0">18</span><span class="sy1">..</span><span class="nu0">31</span><span class="sy1">:</span> &nbsp;nAmount <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">1</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="nu0">36</span><span class="sy1">..</span><span class="nu0">49</span><span class="sy1">:</span> &nbsp;nAmount <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">2</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="nu0">54</span><span class="sy1">..</span><span class="nu0">67</span><span class="sy1">:</span> &nbsp;nAmount <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">3</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="nu0">72</span><span class="sy1">..</span><span class="nu0">84</span><span class="sy1">:</span> &nbsp;nAmount <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">4</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="nu0">88</span><span class="sy1">..</span><span class="nu0">100</span><span class="sy1">:</span> nAmount <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">5</span><span class="sy1">;</span>
&nbsp; &nbsp; end_case</pre></td></tr></table></div></td></tr></tbody></table></div>Другой способ улучшения заключается во введении гистерезиса, когда для переключения количества требуемых насосов недостаточно ещё раз пересечь границу в обратном направлении — требуется пересечь соседнюю границу. Для этого по описанной ранее сетке определяется минимальное количество насосов nMin, а потом двумя сравнениями получают задаваемое nAmount<br />
<div class="codeblock"><table class="pascal"><thead><tr><td colspan="2" id="317876877"  class="head">Pascal</td></tr></thead><tbody><tr class="li1"><td><div id="317876877" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
</pre></td><td class="de1"><pre class="de1">&nbsp; &nbsp; <span class="kw1">CASE</span> real_to_udint<span class="br0">&#40;</span>rPID<span class="br0">&#41;</span> <span class="kw1">OF</span>
&nbsp; &nbsp; &nbsp; &nbsp;<span class="nu0">0</span><span class="sy1">..</span><span class="nu0">9</span><span class="sy1">:</span> &nbsp; nMin <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="nu0">10</span><span class="sy1">..</span><span class="nu0">29</span><span class="sy1">:</span> &nbsp;nMin <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">1</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="nu0">30</span><span class="sy1">..</span><span class="nu0">49</span><span class="sy1">:</span> &nbsp;nMin <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">2</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="nu0">50</span><span class="sy1">..</span><span class="nu0">69</span><span class="sy1">:</span> &nbsp;nMin <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">3</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="nu0">70</span><span class="sy1">..</span><span class="nu0">89</span><span class="sy1">:</span> &nbsp;nMin <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">4</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; <span class="nu0">90</span><span class="sy1">..</span><span class="nu0">100</span><span class="sy1">:</span> nMin <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">5</span><span class="sy1">;</span>
&nbsp; &nbsp; END_CASE
&nbsp; &nbsp; <span class="co1">// число агрегатов в диапазоне нахождения измеренного значения уровня</span>
&nbsp; &nbsp; <span class="co1">// равно nMin или nMin+1 в зависимости от того снижается уровень или растёт</span>
&nbsp; &nbsp; <span class="co1">// Поэтому, если уровень снижается и теперь нужно больше агрегатов (nAmount &lt; nMin),</span>
&nbsp; &nbsp; <span class="co1">// то повышаем их запрашиваемое количество.</span>
&nbsp; &nbsp; <span class="co1">// Если уровень вырос и агрегатов требуется меньше (nAmount &gt; nMin + 1),</span>
&nbsp; &nbsp; <span class="co1">// то понижаем их запрашиваемое количество</span>
&nbsp; &nbsp; <span class="kw1">IF</span> nAmount &lt; nMin <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// если реально включено меньше, то требуем не меньше минимального</span>
&nbsp; &nbsp; &nbsp; &nbsp; nAmount <span class="sy1">:</span><span class="sy3">=</span> nMin<span class="sy1">;</span>
&nbsp; &nbsp; ELSIF nAmount &gt; nMin <span class="sy3">+</span> <span class="nu0">1</span> <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// если уровень поднялся и стало требоваться меньше агрегатов, то на один снижаем</span>
&nbsp; &nbsp; &nbsp; &nbsp; nAmount <span class="sy1">:</span><span class="sy3">=</span> nMin <span class="sy3">+</span> <span class="nu0">1</span><span class="sy1">;</span>
&nbsp; &nbsp; END_IF</pre></td></tr></table></div></td></tr></tbody></table></div><br />
<br />
<h4>2.2 Переключения по интегральному критерию</h4><br />
<br />
Алгоритм имеет сходство с ПИД (И) регулятором, дополненным некоторыми свойствами, предполагающими улучшение качества работы.<br />
<br />
За основу взял алгоритм каскадного регулятора из описания прибора Овен КТР-121.02.41 текущей на данный момент версии, лишь немного изменив его, т.к. в исходном варианте задание регулирования и зона нечувствительности не задаются, а вычисляется из двух задаваемых границ регулирования.<br />
Для ознакомления с оригинальной версией алгоритма прикреплю руководство по эксплуатации прибора — описание находится в разделе 10.5 на странице 29.<br />
<br />
В модифицированном варианте алгоритм получился следующим.<br />
На вход поступают следующие данные:<ul><li>rPV — измеренное значение регулируемой величины</li>
<li>rSP — задание регулятора</li>
<li>rDB — зона нечувствительности (симметричная в каждую сторону от задания регулятора)</li>
<li>rQSub — значение параметро-временного интеграла, по достижении которого работающий насос отключаются</li>
<li>rQAdd — значение параметро-временного интеграла, по достижении которого дополнительный насос включается</li>
<li>nDelayAdd — задержка начала расчета интеграла на подключение ступени, c</li>
<li>nDelaySub — задержка начала расчета интеграла на отключение ступени, c</li>
<li>rSP_DeltaOn — снижение измеренного значения от задания для принудительного запуска агрегата</li>
<li>rSP_Off — максимальное измеренное значения для принудительного отключения всех агрегатов</li>
</ul>На выходе:<ul><li>nAmount — количество насосов, которые должны быть включены</li>
</ul><br />
При выходе значения измеренного уровня воды в резервуаре за пределы зоны нечувствительности начинается вычисление интегрального критерия, причём раздельно для добавления или сокращения количества насосов.<br />
При возвращении измеренного уровня в зону нечувствительности рассчитанные ранее критерии обнуляются.<br />
Если значение интегрального критерия превысило заданное значение (rQAdd или rQSub), то количество насосов изменяется, значение критерия обнуляется и в вычислении делается пауза на стабилизацию уровня продолжительностью nDelayAdd или nDelaySub.<br />
На рисунке демонстрируется работа алгоритма.<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=11714&amp;d=1772939757" rel="Lightbox" id="attachment11714" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=11714&amp;thumb=1&amp;d=1772939757" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: Cascade_Integral.png
Просмотров: 155
Размер:	57.0 Кб
ID:	11714" style="margin: 5px" /></a><br />
<br />

			   <div class="spoiler-wrap">
				   <div class="spoiler-head folded clickable">ФБ каскадного регулятора с интегральным критерием</div>
				   <div class="spoiler-body">
					   <div class="codeblock"><table class="pascal"><thead><tr><td colspan="2" id="589231040"  class="head">Pascal</td></tr></thead><tbody><tr class="li1"><td><div id="589231040" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
</pre></td><td class="de1"><pre class="de1"><span class="co1">/// &lt;Description&gt;ФБ каскадного регулирования инерционной нагрузкой - позволяет увеличивать и уменьшать количество одновременно работающих агрегатов&lt;/Description&gt;</span>
<span class="co1">/// &lt;Author&gt;!!FPA!!&lt;/Author&gt;</span>
<span class="co1">/// &lt;GroupName&gt;Регуляторы&lt;/GroupName&gt;</span>
&nbsp;
FUNCTION_BLOCK Cascade_Integral_
&nbsp;
&nbsp; &nbsp; VAR_INPUT
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">/// &lt;Description&gt;Разрешение работы. При FALSE выход рабен нулю - все агрегаты одновременно выключаются.&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bEnable<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">/// &lt;Description&gt;Запрос включения регулятора&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bRequest<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">/// &lt;Description&gt;Исправность агрегата 1 к работе&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bReady1<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">/// &lt;Description&gt;Исправность агрегата 2 к работе&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bReady2<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">/// &lt;Description&gt;Исправность агрегата 3 к работе&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bReady3<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">/// &lt;Description&gt;Исправность агрегата 4 к работе&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bReady4<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">/// &lt;Description&gt;Исправность агрегата 5 к работе&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bReady5<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">/// &lt;Description&gt;Измеренное значение регулируемой величины&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rPV<span class="sy1">:</span> <span class="kw4">REAL</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">/// &lt;Description&gt;Задание регулятора&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rSP<span class="sy1">:</span> <span class="kw4">REAL</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">/// &lt;Description&gt;Зона нечувствительности (симметричная в каждую сторону)&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rDB<span class="sy1">:</span> <span class="kw4">REAL</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">/// &lt;Description&gt;Значение параметро-временного интеграла, по достижении которого ступень включается&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rQSub<span class="sy1">:</span> <span class="kw4">REAL</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">/// &lt;Description&gt;Значение параметро-временного интеграла, по достижении которого ступень отключаются&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rQAdd<span class="sy1">:</span> <span class="kw4">REAL</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">/// &lt;Description&gt;Задержка начала расчета интеграла на подключение ступени, c&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; nDelayAdd<span class="sy1">:</span> UDINT<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">/// &lt;Description&gt;Задержка начала расчета интеграла на отключение ступени, c&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; nDelaySub<span class="sy1">:</span> UDINT<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">/// &lt;Description&gt;Снижение измеренного значения от задания для принудительного запуска агрегата&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rSP_DeltaOn<span class="sy1">:</span> <span class="kw4">REAL</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">/// &lt;Description&gt;Максимальное измеренное значения для принудительного отключения всех агрегатов&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rSP_Off<span class="sy1">:</span> <span class="kw4">REAL</span><span class="sy1">;</span>
&nbsp; &nbsp; END_VAR
&nbsp;
&nbsp; &nbsp; VAR_OUTPUT
&nbsp; &nbsp; &nbsp; &nbsp; nAmount<span class="sy1">:</span> UDINT<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; nMax<span class="sy1">:</span> UDINT<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bAddRequest<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bSubRequest<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bCalcEn<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; nTimeCount<span class="sy1">:</span> UDINT<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rQCalcAdd<span class="sy1">:</span> <span class="kw4">REAL</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rQCalcSub<span class="sy1">:</span> <span class="kw4">REAL</span><span class="sy1">;</span>
&nbsp; &nbsp; END_VAR
&nbsp;
&nbsp; &nbsp; <span class="kw1">VAR</span>
&nbsp; &nbsp; &nbsp; &nbsp; bInitDone<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; nAmountPrevious<span class="sy1">:</span> UDINT<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; nTimeNow<span class="sy1">:</span> UDINT<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; nTimePrevious<span class="sy1">:</span> UDINT<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; nTimeDelta<span class="sy1">:</span> UDINT<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rTimeDelta<span class="sy1">:</span> <span class="kw4">REAL</span><span class="sy1">;</span>
&nbsp; &nbsp; END_VAR
&nbsp;
&nbsp; &nbsp; <span class="co1">// подсчёт готовых агрегатов</span>
&nbsp; &nbsp; nMax <span class="sy1">:</span><span class="sy3">=</span> BOOL_TO_UDINT<span class="br0">&#40;</span>bReady1<span class="br0">&#41;</span> <span class="sy3">+</span>
&nbsp; &nbsp; BOOL_TO_UDINT<span class="br0">&#40;</span>bReady2<span class="br0">&#41;</span> <span class="sy3">+</span>
&nbsp; &nbsp; BOOL_TO_UDINT<span class="br0">&#40;</span>bReady3<span class="br0">&#41;</span> <span class="sy3">+</span>
&nbsp; &nbsp; BOOL_TO_UDINT<span class="br0">&#40;</span>bReady4<span class="br0">&#41;</span> <span class="sy3">+</span>
&nbsp; &nbsp; BOOL_TO_UDINT<span class="br0">&#40;</span>bReady5<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; <span class="co1">// вычисление длительности предыдущего цикла</span>
&nbsp; &nbsp; nTimeNow <span class="sy1">:</span><span class="sy3">=</span> TIME_TO_UDINT<span class="br0">&#40;</span>get_time<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; <span class="kw1">IF</span> <span class="kw1">NOT</span> bInitDone <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; nTimePrevious <span class="sy1">:</span><span class="sy3">=</span> nTimeNow<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; nTimeCount <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; nAmount <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; nAmountPrevious <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bInitDone <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">TRUE</span><span class="sy1">;</span>
&nbsp; &nbsp; END_IF
&nbsp; &nbsp; nTimeDelta <span class="sy1">:</span><span class="sy3">=</span> nTimeNow <span class="sy3">-</span> nTimePrevious<span class="sy1">;</span>
&nbsp; &nbsp; rTimeDelta <span class="sy1">:</span><span class="sy3">=</span> UDINT_TO_REAL<span class="br0">&#40;</span>nTimeDelta<span class="br0">&#41;</span> <span class="sy3">*</span> <span class="nu0">0.001</span><span class="sy1">;</span>
&nbsp; &nbsp; nTimePrevious <span class="sy1">:</span><span class="sy3">=</span> nTimeNow<span class="sy1">;</span>
&nbsp;
&nbsp; &nbsp; <span class="kw1">IF</span> bEnable <span class="kw1">AND</span> bRequest <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// если значение опустилось слишком низко - включаем первый агрегат</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// и не ждём значительного снижения до появления запроса от интегрального критерия</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> <span class="br0">&#40;</span>nAmount <span class="sy3">=</span> <span class="nu0">0</span><span class="br0">&#41;</span> <span class="kw1">AND</span> <span class="br0">&#40;</span>rPV &lt;<span class="sy3">=</span> rSP <span class="sy3">-</span> rSP_DeltaOn<span class="br0">&#41;</span> <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; nAmount <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">1</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; END_IF
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// если значение поднялось слишком высоко - отключаем все агрегаты</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// и не ждём значительного повышения до появления запроса от интенрального критерия</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> <span class="br0">&#40;</span>nAmount &gt; <span class="nu0">0</span><span class="br0">&#41;</span> <span class="kw1">AND</span> <span class="br0">&#40;</span>rPV &gt; rSP_Off<span class="br0">&#41;</span> <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; nAmount <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; END_IF
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// при входе в зону нечувствительности обнуляются интегральные критерии</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> <span class="br0">&#40;</span>rPV &lt; rSP <span class="sy3">+</span> rDB<span class="br0">&#41;</span> <span class="kw1">AND</span> <span class="br0">&#40;</span>rPV &gt; rSP <span class="sy3">-</span> rDB<span class="br0">&#41;</span> <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rQCalcSub <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bSubRequest <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">FALSE</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rQCalcAdd <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bAddRequest <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">FALSE</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bCalcEn <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">TRUE</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; END_IF
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// вычисление интегрального критерия</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> bCalcEn <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// запрос - по возможности добавить количество работающих агрегатов</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> <span class="br0">&#40;</span>rPV &lt;<span class="sy3">=</span> rSP <span class="sy3">-</span> rDB<span class="br0">&#41;</span> <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// обнуление интеграла противоположного направления</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rQCalcSub <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bSubRequest <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">FALSE</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// вычисление интегрального критерия</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rQCalcAdd <span class="sy1">:</span><span class="sy3">=</span> rQCalcAdd <span class="sy3">+</span> <span class="br0">&#40;</span>rSP <span class="sy3">-</span> rDB <span class="sy3">-</span> rPV<span class="br0">&#41;</span> <span class="sy3">*</span> rTimeDelta<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bAddRequest <span class="sy1">:</span><span class="sy3">=</span> <span class="br0">&#40;</span>rQCalcAdd &gt;<span class="sy3">=</span> rQAdd<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// ограничение вычислений от переполнения</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> bAddRequest <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rQCalcAdd <span class="sy1">:</span><span class="sy3">=</span> rQAdd<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; END_IF
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; END_IF
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// запрос - по возможности сократить количество работающих агрегатов</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> <span class="br0">&#40;</span>rPV &gt;<span class="sy3">=</span> rSP <span class="sy3">+</span> rDB<span class="br0">&#41;</span> <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// обнуление интеграла противоположного направления</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rQCalcAdd <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bAddRequest <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">FALSE</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// вычисление интегрального критерия</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rQCalcSub <span class="sy1">:</span><span class="sy3">=</span> rQCalcSub <span class="sy3">+</span> <span class="br0">&#40;</span>rPV <span class="sy3">-</span> rSP <span class="sy3">-</span> rDB<span class="br0">&#41;</span> <span class="sy3">*</span> rTimeDelta<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bSubRequest <span class="sy1">:</span><span class="sy3">=</span> <span class="br0">&#40;</span>rQCalcSub &gt;<span class="sy3">=</span> rQSub<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// ограничение вычислений от переполнения</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> bSubRequest <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rQCalcSub <span class="sy1">:</span><span class="sy3">=</span> rQSub<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; END_IF
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; END_IF
&nbsp; &nbsp; &nbsp; &nbsp; END_IF
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> nAmount &gt; nMax <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; nAmount <span class="sy1">:</span><span class="sy3">=</span> nMax<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; nAmountPrevious <span class="sy1">:</span><span class="sy3">=</span> nMax<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; END_IF
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// если есть запрос и возможность увеличения количества - увеличиваем</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> <span class="br0">&#40;</span>nAmount &lt; nMax<span class="br0">&#41;</span> <span class="kw1">AND</span> bAddRequest <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bAddRequest <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">FALSE</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rQCalcAdd <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bCalcEn <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">FALSE</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; nAmount <span class="sy1">:</span><span class="sy3">=</span> nAmount <span class="sy3">+</span> <span class="nu0">1</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; END_IF
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// если есть запрос и возможность уменьшения количества - уменьшаем</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> <span class="br0">&#40;</span>nAmount &gt; <span class="nu0">0</span><span class="br0">&#41;</span> <span class="kw1">AND</span> bSubRequest <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bSubRequest <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">FALSE</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rQCalcSub <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bCalcEn <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">FALSE</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; nAmount <span class="sy1">:</span><span class="sy3">=</span> nAmount <span class="sy3">-</span> <span class="nu0">1</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; END_IF
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// если интегрирование запрещено - значит выполняется какой-то отсчёт</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// поверяем его завершение</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> <span class="kw1">NOT</span> bCalcEn <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; nTimeCount <span class="sy1">:</span><span class="sy3">=</span> nTimeCount <span class="sy3">+</span> nTimeDelta<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> <span class="br0">&#40;</span>rPV &lt;<span class="sy3">=</span> rSP <span class="sy3">-</span> rDB<span class="br0">&#41;</span> <span class="kw1">AND</span> <span class="br0">&#40;</span>nTimeCount &gt;<span class="sy3">=</span> nDelayAdd <span class="sy3">*</span> <span class="nu0">1000</span><span class="br0">&#41;</span> <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bCalcEn <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">TRUE</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; END_IF
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> <span class="br0">&#40;</span>rPv &gt;<span class="sy3">=</span> rSP <span class="sy3">+</span> rDB<span class="br0">&#41;</span> <span class="kw1">AND</span> <span class="br0">&#40;</span>nTimeCount &gt;<span class="sy3">=</span> nDelaySub <span class="sy3">*</span> <span class="nu0">1000</span><span class="br0">&#41;</span> <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bCalcEn <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">TRUE</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; END_IF
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">ELSE</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// если количество агрегатов изменилось, то делаем паузу в вычислениях</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bCalcEn <span class="sy1">:</span><span class="sy3">=</span> <span class="br0">&#40;</span>nAmount <span class="sy3">=</span> nAmountPrevious<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; nTimeCount <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; END_IF
&nbsp; &nbsp; <span class="kw1">ELSE</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// при запрете работы или отключении регулирования</span>
&nbsp; &nbsp; &nbsp; &nbsp; nAmount <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bCalcEn <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">FALSE</span><span class="sy1">;</span>
&nbsp; &nbsp; END_IF
&nbsp; &nbsp; nAmountPrevious <span class="sy1">:</span><span class="sy3">=</span> nAmount<span class="sy1">;</span>
END_FUNCTION_BLOCK</pre></td></tr></table></div></td></tr></tbody></table></div>
				   </div>
			   </div><br />
<br />
<h4>2.3 Переключение по сетке уставок</h4><br />
<br />
Алгоритм по сути является П-регулятором с ярко выраженной статической ошибкой регулирования и нелинейной шкалой измерения переменной процесса.<br />
<br />
Определяются задание регулирования и несколько отклонений от задания, т.е. шесть диапазонов уровня, внутри которых должны быть включены от 0 до 5 насосов соответственно. При равенстве измеренного значения уровня воды в резервуаре и уставки выполняется переключение количества включённых насосов.<br />
Для устранения частых переключений вводится гистерезис, когда для переключения количества требуемых насосов недостаточно ещё раз пересечь границу в обратном направлении — требуется пересечь соседнюю границу.<br />
<br />
При работе этого алгоритма придётся смириться с тем, что уровень воды в резервуаре никогда не будет равен конкретной уставке, а будет колебаться возле той из них, которая ближе соответствует балансу поступления и отбора воды.<br />

			   <div class="spoiler-wrap">
				   <div class="spoiler-head folded clickable">Кликните здесь для просмотра всего текста</div>
				   <div class="spoiler-body">
					   <div class="codeblock"><table class="pascal"><thead><tr><td colspan="2" id="100018299"  class="head">Pascal</td></tr></thead><tbody><tr class="li1"><td><div id="100018299" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
</pre></td><td class="de1"><pre class="de1"><span class="co1">/// &lt;Description&gt;ФБ каскадного регулирования нагрузкой - позволяет увеличивать и уменьшать количество одновременно работающих агрегатов&lt;/Description&gt;</span>
<span class="co1">/// &lt;Author&gt;!!FPA!!&lt;/Author&gt;</span>
<span class="co1">/// &lt;GroupName&gt;Регуляторы&lt;/GroupName&gt;</span>
&nbsp;
FUNCTION_BLOCK Cascade_Grid_SP_
&nbsp;
&nbsp; &nbsp; VAR_INPUT
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">/// &lt;Description&gt;Разрешение работы. При FALSE выход равен нулю - все агрегаты одновременно выключаются.&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bEnable<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">/// &lt;Description&gt;Запрос включения регулятора&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bRequest<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">/// &lt;Description&gt;Исправность и готовность к работе агрегата 1&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bReady1<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">/// &lt;Description&gt;Исправность агрегата 2 к работе&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bReady2<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">/// &lt;Description&gt;Исправность агрегата 3 к работе&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bReady3<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">/// &lt;Description&gt;Исправность агрегата 4 к работе&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bReady4<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">/// &lt;Description&gt;Исправность агрегата 5 к работе&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bReady5<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">/// &lt;Description&gt;Измеренное значение регулируемой величины&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rPV<span class="sy1">:</span> <span class="kw4">REAL</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">/// &lt;Description&gt;Задание регулятора&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rSP<span class="sy1">:</span> <span class="kw4">REAL</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">/// &lt;Description&gt;Отклонение от задания, при превышении которого отключаются все агрегаты&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rDelta_0Pumps<span class="sy1">:</span> <span class="kw4">REAL</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">/// &lt;Description&gt;Отклонение от задания, при снижении менее которой включается - 1 агрегат&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rDelta_1Pumps<span class="sy1">:</span> <span class="kw4">REAL</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">/// &lt;Description&gt;Отклонение от задания, при снижении менее которой включается - 2 агрегата&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rDelta_2Pumps<span class="sy1">:</span> <span class="kw4">REAL</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">/// &lt;Description&gt;Отклонение от задания, при снижении менее которой включается - 3 агрегата&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rDelta_3Pumps<span class="sy1">:</span> <span class="kw4">REAL</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">/// &lt;Description&gt;Отклонение от задания, при снижении менее которой включается - 4 агрегата&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rDelta_4Pumps<span class="sy1">:</span> <span class="kw4">REAL</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">/// &lt;Description&gt;Отклонение от задания, при снижении менее которой включается - 5 агрегатов&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rDelta_5Pumps<span class="sy1">:</span> <span class="kw4">REAL</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">/// &lt;Description&gt;Максимальное измеренное значения для принудительного отключения всех агрегатов&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rSP_Off<span class="sy1">:</span> <span class="kw4">REAL</span><span class="sy1">;</span>
&nbsp; &nbsp; END_VAR
&nbsp;
&nbsp; &nbsp; VAR_OUTPUT
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">/// &lt;Description&gt;Запрашиваемое для работы количество агрегатов&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; nAmount<span class="sy1">:</span> UDINT<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">/// &lt;Description&gt;Количество агрегатов, исправных и готовых к работе&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; nReady<span class="sy1">:</span> UDINT<span class="sy1">;</span>
&nbsp; &nbsp; END_VAR
&nbsp;
&nbsp; &nbsp; <span class="kw1">VAR</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// минимальное требуемое количество агрегатов при данном значении регулируемого параметра</span>
&nbsp; &nbsp; &nbsp; &nbsp; nMin<span class="sy1">:</span> UDINT<span class="sy1">;</span>
&nbsp; &nbsp; END_VAR
&nbsp;
&nbsp; &nbsp; <span class="co1">// подсчёт готовых агрегатов</span>
&nbsp; &nbsp; nReady <span class="sy1">:</span><span class="sy3">=</span> BOOL_TO_UDINT<span class="br0">&#40;</span>bReady1<span class="br0">&#41;</span> <span class="sy3">+</span>
&nbsp; &nbsp; BOOL_TO_UDINT<span class="br0">&#40;</span>bReady2<span class="br0">&#41;</span> <span class="sy3">+</span>
&nbsp; &nbsp; BOOL_TO_UDINT<span class="br0">&#40;</span>bReady3<span class="br0">&#41;</span> <span class="sy3">+</span>
&nbsp; &nbsp; BOOL_TO_UDINT<span class="br0">&#40;</span>bReady4<span class="br0">&#41;</span> <span class="sy3">+</span>
&nbsp; &nbsp; BOOL_TO_UDINT<span class="br0">&#40;</span>bReady5<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp;
&nbsp; &nbsp; <span class="kw1">IF</span> <span class="br0">&#40;</span>bEnable <span class="kw1">AND</span> bRequest<span class="br0">&#41;</span> <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// если работа разрешена</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// определяем минимально возможное количество в данном диапазоне</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// можно немного оптимизировать, если заменить</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// (rPV &lt; rSP + rDelta_5Pumps)</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// (rPV - rSP &lt; rDelta_5Pumps)</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// (rDelta &lt; rDelta_5Pumps), где rDelta := (rPV - rSP)</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> <span class="br0">&#40;</span>rPV &lt; rSP <span class="sy3">+</span> rDelta_5Pumps<span class="br0">&#41;</span> <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; nMin <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">5</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; ELSIF <span class="br0">&#40;</span>rPV &lt; rSP <span class="sy3">+</span> rDelta_4Pumps<span class="br0">&#41;</span> <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; nMin <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">4</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; ELSIF <span class="br0">&#40;</span>rPV &lt; rSP <span class="sy3">+</span> rDelta_3Pumps<span class="br0">&#41;</span> <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; nMin <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">3</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; ELSIF <span class="br0">&#40;</span>rPV &lt; rSP <span class="sy3">+</span> rDelta_2Pumps<span class="br0">&#41;</span> <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; nMin <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">2</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; ELSIF <span class="br0">&#40;</span>rPV &lt; rSP <span class="sy3">+</span> rDelta_1Pumps<span class="br0">&#41;</span> <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; nMin <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">1</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">ELSE</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; nMin <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; END_IF
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// число агрегатов в диапазоне нахождения измеренного значения уровня</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// равно nMin или nMin+1 в зависимости от того снижается уровень или растёт</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// Поэтому, если уровень снижается и теперь нужно больше агрегатов (nAmount &lt; nMin),</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// то повышаем их запрашиваемое количество.</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// Если уровень вырос и агрегатов требуется меньше (nAmount &gt; nMin + 1),</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// то понижаем их запрашиваемое количество</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> nAmount &lt; nMin <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// если реально включено меньше, то требуем не меньше минимального</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; nAmount <span class="sy1">:</span><span class="sy3">=</span> nMin<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; ELSIF nAmount &gt; nMin <span class="sy3">+</span> <span class="nu0">1</span> <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// если уровень поднялся и стало требоваться меньше агрегатов, то на один снижаем</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; nAmount <span class="sy1">:</span><span class="sy3">=</span> nMin <span class="sy3">+</span> <span class="nu0">1</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; END_IF
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// т.к. отклонение для отключения насосов из-за отсутствия отрицательных чисел невозможно</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// учесть в рамках единой проверки (nMin:=-1), то в конце вычислений отдельно</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// рассматриваем этот случай</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> <span class="br0">&#40;</span>rPV &gt;<span class="sy3">=</span> rSP <span class="sy3">+</span> rDelta_0Pumps<span class="br0">&#41;</span> <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; nAmount <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; END_IF
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// Если сетка отклонений от уставки выбрана неудачно и произошло превышение</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// заданного значения, то останавливаем наполнение</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> <span class="br0">&#40;</span>rPV &gt;<span class="sy3">=</span> rSP_Off<span class="br0">&#41;</span> <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; nAmount <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; END_IF
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// Если готовых к работе меньше, чем запрашиваем, то ограничиваем запрос</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> <span class="br0">&#40;</span>nAmount &gt; nReady<span class="br0">&#41;</span> <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; nAmount <span class="sy1">:</span><span class="sy3">=</span> nReady<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; END_IF
&nbsp; &nbsp; <span class="kw1">ELSE</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// если работа запрещена, то агрегаты не требуются</span>
&nbsp; &nbsp; &nbsp; &nbsp; nAmount <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">;</span>
&nbsp; &nbsp; END_IF
&nbsp;
END_FUNCTION_BLOCK</pre></td></tr></table></div></td></tr></tbody></table></div>
				   </div>
			   </div><br />
<br />
<br />
<h3>3 МОДЕЛИРОВАНИЕ</h3><br />
<br />
<h4>3.1 Моделирование объекта регулирования</h4><br />
<br />
Объектом регулирования являются насосы и резервуар.<br />
<br />
Модель насоса.<br />
На входе:<ul><li>bStart — команда включить насос</li>
<li>bReadyPower — состояние исправности насоса</li>
<li>bReadyStream — состояние исправности трубопровода (возможность создания протока при включении насоса)</li>
<li>nDelayRun — задержка формирования выходного сигнала включения насоса (замыкания допконтакта пускателя)</li>
<li>nDelayStream — задержка формирования протока через насос после электрического включения</li>
</ul>На выходе:<ul><li>bRun — состояние включения пускателя</li>
<li>bStream — состояние наличия протока через насос</li>
<li>nOT_total_m — общее время наработки насоса в минутах</li>
</ul>Имитируется готовность электрической схемы пуска насоса и состояния задвижек, т.е. возможно получить включённое состояние насоса, но без протока воды через него.<br />
Насос включается с небольшой задержкой после подачи команды «Пуск» и ещё с небольшой задержкой формируется сигнал наличия протока.<br />
Также, для проверки ротации насосов во время работы ведётся учёт наработки.<br />
<br />
Модель резервуара.<br />
На вход модели поступает количество включённых насосов заполнения. Каждый насос в секунду повышает уровень воды на rAddPerPump миллиметров.<br />
Также на вход поступает состояние включения насоса отбора воды — при включении он опустошает резервуар на rSub миллиметров в секунду.<br />
<br />
Модель насоса откачивающего воду из резервуара не очень важна, т.к. в процессе регулирования он всегда включён и длительное время поддерживает постоянный расход. Единственно, учитывается, что разрешение на его включение происходит при работе всех насосов заполнения при повышении уровня выше некоторого значения.<br />
<br />
<h4>3.2 Моделирование регулятора</h4><br />
<br />
Регулятор дополнил двумя функциональными блоками:<br />
Dispatcher_ — выбор конкретных насосов для включения или отключения с приоритетом наработки, побочным эффектом является автоматический ввод резервного (АВР) насоса,<br />

			   <div class="spoiler-wrap">
				   <div class="spoiler-head folded clickable">Исходный код Dispatcher_ на языках ST и FBD</div>
				   <div class="spoiler-body">
					   <br />

			   <div class="spoiler-wrap">
				   <div class="spoiler-head folded clickable">ST</div>
				   <div class="spoiler-body">
					   <div class="codeblock"><table class="pascal"><thead><tr><td colspan="2" id="195020726"  class="head">Pascal</td></tr></thead><tbody><tr class="li1"><td><div id="195020726" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
</pre></td><td class="de1"><pre class="de1"><span class="co1">///&lt;Description&gt;ФБ управляет выбором насоса для поддержания заданного количества работающих. Включение и отключение по критерию наработки.&lt;/Description&gt;</span>
<span class="co1">///&lt;Author&gt;!!FPA!!&lt;/Author&gt;</span>
<span class="co1">///&lt;GroupName&gt;Насосы&lt;/GroupName&gt;</span>
&nbsp;
FUNCTION_BLOCK Dispatcher_
&nbsp;
&nbsp; &nbsp; VAR_INPUT
&nbsp; &nbsp; &nbsp; &nbsp; bEnable<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bIsReady1<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bIsReady2<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bIsReady3<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bIsReady4<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bIsReady5<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; nPriority1<span class="sy1">:</span> UDINT<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; nPriority2<span class="sy1">:</span> UDINT<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; nPriority3<span class="sy1">:</span> UDINT<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; nPriority4<span class="sy1">:</span> UDINT<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; nPriority5<span class="sy1">:</span> UDINT<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; nAmount<span class="sy1">:</span> UDINT<span class="sy1">;</span>
&nbsp; &nbsp; END_VAR
&nbsp;
&nbsp; &nbsp; VAR_OUTPUT
&nbsp; &nbsp; &nbsp; &nbsp; bRequest1<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bRequest2<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bRequest3<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bRequest4<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bRequest5<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; nCountReady<span class="sy1">:</span> UDINT<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; nCountRequest<span class="sy1">:</span> UDINT<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; OffNumber<span class="sy1">:</span> UDINT<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; OnNumber<span class="sy1">:</span> UDINT<span class="sy1">;</span>
&nbsp; &nbsp; END_VAR
&nbsp;
&nbsp; &nbsp; <span class="kw1">VAR</span>
&nbsp; &nbsp; &nbsp; &nbsp; OffPriority_Value<span class="sy1">:</span> UDINT<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; OffPriority_Number<span class="sy1">:</span> UDINT<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; OffPriority_Quality<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; OnPriority_Value<span class="sy1">:</span> UDINT<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; OnPriority_Number<span class="sy1">:</span> UDINT<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; OnPriority_Quality<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; MIN_AI_<span class="sy1">:</span> MIN_AI<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; MAX_AI_<span class="sy1">:</span> MAX_AI<span class="sy1">;</span>
&nbsp; &nbsp; END_VAR
&nbsp;
&nbsp; &nbsp; <span class="co1">// подсчёт готовых агрегатов</span>
&nbsp; &nbsp; nCountReady <span class="sy1">:</span><span class="sy3">=</span> BOOL_TO_UDINT<span class="br0">&#40;</span>bIsReady1<span class="br0">&#41;</span> <span class="sy3">+</span>
&nbsp; &nbsp; BOOL_TO_UDINT<span class="br0">&#40;</span>bIsReady2<span class="br0">&#41;</span> <span class="sy3">+</span>
&nbsp; &nbsp; BOOL_TO_UDINT<span class="br0">&#40;</span>bIsReady3<span class="br0">&#41;</span> <span class="sy3">+</span>
&nbsp; &nbsp; BOOL_TO_UDINT<span class="br0">&#40;</span>bIsReady4<span class="br0">&#41;</span> <span class="sy3">+</span>
&nbsp; &nbsp; BOOL_TO_UDINT<span class="br0">&#40;</span>bIsReady5<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; <span class="co1">// включаемых этим алгоритмом и доступных управлению</span>
&nbsp; &nbsp; nCountRequest <span class="sy1">:</span><span class="sy3">=</span> BOOL_TO_UDINT<span class="br0">&#40;</span>bRequest1 <span class="kw1">AND</span> bIsReady1<span class="br0">&#41;</span> <span class="sy3">+</span>
&nbsp; &nbsp; BOOL_TO_UDINT<span class="br0">&#40;</span>bRequest2 <span class="kw1">AND</span> bIsReady2<span class="br0">&#41;</span> <span class="sy3">+</span>
&nbsp; &nbsp; BOOL_TO_UDINT<span class="br0">&#40;</span>bRequest3 <span class="kw1">AND</span> bIsReady3<span class="br0">&#41;</span> <span class="sy3">+</span>
&nbsp; &nbsp; BOOL_TO_UDINT<span class="br0">&#40;</span>bRequest4 <span class="kw1">AND</span> bIsReady4<span class="br0">&#41;</span> <span class="sy3">+</span>
&nbsp; &nbsp; BOOL_TO_UDINT<span class="br0">&#40;</span>bRequest5 <span class="kw1">AND</span> bIsReady5<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp;
&nbsp; &nbsp; <span class="co1">// определить агрегат - претендента на включение</span>
&nbsp; &nbsp; MIN_AI_<span class="br0">&#40;</span>A_Value <span class="sy1">:</span><span class="sy3">=</span> nPriority1<span class="sy1">,</span> A_Quality <span class="sy1">:</span><span class="sy3">=</span> <span class="br0">&#40;</span>bIsReady1 <span class="kw1">AND</span> <span class="kw1">NOT</span> bRequest1<span class="br0">&#41;</span><span class="sy1">,</span> A_Position <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">1</span><span class="sy1">,</span>
&nbsp; &nbsp; B_Value <span class="sy1">:</span><span class="sy3">=</span> nPriority2<span class="sy1">,</span> B_Quality <span class="sy1">:</span><span class="sy3">=</span> <span class="br0">&#40;</span>bIsReady2 <span class="kw1">AND</span> <span class="kw1">NOT</span> bRequest2<span class="br0">&#41;</span><span class="sy1">,</span> B_Position <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">2</span><span class="sy1">,</span>
&nbsp; &nbsp; Default_Value <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">,</span> Default_Position <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">,</span>
&nbsp; &nbsp; Min_Value <span class="sy3">=</span>&gt; OnPriority_Value<span class="sy1">,</span> Min_Quality <span class="sy3">=</span>&gt; OnPriority_Quality<span class="sy1">,</span> Min_Position <span class="sy3">=</span>&gt; OnPriority_Number<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp;
&nbsp; &nbsp; MIN_AI_<span class="br0">&#40;</span> A_Value <span class="sy1">:</span><span class="sy3">=</span> OnPriority_Value<span class="sy1">,</span> A_Quality <span class="sy1">:</span><span class="sy3">=</span> OnPriority_Quality<span class="sy1">,</span> A_Position <span class="sy1">:</span><span class="sy3">=</span> OnPriority_Number<span class="sy1">,</span>
&nbsp; &nbsp; B_Value <span class="sy1">:</span><span class="sy3">=</span> nPriority3<span class="sy1">,</span> B_Quality <span class="sy1">:</span><span class="sy3">=</span> <span class="br0">&#40;</span>bIsReady3 <span class="kw1">AND</span> <span class="kw1">NOT</span> bRequest3<span class="br0">&#41;</span><span class="sy1">,</span> B_Position <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">3</span><span class="sy1">,</span>
&nbsp; &nbsp; Default_Value <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">,</span> Default_Position <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">,</span>
&nbsp; &nbsp; Min_Value <span class="sy3">=</span>&gt; OnPriority_Value<span class="sy1">,</span> Min_Quality <span class="sy3">=</span>&gt; OnPriority_Quality<span class="sy1">,</span> Min_Position <span class="sy3">=</span>&gt; OnPriority_Number<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp;
&nbsp; &nbsp; MIN_AI_<span class="br0">&#40;</span>A_Value <span class="sy1">:</span><span class="sy3">=</span> OnPriority_Value<span class="sy1">,</span> A_Quality <span class="sy1">:</span><span class="sy3">=</span> OnPriority_Quality<span class="sy1">,</span> A_Position <span class="sy1">:</span><span class="sy3">=</span> OnPriority_Number<span class="sy1">,</span>
&nbsp; &nbsp; B_Value <span class="sy1">:</span><span class="sy3">=</span> nPriority4<span class="sy1">,</span> B_Quality <span class="sy1">:</span><span class="sy3">=</span> <span class="br0">&#40;</span>bIsReady4 <span class="kw1">AND</span> <span class="kw1">NOT</span> bRequest4<span class="br0">&#41;</span><span class="sy1">,</span> B_Position <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">4</span><span class="sy1">,</span>
&nbsp; &nbsp; Default_Value <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">,</span> Default_Position <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">,</span>
&nbsp; &nbsp; Min_Value <span class="sy3">=</span>&gt; OnPriority_Value<span class="sy1">,</span> Min_Quality <span class="sy3">=</span>&gt; OnPriority_Quality<span class="sy1">,</span> Min_Position <span class="sy3">=</span>&gt; OnPriority_Number<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp;
&nbsp; &nbsp; MIN_AI_<span class="br0">&#40;</span>A_Value <span class="sy1">:</span><span class="sy3">=</span> OnPriority_Value<span class="sy1">,</span> A_Quality <span class="sy1">:</span><span class="sy3">=</span> OnPriority_Quality<span class="sy1">,</span> A_Position <span class="sy1">:</span><span class="sy3">=</span> OnPriority_Number<span class="sy1">,</span>
&nbsp; &nbsp; B_Value <span class="sy1">:</span><span class="sy3">=</span> nPriority5<span class="sy1">,</span> B_Quality <span class="sy1">:</span><span class="sy3">=</span> <span class="br0">&#40;</span>bIsReady5 <span class="kw1">AND</span> <span class="kw1">NOT</span> bRequest5<span class="br0">&#41;</span><span class="sy1">,</span> B_Position <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">5</span><span class="sy1">,</span>
&nbsp; &nbsp; Default_Value <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">,</span> Default_Position <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">,</span>
&nbsp; &nbsp; Min_Value <span class="sy3">=</span>&gt; OnPriority_Value<span class="sy1">,</span> Min_Quality <span class="sy3">=</span>&gt; OnPriority_Quality<span class="sy1">,</span> Min_Position <span class="sy3">=</span>&gt; OnPriority_Number<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp;
&nbsp; &nbsp; <span class="co1">// определить агрегат - претендента на выключение</span>
&nbsp; &nbsp; MAX_AI_<span class="br0">&#40;</span>A_Value <span class="sy1">:</span><span class="sy3">=</span> nPriority1<span class="sy1">,</span> A_Quality <span class="sy1">:</span><span class="sy3">=</span> <span class="br0">&#40;</span>bIsReady1 <span class="kw1">AND</span> bRequest1<span class="br0">&#41;</span><span class="sy1">,</span> A_Position <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">1</span><span class="sy1">,</span>
&nbsp; &nbsp; B_Value <span class="sy1">:</span><span class="sy3">=</span> nPriority2<span class="sy1">,</span> B_Quality <span class="sy1">:</span><span class="sy3">=</span> <span class="br0">&#40;</span>bIsReady2 <span class="kw1">AND</span> bRequest2<span class="br0">&#41;</span><span class="sy1">,</span> B_Position <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">2</span><span class="sy1">,</span>
&nbsp; &nbsp; Default_Value <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">,</span> Default_Position <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">,</span>
&nbsp; &nbsp; Max_Value <span class="sy3">=</span>&gt; OffPriority_Value<span class="sy1">,</span> Max_Quality <span class="sy3">=</span>&gt; OffPriority_Quality<span class="sy1">,</span> Max_Position <span class="sy3">=</span>&gt; OffPriority_Number<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp;
&nbsp; &nbsp; MAX_AI_<span class="br0">&#40;</span> A_Value <span class="sy1">:</span><span class="sy3">=</span> OffPriority_Value<span class="sy1">,</span> A_Quality <span class="sy1">:</span><span class="sy3">=</span> OffPriority_Quality<span class="sy1">,</span> A_Position <span class="sy1">:</span><span class="sy3">=</span> OffPriority_Number<span class="sy1">,</span>
&nbsp; &nbsp; B_Value <span class="sy1">:</span><span class="sy3">=</span> nPriority3<span class="sy1">,</span> B_Quality <span class="sy1">:</span><span class="sy3">=</span> <span class="br0">&#40;</span>bIsReady3 <span class="kw1">AND</span> bRequest3<span class="br0">&#41;</span><span class="sy1">,</span> B_Position <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">3</span><span class="sy1">,</span>
&nbsp; &nbsp; Default_Value <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">,</span> Default_Position <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">,</span>
&nbsp; &nbsp; Max_Value <span class="sy3">=</span>&gt; OffPriority_Value<span class="sy1">,</span> Max_Quality <span class="sy3">=</span>&gt; OffPriority_Quality<span class="sy1">,</span> Max_Position <span class="sy3">=</span>&gt; OffPriority_Number<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp;
&nbsp; &nbsp; MAX_AI_<span class="br0">&#40;</span> A_Value <span class="sy1">:</span><span class="sy3">=</span> OffPriority_Value<span class="sy1">,</span> A_Quality <span class="sy1">:</span><span class="sy3">=</span> OffPriority_Quality<span class="sy1">,</span> A_Position <span class="sy1">:</span><span class="sy3">=</span> OffPriority_Number<span class="sy1">,</span>
&nbsp; &nbsp; B_Value <span class="sy1">:</span><span class="sy3">=</span> nPriority4<span class="sy1">,</span> B_Quality <span class="sy1">:</span><span class="sy3">=</span> <span class="br0">&#40;</span>bIsReady4 <span class="kw1">AND</span> bRequest4<span class="br0">&#41;</span><span class="sy1">,</span> B_Position <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">4</span><span class="sy1">,</span>
&nbsp; &nbsp; Default_Value <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">,</span> Default_Position <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">,</span>
&nbsp; &nbsp; Max_Value <span class="sy3">=</span>&gt; OffPriority_Value<span class="sy1">,</span> Max_Quality <span class="sy3">=</span>&gt; OffPriority_Quality<span class="sy1">,</span> Max_Position <span class="sy3">=</span>&gt; OffPriority_Number<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp;
&nbsp; &nbsp; MAX_AI_<span class="br0">&#40;</span> A_Value <span class="sy1">:</span><span class="sy3">=</span> OffPriority_Value<span class="sy1">,</span> A_Quality <span class="sy1">:</span><span class="sy3">=</span> OffPriority_Quality<span class="sy1">,</span> A_Position <span class="sy1">:</span><span class="sy3">=</span> OffPriority_Number<span class="sy1">,</span>
&nbsp; &nbsp; B_Value <span class="sy1">:</span><span class="sy3">=</span> nPriority5<span class="sy1">,</span> B_Quality <span class="sy1">:</span><span class="sy3">=</span> <span class="br0">&#40;</span>bIsReady5 <span class="kw1">AND</span> bRequest5<span class="br0">&#41;</span><span class="sy1">,</span> B_Position <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">5</span><span class="sy1">,</span>
&nbsp; &nbsp; Default_Value <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">,</span> Default_Position <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">,</span>
&nbsp; &nbsp; Max_Value <span class="sy3">=</span>&gt; OffPriority_Value<span class="sy1">,</span> Max_Quality <span class="sy3">=</span>&gt; OffPriority_Quality<span class="sy1">,</span> Max_Position <span class="sy3">=</span>&gt; OffPriority_Number<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp;
&nbsp;
&nbsp; &nbsp; <span class="kw1">IF</span> bEnable <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> nAmount &gt; nCountReady <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; nAmount <span class="sy1">:</span><span class="sy3">=</span> nCountReady<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; END_IF
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> nAmount <span class="sy3">=</span> nCountRequest <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; OnNumber <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; OffNumber <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; ELSIF nAmount &lt; nCountRequest <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; OnNumber <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; OffNumber <span class="sy1">:</span><span class="sy3">=</span> OffPriority_Number<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">ELSE</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; OnNumber <span class="sy1">:</span><span class="sy3">=</span> OnPriority_Number<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; OffNumber <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; END_IF
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// отключение неготовых, выбранных для отключения</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// включение выбранныз для включения</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// RS-trig с приоритетом сброса R</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// Q := (Q OR S) AND (NOT R)</span>
&nbsp; &nbsp; &nbsp; &nbsp; bRequest1 <span class="sy1">:</span><span class="sy3">=</span> <span class="br0">&#40;</span>bRequest1 <span class="kw1">OR</span> <span class="br0">&#40;</span>OnNumber <span class="sy3">=</span> <span class="nu0">1</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="kw1">AND</span> bIsReady1 <span class="kw1">AND</span> <span class="kw1">NOT</span><span class="br0">&#40;</span>OffNumber <span class="sy3">=</span> <span class="nu0">1</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bRequest2 <span class="sy1">:</span><span class="sy3">=</span> <span class="br0">&#40;</span>bRequest2 <span class="kw1">OR</span> <span class="br0">&#40;</span>OnNumber <span class="sy3">=</span> <span class="nu0">2</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="kw1">AND</span> bIsReady2 <span class="kw1">AND</span> <span class="kw1">NOT</span><span class="br0">&#40;</span>OffNumber <span class="sy3">=</span> <span class="nu0">2</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bRequest3 <span class="sy1">:</span><span class="sy3">=</span> <span class="br0">&#40;</span>bRequest3 <span class="kw1">OR</span> <span class="br0">&#40;</span>OnNumber <span class="sy3">=</span> <span class="nu0">3</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="kw1">AND</span> bIsReady3 <span class="kw1">AND</span> <span class="kw1">NOT</span><span class="br0">&#40;</span>OffNumber <span class="sy3">=</span> <span class="nu0">3</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bRequest4 <span class="sy1">:</span><span class="sy3">=</span> <span class="br0">&#40;</span>bRequest4 <span class="kw1">OR</span> <span class="br0">&#40;</span>OnNumber <span class="sy3">=</span> <span class="nu0">4</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="kw1">AND</span> bIsReady4 <span class="kw1">AND</span> <span class="kw1">NOT</span><span class="br0">&#40;</span>OffNumber <span class="sy3">=</span> <span class="nu0">4</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bRequest5 <span class="sy1">:</span><span class="sy3">=</span> <span class="br0">&#40;</span>bRequest5 <span class="kw1">OR</span> <span class="br0">&#40;</span>OnNumber <span class="sy3">=</span> <span class="nu0">5</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="kw1">AND</span> bIsReady5 <span class="kw1">AND</span> <span class="kw1">NOT</span><span class="br0">&#40;</span>OffNumber <span class="sy3">=</span> <span class="nu0">5</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; <span class="kw1">ELSE</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// отключение всех</span>
&nbsp; &nbsp; &nbsp; &nbsp; bRequest1 <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">FALSE</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bRequest2 <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">FALSE</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bRequest3 <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">FALSE</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bRequest4 <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">FALSE</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bRequest5 <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">FALSE</span><span class="sy1">;</span>
&nbsp; &nbsp; END_IF
END_FUNCTION_BLOCK</pre></td></tr></table></div></td></tr></tbody></table></div>
				   </div>
			   </div><br />

			   <div class="spoiler-wrap">
				   <div class="spoiler-head folded clickable">FBD</div>
				   <div class="spoiler-body">
					   <a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=11748&amp;d=1773511534" target="_blank">Dispatcher_fbd.pdf</a>
				   </div>
			   </div><br />

				   </div>
			   </div><br />
NotAllAtOnce5_ — включения пускателей с исключением возможности одновременной коммутации нескольких пускателей.<br />

			   <div class="spoiler-wrap">
				   <div class="spoiler-head folded clickable">Исходный код NotAllAtOnce5_ на языке ST</div>
				   <div class="spoiler-body">
					   <div class="codeblock"><table class="pascal"><thead><tr><td colspan="2" id="658748676"  class="head">Pascal</td></tr></thead><tbody><tr class="li1"><td><div id="658748676" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
</pre></td><td class="de1"><pre class="de1"><span class="co1">///&lt;Description&gt;ФБ предотвращает одновременный пуск или останов нескольких (пяти) агрегатов, разделяя изменения состояний заданной паузой&lt;/Description&gt;</span>
<span class="co1">///&lt;Author&gt;!!FPA!!&lt;/Author&gt;</span>
<span class="co1">///&lt;GroupName&gt;Насосы&lt;/GroupName&gt;</span>
&nbsp;
FUNCTION_BLOCK NotAllAtOnce5_
&nbsp;
&nbsp; &nbsp; VAR_INPUT
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">///&lt;Description&gt;Разрешение работы. При FALSE все выходы одновременно выключаются.&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bEnable<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">///&lt;Description&gt;Запрос включения агрегата 1&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bRequest1<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">///&lt;Description&gt;Запрос включения агрегата 2&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bRequest2<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">///&lt;Description&gt;Запрос включения агрегата 3&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bRequest3<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">///&lt;Description&gt;Запрос включения агрегата 4&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bRequest4<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">///&lt;Description&gt;Запрос включения агрегата 5&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bRequest5<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">///&lt;Description&gt;Задержка между переключениями&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; nDelay_s<span class="sy1">:</span> UDINT<span class="sy1">;</span>
&nbsp; &nbsp; END_VAR
&nbsp;
&nbsp; &nbsp; VAR_OUTPUT
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">///&lt;Description&gt;Команда включения агрегата 1&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bStart1<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">///&lt;Description&gt;Команда включения агрегата 2&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bStart2<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">///&lt;Description&gt;Команда включения агрегата 3&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bStart3<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">///&lt;Description&gt;Команда включения агрегата 4&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bStart4<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">///&lt;Description&gt;Команда включения агрегата 5&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bStart5<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; END_VAR
&nbsp;
&nbsp; &nbsp; <span class="kw1">VAR</span>
&nbsp; &nbsp; &nbsp; &nbsp; tDelay<span class="sy1">:</span> TIME<span class="sy1">;</span>
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; bStartPrevious1<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bStartPrevious2<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bStartPrevious3<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bStartPrevious4<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bStartPrevious5<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; tpPause<span class="sy1">:</span> SYS<span class="sy1">.</span><span class="me1">TP</span><span class="sy1">;</span>
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; bSwitchEn<span class="sy1">:</span> BOOL<span class="sy1">;</span> <span class="co1">// разрешение изменить состояние</span>
&nbsp;
&nbsp; &nbsp; END_VAR
&nbsp;
&nbsp; &nbsp; <span class="co1">// преобразование типа и пересчёт значения в [с]</span>
&nbsp; &nbsp; tDelay <span class="sy1">:</span><span class="sy3">=</span> UDINT_TO_TIME<span class="br0">&#40;</span>nDelay_s <span class="sy3">*</span> <span class="nu0">1000</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; <span class="co1">// ограничение минимального значения</span>
&nbsp; &nbsp; <span class="kw1">IF</span> tDelay &lt; T<span class="re1">#1</span>s <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; tDelay <span class="sy1">:</span><span class="sy3">=</span> T<span class="re1">#1</span>s<span class="sy1">;</span>
&nbsp; &nbsp; END_IF
&nbsp; &nbsp; <span class="co1">// присвоение таймеру для уменьшения параметров в следующих вызовах</span>
&nbsp; &nbsp; tpPause<span class="br0">&#40;</span>T <span class="sy1">:</span><span class="sy3">=</span> tDelay<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp;
&nbsp; &nbsp; <span class="kw1">IF</span> bEnable <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// обработка для агрегата 1</span>
&nbsp; &nbsp; &nbsp; &nbsp; bSwitchEn <span class="sy1">:</span><span class="sy3">=</span> <span class="kw1">NOT</span><span class="br0">&#40;</span>tpPause<span class="sy1">.</span><span class="me1">Q</span><span class="br0">&#41;</span> <span class="kw1">AND</span> <span class="br0">&#40;</span>bRequest1 &lt;&gt; bStart1<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> bSwitchEn <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bStart1 <span class="sy1">:</span><span class="sy3">=</span> bRequest1<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; END_IF
&nbsp; &nbsp; &nbsp; &nbsp; tpPause<span class="br0">&#40;</span>I <span class="sy1">:</span><span class="sy3">=</span> bSwitchEn<span class="br0">&#41;</span><span class="sy1">;</span> <span class="co1">// обновление значения выхода таймера для следующего обработчика</span>
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// обработка для агрегата 2</span>
&nbsp; &nbsp; &nbsp; &nbsp; bSwitchEn <span class="sy1">:</span><span class="sy3">=</span> <span class="kw1">NOT</span><span class="br0">&#40;</span>tpPause<span class="sy1">.</span><span class="me1">Q</span><span class="br0">&#41;</span> <span class="kw1">AND</span> <span class="br0">&#40;</span>bRequest2 &lt;&gt; bStart2<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> bSwitchEn <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bStart2 <span class="sy1">:</span><span class="sy3">=</span> bRequest2<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; END_IF
&nbsp; &nbsp; &nbsp; &nbsp; tpPause<span class="br0">&#40;</span>I <span class="sy1">:</span><span class="sy3">=</span> bSwitchEn<span class="br0">&#41;</span><span class="sy1">;</span> <span class="co1">// обновление значения выхода таймера для следующего обработчика</span>
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// обработка для агрегата 3</span>
&nbsp; &nbsp; &nbsp; &nbsp; bSwitchEn <span class="sy1">:</span><span class="sy3">=</span> <span class="kw1">NOT</span><span class="br0">&#40;</span>tpPause<span class="sy1">.</span><span class="me1">Q</span><span class="br0">&#41;</span> <span class="kw1">AND</span> <span class="br0">&#40;</span>bRequest3 &lt;&gt; bStart3<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> bSwitchEn <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bStart3 <span class="sy1">:</span><span class="sy3">=</span> bRequest3<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; END_IF
&nbsp; &nbsp; &nbsp; &nbsp; tpPause<span class="br0">&#40;</span>I <span class="sy1">:</span><span class="sy3">=</span> bSwitchEn<span class="br0">&#41;</span><span class="sy1">;</span> <span class="co1">// обновление значения выхода таймера для следующего обработчика</span>
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// обработка для агрегата 4</span>
&nbsp; &nbsp; &nbsp; &nbsp; bSwitchEn <span class="sy1">:</span><span class="sy3">=</span> <span class="kw1">NOT</span><span class="br0">&#40;</span>tpPause<span class="sy1">.</span><span class="me1">Q</span><span class="br0">&#41;</span> <span class="kw1">AND</span> <span class="br0">&#40;</span>bRequest4 &lt;&gt; bStart4<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> bSwitchEn <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bStart4 <span class="sy1">:</span><span class="sy3">=</span> bRequest4<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; END_IF
&nbsp; &nbsp; &nbsp; &nbsp; tpPause<span class="br0">&#40;</span>I <span class="sy1">:</span><span class="sy3">=</span> bSwitchEn<span class="br0">&#41;</span><span class="sy1">;</span> <span class="co1">// обновление значения выхода таймера для следующего обработчика</span>
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// обработка для агрегата 5</span>
&nbsp; &nbsp; &nbsp; &nbsp; bSwitchEn <span class="sy1">:</span><span class="sy3">=</span> <span class="kw1">NOT</span><span class="br0">&#40;</span>tpPause<span class="sy1">.</span><span class="me1">Q</span><span class="br0">&#41;</span> <span class="kw1">AND</span> <span class="br0">&#40;</span>bRequest5 &lt;&gt; bStart5<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> bSwitchEn <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bStart5 <span class="sy1">:</span><span class="sy3">=</span> bRequest5<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; END_IF
&nbsp; &nbsp; &nbsp; &nbsp; tpPause<span class="br0">&#40;</span>I <span class="sy1">:</span><span class="sy3">=</span> bSwitchEn<span class="br0">&#41;</span><span class="sy1">;</span> <span class="co1">// обновление значения выхода таймера для следующего обработчика</span>
&nbsp; &nbsp; <span class="kw1">ELSE</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// отсчёт от момента выключения, для паузы при быстром переключении bEnable</span>
&nbsp; &nbsp; &nbsp; &nbsp; tpPause<span class="br0">&#40;</span>I <span class="sy1">:</span><span class="sy3">=</span> bStart1 <span class="kw1">OR</span> bStart2 <span class="kw1">OR</span> bStart3 <span class="kw1">OR</span> bStart4 <span class="kw1">OR</span> bStart5<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// выключить все агрегаты</span>
&nbsp; &nbsp; &nbsp; &nbsp; bStart1 <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">FALSE</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bStart2 <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">FALSE</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bStart3 <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">FALSE</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bStart4 <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">FALSE</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bStart5 <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">FALSE</span><span class="sy1">;</span>
&nbsp; &nbsp; END_IF
&nbsp; &nbsp; bStartPrevious1 <span class="sy1">:</span><span class="sy3">=</span> bStart1<span class="sy1">;</span>
&nbsp; &nbsp; bStartPrevious2 <span class="sy1">:</span><span class="sy3">=</span> bStart2<span class="sy1">;</span>
&nbsp; &nbsp; bStartPrevious3 <span class="sy1">:</span><span class="sy3">=</span> bStart3<span class="sy1">;</span>
&nbsp; &nbsp; bStartPrevious4 <span class="sy1">:</span><span class="sy3">=</span> bStart4<span class="sy1">;</span>
&nbsp; &nbsp; bStartPrevious5 <span class="sy1">:</span><span class="sy3">=</span> bStart5<span class="sy1">;</span>
END_FUNCTION_BLOCK</pre></td></tr></table></div></td></tr></tbody></table></div>
				   </div>
			   </div><br />
Код на ST для выбора номера насоса для включения и остановки по критерию выравнивания наработки без применения цикла выглядит несколько обескураживающе. Но это всего лишь особенности восприятия для разных языков программирования. Этот же фрагмент на FBD выглядит просто и легко понятен — определяется номер готового к работе насоса с минимальной (максимальной) наработкой, т.е. выполняется сравнение значений с качеством.<br />
Отказ от цикла считаю обоснованным, т.к. и в цикле и при его разворачивании вычисляю те же самые вещи, но для цикла потребуется излишние манипуляции помещения данных в массив и последующая обработка всего для ничтожных пяти итераций.<br />
<br />
В целом, вся программа моделирования получилась со следующей структурой.<br />
По заданному и измеренному значениям регулирования, состояниям готовности к регулированию каскадный регулятор Cascade_XXX вычисляет требуемое количество насосов.<br />
По состояниям готовности, значениям наработки и требуемому количеству насосов ФБ распределения Dispatcher_1 определяет конкретные насосы для включения.<br />
Для исключения одновременной коммутации нескольких насосов ФБ NotAllAtOnce5_1 формирует сигналы для пускателей насосов.<br />
Сигналы включения поступают на модели насосов, которые формируют с заданными задержками электрические сигналы включения насосов и сигналы наличия протоков воды через них.<br />
Сигналы наличия протоков воды через насосы поступают на модель резервуара, которая формирует значение измеренного уровня — регулируемую переменную — которое поступает на вход каскадного регулятора.<br />

			   <div class="spoiler-wrap">
				   <div class="spoiler-head folded clickable">Программа моделирования</div>
				   <div class="spoiler-body">
					   <a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=11749&amp;d=1773514483" rel="Lightbox" id="attachment11749" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=11749&amp;thumb=1&amp;d=1773514483" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: pgm_fbd.png
Просмотров: 98
Размер:	157.8 Кб
ID:	11749" style="margin: 5px" /></a>
				   </div>
			   </div><br />
<br />
Т.к. языки программирования имеют разную силу выразительности, то для регулятора с «переключением по сетке уставок» реализовал регулятор и диспетчер на языке FBD. Как и ожидалось, регулятор на FBD стал запутаннее, а вот диспетчер наоборот — только выиграл в простоте понимания.<br />
<br />
На экран локальной панели вывел переключатели «включения электропитания» каждого насоса, а также команду включить регулятор.<br />
<br />
<h3>4 РЕЗУЛЬТАТЫ МОДЕЛИРОВАНИЯ</h3><br />
<br />
Не стал проверять ПИД регулятор, т.к. видел серьёзные недочёты с отсутствием гистерезиса. Чуть позже понял, что гистерезис можно было бы просто реализовать по тому же принципу, что сделал для «сетки уставок».<br />
В таком варианте можно было бы не делать пропусков при разделении диапазона выхода ПИД регулятора. <br />
Другим недочётом было не очень простое объяснение представителям эксплуатации алгоритма работы для последующей наладки и коррекции параметров.<br />
<br />
Реализовал регулятор с интегральным критерием в программе моделирования объекта.<br />
Приемлемых результатов добиться не удалось — количество работающих насосов постоянно колебалось от нуля до максимального значения. Иногда возникало ощущение, что немного поправить параметры и количество насосов стабилизируется, но следом приходило понимание, что при изменении нагрузки опять вернутся автоколебания.<br />
Возможно, что в каскадном контроллере КТР-121 алгоритм должен работать, т.к. характер котлов, как объекта управления, более инерционный.<br />
К этому добавилось сложное описание алгоритма, которое требует графических иллюстраций для понимания. Т.к. вместе с контроллером применялась панель оператора, то можно было бы сделать справку. Но неудача в настройке параметров сделала это не нужным.<br />
Самостоятельно попробовать настроить регулятор можно в моделирующей программе.<br />
<br />
Реализовал регулятор, переключающий количество работающих насосов по сетке уставок.<br />
Т.к. система малоинерционная, а регулятор по сути является вариантом П регулятора, то ему присущ основной недостаток этого закона регулирования — статическая ошибка. Т.е. будет поддерживаться не заданный уровень, а тот из сетки, который ближе к балансу притока и оттока воды в резервуар.<br />
Алгоритм легко описывается и интуитивно понятен.<br />
Т.к. при задании уровня воды в резервуаре около 10,0 м колебания по 0,5 м были приемлемы, то и при наладке такую сетку и оставили для исключения частой коммутации насосов.<br />
Самостоятельно попробовать настроить регулятор можно в моделирующей программе.<br />
<br />
Из-за выполнения моделирования в программе реализовал регулятор из нескольких независимых ФБ (Cascade_XXX, Dispatcher_1, NotAllAtOnce5_1). Есть смысл объединить их в один ФБ, для уменьшения ошибок соединения связями, исключения повторных вычислений некоторых состояний.<br />
<br />
По результатам моделирования в итоговой программе реализовал каскадный регулятор с алгоритмом «переключения по сетке уставок» из-за работоспособности в требуемых конкретных условиях, а также из-за простого пояснения работы нестандартного алгоритма для представителей эксплуатации.<br />
<br />
От применения каскадного регулятора на основе интегрального критерия категорически отказываюсь из-за невозможности его настройки на адекватную работу в требуемых конкретных условиях малоинерционного объекта управления.<br />
<br />
Исследования ПИД регулятора с разделением выходного сигнала на диапазоны и добавлением гистерезиса не провел из-за нехватки времени. Поэтому, кроме непростого пояснения алгоритма работы, на данный момент недостатков не вижу. Возможно, такой трёхкомпонентный (ПИД, диапазоны и гистерезис) каскадный регулятор будет вполне работоспособен, а может, и не будет.<br />
<br />
И самое главное, хочется отметить, что выбор алгоритма каскадного регулятора проводился на основе эксперимента над моделью объекта управления, а не на реальном оборудовании в условиях нехватки времени при ПНР.<br />
<br />
<h3>ПРИЛОЖЕНИЕ</h3><ol style="list-style-type: decimal"><li>Программы, моделирующие поведение каскадных регуляторов<ul><li>Программа, моделирующая поведение каскадного регулятора с интегральным критерием</li>
<li>Программа, моделирующая поведение каскадного регулятора с «переключением по сетке уставок»</li>
<li>Программа, моделирующая поведение каскадного регулятора с «переключением по сетке уставок» с реализацией регулятора и диспетчера на FBD</li>
</ul><a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=11747&amp;d=1773508918" >Cascade_5_Pump.7z</a><br /></li>
<li>КТР-121.02.41. Блок автоматического управления котельными. Руководство по эксплуатации. Алгоритм 02.41 (Версия ПО 4.0). 02.2026. версия 1.1<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=11732&amp;d=1773254064" target="_blank">re_ktr-121.02.41_m02.pdf</a></li>
</ol></div>

]]></content:encoded>
			<dc:creator>ФедосеевПавел</dc:creator>
			<guid isPermaLink="true">https://www.cyberforum.ru/blogs/534277/10807.html</guid>
		</item>
		<item>
			<title>Owen Logic: О недопустимости использования связки «аналоговый ПИД» + RegKZR</title>
			<link>https://www.cyberforum.ru/blogs/534277/10659.html</link>
			<pubDate>Mon, 05 Jan 2026 23:06:56 GMT</pubDate>
			<description>Owen Logic: О недопустимости использования связки «аналоговый ПИД» + RegKZR 
 
ВВЕДЕНИЕ 
 
Введу...</description>
			<content:encoded><![CDATA[<div><h2>Owen Logic: О недопустимости использования связки «аналоговый ПИД» + RegKZR</h2><br />
<br />
<h3>ВВЕДЕНИЕ</h3><br />
<br />
Введу сокращения:<ul><li><b>аналоговый ПИД</b> — ПИД регулятор с управляющим выходом в виде числа в диапазоне от 0% до 100%, которое соответствует положению регулирующего клапана с аналоговым управляющим входом;</li>
<li><b>ПИД КЗР</b> — ПИД регулятор с двумя дискретными выходами для управления трёхпозиционным запорно-регулирующим клапаном (КЗР).</li>
</ul><br />
Реализация аналогового ПИД регулятора, обычно, не вызывает затруднений, т. к. описания вполне достаточно. Сложности возникают при реализации ПИД регулятора для привода с трёхпозиционным управлением — ПИД КЗР.<br />
<br />
В экосистеме программируемых реле производства Овен проблема самостоятельной реализации ПИД КЗР постепенно становится всё менее актуальной, т. к. производителем постепенно внедряются встроенные регуляторы, но сама проблема распространена более широко и затрагивает разных производителей.<br />
<br />
Наиболее часто среди советов по реализации встречаются использование существующей реализации аналогового ПИД и библиотечного (или самодельного) преобразователя выхода ПИД в дискретные сигналы, продолжительность которых прямо пропорциональна приращению выхода ПИД и обратно пропорциональна времени полного хода КЗР.<br />
<br />
Примеры реализации подобных советов на библиотечных элементах:<br />
Owen Logic: PID и RegKZR<br />
CODESYS 2.3: PID (Utils.lib) + VALVE_REG_NO_POS (PID_Regulators.lib)<br />
<br />
Но это абсолютно неверное решение задачи построения ПИД КЗР.<br />
<br />
<h3>ВОЗНИКАЮЩАЯ ПРОБЛЕМА</h3><br />
<br />
Проблема такого решения заключается в том, что расчётное положение регулирующего органа (значение выхода аналогового ПИД) не соответствует реальному, а также в том, что в крайних значениях выхода ПИД (при 0% или 100%) производится ограничение результатов вычислений выхода. Т.е. появляется неопределённость в поведении «преобразователя» — и игнорировать отсутствие изменений при граничном значении выхода ПИД нельзя и любая форма реакции (непрерывный управляющий сигнал или серия кратковременных импульсов «доводки») далеки от ожидаемых (расчётных) для выбранного закона управления.<br />
<br />
Вот как описывается поведение «преобразователей» у различных авторов библиотек:<ul><li><u>Owen Logic: RegKZR</u><br />
Если рассчитанный процент открытия равен 100 (oa_Pwr=100), то на выходе макроса будет удерживаться сигнал на открытие (ob_Open=1). Если рассчитанный процент открытия равен 0 (oa_Pwr=0), то на выходе макроса будет удерживаться сигнал на закрытие (ob_Close=1).</li>
<li><u>Siemens S7-300: CONT_S (SFB 42 CONT_S)</u><br />
ФБ ступенчатого ПИД регулятора CONT_S (SFB 42 CONT_S), уже содержит «преобразователь», который согласно документации («Технологические функции CPU 31xC» (A5E00105483-01) глава 7.1.2) обрабатывает состояния следующим образом:<br />
Так как регулятор работает без обратной связи по положению, то внутренне рассчитанное управляющее воздействие не совпадает точно с положением исполнительного устройства. Корректировка выполняется, когда управляющее воздействие (ER * GAIN) становится отрицательным. Тогда регулятор устанавливает выход QLMNDN (низкий уровень управляющего сигнала) до тех пор, пока не будет установлен LMNR_LS (нижний ограничительный сигнал обратной связи по положению).</li>
</ul><br />
На мой взгляд, все варианты поведения «преобразователя» в граничных значениях неприемлемы и дают неудовлетворительные результаты.<br />
<br />
<h3>ДЕМОНСТРАЦИОННОЕ МОДЕЛИРОВАНИЕ ПРОБЛЕМЫ</h3><br />
<br />
Продемонстрирую поведения связки «аналоговый ПИД» + «преобразователь позиции» на примере макросов из библиотеки компонентов Owen Logic (PID_ и RegKZR).<br />
Дополнительно буду использовать макрос эмулятора объекта управления, включающего эмуляцию привода регулирующего органа с трёхпозиционным управлением, взятым из статьи <a href="https://www.cyberforum.ru/blogs/534277/8667.html">https://www.cyberforum.ru/blogs/534277/8667.html</a><br />
<br />
Эмулируется регулирование давление воздуха шибером (направляющим аппаратом).<br />
<br />
Чтобы не ждать излишне долго, подобрал несколько гипертрофированные параметры и клапана и помехи.<br />
Диапазон изменения выхода эмулятора объекта 0…10000 Па.<br />
Полный ход привода 30 с.<br />
Люфт привода при открытии 1 с, при закрытии 2 с.<br />
Помеха это синусоида с периодом 20 с, амплитудой 5% (приведённое к положению клапана эмулятора объекта).<br />
<br />
На экране панели можно наблюдать:<ul><li>SP — задание регулятора,</li>
<li>PV — измеренное значение давления воздуха,</li>
<li>ПИД — значение выхода регулятора (расчётное положение регулирующего органа),</li>
<li>Pos — реальное положение регулирующего органа,</li>
<li>Направление — стрелки, показывающие текущее воздействие на привод регулирующего органа (открытие или закрытие),</li>
<li>Концевые — состояние концевых выключателей привода (открыт или закрыт),</li>
<li>Помеха — значение помехи, прикладываемой к объекту регулирования, приведённое к положению регулирующего органа,</li>
<li>Выпадающий список Выключить / Включить — включение или выключение воздействия помехи на объект регулирования.</li>
</ul><br />
Итак, что наблюдаем и как проявляется проблема.<ol style="list-style-type: decimal"><li>через 3 минуты давление стабилизируется возле задания (1200 Па) — это хорошо. А плохо, что расчётное значение положения шибера (это выход ПИД на вход RegKZR) равно 10,14%, а при этом реальное положение равно 12,00% — рассогласование не очень значительное, но уже есть<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=11393&amp;d=1767649894" rel="Lightbox" id="attachment11393" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=11393&amp;thumb=1&amp;d=1767649894" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: 3м.png
Просмотров: 102
Размер:	17.4 Кб
ID:	11393" style="margin: 5px" /></a><br /></li>
<li>с дисплея включаем периодическую помеху и видим, что значение выхода ПИД постепенно уменьшается, хотя положение реального клапана находится около 12%. Это из-за неравномерности люфтов при открытии и закрытии. И, наконец, примерно через 1 минуту после включения помехи значение выхода ПИД становится равным 0% и RegKZR начинает калибровку — непрерывное закрытие клапана, т.е. подаёт неоправданно длинный импульс закрытия. И начиная с этого момента каждые 2 минуты выполняется «калибровка» — необходимая «кривому» алгоритму, но не нужная технологическому процессу.<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=11394&amp;d=1767649894" rel="Lightbox" id="attachment11394" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=11394&amp;thumb=1&amp;d=1767649894" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: 4м.png
Просмотров: 95
Размер:	16.2 Кб
ID:	11394" style="margin: 5px" /></a><br />
<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=11395&amp;d=1767649894" rel="Lightbox" id="attachment11395" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=11395&amp;thumb=1&amp;d=1767649894" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: fiasco.png
Просмотров: 87
Размер:	16.2 Кб
ID:	11395" style="margin: 5px" /></a></li>
</ol>Это, конечно, модель с гипертрофированными параметрами, но она демонстрирует проблему — периодическую потерю управления объектом на период выполнения «калибровки».<br />
Лично наблюдал такой эффект, когда для регулятора разрежения выбрал подобную схему управления (на ПЛК DirectLogic) — потом пришлось переделывать. Причём, для нескольких соседних котлов с идентичным оборудованием на некоторых выход ПИД «полз» вверх, а на некоторых — вниз, т.е. никакой закономерности не было, период между «калибровками» составлял около 20 минут.<br />
В этой модели не очень большая инерция (чтобы быстрее показать проблему), но в моём случае с разрежением инерция была значительнее и после «калибровки» клапан (направляющий аппарат) успевал перейти почти в крайнее положение, что иногда приводило и к отрыву пламени и к срабатыванию защиты по высокому давлению (уже не разрежению!) в топке.<br />
<br />
<h3>ВЫВОДЫ</h3><br />
<br />
Из этого субъективного рассказа предлагаю сделать выводы:<br />
1. Не применять связку «аналоговый ПИД» + «преобразователь позиции».<br />
2. Применять специализированный для КЗР алгоритм ПИД регулятора (об одном из возможных вариантов рассказывал в статье <a href="https://www.cyberforum.ru/blogs/534277/8438.html">https://www.cyberforum.ru/blogs/534277/8438.html</a>)<br />
<br />
<h3>ПРИЛОЖЕНИЕ</h3><br />
<br />
Программа, моделирующая поведение связки «аналоговый ПИД» + «преобразователь позиции» для среды разработки Owen Logic 2.11.370.0</div>


<!-- attachments -->
	<div style="margin-top:10px">

		
		
		
		
			<fieldset class="fieldset">
				<legend>Вложения</legend>
				<table cellpadding="0" cellspacing="3" border="0">
				<tr>
	<td><img loading="lazy" decoding="async" class="inlineimg" src="http://www.cyberforum.ru//cyberstatic.net/images/attach/7z.gif" alt="Тип файла: 7z" width="16" height="16" border="0" style="vertical-align:baseline" /></td>
	<td><a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=11396&amp;d=1767653368">Test PID + KZR.7z</a> (242.0 Кб, 119 просмотров)</td>
</tr>
				</table>
			</fieldset>
		

	</div>
<!-- / attachments -->
]]></content:encoded>
			<dc:creator>ФедосеевПавел</dc:creator>
			<guid isPermaLink="true">https://www.cyberforum.ru/blogs/534277/10659.html</guid>
		</item>
		<item>
			<title>Owen Logic: методика разработки проекта</title>
			<link>https://www.cyberforum.ru/blogs/534277/10475.html</link>
			<pubDate>Sat, 11 Oct 2025 11:58:45 GMT</pubDate>
			<description>Owen Logic: методика разработки проекта 
 
Введение 
 
Проект из статьи является иллюстративным и...</description>
			<content:encoded><![CDATA[<div><h2>Owen Logic: методика разработки проекта</h2><br />
<br />
<h3>Введение</h3><br />
<br />
Проект из статьи является иллюстративным и никогда не проверялся на практике, поэтому нисколько не удивлюсь, если на реальном объекте он не заработает без исправления ошибок в алгоритме, если вообще скомпилируется :)<br />
<br />
Для эмуляции проекта потребуются изменения:<ul><li>разорвать привязку переменной nRtcSeconds от аппаратной части;</li>
<li>подключить переменную nRtcSeconds к выходу счётчика в самом конце программы.</li>
</ul><br />
Блог ограничивает количество вложений, поэтому иллюстраций будет мало, а архивы будут содержать по несколько файлов, демонстрирующих их эволюцию.<br />
<br />
По мере выполнения небольших проектов для малой автоматизации на основе программируемых реле производства ОВЕН модели ПР205 в среде Owen Logic выработал методику, сокращающую время разработки и сводящую значительную часть работы к механическим действиям.<br />
<br />
Для представления о применимости метода приведу характеристики выполненных проектов:<ul><li>DI - 10-12 шт.;</li>
<li>AI - 2-6 шт.;</li>
<li>DO - 1-3 шт. (сигнальные лампы);</li>
<li>AO - 0 шт.;</li>
<li>Modbus - 1-4 Slave по 4-8 регистра;</li>
<li><b>количество обрабатываемых неисправностей 10-40 шт.;</b></li>
<li><b>параметры настройки 40-50 шт.;</b></li>
<li>управление исполнительными механизмами (ПЧВ) по Modbus, поэтому DO и AO отсутствуют (кроме пары сигнальных ламп).</li>
</ul><br />
<b>Основной критерий для выбора предлагаемого метода</b> — количество обрабатываемых неисправностей достаточно велико и неисправности неравномерно влияют на работоспособность отдельных компонентов всей системы.<br />
Если от программы не требуется обработка ошибок, не предполагается доработка функционала и нужно, чтобы программа просто работала, то эта методика будет только мешать творческому подходу.<br />
<br />
Достоинства применения этой методики, на мой взгляд:<ul><li>сокращение сроков выполнения работ;</li>
<li>сокращение числа ошибок до ПНР;</li>
<li>выход на ПНР с готовыми защитами, которые остановят работу в нештатной ситуации даже при ошибках в алгоритме.</li>
</ul>Стоит отметить и значительную роль повторного использования кода — накопление с опытом библиотечных элементов.<br />
<br />
<h3>Используемые сокращения</h3><br />
<br />
FBD — (Function Block Diagram) графический язык программирования стандарта МЭК 61131-3.<br />
ST — (Structured Text) язык программирования стандарта IEC 61131-3.<br />
ПР — программируемое реле.<br />
ФБ — функциональный блок в языке программирования FBD.<br />
<br />
<h3>Краткое обоснование принятых решений</h3><br />
<br />
<b>Язык разработки — FBD</b><br />
Среда разработки Owen Logic предлагает работу почти исключительно на языке FBD с возможностью применения ещё и языка ST в разработанных макросах (аналогах ФБ) и функциях. Язык ST в Owen Logic реализован со значительными ограничениями и играет вспомогательную роль. Поэтому на данный момент использование ST имеет смысл только в тех макросах, которые излишне сложно реализуются средствами языка FBD.<br />
Значит основным языком разработки является FBD, а ST будет (или не будет) применяться эпизодически.<br />
Отмечу, в связи с развитием возможностей различных ИИ-помощников, появилась возможность генерации кода на ST, поэтому предполагаю улучшение положения ST в Owen Logic, что приведёт к пересмотру тезиса о выборе.<br />
<br />
<b>Именованные связи между элементами</b><br />
Язык FBD позволяет организовать связи между элементами двумя способами — непосредственно соединительными линиями и при помощи именованных связей (переменных). Каждый имеет достоинства и недостатки.<br />
По моему мнению следует отказаться от соединительных линий, т.к. большинство сигналов используются неоднократно (например, измеренная температура участвует и в регулировании и в защитах), что превратит холст в невнятную паутину.<br />
Значит основным способом связи элементов выбирается — именованные связи. Использование именованных связей потребует описания множества переменных.<br />
<br />
<b>Структура программы</b><br />
Выбор языка FBD и организации связей  приводит к идеи декомпозиции программы на разделы, каждый из которых состоит из однотипных ФБ (т.е. требуется реализация соответствующих ФБ). А также некоторой группировке переменных по признаку выхода из одного раздела для группового применения в каком-то другом (других) разделах.<br />
<br />
С учётом некоторого опыта, мне видится создание разделов:<ol style="list-style-type: decimal"><li><b>инициализация</b> — задерживается включение, чтобы успели начать работать все внешние приборы, неиспользуемые выходы отключаются (или переводятся в безопасное состояние),</li>
<li><b>обработка аналоговых входов</b> — масштабирование измерений, формирование сигналов неисправности датчиков,</li>
<li><b>обработка дискретных входов</b> — формирование сигналов состояний, сброса сигнализации,</li>
<li><b>предупредительная сигнализация</b> — формирование сигналов предупредительной сигнализации на основе входных сигналов, обобщающих сигналов для каждой подсистемы,</li>
<li><b>защитные блокировки</b> — формирование сигналов защитных блокировок на основе сигналов предупредительной сигнализации, обобщающих сигналов для каждой подсистемы,</li>
<li><b>обмен с вышестоящей системой</b> — упаковка булевских сигналов состояния, предупредительной сигнализации, защитных блокировок в слова состояния (сетевые переменные), распаковка командного слова на сигналы отдельных команд и обнуление командного слова, для возможности повторения команд,</li>
<li><b>обработка команд</b> — обработка команд, приходящих из нескольких источников (локальная панель, вышестоящая система), формирование итоговых состояний переключения режимов (например, переключение РУЧН-АВТО),</li>
<li><b>вспомогательные переменные, используемые в управлении и выводе на экран</b> — формируются сигналы вспомогательных переменных (например, готовности к работе в автоматическом режиме),</li>
<li><b>рабочие алгоритмы</b> — реализация управляющих алгоритмов.</li>
</ol>Очевидно, раздел <b>рабочие алгоритмы</b> в свою очередь может состоять из нескольких подразделов.<br />
<br />
Несколько упрощённо структура показана на рисунке.<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=11255&amp;d=1759427429" rel="Lightbox" id="attachment11255" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=11255&amp;thumb=1&amp;d=1759427429" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: Структура программы.png
Просмотров: 216
Размер:	21.7 Кб
ID:	11255" style="margin: 5px" /></a><br />
<br />
<b>Функциональное назначение переменных разных классов</b><br />
Среда разработки Owen Logic имеет три класса переменных:<ul><li>стандартные — переменные, область видимости которых ограничивается программой,</li>
<li>сетевые, Slave — переменные, область видимости которых составляет программа и сетевые устройства, опрашивающие ПР в режиме Modbus Slave устройства,</li>
<li>сетевые, Master — переменные, область видимости которых ограничивается программой, и служащие для обмена ПР с Modbus Slave устройством.</li>
</ul><br />
На мой взгляд, удобно распределить функциональное назначение переменных в программе следующим образом:<ul><li>стандартные — переменные линий связи, вспомогательные переменные,</li>
<li>сетевые, Slave — переменные текущего состояния (измеренные значения, регистры флагов состояний, текущих режимов работы), параметры настройки (диапазоны измерений, уставки сигнализаций и блокировок, параметры регулирования),</li>
<li>сетевые, Master — по необходимости опроса сетевых устройств.</li>
</ul>Предлагаемое использование сетевых переменных обусловлено тем, что для программы они являются такими же, как и стандартные, но при неожиданном добавлении в проект панели оператора или требования подключения к облачному сервису изменений проекта не потребуется. Ещё одним доводом является возможность для некоторых линеек ПР при помощи утилиты Owen Configurator считывать, модифицировать, сохранять в файле и восстанавливать из файла настройки в сетевых переменных.<br />
<br />
При выбранной структуре программы, стандартные переменные, связывающие перечисленные разделы группируются следующим образом:<ul><li><b>входы</b></li>
<li><b>выходы</b></li>
<li><b>предупредительная сигнализация</b></li>
<li><b>защитные блокировки</b></li>
<li><b>состояние</b></li>
<li><b>команды от вышестоящей системы</b></li>
<li><b>команды от местного пульта</b></li>
<li><b>привязки к аппаратной части ПР</b></li>
</ul>Т.к. систему автоматики можно разделить на подсистемы, то внутри каждой группы переменные образуют подгруппы, принадлежащие каждой из подсистем.<br />
Например, для насосной станции из двух насосов можно выделить три подсистемы — первый насос, второй насос, общие для обоих насосов. Каждая из подсистем имеет состояния, сигнализации и блокировки, полученные команды. При этом, при неисправности одного из насосов, оставшийся исправным может продолжить работу, а при неисправности в общей части независимо от состояния самих насосов — работа невозможна.<br />
<br />
Сетевые Slave переменные группируются по критериям принадлежности к той или иной функции управления или регулирования. Удобно лишь в самом начале разместить переменные текущего состояния, которые не требуют энергонезависимости, т.к. количество таких переменных почти не будет изменяться в ходе возможных доработок алгоритма.<br />
<br />
<h3>Краткое описание методики</h3><br />
<br />
Последовательность разработки:<ol style="list-style-type: decimal"><li>Описание стандартных переменных:<ul><li>получение образца файла экспорта переменных из Owen Logic</li>
<li>редактирование файла экспорта в Exel для получения описания необходимых переменных</li>
<li>импорт полученного файла в Owen Logic</li>
</ul></li>
<li>Создание разделов программы с пропуском и резервированием места на холсте для переменных состояния или параметров настройки.</li>
<li>С учётом полученных при создании программы сведений о списке переменных состояния и параметров настройки создаются сетевые Slave переменные:<ul><li>получение образца файла экспорта переменных из Owen Logic</li>
<li>редактирование файла экспорта в Exel для получения описания необходимых переменных</li>
<li>импорт полученного файла в Owen Logic</li>
</ul></li>
<li>формирование файла импорта сетевых переменных в вышестоящую систему (панель оператора)</li>
<li>формирование файла импорта сетевых переменных в вышестоящую систему (облачный сервис)</li>
</ol><br />
При описании стандартных переменных в части состояния, сигнализации, блокировок и команд необходимо разделение их между общими для всей системы и индивидуальными для отдельных подсистем. Т.к. влияние срабатывания блокировки различно — на всю систему или только на отдельную подсистему.<br />
<br />
Т.к. качественной группировки сетевых переменных добиться сложно — при изменении алгоритма управления изменятся или добавятся переменные состояния, которые уже не поместятся в младшую часть адресов, т.е. исходная структура будет нарушена, то группирование среди сетевых переменных не должно превращаться в самоцель.<br />
<br />
Каждый из разделов программы создаётся почти машинально — сигналы одного назначения однообразно обрабатываются, а их группировка в дереве переменных (полученная при импорте) только упрощает однообразные действия. Единственное исключение — раздел формирования предупредительной сигнализации.<br />
<br />
<h3>Пример применения методики</h3><br />
<br />
Объект автоматизации — насосная станция из двух насосов, работающих на одну магистраль поочерёдно<br />
<br />

			   <div class="spoiler-wrap">
				   <div class="spoiler-head folded clickable">Описание объекта автоматизации и технического задания</div>
				   <div class="spoiler-body">
					   Этот алгоритм неоднократно реализован в настраиваемых приборах множества производителей:<ul><li>ОВЕН САУ-У (алгоритмы 11 и 15),</li>
<li>ОВЕН СУНА-121 (алгоритм 06.00),</li>
<li>ОВЕН САУ-МП (САУ-МП-Х.11, САУ-МП-Х.15),</li>
<li>Контур-У (алгоритмы 05.01 и 05.02).</li>
</ul><br />
Для демонстрации подходов сформулируем некоторую модификацию этого алгоритма:<ul><li>Алгоритм предназначен для управления двумя работающими поочередно насосами, имеющими индивидуальные датчики «сухого хода» и перепада давления (наличия потока).</li>
<li>Алгоритм предусматривает чередование насосов через заданный промежуток времени.</li>
<li>Если работающий насос признан неисправным, то он выключается и включается оставшийся насос.</li>
<li>Индивидуальные для каждого насоса переключатели РУЧН-АВТО позволяют исключить из управления алгоритмом отдельный насос. Если одновременно исключены оба насоса должна светиться лампа обобщённой неисправности.</li>
<li>По датчику давления после насосов блокируется работа по превышению или снижению по сравнению с уставками.</li>
</ul><br />
Схема автоматизации показана на рисунке<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=11075&amp;d=1756406782" rel="Lightbox" id="attachment11075" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=11075&amp;thumb=1&amp;d=1756406782" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: P_ID.png
Просмотров: 134
Размер:	12.1 Кб
ID:	11075" style="margin: 5px" /></a><br />
На функциональной схеме автоматизации обозначены:<br />
Входные сигналы:<ul><li>LSA1, LSA2 — датчики «сухого хода» перед каждым насосом,</li>
<li>PDSA1, PDSA2 — датчики-реле перепада давления на каждом насосе,</li>
<li>PIA1 — аналоговый датчик давления в магистрали после насосов,</li>
<li>TIA1, TIA2 — аналоговые датчики температур обмоток моторов для каждого насоса,</li>
<li>KV1 — реле контроля фаз (сигнал нормального состояния электропитания от реле контроля фаз),</li>
<li>QF1, QF2 — наличие электропитание для каждого из насосов,</li>
<li>SW1 — запрос работы установки СТОП-СТАРТ (переключатель на два положения),</li>
<li>SW2, SW3 — переключатели режима работы для каждого насоса РУЧН-АВТО,</li>
<li>SB1 — кнопка с фиксацией СТОП,</li>
<li>SB2 — кнопка без фиксации СБРОС СИГНАЛИЗАЦИИ.</li>
<li>Выходные сигналы:</li>
<li>KM1, KM2 — пускатель каждого насоса,</li>
<li>HL1 — лампа обобщённой неисправности,</li>
<li>HL2, HL3 — лампы отказов каждого насоса.</li>
</ul><br />
Защитные блокировки насосов:<ul><li>«сухой ход»,</li>
<li>отсутствие перепада давления работающего насоса,</li>
<li>перегрев мотора,</li>
<li>неисправность датчика температуры обмоток мотора насоса,</li>
<li>отключено электропитание насоса.</li>
</ul><br />
Защитные блокировки общие для всей насосной станции:<ul><li>высокое давление воды в магистрали после насосов,</li>
<li>низкое давление воды в магистрали после насосов,</li>
<li>неисправность датчика давления воды в магистрали после насосов,</li>
<li>ошибка от реле контроля фаз,</li>
<li>ошибка соединения с модулем расширения входов и выходов или неисправность самого модуля.</li>
</ul><br />
Технологическая сигнализация:<ul><li>разряжена батарейка, поддерживающая часы реального времени и энергонезависимые переменные</li>
</ul><br />
Т.е. система содержит независимые защитные блокировки (для каждого из насосов), а также общие для всей системы Кроме того, присутствуют сигналы, не вызывающие блокировку установки, но требующие вмешательства оперативного или обслуживающего персонала.
				   </div>
			   </div><br />
<br />
Стиль оформления проекта<br />

			   <div class="spoiler-wrap">
				   <div class="spoiler-head folded clickable">Стиль оформления проекта</div>
				   <div class="spoiler-body">
					   <h4>Именование переменных</h4><br />
<br />
По разным причинам принял для себя правила именования переменных.<br />
Префикс переменной обозначает источник или приёмник сигнала, тип данных:<ul><li>x — дискретный входной сигнал</li>
<li>b — дискретный сигнал в пределах алгоритма</li>
<li>y — дискретный выходной сигнал</li>
<li>a — аналоговый входной сигнал от АЦП до масштабирования</li>
<li>r — аналоговый сигнал после масштабирования или переменная типа real=float=float32=single</li>
<li>o — аналоговый выходной сигнал в безразмерных единицах ЦАП</li>
<li>n — целочисленная переменная без знака</li>
<li>w — целочисленная переменная без знака размером 16 бит, используемая как набор бит (флагов)</li>
</ul>Префикс может дополняться несколькими символами, обозначающими источник данных:<ul><li>MS — переменная получена от вышестоящей системы по цифровому интерфейсу Modbus, для которой контроллер является Slave устройством</li>
<li>SCR — переменная используется для взаимодействия с экраном местной панели контроллера, при использовании на экране приобретает дополнительное опциональное свойство «запись в конце цикла», востребованное в алгоритме</li>
<li>HW — переменная, «привязанная» к аппаратным переменным контроллера</li>
</ul>Например, <b>nHW_Rtc_Seconds</b> — целочисленная переменная, привязанная к аппаратной части ПР — секундам часов реального времени.<br />
<br />
<h4>Оформление макросов (функциональных блоков, функций)</h4><br />
<br />
В самом верху программы или макроса размещён текстовый комментарий, содержащий краткое описание кода, автора, имя группы, которой принадлежит макрос, дату последней редакции.<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=11083&amp;d=1756484903" rel="Lightbox" id="attachment11083" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=11083&amp;thumb=1&amp;d=1756484903" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: 1_Описание_программы.png
Просмотров: 150
Размер:	25.5 Кб
ID:	11083" style="margin: 5px" /></a><br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=11084&amp;d=1756484903" rel="Lightbox" id="attachment11084" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=11084&amp;thumb=1&amp;d=1756484903" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: 1_Описание_макроса.png
Просмотров: 146
Размер:	23.6 Кб
ID:	11084" style="margin: 5px" /></a><br />
Стиль соединительных линий произвольный, т.к., обычно, макрос это небольшой фрагмент кода, не отличающийся высокой сложностью ни алгоритма ни связей между элементами.<br />

				   </div>
			   </div><br />
<br />
Описание стандартных переменных<br />

			   <div class="spoiler-wrap">
				   <div class="spoiler-head folded clickable">Описание стандартных переменных</div>
				   <div class="spoiler-body">
					   Переменные можно описывать штатным способом — по одной в окне ввода переменных.<br />
Но можно гораздо быстрее, при помощи импорта описаний переменных из подготовленного файла.<br />
Формат файла значительно отличается от версии к версии Owen Logic, а также для различных моделей ПР в рамках одной версии. При этом тип файла csv удобно редактируется в Exel (или в Calc из LibreOffice) — заполнение «протягиванием», копированием и заменой в выделенном фрагменте и пр.<br />
<div class="smallfont offtopic" onmouseover="this.style.color='#000000';" onmouseout="this.style.color='#888888';">
				<p><b>Не по теме:</b></p>
				<p>Для Exel существует удобная надстройка, позволяющая быстро выполнять групповую обработку ячеек — Ёxel, доступная по ссылке <a rel="nofollow noopener noreferrer" href="https://www.e-xcel.ru/index.php/joxcel" target="_blank" title="https://www.e-xcel.ru/index.php/joxcel">https://www.e-xcel.ru/index.php/joxcel</a><br />
Для Calc (LibOo) о подобном расширении не знаю, но в окне Поиск-Замена возможно использовать регулярные выражения, что хоть и сложнее, но всё равно заметно быстрее поэлементного изменения.</p>
				</div><br />
<br />
Для этого нужно получить образец файла импорта —  объявить три переменные разных типов (целочисленная, булевская, с плавающей запятой) и выполнить экспорт переменных — получить csv файл с описанием.<br />
Для Owen Logic 2.11.368.0 и ПР205 (для других версий и моделей будут отличия) пробный экспорт приведён в файле<br />
«<b>1_1_Экспорт_переменных_для_заготовки.csv</b>»<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=11132&amp;d=1757257609" rel="Lightbox" id="attachment11132" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=11132&amp;thumb=1&amp;d=1757257609" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: 1_1_Скрин_экспорта_переменных.png
Просмотров: 106
Размер:	10.5 Кб
ID:	11132" style="margin: 5px" /></a><br />
<br />
Каждая из подсистем насосной станции (общая, первый насос и второй насос) имеет однотипные наборы данных — переменных:<ul><li>состояния (например, включено, отключено, отказ, выполняется инициализация и т.п.)</li>
<li>предупредительная сигнализация (начался отсчёт таймера срабатывания защитной блокировки) по отдельным параметрам</li>
<li>защитная блокировка и сигнализация по отдельным параметрам</li>
<li>команды, принимаемые из вышестоящей системы (панель оператора, SCADA, облако OwenCloud)</li>
</ul><br />
Полученный csv файл открыть в Exel (или в Calc из LibreOffice) и на основе образцов объявить требуемые переменные, пока без привязки к объекту управления (просто нумерованные состояния, сигнализации и пр.). <br />
На этапе получения заготовки описаний переменных для подсистем удобно описать их с запасом. Предпочитаю резервировать переменные в количестве кратном 16 — по числу бит в одном регистре Modbus. Данная система не очень сложная, состояний не много, достаточно будет по 16 переменных.<br />
<br />
Результат предварительного определения переменных с разделением на функциональные группы показан в файле<br />
«<b>1_2_Заготовка_для описания_переменных.csv</b>»<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=11133&amp;d=1757257609" rel="Lightbox" id="attachment11133" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=11133&amp;thumb=1&amp;d=1757257609" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: 1_2_Скрин_заготовки_.png
Просмотров: 100
Размер:	164.2 Кб
ID:	11133" style="margin: 5px" /></a><br />
<br />
Далее, нужно последовательно уточнить описания, ведь алгоритм определён ещё на этапе ТЗ.<br />
Получаем окончательный файл импорта переменных. Он окончательный не только потому, что так захотелось или переоцениваю силы — повторный импорт невозможен (он не обновляет существующие переменные, а создаёт повторно с генерацией ошибок одинакового имени), так уж устроена среда разработки.<br />
Результат окончательного определения стандартных переменных показан в файле<br />
«<b>1_3_Импорт_переменных.csv</b>»<br />
Именно из этого файла и выполняется импорт стандартных переменных в программу.<br />
<br />
Архив содержит все три csv файла для примера — экспорт из Owen Logic, заготовка для уточнения переменных и итог обработки в Exel.<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=11134&amp;d=1757258788" >1_Импорт_Стандартных переменных.7z</a><br />

				   </div>
			   </div><br />
<br />
Создание разделов программы (кроме рабочего алгоритма) с пропуском и резервированием места на холсте для переменных состояния или параметров настройки, которые будут добавлены после описания сетевых переменных Slave<br />

			   <div class="spoiler-wrap">
				   <div class="spoiler-head folded clickable">Кликните здесь для просмотра всего текста</div>
				   <div class="spoiler-body">
					   Следующий этап — набрать основную часть программы без алгоритмов работы. Также будут пропускаться обращения к переменным, которые относятся к настройкам и результатам измерения, т.к. это сетевые переменные и их ещё предстоит создать и импортировать.<br />
Создаются разделы:<ol style="list-style-type: decimal"><li><b>инициализация</b> — задерживается включение, чтобы успели начать работать все внешние приборы, неиспользуемые выходы отключаются,</li>
<li><b>обработка аналоговых входов</b> — масштабирование измерений, формирование сигналов неисправности датчиков,</li>
<li><b>обработка дискретных входов</b> — формирование сигналов состояний, сброса сигнализации,</li>
<li><b>предупредительная сигнализация</b> — формирование сигналов предупредительной сигнализации на основе входных сигналов, обобщающих сигналов для каждой подсистемы,</li>
<li><b>защитные блокировки</b> — формирование сигналов защитных блокировок на основе сигналов предупредительной сигнализации, обобщающих сигналов для каждой подсистемы,</li>
<li><b>обмен с вышестоящей системой</b> — упаковка булевских сигналов состояния, предупредительной сигнализации, защитных блокировок в слова состояния (сетевые переменные), распаковка командного слова на сигналы команд и обнуление командного слова, для возможности повторения команд,</li>
<li><b>формирование сигналов готовности к работе</b> — формируются сигналы состояния исправности и готовности к работе в автоматическом режиме.</li>
</ol><br />
На скрине показаны разделы инициализации и обработки аналоговых выходов без части переменных, которые будут получены импортом сетевых переменных.<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=11136&amp;d=1757259547" rel="Lightbox" id="attachment11136" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=11136&amp;thumb=1&amp;d=1757259547" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: 2_1_Программа.png
Просмотров: 159
Размер:	83.1 Кб
ID:	11136" style="margin: 5px" /></a><br />

				   </div>
			   </div><br />
<br />
Дополнение программы разделом рабочего алгоритма с пропуском и резервированием места на холсте для переменных состояния или параметров настройки, которые будут добавлены после описания сетевых переменных Slave<br />

			   <div class="spoiler-wrap">
				   <div class="spoiler-head folded clickable">Кликните здесь для просмотра всего текста</div>
				   <div class="spoiler-body">
					   <br />
На этом этапе реализуются алгоритмы работы. Эти алгоритмы индивидуальны для каждого проекта и не поддаются формализации.<br />
Остаётся только требование — по возможности, не объявлять новые переменные состояния, а использовать незадействованные из состава слов состояния подсистемы (в данном случае первого или второго насоса, общей части). Требование нацелено на обработку в вышестоящей системе для отображения на мнемосхеме. Если его не соблюдать, то придётся копировать вновьобъявленные биты в уже существующие биты состояний.<br />
<br />
Получаем программу без сетевых Slave переменных<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=11137&amp;d=1757260423" rel="Lightbox" id="attachment11137" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=11137&amp;thumb=1&amp;d=1757260423" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: 2_2_Добавление_рабочих_алгоритмов.png
Просмотров: 130
Размер:	48.9 Кб
ID:	11137" style="margin: 5px" /></a><br />

				   </div>
			   </div><br />
<br />
Добавление сетевых Slave переменных<br />

			   <div class="spoiler-wrap">
				   <div class="spoiler-head folded clickable">Кликните здесь для просмотра всего текста</div>
				   <div class="spoiler-body">
					   <br />
Последовательность получения файла импорта можно проследить по файлам из архива<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=11139&amp;d=1757261141" >3_Импорт_переменных_Сетевых_Slave.7z</a><br />
<br />
По аналогии со стандартными переменными, следует получить образец файла экспорта сетевых Slave переменных.<br />
«<b>3_1_Экспорт_переменных_для заготовки_Сетевые, Slave.csv</b>»<br />
<br />
По мере создания программы дополнять переменные в этот список переменных, заполняя лишь уникальные поля — имя переменной, тип, комментарий, начальное значение, диапазон изменения.<br />
В файле «<b>3_2_Импорт_переменных_Сетевые_заготовка, Slave.csv</b>» из архива видно, что часть полей (например, адрес) заполнена некорректно.<br />
<br />
Когда программа уже будет завершена и потребуются сетевые Slave переменные, то в файле экспорта останется расставить строки по группам, дать имена группам, при помощи формул с условием пересчитать адреса, копированием заполнить имена путей, дополнительное описание, энергонезависимость.<br />
Эту работу очень удобно выполнять в Exel, а не в редакторе переменных Owen Logic, поэтому её выполнение займёт очень мало времени.<br />
В файле «<b>3_3_Импорт_переменных_Сетевые_итоговый, Slave.csv</b>» видно, что заполнены все поля и они не содержат ошибок.<br />

				   </div>
			   </div><br />
<br />
Завершение программы, эмуляция, исправление ошибок в алгоритмах и макросах<br />

			   <div class="spoiler-wrap">
				   <div class="spoiler-head folded clickable">Кликните здесь для просмотра всего текста</div>
				   <div class="spoiler-body">
					   После импорта сетевых переменных, остаётся завершить программу — расставить пропущенные переменные.<br />
Проверка эмуляцией позволяет выявить и устранить ошибки, поэтому в исправленных макросах уточняются даты создания.<br />
<br />
Получаем итоговый проект<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=11278&amp;d=1760175218" >2_Программа.7z</a><br />
<i><b>Примечание.</b></i> Архив содержит поэтапное развитие программы от основных разделов, добавления рабочих алгоритмов и завершения после импорта сетевых переменных.
				   </div>
			   </div><br />
<br />
Формирование файла импорта сетевых переменных в вышестоящую систему (панель оператора)<br />

			   <div class="spoiler-wrap">
				   <div class="spoiler-head folded clickable">Кликните здесь для просмотра всего текста</div>
				   <div class="spoiler-body">
					   Большое количество переменных, передаваемых в вышестояющую систему (панель оператора) есть смысл не описывать по одной, а по аналогии с другими случаями импортировать.<br />
Форматы файлов импорта в среде разработки проекта панели оператора не совпадают с форматом экспорта Owen Logic. Решение состоит в конвертации файла экспорта в формат файла импорта.<br />
<br />
Пример для панели Weintek ранее рассматривал в статье<br />
<a href="https://www.cyberforum.ru/blogs/534277/10383.html">OwenLogic: перенос сетевых переменных в панель Weintek (EasyBuilder Pro)</a><br />

				   </div>
			   </div><br />
<br />
Формирование файла импорта сетевых переменных в вышестоящую систему (облачный сервис)<br />

			   <div class="spoiler-wrap">
				   <div class="spoiler-head folded clickable">Кликните здесь для просмотра всего текста</div>
				   <div class="spoiler-body">
					   Приборы производства ОВЕН часто подключают к облачному сервису OwenCloud.<br />
На данный момент формат файла импорта для переменных является json.<br />
Среда разработки Owen Logic позволяет выполнить экспорт переменных для OwenCloud, но заполнение полей в json получается неполным и не вполне адекватным, что после импорта дополняется невозможностью редактирования некоторых полей.<br />
<br />
Пользователи разработали утилиту для конвертации csv файла экспорта сетевых переменных в формат json импорта параметров для облачного сервиса.<br />
Скачать её и ознакомиться с описанием можно по ссылке<br />
<a rel="nofollow noopener noreferrer" href="https://owen.ru/forum/showthread.php?t=41462&amp;p=466460&amp;viewfull=1#post466460" target="_blank" title="https://owen.ru/forum/showthread.php?t=41462&amp;p=466460&amp;viewfull=1#post466460">https://owen.ru/forum/showthre... post466460</a><br />
<br />
В утилите требуется выбрать номера позиций в csv и выполнить конвертацию.<br />
Скрин показывает заполнение этих номеров для Owen Logic 2.11.368.0 и ПР205<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=11140&amp;d=1757263646" rel="Lightbox" id="attachment11140" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=11140&amp;thumb=1&amp;d=1757263646" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: 5_Конвертация_для_OwenCloud.png
Просмотров: 102
Размер:	84.2 Кб
ID:	11140" style="margin: 5px" /></a><br />

				   </div>
			   </div><br />
<br />
<h3>Выводы и рекомендации</h3><br />
<br />
Приведено описание методики разработки проекта и приведён пример её применения.<br />
<br />
Суть методики заключается в формализации части программы, которая создаётся почти механистически.<br />
Негативным побочным эффектом такого подхода является резкое увеличение количества переменных.<br />
Решением для ускорения описания переменных является импорт файла описаний, который редактируется в Exel.<br />
Всё вместе — и формализация и импорт переменных — приводят к сокращению сроков реализации работы.<br />
<br />
Полученные навыки в дальнейшем приводят к решениям по сокращениям сроков разработки для других компонент автоматизации — панелей оператора и облачных сервисов.<br />
<br />
Для оценки, приведу длительность разработки с чистого холста и использованием уже готовых макросов (без учёта проведения ПНР):<ul><li>проект насосной станции из 3 насосов (АВР, чередование, каскадирование): 5 дней по 3 часа после работы и один полный выходной — наверное, можно оценить в 3 рабочих дня</li>
<li>проект насосной станции из 2 насосов (АВР, чередование) для этой статьи: 3 рабочих дня</li>
</ul></div>

]]></content:encoded>
			<dc:creator>ФедосеевПавел</dc:creator>
			<guid isPermaLink="true">https://www.cyberforum.ru/blogs/534277/10475.html</guid>
		</item>
		<item>
			<title>OwenLogic: перенос сетевых переменных в панель Weintek (EasyBuilder Pro)</title>
			<link>https://www.cyberforum.ru/blogs/534277/10383.html</link>
			<pubDate>Tue, 03 Jun 2025 17:33:35 GMT</pubDate>
			<description>ВВЕДЕНИЕ 
 
*ПЕРЕД ЭКСПЕРИМЕНТАМИ - СОЗДАЙТЕ РЕЗЕРВНЫЕ КОПИИ ПРОЕКТОВ* 
 
На момент написания...</description>
			<content:encoded><![CDATA[<div><h2>ВВЕДЕНИЕ</h2><br />
<br />
<b><font color="Red">ПЕРЕД ЭКСПЕРИМЕНТАМИ - СОЗДАЙТЕ РЕЗЕРВНЫЕ КОПИИ ПРОЕКТОВ</font></b><br />
<br />
На момент написания статьи (02 июня 2025 г.) самыми актуальными версиями ПО являются:<ul><li>OwenLogic v. 2.10.366</li>
<li>EasyBuilder Pro v. 6.10.01.359</li>
</ul><br />
Программируемые реле производства ОВЕН могут снабжаться средствами человеко машинного интерфейса в виде сенсорной панели оператора производства Weintek.<br />
<br />
Перенос тегов (сетевых переменных) из OwenLogic в среду EasyBuilder Pro можно значительно ускорить с помощью нескольких способов:<ul><li>перестановка полей cvs файла экспорта OwenLogic в соответствии с их расположением в cvs файле импорта EasyBuilder Pro</li>
<li>создание описаний в электронной таблице и получение cvs файла импорта для EasyBuilder Pro</li>
</ul><br />
Сразу отмечу, хотя и бездоказательно - по моим ощущениям, в EasyBuilder формат импорта и возможности замены одноимённых переменных постоянно меняются.<br />
Формат экспорта в OwenLogic точно меняется и не только от версии к версии, но и от модели к модели ПР в рамках одной версии среды разработки.<br />
<br />
Поэтому, <u>предлагаемые ниже средства не рассчитаны на &quot;вечное&quot; использование, а являются инструментом только сейчас, а для последующих обновлений сред разработки - всего лишь заготовками.</u><br />
<br />
Также добавлю, что пользуюсь консольной версией, а не программой с GUI по идеологическим соображениям - утилита нужна здесь и сейчас, при изменении формата входных и выходных данных нужно быстро модифицировать утилиту. Сохранение названий файлов и другой конфигурационной информации сокращает время отладки.<br />
<br />
Встречал реализацию на Python - она много проще выглядит, но пока нет заинтересованности в его освоении, что не отменяет такой возможности.<br />
<br />
<h2>1. КОНВЕРТАЦИЯ ФАЙЛОВ ЭКСПОРТА В ФОРМАТ ФАЙЛОВ ИМПОРТА</h2><br />
<br />
<h3>1.1 ИСХОДНИКИ УТИЛИТЫ КОНВЕРТАЦИИ</h3><br />
<br />
Для ПР205 пакетный файл (bat):<br />

			   <div class="spoiler-wrap">
				   <div class="spoiler-head folded clickable">OL_2_10_366_PR205_to_Weintek.bat</div>
				   <div class="spoiler-body">
					   <div class="codeblock"><table class="winbatch"><thead><tr><td colspan="2" id="379429681"  class="head">Windows Batch file</td></tr></thead><tbody><tr class="li1"><td><div id="379429681" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
</pre></td><td class="de1"><pre class="de1"><span class="sy0">@</span><span class="kw2">chcp</span> <span class="nu0">1251</span><span class="sy0">&gt;</span>nul
&nbsp;
<span class="sy0">@</span>rem Размещение утилиты <span class="co101">-</span> потокового редактора sed
<span class="sy0">@</span><span class="kw1">set</span> sed_exe=<span class="st0">&quot;C:\Program Files\binutils\ssed.exe&quot;</span>
<span class="sy0">@</span>rem <span class="kw1">set</span> sed_exe=<span class="st0">&quot;C:\Program Files\binutils\sed\bin\sed.exe&quot;</span>
<span class="sy0">@</span><span class="kw1">set</span> iconv_exe=<span class="st0">&quot;C:\Program Files\binutils\iconv\bin\iconv.exe&quot;</span>
&nbsp;
<span class="sy0">@</span>rem Название исходного файла с экспортируемыми из Owen Logic тегами
<span class="sy0">@</span><span class="kw1">set</span> source=<span class="st0">&quot;ПР205_экспорт_сетевых_переменных_Slave.csv&quot;</span>
<span class="sy0">@</span>rem Название итогового файла с тегами для Easy Builder <span class="br0">&#40;</span>Weintek<span class="br0">&#41;</span>
<span class="sy0">@</span><span class="kw1">set</span> <span class="st0">&quot;target=result.csv&quot;</span>
<span class="sy0">@</span>rem Название устройства, уже описанного в системных параметрах проекта Easy Builder
<span class="sy0">@</span>rem Лучше на латинице, т.к. многобайтные символы сложнее обрабатывать
<span class="sy0">@</span><span class="kw1">set</span> <span class="st0">&quot;device_name=MODBUS RTU (Zero-based Addressing)&quot;</span>
&nbsp;
<span class="sy0">@</span>rem Временный файл
<span class="sy0">@</span><span class="kw1">set</span> <span class="st0">&quot;tmp_file=temp.$$1&quot;</span>
<span class="sy0">@</span>rem Регулярное выражение для выделения полей в csv экспортируемых тегов
<span class="sy0">@</span><span class="kw1">set</span> <span class="st0">&quot;field=([^;]*)[;]&quot;</span>
&nbsp;
<span class="sy0">@</span>rem Названия типов данных в кодировке UTF<span class="co101">-8</span>
<span class="sy0">@</span><span class="kw1">set</span> <span class="st0">&quot;Owen_Unsigned_16=Long&quot;</span>
<span class="sy0">@</span><span class="kw1">set</span> Owen_Float_32=Float
<span class="sy0">@</span><span class="kw1">set</span> <span class="st0">&quot;Weintek_Unsigned_16=16-bit Unsigned&quot;</span>
<span class="sy0">@</span><span class="kw1">set</span> <span class="st0">&quot;Weintek_Float_32=32-bit Float&quot;</span>
&nbsp;
<span class="sy0">@</span>rem Удалить заголовок <span class="br0">&#40;</span>напечатать c <span class="nu0">3</span> по последнюю строку<span class="br0">&#41;</span>
<span class="sy0">@</span><span class="co103">%sed_exe%</span> <span class="co101">-n</span> <span class="st0">&quot;3,$p&quot;</span> <span class="co103">%source%&gt;%tmp_file%</span>
<span class="sy0">@</span>rem Переставить поля
<span class="sy0">@</span><span class="co103">%sed_exe%</span> <span class="co101">-n</span> <span class="co101">-r</span> <span class="st0">&quot;s/<span class="es100">%field%%field%%field%%field%%field%%field%.*/\1\,%device_name%</span>\,3x\,\4\,\&quot;</span>\<span class="nu0">6</span>\<span class="st0">&quot;\,\2/p&quot;</span> <span class="co103">%tmp_file%&gt;%target%</span>
<span class="sy0">@</span>rem Заменить тип регистра <span class="st0">&quot;Long&quot;</span> на <span class="st0">&quot;16-bit Unsigned&quot;</span>
<span class="sy0">@</span>rem Заменить тип регистра <span class="st0">&quot;Float&quot;</span> на <span class="st0">&quot;32-bit Float&quot;</span>
<span class="sy0">@</span><span class="co103">%sed_exe%</span> <span class="co101">-r</span> <span class="st0">&quot;s/<span class="es100">%Owen_Unsigned_16%$/%Weintek_Unsigned_16%</span>/&quot;</span> <span class="co103">%target%&gt;%tmp_file%</span>
<span class="co103">@%sed_exe%</span> <span class="co101">-r</span> <span class="st0">&quot;s/<span class="es100">%Owen_Float_32%$/%Weintek_Float_32%</span>/&quot;</span> <span class="co103">%tmp_file%&gt;%target%</span>
&nbsp;
<span class="sy0">@</span>rem Изменить кодировку с UTF<span class="co101">-8</span> на CP1251
<span class="sy0">@</span><span class="co103">%sed_exe%</span> <span class="co101">-n</span> <span class="st0">&quot;1,$p&quot;</span> <span class="co103">%target%&gt;%tmp_file%</span>
<span class="co103">@%iconv_exe%</span> <span class="co101">-f</span> UTF<span class="co101">-8</span> <span class="co101">-t</span> CP1251 <span class="co103">%tmp_file%&gt;%target%</span>
<span class="sy0">@</span><span class="kw2">del</span> <span class="co103">%tmp_file%</span></pre></td></tr></table></div></td></tr></tbody></table></div>
				   </div>
			   </div><br />
<br />
<h3>1.2 КРАТКИЕ ПОЯСНЕНИЯ</h3><br />
<br />
В OwenLogic v. 2.10.366 используется вариация формата cvs со следующими особенностями:<ul><li>разделитель полей - точка с запятой</li>
<li>две строки с заголовком (одна по английски, другая по русски)</li>
<li>кодировка UTF-8</li>
</ul><br />
В EasyBuilder Pro v. 6.10.01.359 используется вариация формата cvs со следующими особенностями<ul><li>разделитель полей - запятая</li>
<li>заголовок отсутствует</li>
<li>кодировка однобайтная для Windows GUI приложений - CP1251</li>
</ul><br />
Для конвертации использованы консольные UNIX утилиты из binutils и strutils:<ul><li>sed - потоковый текстовый редактор</li>
<li>iconv - утилита для преобразования текста из одной кодировки в другую</li>
</ul><br />
Вместо sed можно использовать его форк ssed - обе работают одинаково в данном случае.<br />
<br />
Утилиты пакета binutils очень распространены и чаще всего уже установлены с каким-нибудь пакетом программирования: WinAVR, Visual Studio и другими. Но при их отсутствии - легко скачиваются и устанавливаются.<br />
<br />
<a rel="nofollow noopener noreferrer" href="https://sourceforge.net/projects/gnuwin32/files/" target="_blank" title="https://sourceforge.net/projects/gnuwin32/files/">https://sourceforge.net/projects/gnuwin32/files/</a><br />
<a rel="nofollow noopener noreferrer" href="https://sed.sourceforge.io" target="_blank" title="https://sed.sourceforge.io">https://sed.sourceforge.io</a><br />
Их установка - просто распаковка в какой-нибудь каталог.<br />
В случае gnuwin32 скачать и распаковать в папку утилиты:<ul><li>sed-4.2.1-dep.zip и sed-4.2.1-bin.zip</li>
<li>libiconv-1.9.2-1-lib.zip, libiconv-1.9.2-1-doc.zip, libiconv-1.9.2-1-dep.zip, libiconv-1.9.2-1-bin.zip</li>
</ul>Встречал в Internet и более &quot;свежие&quot; и 64-разрядные сборки, но не проверял работоспособность.<br />
<br />
Для работы нужно в скрипте уточнить:<ul><li>полный путь к sed (или ssed)</li>
<li>полный путь к iconv</li>
<li>название файла экспорта переменных из OL</li>
<li>название итогового файла импорта для Easy Builder Pro</li>
<li>название устройства, которое задано в проекте EasyBuilder Pro для связи с Овен ПР (обычно, это название протокола связи), здесь используется название &quot;MODBUS RTU (Zero-based Addressing)&quot;</li>
</ul><br />
Если название устройства содержит кириллицу, то после завершения работы скрипта нужно в текстовом редакторе открыть итоговый файл и заменить дефолтное название устройства &quot;MODBUS RTU (Zero-based Addressing)&quot; на то, которое задано в проекте EasyBuilder Pro, иначе импорт будет невозможен.<br />
<br />
Отдельное действие с названием устройства получилось из-за того, что не разобрался с кодировками.<br />
Но и так, хорошо получилось - много быстрее, чем перенос руками.<br />
<br />
В среде OwenLogic названия типов данных меняются, на данный момент словами &quot;Long&quot; и &quot;Float&quot; обозначаются соответственно целые 16 разрядные числа без знака и 32-разрядные числа в формате с плавающей запятой.<br />
Их обозначения приводятся в строках 22 и 23. Если изменятся очередной раз - менять их названия нужно в этом месте.<br />
<br />
Ещё недавно файл экспорта содержал всего одну строку заголовка таблицы, а в версии OwenLogic v. 2.10.366 их стало две. Если вернут одну строку заголовка, то нужно изменить строки<br />
<div class="codeblock"><table class="winbatch"><thead><tr><td colspan="2" id="590931278"  class="head">Windows Batch file</td></tr></thead><tbody><tr class="li1"><td><div id="590931278" style="height: 62px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">27
28
</pre></td><td class="de1"><pre class="de1"><span class="sy0">@</span>rem Удалить заголовок <span class="br0">&#40;</span>напечатать cо <span class="nu0">2</span> по последнюю строку<span class="br0">&#41;</span>
<span class="sy0">@</span><span class="co103">%sed_exe%</span> <span class="co101">-n</span> <span class="st0">&quot;2,$p&quot;</span> <span class="co103">%source%&gt;%tmp_file%</span></pre></td></tr></table></div></td></tr></tbody></table></div>Количество полей, их порядок и назначение в OwenLogic постоянно меняется. На данный момент, интересующие поля (название, тип, адрес, комментарий) располагаются в полях файла экспорта ПР205 в позициях соответственно 1, 2, 4 и 6.<br />
Поэтому в строке поиска находится 6 шаблонов <code class="inlinecode">%fields%</code>, и номера позиций нужных полей расставлены в нужном для EasyBuilder Pro порядке (<code class="inlinecode">\1</code>\,%device_name%\,3x\,<code class="inlinecode">\4</code>\,&quot;<code class="inlinecode">\6</code>&quot;\,<code class="inlinecode">\2</code>)<br />
<div class="codeblock"><table class="winbatch"><thead><tr><td colspan="2" id="53652193"  class="head">Windows Batch file</td></tr></thead><tbody><tr class="li1"><td><div id="53652193" style="height: 62px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">29
30
</pre></td><td class="de1"><pre class="de1"><span class="sy0">@</span>rem Переставить поля
<span class="sy0">@</span><span class="co103">%sed_exe%</span> <span class="co101">-n</span> <span class="co101">-r</span> <span class="st0">&quot;s/<span class="es100">%field%%field%%field%%field%%field%%field%.*/\1\,%device_name%</span>\,3x\,\4\,&quot;</span>\<span class="nu0">6</span><span class="st0">&quot;\,\2/p&quot;</span> <span class="co103">%tmp_file%&gt;%target%</span></pre></td></tr></table></div></td></tr></tbody></table></div>Если изменится число или порядок полей в экспорте, то именно в этих строках и выполнять коррекцию.<br />
<br />
Импорт в EasyBuilder выполняется в OEM кодировке, поэтому результат дополнительно перекодируется утилитой iconv.<br />
<br />
<h2>2. ДОПОЛНЕНИЕ БУЛЕВЫМИ ПЕРЕМЕННЫМИ ПРИ ПОМОЩИ ТАБЛИЧНЫХ ПРОЦЕССОРОВ</h2><br />
<br />
Множество булевых переменных для передачи упаковываются в 16-разрядные целые числа. В EasyBuilder для них введено обозначение &quot;3x_Bit&quot; и адрес задаётся особым образом DDDDDdd (DDDDD - это адрес регистра, dd - номер бита с лидирующим нулём).<br />
Очень удобно сделать экспорт из EasyBuilder одной такой переменной, открыть cvs файл в табличном процессоре (Exel ил MS Office или Calc из LibreOffice), размножить строки и заполнить комментариями. После чего сохранить и импортировать в EasyBuilder Pro.<br />
<br />
<h2>ВЫВОДЫ И РЕКОМЕНДАЦИИ</h2><br />
<br />
Формат экспорта переменных OwenLogic меняется очень часто, поэтому предложенное решение работоспособно весьма ограниченное время. Но при помощи небольших изменений оно позволяет адаптировать алгоритм под текущее состояние.<br />
Время на адаптацию несоизмеримо меньше времени ручного набора тегов.<br />
<br />
Обработку в Exel (Calc) можно использовать и в самом начале работ над проектом для ПР205 при описании сетевых переменных - описать уникальные имена, копированием заполнить типы и другие параметры, настроить автоматическое вычисление адресов регистров при помощи условия названия типа предыдущей переменной.</div>

]]></content:encoded>
			<dc:creator>ФедосеевПавел</dc:creator>
			<guid isPermaLink="true">https://www.cyberforum.ru/blogs/534277/10383.html</guid>
		</item>
		<item>
			<title>OwenLogic: эмулятор объекта управления (для отладки регуляторов)</title>
			<link>https://www.cyberforum.ru/blogs/534277/8667.html</link>
			<pubDate>Sat, 12 Oct 2024 17:01:24 GMT</pubDate>
			<description>*OwenLogic: эмулятор объекта управления (для отладки регуляторов) 
* 
 
 
*1 ВВЕДЕНИЕ* 
 
При...</description>
			<content:encoded><![CDATA[<div><b><font size="+2"><div align="center">OwenLogic: эмулятор объекта управления (для отладки регуляторов)</div></font></b><br />
<br />
<br />
<b>1 ВВЕДЕНИЕ</b><br />
<br />
При разработке регулятора актуальным остаётся вопрос проверки и отладки алгоритма до начала ПНР на объекте.<br />
<br />
Для регулятора с управлением исполнительным механизмом через аналоговый выход можно собрать макет из датчика температуры, твердотельного реле с регулируемым выходом, нагревательного элемента (лампы накаливания или керамического резистором).<br />
<br />
Для регулятора с трёхпозиционным исполнительным механизмом собрать простой макет уже затруднительно.<br />
<br />
Хорошую идею предлагают разработчики ПЛК Siemens S7-300 - библиотека этого ПЛК содержит три функциональных блока (ФБ) эмулятора объекта управления FB100, FB101 и FB102, соответственно для случаев непрерывного, трёхпозизионного и ШИМ управления исполнительным механизмом.<br />
Основой данных ФБ является FB100, на вход которого подаётся состояние выхода регулятора и производятся последующие вычисления. Для ФБ FB101 и FB102 состояния управляющих входов сначала преобразуются в положение регулирующего органа исполнительного механизма, а потом производятся вычисления как в FB100.<br />
<br />
Алгоритм FB100 эмулирует объект управления, который описывается тремя последовательно соединёнными апериодическими звеньями 1-го порядка. Общий коэффициент усиления позволяет сделать приведение выхода эмулятора к любым единицам измерения.<br />
<br />
С подробным описанием ФБ FB100, FB101 и FB102 можно ознакомиться в документации к ПЛК.<br />
<br />
Предлагаю самостоятельные реализации эмуляторов для среды разработки OwenLogic, появившиеся благодаря идеям FB100, FB101. Каждый из эмуляторов реализован в двух вариантах - на языках программирования ST и FBD, что связано с неравноценной реализацией отсчёта времени и возможностей вложения полученных ФБ в другие ФБ в среде Owen Logic.<br />
<br />
<b>2 ЭМУЛЯТОР ОБЪЕКТА УПРАВЛЕНИЯ С &quot;НЕПРЕРЫВНЫМ&quot; УПРАВЛЕНИЕМ</b><br />
<br />
<b>2.1 Состав алгоритма</b><br />
<br />
Определимся со структурой алгоритма. ФБ будет состоять из двух частей: инициализация, три последовательных апериодических звена. Для эмуляции помех, добавлен вход, суммирующийся с позицией регулирующего клапана перед апериодическими звеньями.<br />
<br />
Структура эмулятора объекта управления с &quot;непрерывным&quot; регулированием показана на рисунке<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=8965&amp;d=1727633757" rel="Lightbox" id="attachment8965" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=8965&amp;thumb=1&amp;d=1727633757" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: Схема Emulator_Cont_.png
Просмотров: 357
Размер:	2.8 Кб
ID:	8965" style="margin: 5px" /></a><br />
<br />
Инициализация определится по результатам реализации алгоритма интегрирования диференциальных уравнений.<br />
<br />
Для интегрирования дифференциальных уравнений выбираю метод Эйлера по причине простоты реализации и предположительного использования ФБ для эмуляции объектов регулирования с постоянными времени значительно превышающими машинный цикл, т.е. удовлетворительной точности вычислений.<br />
<br />
<b>2.2 Интегрирование дифференциальных уравнений</b><br />
<br />
Для вычисления значения выхода апериодического звена нужно интегрировать дифференциальное уравнение.<br />
<br />

			   <div class="spoiler-wrap">
				   <div class="spoiler-head folded clickable">Вывод формулы для численного интегрирования</div>
				   <div class="spoiler-body">
					   Передаточная функция апериодического звена 1-го порядка в операторной форме<br />
<br />
<img src="https://www.cyberforum.ru/cgi-bin/latex.cgi?W%28p%29%3D%5Cfrac%7BY%28p%29%7D%7BX%28p%29%7D%3D%5Cfrac%7Bk%7D%7BT%5Ccdot%20p%2B1%7D" border="0" alt="https://www.cyberforum.ru/cgi-bin/latex.cgi?W(p)=\frac{Y(p)}{X(p)}=\frac{k}{T\cdot p+1}" /><br />
<br />
Уравнение в операторной форме<br />
<br />
<img src="https://www.cyberforum.ru/cgi-bin/latex.cgi?T%5Ccdot%20p%5Ccdot%20Y%28p%29%2BY%28p%29%3Dk%5Ccdot%20X%28p%29" border="0" alt="https://www.cyberforum.ru/cgi-bin/latex.cgi?T\cdot p\cdot Y(p)+Y(p)=k\cdot X(p)" /><br />
<br />
Дифференциальное уравнение<br />
<br />
<img src="https://www.cyberforum.ru/cgi-bin/latex.cgi?T%5Ccdot%20y%27%28t%29%2By%28t%29%3Dk%5Ccdot%20x%28t%29" border="0" alt="https://www.cyberforum.ru/cgi-bin/latex.cgi?T\cdot y'(t)+y(t)=k\cdot x(t)" /><br />
<br />
<img src="https://www.cyberforum.ru/cgi-bin/latex.cgi?y%27%28t%29%3D%5Cfrac%7B1%7D%7BT%7D%5Cleft%28k%5Ccdot%20x%28t%29-y%28t%29%20%5Cright%29" border="0" alt="https://www.cyberforum.ru/cgi-bin/latex.cgi?y'(t)=\frac{1}{T}\left(k\cdot x(t)-y(t) \right)" /><br />
<br />
Приняв начальные условия нулевыми и решая методом Эйлера получаем значение выхода на каждом цикле вычислений.<br />
Пусть длительность цикла равна h.<br />
<br />
<img src="https://www.cyberforum.ru/cgi-bin/latex.cgi?y_%7Bi%7D%3Dy_%7Bi-1%7D%2Bh%5Ccdot%20%5Cfrac%7B1%7D%7BT%7D%5Cleft%28k%5Ccdot%20x_i%20-y_%7Bi-1%7D%20%5Cright%29" border="0" alt="https://www.cyberforum.ru/cgi-bin/latex.cgi?y_{i}=y_{i-1}+h\cdot \frac{1}{T}\left(k\cdot x_i -y_{i-1} \right)" /><br />
<br />
Подставляя полученное значение y первого звена в качестве параметра x второго звена, и проведя аналогичные вычисления, сможем получить выход из второго звена. Аналогично получим выход из третьего звена.<br />

				   </div>
			   </div><br />
<b>2.2 Реализация эмулятора с &quot;непрерывным&quot; управляющим входом</b><br />
<br />

			   <div class="spoiler-wrap">
				   <div class="spoiler-head folded clickable">На языке ST</div>
				   <div class="spoiler-body">
					   <div class="codeblock"><table class="pascal"><thead><tr><td colspan="2" id="958441066"  class="head">Pascal</td></tr></thead><tbody><tr class="li1"><td><div id="958441066" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
</pre></td><td class="de1"><pre class="de1"><span class="co1">///&lt;Description&gt;Эмулятор объекта управления. Эмулируется последовательное соединение трёх апериодических звеньев 1-го порядка.&lt;/Description&gt;</span>
<span class="co1">///&lt;Author&gt;!!FPA!!&lt;/Author&gt;</span>
<span class="co1">///&lt;GroupName&gt;Управляющие и регулирующие модули&lt;/GroupName&gt;</span>
&nbsp;
function_block Emulator_Cont_ST_
&nbsp;
&nbsp; &nbsp; var_input
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">///&lt;Description&gt;Перезапуск работы блока (инициализация всех переменных состояния) по фронту&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; xReset<span class="sy1">:</span> bool <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">false</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">///&lt;Description&gt;Входная переменная. Управляющий сигнал с ПИД&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rVLV_Pos<span class="sy1">:</span> <span class="kw4">real</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">///&lt;Description&gt;Внешнее возмущающее воздействие (суммируется с rVLV_Pos для вычислений внутри ФБ)&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rDisturbance<span class="sy1">:</span> <span class="kw4">REAL</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">///&lt;Description&gt;Значение выхода при rVLV_Pos=0&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rPV_0<span class="sy1">:</span> <span class="kw4">REAL</span><span class="sy1">:</span><span class="sy3">=</span><span class="nu0">20.0</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">///&lt;Description&gt;Коэффициент передачи объекта управления (трёх последовательных апериодических звеньев 1-го порядка)&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rGain<span class="sy1">:</span> <span class="kw4">REAL</span><span class="sy1">:</span><span class="sy3">=</span><span class="nu0">1.5</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">///&lt;Description&gt;Постоянная времени первого апериодического звена 1-го порядка, [мс]&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; dwTime_T1<span class="sy1">:</span> udint<span class="sy1">:</span><span class="sy3">=</span><span class="nu0">60000</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">///&lt;Description&gt;Постоянная времени второго апериодического звена 1-го порядка, [мс]&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; dwTime_T2<span class="sy1">:</span> udint<span class="sy1">:</span><span class="sy3">=</span><span class="nu0">10000</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">///&lt;Description&gt;Постоянная времени третьего апериодического звена 1-го порядка, [мс]&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; dwTime_T3<span class="sy1">:</span> udint<span class="sy1">:</span><span class="sy3">=</span><span class="nu0">0</span><span class="sy1">;</span>
&nbsp; &nbsp; end_var
&nbsp;
&nbsp; &nbsp; var_output
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">///&lt;Description&gt;Выходное значение переменной процесса&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rPV<span class="sy1">:</span> <span class="kw4">REAL</span><span class="sy1">;</span>
&nbsp; &nbsp; end_var
&nbsp;
&nbsp; &nbsp; <span class="kw1">var</span>
&nbsp; &nbsp; &nbsp; &nbsp; dwCurrent<span class="sy1">,</span> dwCycle<span class="sy1">:</span> udint<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; dwPrevious<span class="sy1">:</span> udint <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bReset_previous<span class="sy1">:</span> bool <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">false</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rCycle<span class="sy1">:</span> <span class="kw4">REAL</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rT1<span class="sy1">:</span> <span class="kw4">REAL</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rT2<span class="sy1">:</span> <span class="kw4">REAL</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rT3<span class="sy1">:</span> <span class="kw4">REAL</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rPV_Prev1<span class="sy1">:</span> <span class="kw4">real</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rPV_Prev2<span class="sy1">:</span> <span class="kw4">real</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rPV_Prev3<span class="sy1">:</span> <span class="kw4">real</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rPV_Inner<span class="sy1">:</span> <span class="kw4">real</span><span class="sy1">;</span>
&nbsp; &nbsp; end_var
&nbsp;
&nbsp; &nbsp; <span class="co1">// вычисление длительности предыдущего цикла</span>
&nbsp; &nbsp; dwCurrent &nbsp;<span class="sy1">:</span><span class="sy3">=</span> time_to_udint<span class="br0">&#40;</span>get_time<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; dwCycle &nbsp; &nbsp;<span class="sy1">:</span><span class="sy3">=</span> dwCurrent <span class="sy3">-</span> dwPrevious<span class="sy1">;</span>
&nbsp; &nbsp; dwPrevious <span class="sy1">:</span><span class="sy3">=</span> dwCurrent<span class="sy1">;</span>
&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>xReset <span class="kw1">and</span> <span class="br0">&#40;</span><span class="kw1">not</span> bReset_previous<span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="kw1">or</span> <span class="br0">&#40;</span>dwCycle <span class="sy3">=</span> <span class="nu0">0</span><span class="br0">&#41;</span> <span class="kw1">then</span>
&nbsp; &nbsp; &nbsp; &nbsp; rPV_Prev1 <span class="sy1">:</span><span class="sy3">=</span> <span class="br0">&#40;</span>rVLV_Pos <span class="sy3">+</span> rDisturbance<span class="br0">&#41;</span> <span class="sy3">*</span> rGain<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rPV_Prev2 <span class="sy1">:</span><span class="sy3">=</span> rPV_Prev1<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rPV_Prev3 <span class="sy1">:</span><span class="sy3">=</span> rPV_Prev1<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rPV <span class="sy1">:</span><span class="sy3">=</span> rPV_Prev1 <span class="sy3">+</span> rPV_0<span class="sy1">;</span>
&nbsp; &nbsp; <span class="kw1">else</span>
&nbsp; &nbsp; &nbsp; &nbsp; rCycle &nbsp;<span class="sy1">:</span><span class="sy3">=</span> udint_to_real<span class="br0">&#40;</span>dwCycle<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rT1 <span class="sy1">:</span><span class="sy3">=</span> udint_to_real<span class="br0">&#40;</span>dwTime_T1<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rT2 <span class="sy1">:</span><span class="sy3">=</span> udint_to_real<span class="br0">&#40;</span>dwTime_T2<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rT3 <span class="sy1">:</span><span class="sy3">=</span> udint_to_real<span class="br0">&#40;</span>dwTime_T3<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// вычисление выхода</span>
&nbsp; &nbsp; &nbsp; &nbsp; rPV_Inner <span class="sy1">:</span><span class="sy3">=</span> <span class="br0">&#40;</span>rVLV_Pos <span class="sy3">+</span> rDisturbance<span class="br0">&#41;</span> <span class="sy3">*</span> rGain<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> rT1 &gt;<span class="sy3">=</span> rCycle <span class="kw1">then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rPV_Inner <span class="sy1">:</span><span class="sy3">=</span> rPV_Prev1 <span class="sy3">+</span> rCycle <span class="sy3">*</span> <span class="br0">&#40;</span>rPV_Inner <span class="sy3">-</span> rPV_Prev1<span class="br0">&#41;</span> <span class="sy3">/</span> rT1<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rPV_Prev1 <span class="sy1">:</span><span class="sy3">=</span> rPV_Inner<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; end_if<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> rT2 &gt;<span class="sy3">=</span> rCycle <span class="kw1">then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rPV_Inner <span class="sy1">:</span><span class="sy3">=</span> rPV_Prev2 <span class="sy3">+</span> rCycle <span class="sy3">*</span> <span class="br0">&#40;</span>rPV_Inner <span class="sy3">-</span> rPV_Prev2<span class="br0">&#41;</span> <span class="sy3">/</span> rT2<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rPV_Prev2 <span class="sy1">:</span><span class="sy3">=</span> rPV_Inner<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; end_if<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> rT3 &gt;<span class="sy3">=</span> rCycle <span class="kw1">then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rPV_Inner <span class="sy1">:</span><span class="sy3">=</span> rPV_Prev3 <span class="sy3">+</span> rCycle <span class="sy3">*</span> <span class="br0">&#40;</span>rPV_Inner <span class="sy3">-</span> rPV_Prev3<span class="br0">&#41;</span> <span class="sy3">/</span> rT3<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rPV_Prev3 <span class="sy1">:</span><span class="sy3">=</span> rPV_Inner<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; end_if<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rPV <span class="sy1">:</span><span class="sy3">=</span> rPV_0 <span class="sy3">+</span> rPV_Inner<span class="sy1">;</span>
&nbsp; &nbsp; end_if<span class="sy1">;</span>
&nbsp;
&nbsp; &nbsp; bReset_previous <span class="sy1">:</span><span class="sy3">=</span> xReset<span class="sy1">;</span>
&nbsp;
end_function_block</pre></td></tr></table></div></td></tr></tbody></table></div>
				   </div>
			   </div><br />
<br />

			   <div class="spoiler-wrap">
				   <div class="spoiler-head folded clickable">На FBD</div>
				   <div class="spoiler-body">
					   <a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=8962&amp;d=1727629559" rel="Lightbox" id="attachment8962" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=8962&amp;thumb=1&amp;d=1727629559" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: Emulator_Cont_(A4 200dpi)_1.png
Просмотров: 460
Размер:	54.4 Кб
ID:	8962" style="margin: 5px" /></a><br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=8963&amp;d=1727629559" rel="Lightbox" id="attachment8963" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=8963&amp;thumb=1&amp;d=1727629559" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: Emulator_Cont_(A4 200dpi)_2.png
Просмотров: 375
Размер:	62.0 Кб
ID:	8963" style="margin: 5px" /></a>
				   </div>
			   </div><br />
<br />

			   <div class="spoiler-wrap">
				   <div class="spoiler-head folded clickable">Тестовая программа</div>
				   <div class="spoiler-body">
					   <a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=8993&amp;d=1728658136" rel="Lightbox" id="attachment8993" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=8993&amp;thumb=1&amp;d=1728658136" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: Test_EMU_Cont_(A4_150dpi).png
Просмотров: 375
Размер:	83.4 Кб
ID:	8993" style="margin: 5px" /></a>
				   </div>
			   </div><br />
<br />
<b>2.3 Описание функционального блока Emulator_Cont_</b><br />
<br />
На входы функционального блока Emulator_Cont_ подаются следующие сигналы<br />
<table cellspacing="1" cellpadding="5" class="stg_table tborder sortable" id="sorttable4311"><thead class="thead"><tr><th>Обозначение</th><th>Тип</th><th>Описание</th></tr></thead><tbody><tr class="alt2"><td><b>xStart</b></td><td><div align="center">BOOL</div></td><td>По переднему фронту входа <code class="inlinecode">xStart</code> произойдёт перезапуск всех переменных состояния.<br /><br />Значение выхода <code class="inlinecode">rPV</code> станет равным установившемуся значению<br /><br /><b>rPV</b> := (<b>rVLV_Pos</b> + <b>rDisturbance</b>) × <b>rGain</b> + <b>rPV_0</b></td></tr><tr class="alt1"><td><b>rVLV_Pos</b></td><td><div align="center">REAL</div></td><td>Входной сигнал от регулятора (требуемое положение регулирующего клапана)</td></tr><tr class="alt2"><td><b>rDisturbance</b></td><td><div align="center">REAL</div></td><td>Значение внешнего возмущающего воздействия (помехи), приведённое к положению клапана.</td></tr><tr class="alt1"><td><b>rPV_0</b></td><td><div align="center">REAL</div></td><td>Значение выхода, которое установится при нулевом сигнале от регулятора (<code class="inlinecode">rVLV_Pos</code>=0).</td></tr><tr class="alt2"><td><b>rGain</b></td><td><div align="center">REAL</div></td><td>Коэффициент передачи объекта управления (трёх последовательных апериодических звеньев 1-го порядка).</td></tr><tr class="alt1"><td><b>dwTime_T1</b></td><td><div align="center">UDINT</div></td><td>Постоянная времени первого апериодического звена 1-го порядка.<br />Единицы измерения: мс.</td></tr><tr class="alt2"><td><b>dwTime_T2</b></td><td><div align="center">UDINT</div></td><td>Постоянная времени второго апериодического звена 1-го порядка.<br />Единицы измерения: мс.</td></tr><tr class="alt1"><td><b>dwTime_T3</b></td><td><div align="center">UDINT</div></td><td>Постоянная времени третьего апериодического звена 1-го порядка.<br />Единицы измерения: мс.</td></tr></tbody></table><br />
С выходов функционального блока Emulator_Cont_ принимаются сигналы<br />
<table cellspacing="1" cellpadding="5" class="stg_table tborder sortable" id="sorttable8678"><thead class="thead"><tr><th>Обозначение</th><th>Тип</th><th>Описание</th></tr></thead><tbody><tr class="alt2"><td><b>rPV</b></td><td><div align="center">REAL</div></td><td>Выходное значение переменной процесса.</td></tr></tbody></table><br />
Для эмуляции внешних помех на вход <code class="inlinecode">rDisturbance</code> подаётся значение, отличное от нулевого. Это значение суммируется с входным положением регулирующего органа и полученная сумма участвует в вычислении переменной процесса. Т.е. установившееся значение переменной процесса стремится к<br />
<b>rPV</b> := (<b>rVLV_Pos</b> + <b>rDisturbance</b>) × <b>rGain</b> + <b>rPV_0</b><br />
<br />
Имеется возможность эмулировать выходные значения различных диапазонов.<br />

			   <div class="spoiler-wrap">
				   <div class="spoiler-head folded clickable">Примеры настройки диапазона переменной процесса</div>
				   <div class="spoiler-body">
					   <u>Пример 1.</u><br />
Диапазон входного сигнала от регулятора (выхода регулятора) от 0 до 100 %.<br />
Соответствующий ему диапазон изменения выхода объекта от 20 до 80 °C.<br />
Для получения требуемого диапазона необходимо задать rGain=0.6, rPV_0=20.<br />
<br />
<u>Пример 2.</u><br />
Диапазон входного сигнала от регулятора (выхода регулятора) от 0 до 100 %.<br />
Соответствующий ему диапазон изменения выхода объекта от 0 до 10000 Па.<br />
Для получения требуемого диапазона необходимо задать rGain=100, rPV_0=0.
				   </div>
			   </div><br />
<br />
<b>3 ЭМУЛЯТОР ОБЪЕКТА УПРАВЛЕНИЯ С ТРЁХПОЗИЦИОННЫМ УПРАВЛЕНИЕМ</b><br />
<br />
<b>3.1 Состав алгоритма</b><br />
<br />
Определимся со структурой алгоритма. ФБ будет состоять из двух частей: эмуляция привода с трёхпозиционным управлением (на вход которого поступают сигналы &quot;открыть&quot; и &quot;закрыть&quot;, а на выходе формируются сигналы положения регулирующего клапана и состояния концевых выключателей &quot;открыт&quot; и &quot;закрыт&quot;) и эмуляция объекта управления с непрерывным управлением. Итого - из четырёх частей: инициализация, пересчёт управляющих импульсов в положение регулирующего органа, эмуляция концевых выключателей &quot;открыт&quot; и &quot;закрыт&quot;, три последовательных апериодических звена.<br />
<br />
Структура эмулятора объекта управления с трёхпозиционным регулированием показана на рисунке<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=8992&amp;d=1728653174" rel="Lightbox" id="attachment8992" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=8992&amp;thumb=1&amp;d=1728653174" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: Схема Emulator_3Pos.png
Просмотров: 332
Размер:	6.1 Кб
ID:	8992" style="margin: 5px" /></a><br />
<br />
Инициализация определится по результатам выбора алгоритмов пересчёта и интегрирования диференциальных уравнений.<br />
Пересчёт управляющих сигналов тоже не должен вызывать трудностей, можно усложнить модель, сделав различными длительности полного открытия и закрытия регулирующего органа.<br />
<br />

			   <div class="spoiler-wrap">
				   <div class="spoiler-head folded clickable">На языке ST</div>
				   <div class="spoiler-body">
					   <div class="codeblock"><table class="pascal"><thead><tr><td colspan="2" id="349699646"  class="head">Pascal</td></tr></thead><tbody><tr class="li1"><td><div id="349699646" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
</pre></td><td class="de1"><pre class="de1"><span class="co1">///&lt;Description&gt;Эмулятор объекта управления. Эмулируется последовательное соединение трёх апериодических звеньев 1-го порядка. Выходной параметр изменяется под воздействием трехпозиционного ПИД регулятора.&lt;/Description&gt;</span>
<span class="co1">///&lt;Author&gt;!!FPA!!&lt;/Author&gt;</span>
<span class="co1">///&lt;GroupName&gt;Управляющие и регулирующие модули&lt;/GroupName&gt;</span>
&nbsp;
function_block Emulator_3Pos_
&nbsp;
&nbsp; &nbsp; var_input
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">///&lt;Description&gt;Перезапуск работы блока (инициализация всех переменных состояния) по фронту&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; xReset<span class="sy1">:</span> bool <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">false</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">///&lt;Description&gt;Сигнал Открыть&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; xOpen<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">///&lt;Description&gt;Сигнал Закрыть&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; xClose<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">///&lt;Description&gt;Время полного хода исполнительного устройства при открытии&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; dwFullStroke_Open<span class="sy1">:</span> udint<span class="sy1">:</span><span class="sy3">=</span><span class="nu0">30000</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">///&lt;Description&gt;Время полного хода исполнительного устройства при закрытии&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; dwFullStroke_Close<span class="sy1">:</span> udint<span class="sy1">:</span><span class="sy3">=</span><span class="nu0">30000</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">///&lt;Description&gt;Время выборки люфта исполнительного устройства при открытии&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; dwVLV_Backlash_Open<span class="sy1">:</span> udint <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">///&lt;Description&gt;Время выборки люфта исполнительного устройства при закрытии&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; dwVLV_Backlash_Close<span class="sy1">:</span> udint <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">///&lt;Description&gt;Верхний предел обратного сигнала клапана. Для клапана с контролем положения&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rVLV_Max<span class="sy1">:</span> <span class="kw4">REAL</span><span class="sy1">:</span><span class="sy3">=</span><span class="nu0">100.0</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">///&lt;Description&gt;Нижний предел обратного сигнала клапана. Для клапана с контролем положения&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rVLV_Min<span class="sy1">:</span> <span class="kw4">REAL</span><span class="sy1">:</span><span class="sy3">=</span><span class="nu0">0.0</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">///&lt;Description&gt;Внешнее возмущающее воздействие (суммируется с rVLV_Pos для вычислений внутри ФБ)&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rDisturbance<span class="sy1">:</span> <span class="kw4">REAL</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">///&lt;Description&gt;Значение выхода при rVLV_Pos=0&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rPV_0<span class="sy1">:</span> <span class="kw4">REAL</span><span class="sy1">:</span><span class="sy3">=</span><span class="nu0">20.0</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">///&lt;Description&gt;Коэффициент передачи объекта управления (трёх последовательных апериодических звеньев 1-го порядка)&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rGain<span class="sy1">:</span> <span class="kw4">REAL</span><span class="sy1">:</span><span class="sy3">=</span><span class="nu0">1.5</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">///&lt;Description&gt;Постоянная времени первого апериодического звена 1-го порядка, [мс]&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; dwTime_T1<span class="sy1">:</span> udint<span class="sy1">:</span><span class="sy3">=</span><span class="nu0">60000</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">///&lt;Description&gt;Постоянная времени второго апериодического звена 1-го порядка, [мс]&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; dwTime_T2<span class="sy1">:</span> udint<span class="sy1">:</span><span class="sy3">=</span><span class="nu0">10000</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">///&lt;Description&gt;Постоянная времени третьего апериодического звена 1-го порядка, [мс]&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; dwTime_T3<span class="sy1">:</span> udint<span class="sy1">:</span><span class="sy3">=</span><span class="nu0">0</span><span class="sy1">;</span>
&nbsp; &nbsp; end_var
&nbsp;
&nbsp; &nbsp; var_output
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">///&lt;Description&gt;Выходное значение переменной процесса&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rPV<span class="sy1">:</span> <span class="kw4">REAL</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">///&lt;Description&gt;Обратный сигнал клапана. Для клапана с контролем положения&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rVLV_Pos<span class="sy1">:</span> <span class="kw4">REAL</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">///&lt;Description&gt;Сигнал верхнего предела клапана&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bVLV_Opened<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">///&lt;Description&gt;Сигнал нижнего предела клапана&lt;/Description&gt;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bVLV_Closed<span class="sy1">:</span> BOOL<span class="sy1">;</span>
&nbsp; &nbsp; end_var
&nbsp;
&nbsp; &nbsp; <span class="kw1">var</span>
&nbsp; &nbsp; &nbsp; &nbsp; dwCurrent<span class="sy1">,</span> dwCycle<span class="sy1">:</span> udint<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; dwPrevious<span class="sy1">:</span> udint <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; bReset_previous<span class="sy1">:</span> bool <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">false</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rCycle<span class="sy1">:</span> <span class="kw4">REAL</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rFullStroke_Open<span class="sy1">:</span> <span class="kw4">REAL</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rFullStroke_Close<span class="sy1">:</span> <span class="kw4">REAL</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rPos<span class="sy1">:</span> <span class="kw4">real</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rT1<span class="sy1">:</span> <span class="kw4">REAL</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rT2<span class="sy1">:</span> <span class="kw4">REAL</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rT3<span class="sy1">:</span> <span class="kw4">REAL</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; h<span class="sy1">:</span> <span class="kw4">real</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rPV_Prev1<span class="sy1">:</span> <span class="kw4">real</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rPV_Prev2<span class="sy1">:</span> <span class="kw4">real</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rPV_Prev3<span class="sy1">:</span> <span class="kw4">real</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rPV_Inner<span class="sy1">:</span> <span class="kw4">real</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; dwVLV_Backlash_Open_<span class="sy1">:</span> udint<span class="sy1">;</span> &nbsp; &nbsp;<span class="co1">// выбранная часть люфта при открытии</span>
&nbsp; &nbsp; &nbsp; &nbsp; dwVLV_Backlash_Close_<span class="sy1">:</span> udint<span class="sy1">;</span> &nbsp; <span class="co1">// выбранная часть люфта при закрытии</span>
&nbsp; &nbsp; &nbsp; &nbsp; dwRunTime<span class="sy1">:</span> udint<span class="sy1">;</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// часть времени dwCycle, потраченная на перемещение</span>
&nbsp; &nbsp; end_var
&nbsp;
&nbsp; &nbsp; <span class="co1">// вычисление времени текущего цикла</span>
&nbsp; &nbsp; dwCurrent &nbsp;<span class="sy1">:</span><span class="sy3">=</span> time_to_udint<span class="br0">&#40;</span>get_time<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; dwCycle &nbsp; &nbsp;<span class="sy1">:</span><span class="sy3">=</span> dwCurrent <span class="sy3">-</span> dwPrevious<span class="sy1">;</span>
&nbsp; &nbsp; dwPrevious <span class="sy1">:</span><span class="sy3">=</span> dwCurrent<span class="sy1">;</span>
&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>xReset <span class="kw1">and</span> <span class="br0">&#40;</span><span class="kw1">not</span> bReset_previous<span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="kw1">or</span> <span class="br0">&#40;</span>dwCycle <span class="sy3">=</span> <span class="nu0">0</span><span class="br0">&#41;</span> <span class="kw1">then</span>
&nbsp; &nbsp; &nbsp; &nbsp; rPV_Prev1 <span class="sy1">:</span><span class="sy3">=</span> <span class="br0">&#40;</span>rVLV_Pos <span class="sy3">-</span> rVLV_Min <span class="sy3">+</span> rDisturbance<span class="br0">&#41;</span> <span class="sy3">*</span> rGain<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rPV_Prev2 <span class="sy1">:</span><span class="sy3">=</span> rPV_Prev1<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rPV_Prev3 <span class="sy1">:</span><span class="sy3">=</span> rPV_Prev1<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rPV <span class="sy1">:</span><span class="sy3">=</span> rPV_Prev1 <span class="sy3">+</span> rPV_0<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; dwVLV_Backlash_Open_ <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; dwVLV_Backlash_Close_ <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">;</span>
&nbsp; &nbsp; <span class="kw1">else</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// проверка ограничений диапазонов входных переменных</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> dwCycle &lt;<span class="sy3">=</span> <span class="nu0">1</span> <span class="kw1">then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; dwCycle <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">1</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; end_if<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> dwFullStroke_Open &lt; dwCycle <span class="kw1">then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; dwFullStroke_Open <span class="sy1">:</span><span class="sy3">=</span> dwCycle<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; end_if<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> dwFullStroke_Close &lt; dwCycle <span class="kw1">then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; dwFullStroke_Close <span class="sy1">:</span><span class="sy3">=</span> dwCycle<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; end_if<span class="sy1">;</span>
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; rCycle &nbsp;<span class="sy1">:</span><span class="sy3">=</span> udint_to_real<span class="br0">&#40;</span>dwCycle<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rT1 <span class="sy1">:</span><span class="sy3">=</span> udint_to_real<span class="br0">&#40;</span>dwTime_T1<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rT2 <span class="sy1">:</span><span class="sy3">=</span> udint_to_real<span class="br0">&#40;</span>dwTime_T2<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rT3 <span class="sy1">:</span><span class="sy3">=</span> udint_to_real<span class="br0">&#40;</span>dwTime_T3<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rFullStroke_Open &nbsp;<span class="sy1">:</span><span class="sy3">=</span> udint_to_real<span class="br0">&#40;</span>dwFullStroke_Open<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rFullStroke_Close <span class="sy1">:</span><span class="sy3">=</span> udint_to_real<span class="br0">&#40;</span>dwFullStroke_Close<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// обработка люфта - часть сигнала перемещения расходуется на компенсацию люфта</span>
&nbsp; &nbsp; &nbsp; &nbsp; dwRunTime <span class="sy1">:</span><span class="sy3">=</span> dwCycle<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> xOpen <span class="kw1">then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> dwRunTime &lt;<span class="sy3">=</span> dwVLV_Backlash_Close_ <span class="kw1">then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; dwVLV_Backlash_Close_ <span class="sy1">:</span><span class="sy3">=</span> dwVLV_Backlash_Close_ <span class="sy3">-</span> dwRunTime<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">else</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; dwVLV_Backlash_Close_ <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; end_if<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> dwVLV_Backlash_Open_ &lt; dwVLV_Backlash_Open <span class="kw1">then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; dwVLV_Backlash_Open_ <span class="sy1">:</span><span class="sy3">=</span> dwVLV_Backlash_Open_ <span class="sy3">+</span> dwRunTime<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> dwVLV_Backlash_Open_ &gt; dwVLV_Backlash_Open <span class="kw1">then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; dwRunTime <span class="sy1">:</span><span class="sy3">=</span> dwVLV_Backlash_Open_ <span class="sy3">-</span> dwVLV_Backlash_Open<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; dwVLV_Backlash_Open_ <span class="sy1">:</span><span class="sy3">=</span> dwVLV_Backlash_Open<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">else</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; dwRunTime <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; end_if<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; end_if<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; end_if<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> xClose <span class="kw1">then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> dwRunTime &lt;<span class="sy3">=</span>dwVLV_Backlash_Open_ <span class="kw1">then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; dwVLV_Backlash_Open_ <span class="sy1">:</span><span class="sy3">=</span> dwVLV_Backlash_Open_ <span class="sy3">-</span> dwRunTime<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">else</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; dwVLV_Backlash_Open_ <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; end_if<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> dwVLV_Backlash_Close_ &lt; dwVLV_Backlash_Close <span class="kw1">then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; dwVLV_Backlash_Close_ <span class="sy1">:</span><span class="sy3">=</span> dwVLV_Backlash_Close_ <span class="sy3">+</span> dwRunTime<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> dwVLV_Backlash_Close_ &gt; dwVLV_Backlash_Close <span class="kw1">then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; dwRunTime <span class="sy1">:</span><span class="sy3">=</span> dwVLV_Backlash_Close_ <span class="sy3">-</span> dwVLV_Backlash_Close<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; dwVLV_Backlash_Close_ <span class="sy1">:</span><span class="sy3">=</span> dwVLV_Backlash_Close<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">else</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; dwRunTime <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; end_if<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; end_if<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; end_if<span class="sy1">;</span>
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// вычисление положения клапана, концевых переключателей</span>
&nbsp; &nbsp; &nbsp; &nbsp; h <span class="sy1">:</span><span class="sy3">=</span> <span class="br0">&#40;</span>rVLV_Max <span class="sy3">-</span> rVLV_Min<span class="br0">&#41;</span> <span class="sy3">*</span> udint_to_real<span class="br0">&#40;</span>dwRunTime<span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> xOpen <span class="kw1">then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rPos <span class="sy1">:</span><span class="sy3">=</span> rVLV_Pos <span class="sy3">+</span> h <span class="sy3">/</span> rFullStroke_Open<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; elsif xClose <span class="kw1">then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rPos <span class="sy1">:</span><span class="sy3">=</span> rVLV_Pos <span class="sy3">-</span> h <span class="sy3">/</span> rFullStroke_Close<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">else</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rPos <span class="sy1">:</span><span class="sy3">=</span> rVLV_Pos<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; end_if<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> rPos &gt;<span class="sy3">=</span> rVLV_Max <span class="kw1">then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rPos <span class="sy1">:</span><span class="sy3">=</span> rVLV_Max<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bVLV_Opened <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">true</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bVLV_Closed <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">false</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; elsif rPos &lt;<span class="sy3">=</span> rVLV_Min <span class="kw1">then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rPos <span class="sy1">:</span><span class="sy3">=</span> rVLV_Min<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bVLV_Opened <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">false</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bVLV_Closed <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">true</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">else</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bVLV_Opened <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">false</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bVLV_Closed <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">false</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; end_if<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rVLV_Pos <span class="sy1">:</span><span class="sy3">=</span> rPos<span class="sy1">;</span>
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// вычисление выхода</span>
&nbsp; &nbsp; &nbsp; &nbsp; rPV_Inner <span class="sy1">:</span><span class="sy3">=</span> <span class="br0">&#40;</span>rVLV_Pos <span class="sy3">-</span> rVLV_Min <span class="sy3">+</span> rDisturbance<span class="br0">&#41;</span> <span class="sy3">*</span> rGain<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> dwTime_T1 &gt;<span class="sy3">=</span> dwCycle <span class="kw1">then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rPV_Inner <span class="sy1">:</span><span class="sy3">=</span> rPV_Prev1 <span class="sy3">+</span> rCycle <span class="sy3">*</span> <span class="br0">&#40;</span>rPV_Inner <span class="sy3">-</span> rPV_Prev1<span class="br0">&#41;</span> <span class="sy3">/</span> rT1<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rPV_Prev1 <span class="sy1">:</span><span class="sy3">=</span> rPV_Inner<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; end_if<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> dwTime_T2 &gt;<span class="sy3">=</span> dwCycle <span class="kw1">then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rPV_Inner <span class="sy1">:</span><span class="sy3">=</span> rPV_Prev2 <span class="sy3">+</span> rCycle <span class="sy3">*</span> <span class="br0">&#40;</span>rPV_Inner <span class="sy3">-</span> rPV_Prev2<span class="br0">&#41;</span> <span class="sy3">/</span> rT2<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rPV_Prev2 <span class="sy1">:</span><span class="sy3">=</span> rPV_Inner<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; end_if<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> dwTime_T3 &gt;<span class="sy3">=</span> dwCycle <span class="kw1">then</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rPV_Inner <span class="sy1">:</span><span class="sy3">=</span> rPV_Prev3 <span class="sy3">+</span> rCycle <span class="sy3">*</span> <span class="br0">&#40;</span>rPV_Inner <span class="sy3">-</span> rPV_Prev3<span class="br0">&#41;</span> <span class="sy3">/</span> rT3<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rPV_Prev3 <span class="sy1">:</span><span class="sy3">=</span> rPV_Inner<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; end_if<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; rPV <span class="sy1">:</span><span class="sy3">=</span> rPV_0 <span class="sy3">+</span> rPV_Inner<span class="sy1">;</span>
&nbsp; &nbsp; end_if<span class="sy1">;</span>
&nbsp;
&nbsp; &nbsp; bReset_previous <span class="sy1">:</span><span class="sy3">=</span> xReset<span class="sy1">;</span>
&nbsp;
end_function_block</pre></td></tr></table></div></td></tr></tbody></table></div>
				   </div>
			   </div><br />
<br />

			   <div class="spoiler-wrap">
				   <div class="spoiler-head folded clickable">Тестовая программа</div>
				   <div class="spoiler-body">
					   <a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=8990&amp;d=1728651637" rel="Lightbox" id="attachment8990" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=8990&amp;thumb=1&amp;d=1728651637" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: Test_EMU_3pos_(A4_150dpi).png
Просмотров: 352
Размер:	70.8 Кб
ID:	8990" style="margin: 5px" /></a>
				   </div>
			   </div><br />
<br />
<b>3.2 Описание функционального блока Emulator_3Pos_</b><br />
<br />
На вход функционального блока Emulator_3Pos_ подаются следующие сигналы<br />
<table cellspacing="1" cellpadding="5" class="stg_table tborder sortable" id="sorttable8810"><thead class="thead"><tr><th>Обозначение</th><th>Тип</th><th>Описание</th></tr></thead><tbody><tr class="alt2"><td><b>xStart</b></td><td><div align="center">BOOL</div></td><td>По переднему фронту входа <code class="inlinecode">xStart</code> произойдёт перезапуск всех переменных состояния.<br /><br />Значение выхода <code class="inlinecode">rPV</code> станет равным установившемуся значению<br /><br /><b>rPV</b> := (<b>rVLV_Pos</b> - <b>rVLV_Min</b> + <b>rDisturbance</b>) × <b>rGain</b> + <b>rPV_0</b><br /><br />При этом, расчётное значение положения регулирующего органа останется без изменения.</td></tr><tr class="alt1"><td><b>xOpen</b></td><td><div align="center">BOOL</div></td><td>Команда &quot;открыть&quot; регулирующий орган от регулятора.</td></tr><tr class="alt2"><td><b>xClose</b></td><td><div align="center">BOOL</div></td><td>Команда &quot;закрыть&quot; регулирующий орган от регулятора.</td></tr><tr class="alt1"><td><b>dwFullStroke_Open</b></td><td><div align="center">UDINT</div></td><td>Время полного хода исполнительного устройства при открытии, мс.</td></tr><tr class="alt2"><td><b>dwFullStroke_Close</b></td><td><div align="center">UDINT</div></td><td>Время полного хода исполнительного устройства при закрытии, мс.</td></tr><tr class="alt1"><td><b>dwVLV_Backlash_Open</b></td><td><div align="center">UDINT</div></td><td>Время выборки люфта исполнительного устройства при открытии, мс.</td></tr><tr class="alt2"><td><b>dwVLV_Backlash_Close</b></td><td><div align="center">UDINT</div></td><td>Время выборки люфта исполнительного устройства при закрытии, мс.</td></tr><tr class="alt1"><td><b>rVLV_Max</b></td><td><div align="center">REAL</div></td><td>Верхний предел обратного сигнала клапана. Для клапана с контролем положения.</td></tr><tr class="alt2"><td><b>rVLV_Min</b></td><td><div align="center">REAL</div></td><td>Нижний предел обратного сигнала клапана. Для клапана с контролем положения.</td></tr><tr class="alt1"><td><b>rDisturbance</b></td><td><div align="center">REAL</div></td><td>Значение внешнего возмущающего воздействия (помехи), приведённое к положению клапана.</td></tr><tr class="alt2"><td><b>rPV_0</b></td><td><div align="center">REAL</div></td><td>Значение выхода, которое установится при нулевом сигнале от регулятора (<code class="inlinecode">rVLV_Pos</code>=<code class="inlinecode">rVLV_Min</code>).</td></tr><tr class="alt1"><td><b>rGain</b></td><td><div align="center">REAL</div></td><td>Коэффициент передачи объекта управления (трёх последовательных апериодических звеньев 1-го порядка).</td></tr><tr class="alt2"><td><b>dwTime_T1</b></td><td><div align="center">UDINT</div></td><td>Постоянная времени первого апериодического звена 1-го порядка.<br />Единицы измерения: мс.</td></tr><tr class="alt1"><td><b>dwTime_T2</b></td><td><div align="center">UDINT</div></td><td>Постоянная времени второго апериодического звена 1-го порядка.<br />Единицы измерения: мс.</td></tr><tr class="alt2"><td><b>dwTime_T3</b></td><td><div align="center">UDINT</div></td><td>Постоянная времени третьего апериодического звена 1-го порядка.<br />Единицы измерения: мс.</td></tr></tbody></table><br />
С выходов функционального блока Emulator_3Pos_ принимаются сигналы<br />
<table cellspacing="1" cellpadding="5" class="stg_table tborder sortable" id="sorttable4414"><thead class="thead"><tr><th>Обозначение</th><th>Тип</th><th>Описание</th></tr></thead><tbody><tr class="alt2"><td><b>rPV</b></td><td><div align="center">REAL</div></td><td>Выходное значение переменной процесса.</td></tr><tr class="alt1"><td><b>rVLV_Pos</b></td><td><div align="center">REAL</div></td><td>Расчётное положение регулирующего клапана.</td></tr><tr class="alt2"><td><b>bVLV_Opened</b></td><td><div align="center">BOOL</div></td><td>Состояние концевого выключателя &quot;открыто&quot;.</td></tr><tr class="alt1"><td><b>bVLV_Closed</b></td><td><div align="center">BOOL</div></td><td>Состояние концевого выключателя &quot;закрыто&quot;.</td></tr></tbody></table><br />
Для эмуляции внешних помех на вход <code class="inlinecode">rDisturbance</code> подаётся значение, отличное от нулевого. Это значение суммируется с расчётным положением регулирующего органа и полученная сумма участвует в вычислении переменной процесса. Т.е. установившееся значение переменной процесса стремится к<br />
<b>rPV</b> := (<b>rVLV_Pos</b> - <b>rVLV_Min</b> + <b>rDisturbance</b>) × <b>rGain</b> + <b>rPV_0</b><br />
<br />
Имеется возможность эмулировать выходные значения различных диапазонов.<br />

			   <div class="spoiler-wrap">
				   <div class="spoiler-head folded clickable">Примеры настройки диапазона переменной процесса</div>
				   <div class="spoiler-body">
					   <u>Пример 1.</u><br />
Диапазон входного сигнала от регулятора (выхода регулятора) от rVLV_Min=0 до rVLV_Max=100 %.<br />
Соответствующий ему диапазон изменения выхода объекта от 20 до 80 °C.<br />
Для получения требуемого диапазона необходимо задать rGain=0.6, rPV_0=20.<br />
<br />
<u>Пример 2.</u><br />
Диапазон входного сигнала от регулятора (выхода регулятора) от rVLV_Min=0 до rVLV_Max=100 %.<br />
Соответствующий ему диапазон изменения выхода объекта от 0 до 10000 Па.<br />
Для получения требуемого диапазона необходимо задать rGain=100, rPV_0=0.
				   </div>
			   </div><br />
<br />
<b>4 ВЫВОДЫ И РЕКОМЕНДАЦИИ</b><br />
<br />
Созданы эмуляторы объектов управления на основе трёх апериодических звеньев 1-го порядка для непрерывного и трёхпозиционного регулирования.<br />
Эмуляторы позволяют экспериментально проверить работоспособность регуляторов на этапе создания программы до выхода на ПНР.<br />
<br />
Алгоритм эмулятора с трёхпозиционным управлением получился достаточно сложным из-за добавившейся модели трёхпозиционного привода, программа на языке FBD оказалась бы излишне трудоёмкой, поэтому решил, что целесообразно реализовать только на ST.<br />
Возможно, если нужда и ограничения среды программирования OwenLogic вынудят - в будущем реализую и на FBD.<br />
<br />
Одной из причин неоправданного усложнения программы на FBD является то, что отсчёт времени в среде программирования OwenLogic очень по разному реализован для языков ST и FBD, и приводит к различиям в реализациях:<ul><li>для ST возможно вызовом функции get_time() получить текущее время, вычислить длительность прошедшего машинного цикла и на основе этой длительности выполнять пересчёт состояний объекта,</li>
<li>для FBD такой возможности нет и приходится период пересчёта реализовывать при помощи генератора BLINK и вычислений только в моменты появления фронта на выходе генератора,</li>
<li>для FBD в режиме эмуляции минимальная длительность периодов в параметрах таймеров и генератора составляет около 100 мс, поэтому пересчёт выполняется с такой периодичностью,</li>
<li>для ST в режиме эмуляции длительность машинного цикла вычисляется и, обычно, составляет 30 мс.</li>
</ul><br />
Полагаю, что указанные ограничения носят хоть и длительный, но всё же временный характер, и настанет время, когда реализации одного алгоритма на FBD будут строже соответствовать.<br />
<br />
Возможно, есть смысл все положения регулирующего органа всегда вычислять в процентах, а если потребуется иные единицы измерения - масштабирование выполнять дополнительными элементами. Это сократит количество входов, переменных и упростит некоторые формулы.<br />
<br />
<b>5 ЛИТЕРАТУРА</b><br />
<br />
1. Примеры программ для технологических функций. A5E00130042-01 (является частью документа 6ES7 398-8FA00-8AA0)<br />
<a rel="nofollow noopener noreferrer" href="https://www.siemens-ru.com/doc/Primeri_programm.pdf" target="_blank" title="https://www.siemens-ru.com/doc/Primeri_programm.pdf">https://www.siemens-ru.com/doc... ogramm.pdf</a><br />
<br />
<b>6 ПРИЛОЖЕНИЕ</b><br />
<br />
Тест эмулятора объекта управления от непрерывного регулятора<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=8994&amp;d=1728658573" >Test_Emu_Cont.owle.7z</a><br />
<br />
Тест эмулятора объекта управления от трёхпозиционного регулятора<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=8995&amp;d=1728669096" >Test_EMU_3pos.owle.7z</a></div>

]]></content:encoded>
			<dc:creator>ФедосеевПавел</dc:creator>
			<guid isPermaLink="true">https://www.cyberforum.ru/blogs/534277/8667.html</guid>
		</item>
		<item>
			<title>Краткие заметки по работе с ПЛК комплекса КОНТАР (МЗТА)</title>
			<link>https://www.cyberforum.ru/blogs/534277/6263.html</link>
			<pubDate>Sun, 17 Mar 2024 14:44:15 GMT</pubDate>
			<description>*Краткие заметки по работе с ПЛК комплекса КОНТАР (МЗТА) 
* 
 
*ПРЕАМБУЛА* 
Эта статья не является...</description>
			<content:encoded><![CDATA[<div><b><div align="center"><font size="+2">Краткие заметки по работе с ПЛК комплекса КОНТАР (МЗТА)</font></div></b><br />
<br />
<b>ПРЕАМБУЛА</b><br />
Эта статья не является учебником, самоучителем по работе с ПЛК комплекса КОНТАР. В статье я отразил ответы на те вопросы, которые при работе не смог найти среди документации и примеров на официальном сайте производителя.<br />
Таким образом, для начала работы следует изучить документацию, приведённую у производителя.<br />
Источники:<br />
<a rel="nofollow noopener noreferrer" href="https://www.mzta.ru" target="_blank" title="https://www.mzta.ru">https://www.mzta.ru</a> — официальный сайт производителя<br />
<a rel="nofollow noopener noreferrer" href="https://www.mzta.ru/produkcziya/vypuskaemaya-produktsiya/24-programmno-tekhnicheskij-kompleks-kontar" target="_blank" title="https://www.mzta.ru/produkcziya/vypuskaemaya-produktsiya/24-programmno-tekhnicheskij-kompleks-kontar">https://www.mzta.ru/produkcziy... eks-kontar</a> — страничка на офсайте, посвящённая комплексу КОНТАР (контроллерам, модулям расширения)<br />
<a rel="nofollow noopener noreferrer" href="https://www.mzta.ru/produkcziya/vypuskaemaya-produktsiya/24-programmno-tekhnicheskij-kompleks-kontar/98-mc8-mc12" target="_blank" title="https://www.mzta.ru/produkcziya/vypuskaemaya-produktsiya/24-programmno-tekhnicheskij-kompleks-kontar/98-mc8-mc12">https://www.mzta.ru/produkcziy... 8-mc8-mc12</a> — страничка на офсайте, посвящённая контроллерам комплекса КОНТАР, на которой присутствуют ссылки для скачивания документации и ПО<br />
Также на офсайте присутствуют примеры решений (как проектные, так и алгоритмические)<br />
<a rel="nofollow noopener noreferrer" href="https://www.mzta.ru/resheniya/gotovye-resheniya" target="_blank" title="https://www.mzta.ru/resheniya/gotovye-resheniya">https://www.mzta.ru/resheniya/gotovye-resheniya</a><br />
<a rel="nofollow noopener noreferrer" href="https://www.mzta.ru/resheniya/algoritmy-dlya-ptk-kontar" target="_blank" title="https://www.mzta.ru/resheniya/algoritmy-dlya-ptk-kontar">https://www.mzta.ru/resheniya/... ptk-kontar</a><br />
Страничка вопросов и ответов<br />
<a rel="nofollow noopener noreferrer" href="https://www.mzta.ru/support-mzta/ptk-kontar-voprosy-i-otvety" target="_blank" title="https://www.mzta.ru/support-mzta/ptk-kontar-voprosy-i-otvety">https://www.mzta.ru/support-mz... y-i-otvety</a><br />
Видеоинструкции и ещё много чего.<br />
Для скачивания некоторых документов и ПО потребуется регистрация на сайте. Эта регистрация позже может пригодится для решения вопросов на форуме.<br />
Также, с вопросами удобно обращаться по телефону напрямую в техподдержку — специалисты очень дружелюбны и грамотны.<br />
<br />
<b>Личные впечатления</b><br />
<br />
Начну с того, что перед началом работы был некоторый трепет от работы с изделием производства МЗТА.<br />
Слово «МЗТА» вызывает только тёплые воспоминания — регулятор Р25, как эталон при создании программного ПИД-регулятора, таинственный ламповый комплекс РПИБ-КПИ, который до сих пор регулирует параметры энергетических котлов во множестве ТЭЦ сахарных заводов.<br />
Собственно, впечатления не омрачились и после знакомства с КОНТАР.<br />
<br />
По проекту автоматизации мне довелось работать с сетью из нескольких контролеров MC8.301 и модуля дискретных выходов MR20.<br />
<br />
По личному впечатлению, ПЛК комплекса КОНТАР предназначены для автоматизации небольших процессов. Этим и обусловлено сравнительно небольшое количество входов и выходов на самих ПЛК. Но «изюминка» комплекса состоит в том, что автоматизация на его основе — именно комплекс из множества ПЛК, связанных в единую сеть. Т.е. каждый ПЛК управляет небольшим участком и все ПЛК сети координируют работу между собой и от «главного» ПЛК.<br />
Кроме самих ПЛК существуют модули расширения к ним — модули аналоговых и дискретных входов/выходов.<br />
<br />
Печатные платы очень аккуратны. Приятно видеть и брать в руки.<br />
<br />
Документация — тоже очень хорошая. Она разделена на отдельные описания контроллера, модулей, программного обеспечения, обучающие примеры с пошаговым созданием программы. Читать — много.<br />
<br />
Коллеги показывали рабочие завершённые проекты, в которых реализовано подобие СКАДА-системы различных объектов на основе бесплатного облачного сервиса МЗТА — мнемосхемы, графики параметров, текущие значения. Доступ к сервису через интернет.<br />
<br />
Подключение операторских панелей имеют особенности, о которых ниже. Поэтому список рекомендованных операторских панелей на страничке МЗТА — отнюдь не реклама.<br />
<br />
Несколько непривычен способ работы — программирование в одном ПО, загрузка в контроллер — в другом. Хотя уже встречался с подобным для вычислителей КРЕЙТ ТЭКОН‑19‑05М. Для этого разделения вижу причины — развязка ПО для программирования от способов загрузки (драйверов и прочего), а также необходимость постоянно подстраиваться под формат списка тегов для ПО операторских панелей (почему не реализовали через плагины — трудно судить).<br />
<br />
Непривычен способ компиляции — отправка исходника на сервер и получение в ответ бинарных файлов для загрузки в ПЛК. Альтернатива, правда, дороже — покупка недешёвого компилятора Keil для микроконтроллеров i8051 — и тогда возможна оффлайн компиляция.<br />
<br />
<b>1. Обновление состава сети после её изменения (добавления или удаления приборов)</b><br />
Наверняка, это отображено где-то в документации. Выделю только потому, что сам запнулся при добавлении ещё одного контроллера в сеть.<br />
Когда программа создаётся для одного ПЛК, то всё просто — она загружается через WebLinker непосредственно в ПЛК, к которому WebLinker и подключён. Когда же загружаются программы в несколько ПЛК сети, потребуется считать состав сети, присвоить номера, согласно проекта. Для обновления, в программе КОНСОЛЬ нужно выбрать пункт контекстного меню «Обновить состав сети», а не кнопку с похожим названием «Считать состав сети», иначе новый прибор не будет виден из программы КОНСОЛЬ. Контекстное меню обновляет и сохраняет таблицу устройств в сети. Кнопка применяется для показа включённых в данный момент приборов из таблицы.<br />
<br />
<b>2. Первая программа (лампочки и кнопочки)</b><br />
<br />
<b>2.1 Программное и аппаратное обеспечение</b><br />
Для комплекса КОНТАР написание программы для ПЛК и её загрузка осуществляются не в единой среде программирования, а в двух, даже порознь скачиваемых, программах.<br />
Написание и компиляция программы для ПЛК осуществляется в ПО КОНГРАФ<br />
<a rel="nofollow noopener noreferrer" href="https://www.mzta.ru/produkcziya/program/kongraf" target="_blank" title="https://www.mzta.ru/produkcziya/program/kongraf">https://www.mzta.ru/produkcziya/program/kongraf</a><br />
Загрузка программы в ПЛК осуществляется ПО КОНСОЛЬ<br />
<a rel="nofollow noopener noreferrer" href="https://www.mzta.ru/produkcziya/program/konsol" target="_blank" title="https://www.mzta.ru/produkcziya/program/konsol">https://www.mzta.ru/produkcziya/program/konsol</a><br />
Для загрузки программы в ПЛК требуется аппаратный «программатор» (на самом деле это не только «программатор», но и Ethernet порт для подключения операторских панелей) — WebLinker<br />
<a rel="nofollow noopener noreferrer" href="https://www.mzta.ru/produkcziya/vypuskaemaya-produktsiya/24-programmno-tekhnicheskij-kompleks-kontar/98-mc8-mc12" target="_blank" title="https://www.mzta.ru/produkcziya/vypuskaemaya-produktsiya/24-programmno-tekhnicheskij-kompleks-kontar/98-mc8-mc12">https://www.mzta.ru/produkcziy... 8-mc8-mc12</a> — на вкладке «Скачать» имеется руководство по эксплуатации WebLinker<br />
WebLinker из себя представляет электронную плату, устанавливаемую в MASTER ПЛК. Устанавливается или временно для программирования или постоянно для программирования и дальнейшего использования портов (тогда отдельно заказывается крышка корпуса с прорезью под разъёмы).<br />
<br />
<b>2.2. Написание и компиляция управляющей программы</b><br />
После установки запускаем программу КОНГРАФ.<br />
По умолчанию путь к проектам КОНГРАФ — «Мои документы»\Kongraf\Projects\.<br />
Для каждого проекта создаётся папка с названием проекта и файл с таким же названием и расширением «*.alg». Например, для проекта «test» в папке «Мои документы»\Kongraf\Projects\ будут созданы папка «test.m» и файл «test.m.alg».<br />
Это знание пригодится для разбора чужих примеров, которые выложены без файла «*.alg». Для доступа к ним можно будет скопировать с переименованием любой имеющийся собственный файл «*.alg» и, открыв его в текстовом редакторе, скорректировать первую строку на соответствующее название проекта.<br />
<br />
На страничке ПО КОНГРАФ во вкладке «Скачать» есть хорошее обучающее руководство «Работа с программой». Начать обучение требуется с него. Так будет проще.<br />
<br />
<b>2.3. Лампочки и кнопочки для одного ПЛК</b><br />
А теперь пробуем сделать самостоятельную программу «лампочки и кнопочки» для комплекса из одного ПЛК.<br />
Создаём новый проект. Выбираем для него контроллер. В свойствах назначаем его MASTER. И внутри контроллера просто рисуем связь от цифрового входа DI[1] к цифровому выходу DO[1].<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=5783&amp;d=1577913844" rel="Lightbox" id="attachment5783" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=5783&amp;thumb=1&amp;d=1577913844" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: L&amp;B_1plc_1.PNG
Просмотров: 504
Размер:	58.4 Кб
ID:	5783" style="margin: 5px" /></a> <a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=5784&amp;d=1577913844" rel="Lightbox" id="attachment5784" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=5784&amp;thumb=1&amp;d=1577913844" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: L&amp;B_1plc_2.PNG
Просмотров: 409
Размер:	45.4 Кб
ID:	5784" style="margin: 5px" /></a><br />
Компилируем.<br />
Теперь нужно загрузить программу в ПЛК. Для этого на плате контроллера устанавливаем перемычку MASTER. Устанавливаем плату WebLinker. Через WebLinker соединяем ПЛК с компьютером. Подаём питание.<br />
Запускаем программу КОНСОЛЬ. Из программы КОНСОЛЬ загружаем программу в ПЛК.<br />
Подключаем ко входу ПЛК DI[1] кнопку, а к выходу DO[1] лампочку.<br />
Теперь можно наблюдать за включением лампочки при удержании нажатой кнопки.<br />
<br />
<b>2.4. Лампочки и кнопочки для комплекса из двух ПЛК, контроль связи</b><br />
Создадим и проверим проект, в котором по нажатию кнопки, подключённой ко входу одного ПЛК, включалась лампа, подключённая к выходу другого ПЛК.<br />
Пусть кнопка подключена ко входу DI[1] ПЛК из условного щита ЩА (ПЛК MASTER), а лампа подключена к выходу DO[1] ПЛК из условного щита ЩУП1 одной из печей.<br />
<br />
На основном холсте располагаем два ПЛК. Конфигурируем один из них как MASTER, другой как SLAVE, назначаем различные номера в сети.<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=5785&amp;d=1577916726" rel="Lightbox" id="attachment5785" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=5785&amp;thumb=1&amp;d=1577916726" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: L&amp;B_2plc_1.PNG
Просмотров: 335
Размер:	65.4 Кб
ID:	5785" style="margin: 5px" /></a> <a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=5786&amp;d=1577916726" rel="Lightbox" id="attachment5786" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=5786&amp;thumb=1&amp;d=1577916726" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: L&amp;B_2plc_2.PNG
Просмотров: 353
Размер:	71.0 Кб
ID:	5786" style="margin: 5px" /></a><br />
Для того, чтобы передать состояние кнопки, в ПЛК «щит ЩА» создаём виртуальный цифровой (дискретный) выход, а в ПЛК «щит ЩУП1» — виртуальный цифровой (дискретный) вход. В обоих ПЛК обозначу эти контакты псевдонимом «Кнопка».<br />
Сразу предусмотрим контроль связи между ПЛК. Для этого среди блоков КОНГРАФ имеются «ПРВ СВЗ» и «ПРВ СВЗ М». Для их соединения добавим виртуальный целый вход и виртуальный целый выход к ПЛК. В обоих ПЛК обозначу эти контакты псевдонимом «LnkTest».<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=5789&amp;d=1577918177" rel="Lightbox" id="attachment5789" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=5789&amp;thumb=1&amp;d=1577918177" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: L&amp;B_2plc_3.PNG
Просмотров: 323
Размер:	5.6 Кб
ID:	5789" style="margin: 5px" /></a><br />
Т.е., в общем случае, для передачи значения от одного ПЛК к другому, для каждого передаваемого значения (переменной) требуется создать отдельный выход на одном ПЛК и отдельный вход на другом. Например, для передачи состояния трёх кнопок от «щит ЩА» в «щит ЩУП1», в ПЛК «щит ЩА» добавляется три виртуальных выхода, в ПЛК «щит ЩУП1» — три виртуальных входа, которые соединяются связями на основном холсте.<br />
<br />
Создаём программы для каждого ПЛК<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=5790&amp;d=1577920330" rel="Lightbox" id="attachment5790" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=5790&amp;thumb=1&amp;d=1577920330" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: L&amp;B_2plc_4.PNG
Просмотров: 343
Размер:	11.9 Кб
ID:	5790" style="margin: 5px" /></a> <a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=5791&amp;d=1577920330" rel="Lightbox" id="attachment5791" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=5791&amp;thumb=1&amp;d=1577920330" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: L&amp;B_2plc_5.PNG
Просмотров: 288
Размер:	11.8 Кб
ID:	5791" style="margin: 5px" /></a><br />
Компилируем. Загружаем в ПЛК. Проверяем.<br />
<br />
<b>3. Использование комплексных блоков, внутренних списков</b><br />
<br />
<b>4. Использование ModBus</b><br />
<br />
Очень обескуражило, что параметры, получаемые из ModBus (holding registers — HR[0…255]) не могут быть энергонезависимыми, что приводит к необходимости отдельного строба для их сохранения.<br />
Другими словами — панель подключённая через ModBus — годиться только для отображения текущих значений, а для ввода параметров процесса полностью непригодна, т.к. при отключении электропитания все параметры сотрутся.<br />
<br />
<b>5. Подключение операторских панелей, зависание и пропуск запросов</b><br />
<br />
<b>5.1 Общее</b><br />
<br />
Выбор операторской панели и её подключение — не очень очевидный момент при проектировании и последующем программировании.<br />
<br />
В самом общем виде к ПЛК можно подключать произвольные операторские панели по протоколу ModBus к интерфейсу RS‑232, RS‑485. Недостатков в этом варианте немного, но их качество приводит к невозможности нормальной работы.<br />
<br />
Но есть и особый вариант, который оказывается более удобным.<br />
Он подразумевает подключение операторской панели от определённых производителей (Beijer Electronics AB, Weintek) по интерфейсам RS‑232, RS‑485, Ethernet ко внутренней сети из ПЛК. Такой панели доступны переменные не только из локального ПЛК, но и из любого ПЛК, входящего в состав сети. Именно этот вариант и является оптимальным.<br />
<br />
<b>5.2 Подключение операторских панелей по ModBus к RS‑232 или RS‑485 (не рекомендую)</b><br />
<br />
К ПЛК можно подключать произвольные операторские панели по интерфейсу RS‑232, RS‑485.<br />
При этом используются протокол ModBus RTU (возможен выбор между MASTER и SLAVE).<br />
Панели доступен локальный ПЛК, к которому непосредственно она и подключена.<br />
<br />
У данного подключения есть только одно обоснование — вам оно досталось от проектировщиков, всё скомплектовано и изменений не будет — отступать некуда.<br />
<br />
Положительный момент только один — подключить так можно любую панель, поддерживающую протокол ModBus Master.<br />
<br />
Дальше перечислю отрицательные стороны этого варианта.<ol style="list-style-type: decimal"><li>Программный блок «MODBUS SLAVE» не имеет у выходов регистров хранения HR свойства «Энергонезависимый», и для сохранения параметров нужно организовывать дополнительный строб записи от панели.</li>
<li>Параметры настройки, которые передаются от панели в ПЛК должны сохраняться в энергонезависимой памяти ПЛК (у выхода блока ставится свойство «Энергонезависимый»), но количество энергонезависимых ячеек в ПЛК ограничено (для MC8 их всего: 128 байт = 64 Modbus регистра = 32 float переменных) и недостаточно даже для маленького проекта.</li>
<li>Панели доступен только локальный ПЛК, к которому непосредственно она и подключена.</li>
</ol><br />
Таким образом, операторская панель превращается в индикатор состояния объекта без возможностей изменения параметров техпроцесса.<br />
<br />

			   <div class="spoiler-wrap">
				   <div class="spoiler-head folded clickable">«Лично мой единичный опыт»</div>
				   <div class="spoiler-body">
					   <br />
Подключал Delta TP04AL2 (ModBus RTU master RS‑232) к MC8.3 (ModBus RTU slave RS‑232).<br />
Получил «букет» проблем.<ol style="list-style-type: decimal"><li>Панели Delta пришлось использовать лишь для отображения текущих значений. Тем более, что аналоговые (они же float=single) с панели Delta TP04AL2 ввести невозможно — вводятся только целые числа — какой-то дефект. Сам ввод с них какой-то странный — не всегда обновляет значение, и ещё разные чудеса.</li>
<li>При обмене между панелью и ПЛК по RS‑485 происходило зависание панели, а ПЛК оставался доступен по интерфейсу. Поэтому осталось использовать дуплексный RS‑232.<br />
Приведу лог обмена по RS‑485 до зависания панели (именно зависания, т.к. панель не только прекратила обмен, но и перестала реагировать на кнопки). Параметры: 9600@8n1, панель считывает два регистра.<br />
Здесь не видно, но сразу после зависания я через ту же прослушку самодельной программкой с компьютера опросил контроллер и получил корректные значения регистров. Т. е. контроллер оставался в работе.<br />
Не могу объяснить причины появления неправильных двух байт перед зависанием — расстояния всех (двух) проводов не превышает (1…1,5)м, хотя согласующий резистор 620 Ом только в преобразователе к компьютеру. Возможно — попытка одновременной передачи контроллера и панели. В сильные электромагнитные помехи в офисе не верю.<br />
Последние 2 секунды перед зависанием (работа длилась около 2 минут):<br />
<div class="codeblock"><table class="unknown"><thead><tr><td colspan="2" id="436750554"  class="head">Code</td></tr></thead><tbody><tr class="li1"><td><div id="436750554" style="height: 222px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
</pre></td><td class="de1"><pre class="de1">000605 <span class="nu0">18</span>:<span class="nu0">47</span>:<span class="nu0">16.463</span> 01 04 00 00 00 01 <span class="nu0">31</span> CA 01 04 00 00 00 01 <span class="nu0">31</span> CA ……<span class="nu0">1</span>К……<span class="nu0">1</span>К
000606 <span class="nu0">18</span>:<span class="nu0">47</span>:<span class="nu0">16.668</span> 01 04 02 03 <span class="nu0">78</span> B9 E2 01 04 00 01 00 01 <span class="nu0">60</span> 0A 01 ….x№в……`..
000607 <span class="nu0">18</span>:<span class="nu0">47</span>:<span class="nu0">16.888</span> 04 00 01 00 01 <span class="nu0">60</span> 0A 01 04 02 00 <span class="nu0">41</span> <span class="nu0">79</span> 00 01 04 …..`…..Ay…
000608 <span class="nu0">18</span>:<span class="nu0">47</span>:<span class="nu0">17.002</span> 00 00 00 01 <span class="nu0">31</span> CA 01 04 00 00 00 01 <span class="nu0">31</span> CA 01 04 ….1К……<span class="nu0">1</span>К..
000609 <span class="nu0">18</span>:<span class="nu0">47</span>:<span class="nu0">17.274</span> 02 03 <span class="nu0">78</span> B9 E2 01 04 00 01 00 01 <span class="nu0">60</span> 0A 01 04 00 ..x№в……`….
000610 <span class="nu0">18</span>:<span class="nu0">47</span>:<span class="nu0">17.422</span> 01 00 01 <span class="nu0">60</span> 0A 01 04 00 01 00 01 <span class="nu0">60</span> 0A 01 04 02 …`…….`….
000611 <span class="nu0">18</span>:<span class="nu0">47</span>:<span class="nu0">17.589</span> 00 <span class="nu0">41</span> <span class="nu0">79</span> 00 01 04 00 00 00 01 <span class="nu0">31</span> CA 01 04 00 00 .Ay…….1К….
000612 <span class="nu0">18</span>:<span class="nu0">47</span>:<span class="nu0">17.740</span> 00 01 <span class="nu0">31</span> CA 01 04 00 00 00 01 <span class="nu0">31</span> CA 01 04 02 03 ..<span class="nu0">1</span>К……<span class="nu0">1</span>К….
000613 <span class="nu0">18</span>:<span class="nu0">47</span>:<span class="nu0">17.879</span> <span class="nu0">78</span> B9 E2 01 04 00 01 00 01 <span class="nu0">60</span> 0A 01 04 00 01 00 x№в……`……
000614 <span class="nu0">18</span>:<span class="nu0">47</span>:<span class="nu0">18.059</span> 01 <span class="nu0">60</span> 0A 01 04 00 01 00 01 <span class="nu0">60</span> 0A 01 04 02 00 <span class="nu0">41</span> .`…….`…..A
000615 <span class="nu0">18</span>:<span class="nu0">47</span>:<span class="nu0">18.195</span> <span class="nu0">79</span> 00 01 04 00 00 00 01 <span class="nu0">31</span> CA 01 04 00 00 00 01 y…….1К……
000616 <span class="nu0">18</span>:<span class="nu0">47</span>:<span class="nu0">18.378</span> <span class="nu0">31</span> CA 01 04 02 03 <span class="nu0">78</span> B9 E2 4C F9 <span class="nu0">1</span>К….x№вLщ</pre></td></tr></table></div></td></tr></tbody></table></div>В логе обмена видны пропуски ответов со стороны контроллера и повторные (бывает до 3-х попыток) запросы от панели.<br />
Это я к тому, что не всё стоит валить на панель. Пропуски ответов со стороны контроллера — очень плохо.<br />
Думаю, что «виноваты» оба устройства:<br />
- ПЛК: пропускает запросы, отвечает лишь на каждый третий (9600@8n1), возможно, подсовывает ответ в момент очередного запроса панели;<br />
- панель: зависает при возникновении ошибки обмена. Ну ошибка, ну подождали 1 секунду и повторили запрос. Но, нет — зависает.</li>
</ol>
				   </div>
			   </div><br />
<br />
<b>5.3 Подключение операторских панелей по Ethernet к внутренней сети ПЛК (рекомендую)</b><br />
<br />
Возможно подключение операторских панелей от определённых производителей (Beijer Electronics AB, Weintek) по интерфейсам RS‑232, RS‑485, Ethernet ко внутренней сети из ПЛК. Такой панели доступны переменные из списков алгоблоков не только из локального ПЛК, но и из любого ПЛК, входящего в состав сети. Подключение имитирует работу программы КОНСОЛЬ на компьютере.<br />
Подключение выполняется через WebLinker, установленном в ПЛК — MASTER сети. На ПЛК SLAVE подключение невозможно.<br />
<br />
Наиболее практичный выбор интерфейса — Ethernet. Позволяет одновременную работу панели и отладки системы с компьютера без перекоммутации. Скорость обмена достаточно высока.<br />
<br />
<b>5.4 Личный опыт работы с операторской панелью Weintek MT8090XE, подключенной по Ethernet к внутренней сети ПЛК</b><br />
<br />
Для работы в данном режиме требуется установка драйвера в панель. Если панель приобреталась в МЗТА, то драйвер уже установлен и активирован. Если панель приобреталась в другом месте — потребуется купить ключ активации драйвера, скачать и установить драйвер на панель и активировать его.<br />
После активации драйвер никуда не исчезает при перезаписи проектов.<br />
<br />
Среда программирования EasyBuilder постоянно совершенствуется, поэтому скриншоты в примере создания проекта для панели не совсем соответствуют внешне, хотя свойства объектов совпадают полностью.<br />
<br />
В одном моменте не до конца разобрался — передача тегов из ПО КОНСОЛЬ в проект для панели.<br />
При создании нового проекта для панели в EasyBuilderPro с применением опции «Использовать шаблон (template_1024x768.empt)» — создаются автоматически и внутренние для панели теги (можно посмотреть по <b>Проект — Адрес — LocalHMI</b>).<br />
Экспортирую теги из проекта для ПЛК при помощи ПО КОНСОЛЬ — <b>Дополнительно — создать файл свойств контролера — Файл тегов для пультов Weintek — Из проекта КОНГРАФ (Pro)</b>.<br />
Теперь добавляю теги из ПЛК к тегам проекта панели кнопкой «Импорт CSV…». И получаю нерабочий проект для панели! Всё потому, что теги ПЛК не добавились, а полностью заменили существовавшие внутренние теги панели.<br />
Чтобы выйти из этой ситуации, пришлось:<ol style="list-style-type: decimal"><li>после создания проекта для панели сразу экспортировать внутренние теги в отдельный файл — «tagsHMI.csv»</li>
<li>при каждом обновлении проекта для ПЛК получать теги — «tagsPLC.csv»</li>
<li>объединять два файла — теги панели и теги ПЛК при помощи пакетного файла<div class="codeblock"><table class="winbatch"><thead><tr><td colspan="2" id="17241835"  class="head">Windows Batch file</td></tr></thead><tbody><tr class="li1"><td><div id="17241835" style="height: 46px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
</pre></td><td class="de1"><pre class="de1"><span class="kw2">copy</span> <span class="co101">/b</span> tagsHMI.csv <span class="sy0">+</span> tagsPLC.csv tags.csv</pre></td></tr></table></div></td></tr></tbody></table></div></li>
<li>объединённый файл «tags.csv» импортировать в проект панели</li>
</ol><br />
Также, мне советовали создавать проект для панели без использования шаблона — тогда проект создаётся без внутренних тегов, с которыми нужно дополнительно возиться при обновлении проекта для ПЛК.<br />
Возможно, это более правильный выход, но я его не проверил — как всегда, работы нужно выполнять срочно и на переделку всего проекта времени уже не было.<br />
<br />
<b>6. Использование ПИД регуляторов</b><br />
<br />
Работа ПИД регуляторов (как с аналоговым выходом, так и для управления трёхпозиционным приводом) оставила приятное впечатление.<br />
Но вот именно программирование имеет ряд особенностей, которые требуют внимательности.<ol style="list-style-type: decimal"><li>после расстановки на полотне всех элементов, выясняется, что часть настроек блока ПИД-регулятора скрыта среди свойств блока и для нормальной работы требуется часть из них заполнить константами в свойствах, а часть подключить к новым входам и сделать видимыми, чтобы иметь возможность корректировать в ходе эксплуатации.<ul><li>параметр ACTION (направление действия) установить константой в зависимости от необходимости</li>
<li>параметр TF (постоянная времени фильтра измеряемой величины) установить константой</li>
<li>параметр DZONE (зона нечувствительности) установить константой</li>
<li>параметр TP (минимальная длительность импульса) установить константой</li>
<li>параметр B (время люфта исполнительного механизма) установить константой</li>
</ul></li>
<li>выходы блока ПИД регулятора подключать напрямую к выходам ПЛК — об этом есть упоминание в справке к блоку</li>
</ol><br />
<b>7. Подключение аналоговых датчиков</b><br />
<br />
Среди примеров рассмотрена обработка аналоговых входов для всех датчиков, кроме имеющих унифицированный токовый сигнал.<br />
Привожу фрагмент программы обработки для токового сигнала (4…20)мА<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=5797&amp;d=1578082676" rel="Lightbox" id="attachment5797" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=5797&amp;thumb=1&amp;d=1578082676" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: AnalogIn_Current.PNG
Просмотров: 663
Размер:	2.6 Кб
ID:	5797" style="margin: 5px" /></a><br />
<br />
<b>8. Подключение аналоговых выходов</b><br />
<br />
Тип выхода — ток или напряжение — определяется перемычками на плате.<br />
Для использования выходного токового сигнала (4…20)мА следует изменять выход от 20% до 100%.</div>

]]></content:encoded>
			<dc:creator>ФедосеевПавел</dc:creator>
			<guid isPermaLink="true">https://www.cyberforum.ru/blogs/534277/6263.html</guid>
		</item>
		<item>
			<title>OwenLogic: ПИ (ПИД) регулятор для управления трёхпозиционным приводом без обратной связи</title>
			<link>https://www.cyberforum.ru/blogs/534277/8438.html</link>
			<pubDate>Sat, 24 Feb 2024 09:38:27 GMT</pubDate>
			<description>*OwenLogic: ПИ (ПИД) регулятор для управления трёхпозиционным приводом без обратной связи 
* 
 
*1....</description>
			<content:encoded><![CDATA[<div><b><font size="+2"><div align="center">OwenLogic: ПИ (ПИД) регулятор для управления трёхпозиционным приводом без обратной связи</div></font></b><br />
<br />
<b>1. ВВЕДЕНИЕ</b><br />
<br />
В составе компонентов OwenLogic отсутствует готовый функциональный блок ПИД регулятора для управления трёхпозиционным приводом без обратной связи.<br />
<br />
Жил я с этим долго и счастливо, но от судьбы не уйдёшь…<br />
<br />
При реализации немного ошибся, сделал лишнее дифференцирование, усложнил вычисления, но из-за сроков сдачи уже не успел исправить. Это повлекло отказ от дифференциальной составляющей и упростило закон управления до ПИ.<br />
Но в остальном алгоритм остался корректным. Одно успокаивает, в моём конкретном случае техпроцесс инерционный и вклад дифференциальной компоненты был бы незначительным на фоне других слагаемых.<br />
<br />
Во время отладки заметил, что если не ввести корректные значения параметров, то регулятор даже не начинает работать. Вспомнил о безопасном программировании и предложениях PLCopen по написанию безопасного кода. Добавил диагностику некоторых ошибок, вывод на отдельный выход результата диагностики, блокировку вычислений. Ещё добавить бы детализацию состояния, но опять же — сроки поджимали...<br />
<br />
<b>2. АЛГОРИТМ</b><br />
<br />
<b>2.1. ОПИСАНИЕ АЛГОРИТМА</b><br />
<br />
Алгоритм функционально разделяется на три части:<ul><li>вычисление необходимого воздействия по ПИД закону регулирования,</li>
<li>формирование управляющих сигналов для привода (направление, длительность),</li>
<li>выявление и обработка нештатных ситуаций.</li>
</ul><br />
Введём обозначения и определим физический и количественный смысл.<br />
<br />
Привод регулирующего органа характеризуется и описывается параметрами:<ul><li><b>Tfs</b> — время полного хода (full stroke);</li>
<li><b>Tmin</b> — длительность минимального импульса;</li>
<li><b>Trev</b> — минимальная длительность паузы между командами реверса;</li>
<li><b>Tluft</b> — длительность импульса для компенсации люфта.</li>
</ul><br />
ПИД регулятор характеризуется и описывается параметрами:<ul><li><b>Kp</b> — коэффициент пропорциональности, значение полосы пропорциональности <b>Xp=1/Kp</b> равно величине невязки (рассогласования) при котором регулятор сформирует импульс длительностью равный времени полного хода привода;</li>
<li><b>Ti</b> — время интегрирования;</li>
<li><b>Td</b> — время дифференцирования;</li>
<li><b>BIAS</b> — накопленная интегральная составляющая;</li>
<li><b>Y</b> — значение выхода ПИД регулятора, безразмерная величина в диапазоне от (0,0…1,0) пропорциональная положению регулирующего органа (0,0…1,0);</li>
<li><b>dY</b> — приращение выхода ПИД регулятора по отношению к значению на предыдущем пересчёте, безразмерная величина (-1,0…+1,0) по абсолютному значению пропорциональная времени полного хода — требуемое перемещение регулирующего органа;</li>
<li><b>SP</b> — уставка (задание) процесса (setpoint);</li>
<li><b>PV</b> — переменная процесса (process variable);</li>
<li><b>DB</b> — зона нечувствительности (dead band);</li>
<li><b>E</b> — значение невязки, рассогласования (<b>E=SP-PV</b>);</li>
<li><b>Tm</b> — период пересчёта (квантования) значения выхода регулятора.</li>
</ul><br />
Зона нечувствительности действует в каждом из направлений, таким образом, реальная зона нечувствительности в два раза больше.<br />
С учётом зоны нечувствительности (<b>DB</b>) значение рассогласования (<b>E</b>) вычисляется по формуле<br />
<br />
<img src="https://www.cyberforum.ru/cgi-bin/latex.cgi?E%3D%5Cbegin%7Bcases%7DSP-PV%2BDB%20%26amp%3B%20%5Ctext%7B%2C%20if%20%7D%20%28SP-PV%29%20%26lt%3B%20-DB%20%20%5C%5C%200%20%26amp%3B%20%5Ctext%7B%2C%20if%20%7D%20-DB%20%5Cleq%20%28SP-PV%29%20%5Cleq%20%2BDB%20%20%5C%5C%20SP-PV-DB%20%26amp%3B%20%5Ctext%7B%2C%20if%20%7D%20%28SP-PV%29%20%26gt%3B%20%2BDB%20%20%5Cend%7Bcases%7D" border="0" alt="https://www.cyberforum.ru/cgi-bin/latex.cgi?E=\begin{cases}SP-PV+DB &amp; \text{, if } (SP-PV) &lt; -DB  \\ 0 &amp; \text{, if } -DB \leq (SP-PV) \leq +DB  \\ SP-PV-DB &amp; \text{, if } (SP-PV) &gt; +DB  \end{cases}" /><br />
<br />
и иллюстрируется изображением<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=8523&amp;d=1708534934" rel="Lightbox" id="attachment8523" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=8523&amp;thumb=1&amp;d=1708534934" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: DeadBand.png
Просмотров: 284
Размер:	1.4 Кб
ID:	8523" style="margin: 5px" /></a><br />
<b>2.2. ВЫЧИСЛЕНИЕ НЕОБХОДИМОГО ВОЗДЕЙСТВИЯ ПО ПИД ЗАКОНУ РЕГУЛИРОВАНИЯ</b><br />
<br />
Выражение в конечных разностях<br />
<br />
<img src="https://www.cyberforum.ru/cgi-bin/latex.cgi?Y_%7Bn%7D%3DKp%5Ccdot%20E_n%2B%5Cfrac%7BTm%7D%7BTi%7D%5Csum_%7Bi%3D1%7D%5E%7Bn%7D%7BE_i%7D%2BTd%5Ccdot%20%5Cfrac%7B%5CDelta%20E_n%7D%7BTm%7D" border="0" alt="https://www.cyberforum.ru/cgi-bin/latex.cgi?Y_{n}=Kp\cdot E_n+\frac{Tm}{Ti}\sum_{i=1}^{n}{E_i}+Td\cdot \frac{\Delta E_n}{Tm}" /><br />
<br />
Т.к. привод интегрирует, то для получения прироста положения привода дифференцируем выражение<br />
<br />
<img src="https://www.cyberforum.ru/cgi-bin/latex.cgi?dY_%7Bn%7D%3D%5Cfrac%7BY_%7Bn%7D-Y_%7Bn-1%7D%7D%7BTm%7D%3DKp%5Ccdot%20%5Cfrac%7B%5CDelta%20E_n%7D%7BTm%7D%2B%5Cfrac%7B1%7D%7BTi%7DE_%7Bn%7D%2BTd%5Ccdot%20%5Cfrac%7B%7B%5CDelta%7D%5E2%20E_n%7D%7B%28Tm%29%5E2%7D" border="0" alt="https://www.cyberforum.ru/cgi-bin/latex.cgi?dY_{n}=\frac{Y_{n}-Y_{n-1}}{Tm}=Kp\cdot \frac{\Delta E_n}{Tm}+\frac{1}{Ti}E_{n}+Td\cdot \frac{{\Delta}^2 E_n}{(Tm)^2}" /><br />
<br />
Для упрощения вычислений отбрасываем вторую производную (конечную разность второго порядка)<br />
<br />
<img src="https://www.cyberforum.ru/cgi-bin/latex.cgi?dY_%7Bn%7D%3DKp%5Ccdot%20%5Cfrac%7B%5CDelta%20E_n%7D%7BTm%7D%2B%5Cfrac%7B1%7D%7BTi%7DE_%7Bn%7D" border="0" alt="https://www.cyberforum.ru/cgi-bin/latex.cgi?dY_{n}=Kp\cdot \frac{\Delta E_n}{Tm}+\frac{1}{Ti}E_{n}" /><br />
<br />
Получили требуемое приращение положения привода между моментами пересчёта. Нужно учесть, что за период пересчёта привод может не успеть переместиться в заданное положение, поэтому введём положение суммарного перемещения привода, которое будет равно сумме приращений<br />
<br />
<img src="https://www.cyberforum.ru/cgi-bin/latex.cgi?SY_n%3DSY_%7Bn-1%7D%2BdY_%7Bn%7D%3DSY_%7Bn-1%7D%2BKp%5Ccdot%20%5Cfrac%7B%5CDelta%20E_n%7D%7BTm%7D%2B%5Cfrac%7B1%7D%7BTi%7DE_%7Bn%7D" border="0" alt="https://www.cyberforum.ru/cgi-bin/latex.cgi?SY_n=SY_{n-1}+dY_{n}=SY_{n-1}+Kp\cdot \frac{\Delta E_n}{Tm}+\frac{1}{Ti}E_{n}" /><br />
<br />
В итоге получаем<br />
<br />
<img src="https://www.cyberforum.ru/cgi-bin/latex.cgi?SY_n%3DSY_%7Bn-1%7D%2BKp%5Ccdot%20%5Cfrac%7B%5CDelta%20E_n%7D%7BTm%7D%2B%5Cfrac%7B1%7D%7BTi%7DE_%7Bn%7D" border="0" alt="https://www.cyberforum.ru/cgi-bin/latex.cgi?SY_n=SY_{n-1}+Kp\cdot \frac{\Delta E_n}{Tm}+\frac{1}{Ti}E_{n}" /><br />
<br />
Также, для предотвращения взвинчивания интегральной составляющей ограничим значение суммарного перемещения превышением на 20%, т.е. диапазоном (-1,2…+1,2).<br />
<br />
<b>2.3. АЛГОРИТМ ФОРМИРОВАНИЯ УПРАВЛЯЮЩИХ СИГНАЛОВ ДЛЯ ПРИВОДА</b><br />
<br />
Можно сформулировать аналогию алгоритма. Вычислительный алгоритм по ПИД закону в момент пересчёта заряжает конденсатор. После заряда более некоторого порогового значения напряжения (соответствующего <b>Tmin</b>) срабатывает триггер разрешающий управляющий импульс и через некоторое сопротивление (соответствующее <b>Tfs</b>) производится постепенный разряд конденсатора. Разряд прекращается (триггер сбрасывается) после полного разряда конденсатора.<br />
<br />
Произведём формирование численных критериев.<br />
<br />
Привод должен переместиться на величину <b>SY</b> — т.е. требуется сформировать импульс длительностью<br />
<br />
<img src="https://www.cyberforum.ru/cgi-bin/latex.cgi?T%3DSY%5Ccdot%20Tfs" border="0" alt="https://www.cyberforum.ru/cgi-bin/latex.cgi?T=SY\cdot Tfs" /><br />
<br />
Т.к. величина <b>SY</b> за период формирования импульса может несколько раз пересчитываться и значительно меняться, то разумным будет формировать его из множества коротких тактовых импульсов, сливающихся в единый импульс. После окончания такта из <b>SY</b> вычитать значение, равное отношению длительности такта ко времени полного хода:<br />
<br />
величина приращения положения привода за время одного такта<br />
<br />
<img src="https://www.cyberforum.ru/cgi-bin/latex.cgi?Ytact%3D%5Cfrac%7BTtact%7D%7BTfs%7D" border="0" alt="https://www.cyberforum.ru/cgi-bin/latex.cgi?Ytact=\frac{Ttact}{Tfs}" /><br />
<br />
изменение суммарного перемещения после одного такта формирования управляющего импульса<br />
<br />
<img src="https://www.cyberforum.ru/cgi-bin/latex.cgi?SY_t%3DSY_%7Bt-1%7D-Ytact" border="0" alt="https://www.cyberforum.ru/cgi-bin/latex.cgi?SY_t=SY_{t-1}-Ytact" /><br />
<br />
Таким образом, серия тактовых импульсов будет длиться пока расчётное суммарное перемещение превышает значение приращения за один такт<br />
<br />
<img src="https://www.cyberforum.ru/cgi-bin/latex.cgi?SY%20%26gt%3B%20Ytact" border="0" alt="https://www.cyberforum.ru/cgi-bin/latex.cgi?SY &gt; Ytact" /><br />
<br />
Нужно ещё учесть, что управляющий импульс не может быть меньше определённой величины, т.к. для включения реле, выборки небольших люфтов привода требуется некоторое время — длительность минимального импульса <b>Tmin</b>. Обычно, его значение находится в диапазоне (200…500) мс.<br />
Поэтому первый импульс не может быть короче заданной величины <b>Tmin</b>.<br />
<br />
<b>2.4. ВЫЯВЛЕНИЕ И ОБРАБОТКА НЕШТАТНЫХ СИТУАЦИЙ</b><br />
<br />
Очевидно, что при некоторых значениях параметров регулятора и привода управляющие импульсы будут некорректными.<br />
<br />
На мой взгляд, в таких случаях правильным решением будет формирование сигнала ошибки на дополнительном выходе функционального блока и блокировка всех вычислений.<br />
<br />
Пока проверяю всего несколько условий:<ul><li>период пересчёта не менее 1000 мс,</li>
<li>время полного хода не менее 1000 мс,</li>
<li>длительность минимального импульса не менее 200 мс.</li>
</ul>В принципе, можно добавить проверки на неотрицательность fKp, fTi, fDeadBand...<br />
<br />
<b>3. РЕАЛИЗАЦИЯ</b><br />
<br />
Алгоритм реализован в виде функционального блока на языке FBD, т.к. на настоящий момент язык ST имеет ограничения, препятствующие полноценному использованию (самое главное — невозможность встраивать ФБ на ST внутрь других ФБ).<br />
<br />
Входные параметры<br />
<table cellspacing="1" cellpadding="5" class="stg_table tborder sortable" id="sorttable5377"><thead class="thead"><tr><th>Вход</th><th>Тип</th><th>Описание</th></tr></thead><tbody><tr class="alt2"><td><b>fPV</b></td><td><div align="center">с плавающей запятой</div></td><td>переменная процесса</td></tr><tr class="alt1"><td><b>fSP</b></td><td><div align="center">с плавающей запятой</div></td><td>уставка</td></tr><tr class="alt2"><td><b>fDeadBand</b></td><td><div align="center">с плавающей запятой</div></td><td>зона нечувствительности</td></tr><tr class="alt1"><td><b>nTRecalc_[ms]</b></td><td><div align="center">целочисленный</div></td><td>период пересчёта значения выхода ПИД регулятора<br /><br />должно быть не менее 1000 мс</td></tr><tr class="alt2"><td><b>nTFullStroke_[ms]</b></td><td><div align="center">целочисленный</div></td><td>время полного хода привода<br /><br />должно быть не менее 1000 мс</td></tr><tr class="alt1"><td><b>nTPulseMin_[ms]</b></td><td><div align="center">целочисленный</div></td><td>минимальная длительность импульса привода<br /><br />должно быть не менее 200 мс</td></tr><tr class="alt2"><td><b>fKp</b></td><td><div align="center">с плавающей запятой</div></td><td>коэффициент пропорциональности<br /><br />значение полосы пропорциональности <b>Xp=1/Kp</b> равно величине невязки (рассогласования) при котором регулятор сформирует импульс длительностью равный времени полного хода привода</td></tr><tr class="alt1"><td><b>fTi</b></td><td><div align="center">с плавающей запятой</div></td><td>время интегрирования<br /><br />При значении менее 0,001 с вычисление интегральной составляющей отключается</td></tr><tr class="alt2"><td><b>fTd</b></td><td><div align="center">с плавающей запятой</div></td><td>время дифференцирования (не используется)</td></tr><tr class="alt1"><td><b>bEnable</b></td><td><div align="center">булевский</div></td><td>разрешение работы регулятора<br /><br />0 — остановить,<br />1 — разрешить</td></tr><tr class="alt2"><td><b>bReset</b></td><td><div align="center">булевский</div></td><td>сброс и останов работы регулятора<br /><br />0 — работа,<br />1 — сброс и останов</td></tr><tr class="alt1"><td><b>nTLuft_[ms]</b></td><td><div align="center">целочисленный</div></td><td>длительность компенсации люфта (не используется)</td></tr><tr class="alt2"><td><b>nTPauseReverse_[ms]</b></td><td><div align="center">целочисленный</div></td><td>длительность паузы перед реверсом</td></tr></tbody></table><br />
Выходные параметры<br />
<table cellspacing="1" cellpadding="5" class="stg_table tborder sortable" id="sorttable7008"><thead class="thead"><tr><th>Выход</th><th>Тип</th><th>Описание</th></tr></thead><tbody><tr class="alt2"><td><b>bOpen</b></td><td><div align="center">булевский</div></td><td>открывать регулирующий орган</td></tr><tr class="alt1"><td><b>bClose</b></td><td><div align="center">булевский</div></td><td>закрывать регулирующий орган</td></tr><tr class="alt2"><td><b>bError</b></td><td><div align="center">булевский</div></td><td>параметры регулятора содержат ошибку, регулятор остановлен</td></tr></tbody></table><br />
Т.к. алгоритм несложный, то и программа получилась простой.<br />
Вычисление приращения<br />
Сложение суммарного перемещения и приращения, а также вычитание за счёт перемещения по формуле в стиле языка &quot;C&quot; с тернарными операциями<br />
<div class="codeblock"><table class="c"><thead><tr><td colspan="2" id="95052254"  class="head">C</td></tr></thead><tbody><tr class="li1"><td><div id="95052254" style="height: 46px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
</pre></td><td class="de1"><pre class="de1">SY <span class="sy0">:=</span> SY <span class="sy0">+</span> <span class="br0">&#40;</span>bRecalc<span class="sy0">?</span> fY<span class="sy0">:</span> <span class="nu0">0</span><span class="br0">&#41;</span> <span class="sy0">+</span> <span class="br0">&#40;</span>bTact<span class="sy0">?</span> <span class="br0">&#40;</span>bClose <span class="sy0">-</span> bOpen<span class="br0">&#41;</span><span class="sy0">:</span> <span class="nu0">0</span><span class="br0">&#41;</span> <span class="sy0">*</span> fYTact</pre></td></tr></table></div></td></tr></tbody></table></div>Условие начала открытия:<ul><li>расчётное значение суммарного перемещения превышает пропорциональное времени минимального импульса,</li>
<li>расчётное значение суммарного перемещения положительное,</li>
<li>движение в этом направлении не ограничивается паузой для разрешения реверса.</li>
</ul><br />
Для закрытия условие реализуется подобным же образом, но суммарное перемещение должно быть отрицательным.<br />
<br />
Останов перемещения при<ul><li>работа регулятора запрещена,</li>
<li>расчётное значение суммарного перемещения меньше значения, пропорционального времени одного такта для пересчёта (принято 100 мс) и с момента начала перемещения истекло время минимального импульса.</li>
</ul><br />
Разрешение реверса — разрешение открытия после истечения паузы. Поэтому реализация на таймерах задержки выключения.<br />
<br />
<b>4. ПРИМЕР ИСПОЛЬЗОВАНИЯ</b><br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=8524&amp;d=1708537326" rel="Lightbox" id="attachment8524" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=8524&amp;thumb=1&amp;d=1708537326" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: sample.png
Просмотров: 1451
Размер:	21.0 Кб
ID:	8524" style="margin: 5px" /></a><br />
<br />
<b>ВЫВОДЫ И РЕКОМЕНДАЦИИ</b><br />
<br />
Созданный функциональный блок ПИ (ПИД) регулятора работоспособен и прошёл успешную проверку на регуляторе теплосети после гидравлического разделителя — время полного хода смесительного клапана 60 с, сам процесс со стороны возмущений (температура теплоносителя от котлов, температура наружного воздуха) достаточно инерционный.<br />
<br />
Можно улучшить ФБ добавлением:<ul><li>компенсации люфта,</li>
<li>дополнительного сглаживание переменной процесса,</li>
<li>введением времени минимальной паузы между импульсами одного направления,</li>
<li>отдельного выхода диагностического кода состояния,</li>
<li>переключателем режима нагреватель-холодильник.</li>
</ul><br />
Также можно выполнить замену некоторой части соединительных линий на линии обратной связи (задержки на 1 цикл), что избавит от предупреждений о циклических связях.<br />
<br />
Создавая данный ФБ оказался в плену старых наработок — преобразования выхода аналогового регулятора (0...100) % в перемещение привода преобразуя приращения выхода регулятора пропорционально времени полного хода.<br />
Но уже после реализации, проверки и сдачи программы понял, что суммируя <b>SY</b> интегрирую выражение <b>dY</b>, т.е. можно было не дифференцировать выход регулятора <b>Y</b>, оставить дифференциальную составляющую.<br />
<br />
Реализовал некоторую диагностику ошибок. Хорошо бы ещё добавить выход DiagCode, на который выводить код состояния макроса (функционального блока), в идеале — ещё и по рекомендациям PLCopen.<br />
<br />
Указанные недостатки не влияют на работоспособность ФБ, но их устранение повысит функциональное удобство.<br />
<br />
Уже после отладки появилась обновлённая версия среды разработки OwenLogic, в которой доступно применение таймеров в коде на ST и другие улучшения. Возможно, есть смысл переписать программу на ST.<br />
<br />
Сейчас нет возможности внедрить и проверить перечисленные улучшения, поэтому буду ждать другого случая.<br />
<br />
<b>[09/03/2025] Дополнение</b><br />
<br />
Добавил демонстрационную программу с использованием ПИ регулятора и эмулятора объекта управления.<br />
<br />
В новом проекте внёс несущественные изменения в макрос (функциональный блок) ПИ регулятора:<ul><li>изменил ограничение взвинчивания интегральной с 20% на 0%</li>
<li>удалил вход bReset</li>
<li>добавил комментарии к переменным, входам и выходам макроса</li>
</ul><br />
Хочу немного прокомментировать результаты эмуляции - подбор параметров регулятора.<br />
<br />
При выбранной формуле регулятора с независимыми друг от друга коэффициентами Kp и Ti, сами они зависят от диапазона изменения (в физических единицах) регулируемой величины. Получается размерность fKp - 1/[EU], а размерность fTi - [EU]/[s], где [EU] - размерность переменной процесса в физических единицах.<br />
Поэтому достаточно корректно в документации эти параметры иногда называются пропорциональным и интегральным коэффициентами - Ti является не временем, а именно коэффициентом.<br />
<br />
Подобрать fKp просто - примерно в 6 раз меньше требуемого отклонения при регулировании Kp=1/(6*отклонение).<br />
Подобрать Ti - сложнее из-за влияния размерности физических единиц переменной процесса. Поэтому значение параметра fTi для наладчика (и для настройки поля ввода в HMI) может оказаться неожиданно большим или наоборот маленьким. Например, в тестовом примере получены значения fKp=0.0012 и fTi=65000. Но если привести fTi к размерности только времени, умножив на fKp, то получим значение (fTi ⋅ fKp=65000 ⋅ 0,0012=78 с) сопоставимое с максимальным временем апериодического звена модели объекта (150000 мс = 150 с).<br />
<br />
Т.е. при наладке нужно учитывать это неявное преобразование и понимать, что масштаб значений будет отличаться от ожиданий.</div>


<!-- attachments -->
	<div style="margin-top:10px">

		
		
		
		
			<fieldset class="fieldset">
				<legend>Вложения</legend>
				<table cellpadding="0" cellspacing="3" border="0">
				<tr>
	<td><img loading="lazy" decoding="async" class="inlineimg" src="http://www.cyberforum.ru//cyberstatic.net/images/attach/7z.gif" alt="Тип файла: 7z" width="16" height="16" border="0" style="vertical-align:baseline" /></td>
	<td><a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=8525&amp;d=1708538314">test_PID_3pos.owle.7z</a> (192.4 Кб, 1960 просмотров)</td>
</tr><tr>
	<td><img loading="lazy" decoding="async" class="inlineimg" src="http://www.cyberforum.ru//cyberstatic.net/images/attach/7z.gif" alt="Тип файла: 7z" width="16" height="16" border="0" style="vertical-align:baseline" /></td>
	<td><a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=10349&amp;d=1741520784">Test_EMU_3pos.owle.7z</a> (232.6 Кб, 372 просмотров)</td>
</tr>
				</table>
			</fieldset>
		

	</div>
<!-- / attachments -->
]]></content:encoded>
			<dc:creator>ФедосеевПавел</dc:creator>
			<guid isPermaLink="true">https://www.cyberforum.ru/blogs/534277/8438.html</guid>
		</item>
		<item>
			<title>Утилита формирования протокола настройки автоматики из образа памяти ПЛК DirectLogic DL06</title>
			<link>https://www.cyberforum.ru/blogs/534277/7598.html</link>
			<pubDate>Sat, 24 Feb 2024 07:37:42 GMT</pubDate>
			<description>*1. ВВЕДЕНИЕ* 
 
Возможно, что эта запись несколько несвоевременна, т.к. линейке ПЛК DirectLOGIC...</description>
			<content:encoded><![CDATA[<div><b>1. ВВЕДЕНИЕ</b><br />
<br />
Возможно, что эта запись несколько несвоевременна, т.к. линейке ПЛК DirectLOGIC уже около 25 лет от начала выпуска. И ценность предложенной утилиты будет весьма условной.<br />
<br />
Создал программу VMemory для формирования протокола параметров настройки системы автоматики на основе ПЛК DL06 и DL260 из образа памяти, сохранённого на диск.<br />
<br />
Программа востребована при формировании протоколов настройки к отчёту о ПНР или РНИ и во время консультации по телефону при необходимости восстановления каких либо настроек, которые были упущены в отчёте, но сохранились в образе памяти.<br />
<br />
Программа работает совместно с утилитой производителя DNLoader, предназначенной для создания файла образа памяти ПЛК.<br />
<br />
Кроме образа памяти ПЛК, требуется список переменных (тегов): тип, адрес в памяти данных ПЛК и описание назначения. Теги можно получить или от программиста или анализом исходных кодов ПЛК и операторской панели, которые, чаще всего, можно скачать из приборов.<br />
Т.е. <u>для формирования списка тегов <b>обязательно</b> потребуется помощь программиста</u>. И только после получения списка, программой могут воспользоваться все заинтересованные.<br />
<br />
Обработка тегов корректно будет работать и для DL06 и для DL260.<br />
Работа макросов проверялась только для DL06. Для DL260 макрос, формирующий отчёт о настройках порта - игнорируется.<br />
<br />
<b>2. ИСТОРИЯ ПОЯВЛЕНИЯ</b><br />
<br />
Идея появления утилиты возникла в ходе работ на объекте заказчика, когда отсутствовала строительная готовность, наладчики приезжали на объект что-то попробовать с перерывами в 2-3 недели. Щит автоматики, содержащий ПЛК DL06 (DirectLOGIC), в перерывах стоял отключённым. В ПЛК отсутствовала батарейка CR2354, которая должна сохранять данные при отключении питания - поэтому все настройки сбрасывались на нулевые. Набирать каждый раз под сотню параметров было утомительно, поэтому изучение состава пакета программирования DirectSOFT5 быстро привело к обнаружению утилиты DNLoader, которая позволяет считать из ПЛК и сохранить на компьютере образ памяти программ и данных, а также выполнить обратную запись образа в ПЛК. Утилита DNLoader позволяет выбирать, какую область памяти сохранять или загружать в ПЛК - память программ, память данных, память счётчиков, память катушек и т.д.<br />
<br />
Следующий шаг был естественным - имея бинарный образ памяти данных, сформировать из него протокол настроек.<br />
<br />
<b>3. ОБ УТИЛИТЕ DNLoader</b><br />
<br />
Утилита DNLoader входит в состав пакета программирования DirectSOFT5, а также доступна для свободного скачивания на сайте производителя<br />
<a rel="nofollow noopener noreferrer" href="http://support.automationdirect.com/downloads.html" target="_blank" title="http://support.automationdirect.com/downloads.html">http://support.automationdirect.com/downloads.html</a><br />
<a rel="nofollow noopener noreferrer" href="https://hosteng.com/SW-Products/SP_Demo_Utilites.htm#DNLoader" target="_blank" title="https://hosteng.com/SW-Products/SP_Demo_Utilites.htm#DNLoader">https://hosteng.com/SW-Product... m#DNLoader</a><br />
и на сайте официального представителя в России<br />
<a rel="nofollow noopener noreferrer" href="http://plcsystems.ru/support/Demo/index.php" target="_blank" title="http://plcsystems.ru/support/Demo/index.php">http://plcsystems.ru/support/Demo/index.php</a><br />
<br />
Описание утилиты<br />

<table width="95%"  class="bbcode_maincontainer"><tr><td>
	<div class="bbcode_container">
	  <div class="bbcode_quote">
	    <div class="btbtbt">
		<div class="quote_container">
	      	<div class="bbcode_quote_container"></div>
	       	<div class="bbcode_postedby">
	         	
	        </div>
	        <div class="message">DirectNET Program Loader (DNLoader) - is a free utility that allows you to upload and download PLC programs and memory without the use of DirectSOFT. Use DNLoader to read the ladder program  (and optionally include system memory, user memory, and/or retentive memory) from a DirectLOGIC PLC and save all of this data in a file. You can then use DNLoader to write the contents of this file to another DirectLOGIC PLC of the same type.</div>
	      </div>
			</div> 
		</div>
	</div>
</td></tr></table>
<table width="95%"  class="bbcode_maincontainer"><tr><td>
	<div class="bbcode_container">
	  <div class="bbcode_quote">
	    <div class="btbtbt">
		<div class="quote_container">
	      	<div class="bbcode_quote_container"></div>
	       	<div class="bbcode_postedby">
	         	
	        </div>
	        <div class="message">DirectNET Program Loader (DNLoader) -  это бесплатная утилита, которая позволяет загружать и скачивать содержимое памяти программ и данных ПЛК без использования DirectSOFT. Используйте DNLoader для считывания образа программы (и, при необходимости, включая системную память, пользовательскую память и/или сохраняемую память) из ПЛК DirectLOGIC и сохранения всех этих данных в файле. Затем вы можете использовать DNLoader для записи содержимого этого файла в другой ПЛК DirectLOGIC того же типа.</div>
	      </div>
			</div> 
		</div>
	</div>
</td></tr></table><b>4. ОПИСАНИЕ ПРОГРАММЫ</b><br />
<br />
Программа VMemory предназначена для формирования протокола настройки системы автоматики на основе ПЛК DL06, DL260.<br />
Доступа к другим ПЛК у меня не было, поэтому проверить не мог. Проверял только для DL06, DL260.<br />
Программа консольная и не имеет графического интерфейса. Тому две причины - и не умею и графический интерфейс в данном случае не сильно нужен.<br />
Формат формируемого отчёта или текстовый (Plain text) или html.<br />
Отчет выводится в окно консоли программы. При использовании перенаправления вывода в файл, получается итоговый файл.<br />
<br />
Командная строка для запуска:<br />
- для получения протокола в формате html<br />
<div class="codeblock"><table class="winbatch"><thead><tr><td colspan="2" id="615632532"  class="head">Windows Batch file</td></tr></thead><tbody><tr class="li1"><td><div id="615632532" style="height: 46px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
</pre></td><td class="de1"><pre class="de1">VMemory <span class="co101">/html</span> image.dat tags.txt <span class="sy0">&gt;</span> Result.html</pre></td></tr></table></div></td></tr></tbody></table></div>- для получения протокола в формате Plain text<br />
<div class="codeblock"><table class="winbatch"><thead><tr><td colspan="2" id="918805868"  class="head">Windows Batch file</td></tr></thead><tbody><tr class="li1"><td><div id="918805868" style="height: 46px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
</pre></td><td class="de1"><pre class="de1">VMemory image.dat tags.txt <span class="sy0">&gt;</span> Result.txt</pre></td></tr></table></div></td></tr></tbody></table></div>где<br />
<code class="inlinecode">VMemory</code> - название утилиты<br />
<code class="inlinecode">/html</code> - ключ, указывающий на формат результата, при его отсутствии формат вывода - Plain text<br />
<code class="inlinecode">image.dat</code> - файл-образ памяти ПЛК, сформированный DNLoader<br />
<code class="inlinecode">tags.txt</code> - файл, содержащий описания тегов (параметров настройки) системы автоматики<br />
<code class="inlinecode">Result.html</code> или <code class="inlinecode">Result.txt</code> - полученный файл с отчётом в требуемом формате<br />
<br />
<b>5. ОПИСАНИЕ ФОРМАТА ФАЙЛА ТЕГОВ</b><br />
<br />
Файл с описаниями тегов создаётся в формате Plain text. Состоит из строк, которые обрабатываются по отдельности.<br />
Если строка не соответствует формату описания тега, то она без изменений выводится на экран.<br />
Формат строки:<br />
<div class="codeblock"><table class="unknown"><thead><tr><td colspan="2" id="844458664"  class="head">Code</td></tr></thead><tbody><tr class="li1"><td><div id="844458664" style="height: 238px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
</pre></td><td class="de1"><pre class="de1">|---------------- Тип переменной в V-памяти <span class="br0">&#40;</span>v, p, b<span class="br0">&#41;</span>
||--------------- Адрес переменной в V-памяти
|| &nbsp; &nbsp; |--------- Формат данных тега <span class="br0">&#40;</span>F32, BCD, BIN и др.<span class="br0">&#41;</span>
|| &nbsp; &nbsp; | &nbsp; |----- Общее количество выводимых знаков
|| &nbsp; &nbsp; | &nbsp; | |--- Количество выводимых знаков после десятичной точки
|| &nbsp; &nbsp; | &nbsp; | | |- Словесное описание тега
|| &nbsp; &nbsp; | &nbsp; | | |
v10000 F32.3.1 Описание тега
\____/ \_____/ \___________/
&nbsp; &nbsp;<span class="nu0">1</span> &nbsp; &nbsp; &nbsp;<span class="nu0">2</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="nu0">3</span>
&nbsp; &nbsp; \ &nbsp; &nbsp; &nbsp;\ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;\------------- Поле описания
&nbsp; &nbsp; &nbsp;\ &nbsp; &nbsp; &nbsp;\----------------------- Поле формата вывода значения ячейки в отчёт
&nbsp; &nbsp; &nbsp; \----------------------------- Поле адреса ячейки</pre></td></tr></table></div></td></tr></tbody></table></div>Описание тега состоит из трёх полей.<br />
Дублирующиеся символы пробелов между полями при анализе игнорируются.<ol style="list-style-type: decimal"><li>Поле адреса ячейки состоит из двух обязательных элементов<ul><li>тип переменной в V-памяти (v, p, b)</li>
<li>адрес ячейки в V-памяти</li>
</ul>  Эти элементы в точности соответствуют нотации DirectSOFT5 и документации на контроллер DL06.</li>
<li>Поле формата вывода значения ячейки в отчёт<br />
  В этом поле задаётся формат вывода. Поддерживаются следующие типы данных<br />
<div class="codeblock"><table class="unknown"><thead><tr><td colspan="2" id="79237456"  class="head">Code</td></tr></thead><tbody><tr class="li1"><td><div id="79237456" style="height: 190px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
</pre></td><td class="de1"><pre class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; в DS5 &nbsp;Размер &nbsp; &nbsp;Описание
&nbsp; BCD &nbsp; &nbsp; BCD &nbsp; &nbsp;<span class="nu0">1</span> слово &nbsp; Число в формате BCD <span class="br0">&#40;</span>=HEX<span class="br0">&#41;</span> - шестнадцатеричное
&nbsp; LBCD &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="nu0">2</span> слова &nbsp; Число в формате BCD <span class="br0">&#40;</span>=HEX<span class="br0">&#41;</span> - шестнадцатеричное
&nbsp; BIN &nbsp; &nbsp; Bin &nbsp; &nbsp;<span class="nu0">1</span> слово &nbsp; Число в формате Dec - десятичное
&nbsp; LBIN &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="nu0">2</span> слова &nbsp; Число в формате Dec - десятичное
&nbsp; F32 &nbsp; &nbsp; Real &nbsp; <span class="nu0">2</span> слова &nbsp; Число в формате float point
&nbsp; Bits &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="nu0">1</span> слово &nbsp; Побитовое представление числа - двоичное
&nbsp; LBits &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="nu0">2</span> слова &nbsp; Побитовое представление числа - двоичное
&nbsp; Bit &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="nu0">1</span> бит &nbsp; &nbsp; Вывод <span class="nu0">0</span> или <span class="nu0">1</span> - двоичное
&nbsp; Ptr &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="nu0">1</span> слово &nbsp; Указатель на ячейку V-памяти - восьмеричное</pre></td></tr></table></div></td></tr></tbody></table></div>  Для всех целых типов данных можно добавить положение десятичной точки. Общее количество знаков игнорируется. Поля количества знаков могут отсутствовать, тогда при выводе их значение принимается равным нулевыми.<br />
<br />
Так для ячейки со значением (BCD) 1234 можно сделать<br />
<div class="codeblock"><table class="unknown"><thead><tr><td colspan="2" id="604944125"  class="head">Code</td></tr></thead><tbody><tr class="li1"><td><div id="604944125" style="height: 110px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
</pre></td><td class="de1"><pre class="de1">v10000 BCD &nbsp; &nbsp; Вывод BCD &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ---&gt; &nbsp; Вывод BCD &nbsp; &nbsp; &nbsp; &nbsp; <span class="nu0">1234</span>
v10000 BCD.0.0 Вывод BCD.0.0 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ---&gt; &nbsp; Вывод BCD.0.0 &nbsp; &nbsp; <span class="nu0">1234</span>
v10000 BCD.4.1 Вывод BCD.4.1 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ---&gt; &nbsp; Вывод BCD.4.1 &nbsp; &nbsp; <span class="nu0">123.4</span>
v10000 BCD.0.1 Вывод BCD.0.1 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ---&gt; &nbsp; Вывод BCD.0.1 &nbsp; &nbsp; <span class="nu0">123.4</span>
v10000 BCD.0.2 Вывод BCD.0.2 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ---&gt; &nbsp; Вывод BCD.0.2 &nbsp; &nbsp; <span class="nu0">12.34</span></pre></td></tr></table></div></td></tr></tbody></table></div>Для типа указатель в восьмеричном виде формируется значение адреса. Так для ячейки со значением указателя на v10000 (=o10000)<br />
 - строка в файле тегов<br />
<div class="codeblock"><table class="unknown"><thead><tr><td colspan="2" id="309535078"  class="head">Code</td></tr></thead><tbody><tr class="li1"><td><div id="309535078" style="height: 46px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
</pre></td><td class="de1"><pre class="de1">v7640 &nbsp; &nbsp; &nbsp;PTR &nbsp; &nbsp; &nbsp;Начальные адреса таблиц ПИД-регуляторов</pre></td></tr></table></div></td></tr></tbody></table></div> - результат<br />
<div class="codeblock"><table class="unknown"><thead><tr><td colspan="2" id="225565267"  class="head">Code</td></tr></thead><tbody><tr class="li1"><td><div id="225565267" style="height: 46px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
</pre></td><td class="de1"><pre class="de1">Начальные адреса таблиц ПИД-регуляторов v10000</pre></td></tr></table></div></td></tr></tbody></table></div>Поля количества знаков могут отсутствовать, тогда при выводе их значение принимается равным нулевыми.<br />
<br />
Для типа с плавающей точкой выводится число с запрашиваемым количеством знаков после десятичной точки, а если всего знаков будет больше указанного, то будет выведено всё число без усечения, только собьётся форматирование отступа.<br />
 - строка в файле тегов<br />
<div class="codeblock"><table class="unknown"><thead><tr><td colspan="2" id="90195648"  class="head">Code</td></tr></thead><tbody><tr class="li1"><td><div id="90195648" style="height: 46px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
</pre></td><td class="de1"><pre class="de1">v12404 &nbsp; &nbsp; F32.0.3 &nbsp;Давл. газа. &nbsp;Аварийный уровень. Минимальный.</pre></td></tr></table></div></td></tr></tbody></table></div> - результат<br />
<div class="codeblock"><table class="unknown"><thead><tr><td colspan="2" id="437145743"  class="head">Code</td></tr></thead><tbody><tr class="li1"><td><div id="437145743" style="height: 46px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
</pre></td><td class="de1"><pre class="de1">Давл. газа. &nbsp;Аварийный уровень. Минимальный. &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="nu0">0.400</span></pre></td></tr></table></div></td></tr></tbody></table></div>Возможно применение масштабирования к отображаемым тегам. Для этого в скобках указываются две пары чисел - нижняя и верхняя границы, первая пара чисел в скобках - диапазон переменной в памяти, вторая пара чисел - диапазон отображаемого значения.<br />
<code class="inlinecode">F32.3.0(0.1,0)(4,20)</code> Выполняет масштабирование переменных в диапазоне [0.1...0.0] к диапазону [4.0...20.0].<br />
Масштабирование доступно к числам форматов BCD, BIN, F32.</li>
<li>Поле описания.<br />
В этом поле может присутствовать словесное описание тега.</li>
<li> Макросы<br />
В описании тега или в любой переносимой в отчёт без изменения строке может присутствовать макрос - подстрока, которая в отчёте будет заменена на другую.<br />
Поддерживаются макросы<br />
<div class="codeblock"><table class="unknown"><thead><tr><td colspan="2" id="634064769"  class="head">Code</td></tr></thead><tbody><tr class="li1"><td><div id="634064769" style="height: 222px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
</pre></td><td class="de1"><pre class="de1">%%IMAGE_DATE%% &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;- Дата создания файла образа V-памяти в формате ДД.ММ.ГГГГ
%%IMAGE_TIME%% &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;- Время создания файла образа V-памяти в формате ЧЧ:ММ:СС
%%IMAGE_DATE_TIME%% &nbsp; &nbsp; &nbsp; &nbsp; - Дата и время создания файла образа V-памяти в формате ДД.ММ.ГГГГ ЧЧ:ММ:СС
%%TAGS_DATE%% &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; - Дата создания файла тегов в формате ДД.ММ.ГГГГ
%%TAGS_TIME%% &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; - Время создания файла тегов в формате ЧЧ:ММ:СС
%%TAGS_DATE_TIME%% &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;- Дата и время создания файла тегов в формате ДД.ММ.ГГГГ ЧЧ:ММ:СС
%%NOW_DATE%% &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;- Текущая дата в формате ДД.ММ.ГГГГ
%%NOW_TIME%% &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;- Текущее время в формате ЧЧ:ММ:СС
%%NOW_DATE_TIME%% &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; - Текущие дата и время в формате ДД.ММ.ГГГГ ЧЧ:ММ:СС
%%VMEMORY_VERSION%% &nbsp; &nbsp; &nbsp; &nbsp; - Версия программы VMemory
%%PLC_TYPE%% &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;- Модель ПЛК, для которой создавался образ памяти
%%SECONDARY_PORT_INFO%% &nbsp; &nbsp; - Информация о настройках коммуникационного последовательного порта Port2 <span class="br0">&#40;</span>только для ПЛК DL6<span class="br0">&#41;</span></pre></td></tr></table></div></td></tr></tbody></table></div>Регистр символов макроса не имеет значения. Поэтому <code class="inlinecode">%%IMAGE_DATE%%</code> эквивалентен <code class="inlinecode">%%Image_Date%%</code>.<br />
Так, для файла образа созданного 25.10.2010 и текущей даты 01.01.2011<br />
 - строка в файле тегов<br />
<div class="codeblock"><table class="unknown"><thead><tr><td colspan="2" id="934642368"  class="head">Code</td></tr></thead><tbody><tr class="li1"><td><div id="934642368" style="height: 62px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
</pre></td><td class="de1"><pre class="de1">Дата создания образа:%%IMAGE_DATE%%!
Дата составления отчёта: %%Now_Time%%.</pre></td></tr></table></div></td></tr></tbody></table></div> - результат<br />
<div class="codeblock"><table class="unknown"><thead><tr><td colspan="2" id="171748511"  class="head">Code</td></tr></thead><tbody><tr class="li1"><td><div id="171748511" style="height: 62px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
</pre></td><td class="de1"><pre class="de1">Дата создания образа:25.10.2010!
Дата составления отчёта: 01.01.2011.</pre></td></tr></table></div></td></tr></tbody></table></div></li>
</ol><br />
<b>6. ПРИМЕР</b><br />
<br />
В качестве примера приведу свой обычный файл описания тегов. Для публикации он сильно сокращён и в него добавлены некоторые макросы.<br />

			   <div class="spoiler-wrap">
				   <div class="spoiler-head folded clickable">Tags.txt</div>
				   <div class="spoiler-body">
					   <div class="codeblock"><table class="unknown"><thead><tr><td colspan="2" id="285212066"  class="head">Code</td></tr></thead><tbody><tr class="li1"><td><div id="285212066" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
</pre></td><td class="de1"><pre class="de1">=====================================================================
г. Москва, ОАО &quot;Москвич&quot;, Котёл №<span class="nu0">3</span> <span class="br0">&#40;</span>ДЕ-<span class="nu0">16</span><span class="br0">&#41;</span> 
---------------------------------------------------------------------
Дата считывания настроек <span class="br0">&#40;</span>ДД.ММ.ГГГГ<span class="br0">&#41;</span>: %%IMAGE_DATE%%
=====================================================================
Тип ПЛК: %%PLC_TYPE%%
Версия ПО формирования отчёта: VMemory %%VMEMORY_VERSION%%
&nbsp;
Настройки Port2
%%SECONDARY_PORT_INFO%%
------------------------------------------------------------------------
Значения аварийных уровней параметров
------------------------------------------------------------------------
v12304 &nbsp; &nbsp; F32.0.1 &nbsp;Уровень воды в барабане котла. Аварийное максимальное значение, мм 
v11254 &nbsp; &nbsp; BCD.4.0 &nbsp;Уровень воды в барабане котла. Talarm - задержка срабатывания аварии, с 
&nbsp;
------------------------------------------------------------------------
Значения уставок параметров технологического процесса
------------------------------------------------------------------------
v12326 &nbsp; &nbsp; F32.3.1 &nbsp;Уровень &nbsp; . Уставка &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="br0">&#40;</span>H &nbsp;<span class="br0">&#41;</span>
&nbsp;
------------------------------------------------------------------------
Диапазоны измерений датчиков
------------------------------------------------------------------------
v12354 &nbsp; &nbsp; F32.5.1 &nbsp;Уровень &nbsp; . &nbsp;Диапазон датчика. Минимальное &nbsp;значение.
&nbsp;
------------------------------------------------------------------------
Коэффициенты демпфирования
------------------------------------------------------------------------
v12302 &nbsp; &nbsp; F32.3.0<span class="br0">&#40;</span><span class="nu0">0.1</span>,<span class="nu0">0</span><span class="br0">&#41;</span><span class="br0">&#40;</span><span class="nu0">0</span>,<span class="nu0">100</span><span class="br0">&#41;</span> &nbsp;Уровень &nbsp; . &nbsp;Коэф. демпфирования по аналоговому входу 
v12366 &nbsp; &nbsp; BCD.4.2 &nbsp;Уровень &nbsp; . &nbsp;Период опроса датчика по аналоговому входу 
&nbsp;
------------------------------------------------------------------------
Прочее
------------------------------------------------------------------------
v11600 &nbsp; &nbsp; BIN &nbsp; &nbsp; &nbsp;Количество пусков котла
v11602 &nbsp; &nbsp; LBIN &nbsp; &nbsp; Часы наработки
b11600.1 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Бит, просто бит
------------------------------------------------------------------------
Системные переменные PLC
------------------------------------------------------------------------
v7641 &nbsp; &nbsp; &nbsp;BIN &nbsp; &nbsp; &nbsp;Количество ПИД-контуров
v7640 &nbsp; &nbsp; &nbsp;PTR &nbsp; &nbsp; &nbsp;Начальные адреса таблиц ПИД-регуляторов
v7642 &nbsp; &nbsp; &nbsp;BCD &nbsp; &nbsp; &nbsp;Код ошибки контура ПИД-регулятора
&nbsp;
v7770 &nbsp; &nbsp; &nbsp;BCD.2 &nbsp; &nbsp;Текущее время PLC, часы
v7767 &nbsp; &nbsp; &nbsp;BCD.2 &nbsp; &nbsp;Текущее время PLC, минуты
v7766 &nbsp; &nbsp; &nbsp;BCD.2 &nbsp; &nbsp;Текущее время PLC, секунды</pre></td></tr></table></div></td></tr></tbody></table></div>
				   </div>
			   </div><br />
После запуска из командной строки<br />
<div class="codeblock"><table class="winbatch"><thead><tr><td colspan="2" id="196051644"  class="head">Windows Batch file</td></tr></thead><tbody><tr class="li1"><td><div id="196051644" style="height: 46px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
</pre></td><td class="de1"><pre class="de1">VMemory IMAGE.DAT Tags.txt <span class="sy0">&gt;</span> result.txt</pre></td></tr></table></div></td></tr></tbody></table></div>получаю файл отчёта в текстовом формате<br />

			   <div class="spoiler-wrap">
				   <div class="spoiler-head folded clickable">result.txt</div>
				   <div class="spoiler-body">
					   <div class="codeblock"><table class="unknown"><thead><tr><td colspan="2" id="330043791"  class="head">Code</td></tr></thead><tbody><tr class="li1"><td><div id="330043791" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
</pre></td><td class="de1"><pre class="de1">=====================================================================
г. Москва, ОАО &quot;Москвич&quot;, Котёл №<span class="nu0">3</span> <span class="br0">&#40;</span>ДЕ-<span class="nu0">16</span><span class="br0">&#41;</span> 
---------------------------------------------------------------------
Дата считывания настроек <span class="br0">&#40;</span>ДД.ММ.ГГГГ<span class="br0">&#41;</span>: 03.12.2015
=====================================================================
Тип ПЛК: DL06
Версия ПО формирования отчёта: VMemory 1.1.1
&nbsp;
Настройки Port2
Secondary Port settings:
Protocol &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Modbus RTU, DirectNET <span class="br0">&#40;</span>ASCII<span class="br0">&#41;</span>, K-Sequence
TimeOut &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="nu0">100</span>%
RTS Off-delay &nbsp; &nbsp; 0ms
RTS On-delay &nbsp; &nbsp; &nbsp;0ms
Baud rate &nbsp; &nbsp; &nbsp; &nbsp; <span class="nu0">9600</span>
Stop Bits &nbsp; &nbsp; &nbsp; &nbsp; <span class="nu0">1</span> bit<span class="br0">&#40;</span>s<span class="br0">&#41;</span>
Parity &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Odd
Secondary address <span class="nu0">1</span>
------------------------------------------------------------------------
Значения аварийных уровней параметров
------------------------------------------------------------------------
Уровень воды в барабане котла. Аварийное максимальное значение, мм -<span class="nu0">200.0</span>
Уровень воды в барабане котла. Talarm - задержка срабатывания аварии, с 0020
&nbsp;
------------------------------------------------------------------------
Значения уставок параметров технологического процесса
------------------------------------------------------------------------
Уровень &nbsp; . Уставка &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="br0">&#40;</span>H &nbsp;<span class="br0">&#41;</span> &nbsp; &nbsp; &nbsp; &nbsp; <span class="nu0">0.0</span>
&nbsp;
------------------------------------------------------------------------
Диапазоны измерений датчиков
------------------------------------------------------------------------
Уровень &nbsp; . &nbsp;Диапазон датчика. Минимальное &nbsp;значение. &nbsp; &nbsp; &nbsp; -<span class="nu0">315.0</span>
&nbsp;
------------------------------------------------------------------------
Коэффициенты демпфирования
------------------------------------------------------------------------
Уровень &nbsp; . &nbsp;Коэф. демпфирования по аналоговому входу &nbsp; &nbsp; &nbsp; &nbsp; <span class="nu0">97</span>
Уровень &nbsp; . &nbsp;Период опроса датчика по аналоговому входу &nbsp; &nbsp; <span class="nu0">00.00</span>
&nbsp;
------------------------------------------------------------------------
Прочее
------------------------------------------------------------------------
Количество пусков котла &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="nu0">157</span>
Часы наработки &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="nu0">11065</span>
b11600.1 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Бит, просто бит
------------------------------------------------------------------------
Системные переменные PLC
------------------------------------------------------------------------
Количество ПИД-контуров &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="nu0">4</span>
Начальные адреса таблиц ПИД-регуляторов &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; v10000
Код ошибки контура ПИД-регулятора &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 0000
&nbsp;
Текущее время PLC, часы &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 0011
Текущее время PLC, минуты &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 0034
Текущее время PLC, секунды &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;0050
&nbsp;
Total tag processing: <span class="nu0">14</span></pre></td></tr></table></div></td></tr></tbody></table></div>
				   </div>
			   </div><br />
Или же, после выполнения с ключом <code class="inlinecode">/html</code><br />
<div class="codeblock"><table class="winbatch"><thead><tr><td colspan="2" id="373626850"  class="head">Windows Batch file</td></tr></thead><tbody><tr class="li1"><td><div id="373626850" style="height: 46px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
</pre></td><td class="de1"><pre class="de1">VMemory <span class="co101">/html</span> IMAGE.DAT Tags.txt <span class="sy0">&gt;</span> result.html</pre></td></tr></table></div></td></tr></tbody></table></div>получаю тот же отчёт в формате html<br />

			   <div class="spoiler-wrap">
				   <div class="spoiler-head folded clickable">result.html</div>
				   <div class="spoiler-body">
					   <div class="codeblock"><table class="xml"><thead><tr><td colspan="2" id="141122177"  class="head">XML</td></tr></thead><tbody><tr class="li1"><td><div id="141122177" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
</pre></td><td class="de1"><pre class="de1"><span class="sc0">&lt;!DOCTYPE html&gt;</span>
<span class="sc3"><span class="re1">&lt;html<span class="re2">&gt;</span></span></span>
&nbsp; &nbsp;<span class="sc3"><span class="re1">&lt;head<span class="re2">&gt;</span></span></span>
<span class="sc3"><span class="re1">&lt;title<span class="re2">&gt;</span></span></span>
The Report Title
<span class="sc3"><span class="re1">&lt;/title<span class="re2">&gt;</span></span></span>
&nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;meta</span> <span class="re0">charset</span>=<span class="st0">&quot;windows-1251&quot;</span> <span class="re2">/&gt;</span></span>
&nbsp; &nbsp;<span class="sc3"><span class="re1">&lt;/head<span class="re2">&gt;</span></span></span>
&nbsp; &nbsp;<span class="sc3"><span class="re1">&lt;body<span class="re2">&gt;</span></span></span>
<span class="sc3"><span class="re1">&lt;hr</span> <span class="re2">/&gt;</span></span>
г. Москва, ОАО &quot;Москвич&quot;, Котёл №3 (ДЕ-16) <span class="sc3"><span class="re1">&lt;br</span> <span class="re2">/&gt;</span></span>
<span class="sc3"><span class="re1">&lt;hr</span> <span class="re2">/&gt;</span></span>
Дата считывания настроек (ДД.ММ.ГГГГ): 03.12.2015<span class="sc3"><span class="re1">&lt;br</span> <span class="re2">/&gt;</span></span>
<span class="sc3"><span class="re1">&lt;hr</span> <span class="re2">/&gt;</span></span>
Тип ПЛК: DL06<span class="sc3"><span class="re1">&lt;br</span> <span class="re2">/&gt;</span></span>
Версия ПО формирования отчёта: VMemory 1.1.1<span class="sc3"><span class="re1">&lt;br</span> <span class="re2">/&gt;</span></span>
<span class="sc3"><span class="re1">&lt;br</span> <span class="re2">/&gt;</span></span>
Настройки Port2<span class="sc3"><span class="re1">&lt;br</span> <span class="re2">/&gt;</span></span>
Secondary Port settings:<span class="sc3"><span class="re1">&lt;br</span> <span class="re2">/&gt;</span></span>
Protocol &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Modbus RTU, DirectNET (ASCII), K-Sequence<span class="sc3"><span class="re1">&lt;br</span> <span class="re2">/&gt;</span></span>
TimeOut &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 100%<span class="sc3"><span class="re1">&lt;br</span> <span class="re2">/&gt;</span></span>
RTS Off-delay &nbsp; &nbsp; 0ms<span class="sc3"><span class="re1">&lt;br</span> <span class="re2">/&gt;</span></span>
RTS On-delay &nbsp; &nbsp; &nbsp;0ms<span class="sc3"><span class="re1">&lt;br</span> <span class="re2">/&gt;</span></span>
Baud rate &nbsp; &nbsp; &nbsp; &nbsp; 9600<span class="sc3"><span class="re1">&lt;br</span> <span class="re2">/&gt;</span></span>
Stop Bits &nbsp; &nbsp; &nbsp; &nbsp; 1 bit(s)<span class="sc3"><span class="re1">&lt;br</span> <span class="re2">/&gt;</span></span>
Parity &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Odd<span class="sc3"><span class="re1">&lt;br</span> <span class="re2">/&gt;</span></span>
Secondary address 1<span class="sc3"><span class="re1">&lt;br</span> <span class="re2">/&gt;</span></span>
<span class="sc3"><span class="re1">&lt;hr</span> <span class="re2">/&gt;</span></span>
Значения аварийных уровней параметров<span class="sc3"><span class="re1">&lt;br</span> <span class="re2">/&gt;</span></span>
<span class="sc3"><span class="re1">&lt;hr</span> <span class="re2">/&gt;</span></span>
<span class="sc3"><span class="re1">&lt;table</span> <span class="re0">border</span>=<span class="st0">&quot;1&quot;</span> <span class="re0">style</span>=<span class="st0">&quot;width:100%&quot;</span><span class="re2">&gt;</span></span>
&nbsp; &nbsp;<span class="sc3"><span class="re1">&lt;tr<span class="re2">&gt;</span></span></span>
<span class="sc3"><span class="re1">&lt;td</span> <span class="re0">style</span>=<span class="st0">&quot;width:80%&quot;</span><span class="re2">&gt;</span></span>
Уровень воды в барабане котла. Аварийное максимальное значение, мм 
&nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;/td<span class="re2">&gt;</span></span></span>
&nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;td<span class="re2">&gt;</span></span></span>
-200.0
&nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;/td<span class="re2">&gt;</span></span></span>
&nbsp; &nbsp;<span class="sc3"><span class="re1">&lt;/tr<span class="re2">&gt;</span></span></span>
&nbsp; &nbsp;<span class="sc3"><span class="re1">&lt;tr<span class="re2">&gt;</span></span></span>
<span class="sc3"><span class="re1">&lt;td</span> <span class="re0">style</span>=<span class="st0">&quot;width:80%&quot;</span><span class="re2">&gt;</span></span>
Уровень воды в барабане котла. Talarm - задержка срабатывания аварии, с 
&nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;/td<span class="re2">&gt;</span></span></span>
&nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;td<span class="re2">&gt;</span></span></span>
0020
&nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;/td<span class="re2">&gt;</span></span></span>
&nbsp; &nbsp;<span class="sc3"><span class="re1">&lt;/tr<span class="re2">&gt;</span></span></span>
<span class="sc3"><span class="re1">&lt;/table<span class="re2">&gt;</span></span></span>
<span class="sc3"><span class="re1">&lt;br</span> <span class="re2">/&gt;</span></span>
<span class="sc3"><span class="re1">&lt;hr</span> <span class="re2">/&gt;</span></span>
Значения уставок параметров технологического процесса<span class="sc3"><span class="re1">&lt;br</span> <span class="re2">/&gt;</span></span>
<span class="sc3"><span class="re1">&lt;hr</span> <span class="re2">/&gt;</span></span>
<span class="sc3"><span class="re1">&lt;table</span> <span class="re0">border</span>=<span class="st0">&quot;1&quot;</span> <span class="re0">style</span>=<span class="st0">&quot;width:100%&quot;</span><span class="re2">&gt;</span></span>
&nbsp; &nbsp;<span class="sc3"><span class="re1">&lt;tr<span class="re2">&gt;</span></span></span>
<span class="sc3"><span class="re1">&lt;td</span> <span class="re0">style</span>=<span class="st0">&quot;width:80%&quot;</span><span class="re2">&gt;</span></span>
Уровень &nbsp; . Уставка &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(H &nbsp;)
&nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;/td<span class="re2">&gt;</span></span></span>
&nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;td<span class="re2">&gt;</span></span></span>
&nbsp;0.0
&nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;/td<span class="re2">&gt;</span></span></span>
&nbsp; &nbsp;<span class="sc3"><span class="re1">&lt;/tr<span class="re2">&gt;</span></span></span>
<span class="sc3"><span class="re1">&lt;/table<span class="re2">&gt;</span></span></span>
<span class="sc3"><span class="re1">&lt;br</span> <span class="re2">/&gt;</span></span>
<span class="sc3"><span class="re1">&lt;hr</span> <span class="re2">/&gt;</span></span>
Диапазоны измерений датчиков<span class="sc3"><span class="re1">&lt;br</span> <span class="re2">/&gt;</span></span>
<span class="sc3"><span class="re1">&lt;hr</span> <span class="re2">/&gt;</span></span>
<span class="sc3"><span class="re1">&lt;table</span> <span class="re0">border</span>=<span class="st0">&quot;1&quot;</span> <span class="re0">style</span>=<span class="st0">&quot;width:100%&quot;</span><span class="re2">&gt;</span></span>
&nbsp; &nbsp;<span class="sc3"><span class="re1">&lt;tr<span class="re2">&gt;</span></span></span>
<span class="sc3"><span class="re1">&lt;td</span> <span class="re0">style</span>=<span class="st0">&quot;width:80%&quot;</span><span class="re2">&gt;</span></span>
Уровень &nbsp; . &nbsp;Диапазон датчика. Минимальное &nbsp;значение.
&nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;/td<span class="re2">&gt;</span></span></span>
&nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;td<span class="re2">&gt;</span></span></span>
-315.0
&nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;/td<span class="re2">&gt;</span></span></span>
&nbsp; &nbsp;<span class="sc3"><span class="re1">&lt;/tr<span class="re2">&gt;</span></span></span>
<span class="sc3"><span class="re1">&lt;/table<span class="re2">&gt;</span></span></span>
<span class="sc3"><span class="re1">&lt;br</span> <span class="re2">/&gt;</span></span>
<span class="sc3"><span class="re1">&lt;hr</span> <span class="re2">/&gt;</span></span>
Коэффициенты демпфирования<span class="sc3"><span class="re1">&lt;br</span> <span class="re2">/&gt;</span></span>
<span class="sc3"><span class="re1">&lt;hr</span> <span class="re2">/&gt;</span></span>
<span class="sc3"><span class="re1">&lt;table</span> <span class="re0">border</span>=<span class="st0">&quot;1&quot;</span> <span class="re0">style</span>=<span class="st0">&quot;width:100%&quot;</span><span class="re2">&gt;</span></span>
&nbsp; &nbsp;<span class="sc3"><span class="re1">&lt;tr<span class="re2">&gt;</span></span></span>
<span class="sc3"><span class="re1">&lt;td</span> <span class="re0">style</span>=<span class="st0">&quot;width:80%&quot;</span><span class="re2">&gt;</span></span>
Уровень &nbsp; . &nbsp;Коэф. демпфирования по аналоговому входу 
&nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;/td<span class="re2">&gt;</span></span></span>
&nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;td<span class="re2">&gt;</span></span></span>
&nbsp; 97
&nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;/td<span class="re2">&gt;</span></span></span>
&nbsp; &nbsp;<span class="sc3"><span class="re1">&lt;/tr<span class="re2">&gt;</span></span></span>
&nbsp; &nbsp;<span class="sc3"><span class="re1">&lt;tr<span class="re2">&gt;</span></span></span>
<span class="sc3"><span class="re1">&lt;td</span> <span class="re0">style</span>=<span class="st0">&quot;width:80%&quot;</span><span class="re2">&gt;</span></span>
Уровень &nbsp; . &nbsp;Период опроса датчика по аналоговому входу 
&nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;/td<span class="re2">&gt;</span></span></span>
&nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;td<span class="re2">&gt;</span></span></span>
00.00
&nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;/td<span class="re2">&gt;</span></span></span>
&nbsp; &nbsp;<span class="sc3"><span class="re1">&lt;/tr<span class="re2">&gt;</span></span></span>
<span class="sc3"><span class="re1">&lt;/table<span class="re2">&gt;</span></span></span>
<span class="sc3"><span class="re1">&lt;br</span> <span class="re2">/&gt;</span></span>
<span class="sc3"><span class="re1">&lt;hr</span> <span class="re2">/&gt;</span></span>
Прочее<span class="sc3"><span class="re1">&lt;br</span> <span class="re2">/&gt;</span></span>
<span class="sc3"><span class="re1">&lt;hr</span> <span class="re2">/&gt;</span></span>
<span class="sc3"><span class="re1">&lt;table</span> <span class="re0">border</span>=<span class="st0">&quot;1&quot;</span> <span class="re0">style</span>=<span class="st0">&quot;width:100%&quot;</span><span class="re2">&gt;</span></span>
&nbsp; &nbsp;<span class="sc3"><span class="re1">&lt;tr<span class="re2">&gt;</span></span></span>
<span class="sc3"><span class="re1">&lt;td</span> <span class="re0">style</span>=<span class="st0">&quot;width:80%&quot;</span><span class="re2">&gt;</span></span>
Количество пусков котла
&nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;/td<span class="re2">&gt;</span></span></span>
&nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;td<span class="re2">&gt;</span></span></span>
157
&nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;/td<span class="re2">&gt;</span></span></span>
&nbsp; &nbsp;<span class="sc3"><span class="re1">&lt;/tr<span class="re2">&gt;</span></span></span>
&nbsp; &nbsp;<span class="sc3"><span class="re1">&lt;tr<span class="re2">&gt;</span></span></span>
<span class="sc3"><span class="re1">&lt;td</span> <span class="re0">style</span>=<span class="st0">&quot;width:80%&quot;</span><span class="re2">&gt;</span></span>
Часы наработки
&nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;/td<span class="re2">&gt;</span></span></span>
&nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;td<span class="re2">&gt;</span></span></span>
11065
&nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;/td<span class="re2">&gt;</span></span></span>
&nbsp; &nbsp;<span class="sc3"><span class="re1">&lt;/tr<span class="re2">&gt;</span></span></span>
<span class="sc3"><span class="re1">&lt;/table<span class="re2">&gt;</span></span></span>
b11600.1 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Бит, просто бит<span class="sc3"><span class="re1">&lt;br</span> <span class="re2">/&gt;</span></span>
<span class="sc3"><span class="re1">&lt;hr</span> <span class="re2">/&gt;</span></span>
Системные переменные PLC<span class="sc3"><span class="re1">&lt;br</span> <span class="re2">/&gt;</span></span>
<span class="sc3"><span class="re1">&lt;hr</span> <span class="re2">/&gt;</span></span>
<span class="sc3"><span class="re1">&lt;table</span> <span class="re0">border</span>=<span class="st0">&quot;1&quot;</span> <span class="re0">style</span>=<span class="st0">&quot;width:100%&quot;</span><span class="re2">&gt;</span></span>
&nbsp; &nbsp;<span class="sc3"><span class="re1">&lt;tr<span class="re2">&gt;</span></span></span>
<span class="sc3"><span class="re1">&lt;td</span> <span class="re0">style</span>=<span class="st0">&quot;width:80%&quot;</span><span class="re2">&gt;</span></span>
Количество ПИД-контуров
&nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;/td<span class="re2">&gt;</span></span></span>
&nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;td<span class="re2">&gt;</span></span></span>
4
&nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;/td<span class="re2">&gt;</span></span></span>
&nbsp; &nbsp;<span class="sc3"><span class="re1">&lt;/tr<span class="re2">&gt;</span></span></span>
&nbsp; &nbsp;<span class="sc3"><span class="re1">&lt;tr<span class="re2">&gt;</span></span></span>
<span class="sc3"><span class="re1">&lt;td</span> <span class="re0">style</span>=<span class="st0">&quot;width:80%&quot;</span><span class="re2">&gt;</span></span>
Начальные адреса таблиц ПИД-регуляторов
&nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;/td<span class="re2">&gt;</span></span></span>
&nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;td<span class="re2">&gt;</span></span></span>
v10000
&nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;/td<span class="re2">&gt;</span></span></span>
&nbsp; &nbsp;<span class="sc3"><span class="re1">&lt;/tr<span class="re2">&gt;</span></span></span>
&nbsp; &nbsp;<span class="sc3"><span class="re1">&lt;tr<span class="re2">&gt;</span></span></span>
<span class="sc3"><span class="re1">&lt;td</span> <span class="re0">style</span>=<span class="st0">&quot;width:80%&quot;</span><span class="re2">&gt;</span></span>
Код ошибки контура ПИД-регулятора
&nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;/td<span class="re2">&gt;</span></span></span>
&nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;td<span class="re2">&gt;</span></span></span>
0000
&nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;/td<span class="re2">&gt;</span></span></span>
&nbsp; &nbsp;<span class="sc3"><span class="re1">&lt;/tr<span class="re2">&gt;</span></span></span>
<span class="sc3"><span class="re1">&lt;/table<span class="re2">&gt;</span></span></span>
<span class="sc3"><span class="re1">&lt;br</span> <span class="re2">/&gt;</span></span>
<span class="sc3"><span class="re1">&lt;table</span> <span class="re0">border</span>=<span class="st0">&quot;1&quot;</span> <span class="re0">style</span>=<span class="st0">&quot;width:100%&quot;</span><span class="re2">&gt;</span></span>
&nbsp; &nbsp;<span class="sc3"><span class="re1">&lt;tr<span class="re2">&gt;</span></span></span>
<span class="sc3"><span class="re1">&lt;td</span> <span class="re0">style</span>=<span class="st0">&quot;width:80%&quot;</span><span class="re2">&gt;</span></span>
Текущее время PLC, часы
&nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;/td<span class="re2">&gt;</span></span></span>
&nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;td<span class="re2">&gt;</span></span></span>
0011
&nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;/td<span class="re2">&gt;</span></span></span>
&nbsp; &nbsp;<span class="sc3"><span class="re1">&lt;/tr<span class="re2">&gt;</span></span></span>
&nbsp; &nbsp;<span class="sc3"><span class="re1">&lt;tr<span class="re2">&gt;</span></span></span>
<span class="sc3"><span class="re1">&lt;td</span> <span class="re0">style</span>=<span class="st0">&quot;width:80%&quot;</span><span class="re2">&gt;</span></span>
Текущее время PLC, минуты
&nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;/td<span class="re2">&gt;</span></span></span>
&nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;td<span class="re2">&gt;</span></span></span>
0034
&nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;/td<span class="re2">&gt;</span></span></span>
&nbsp; &nbsp;<span class="sc3"><span class="re1">&lt;/tr<span class="re2">&gt;</span></span></span>
&nbsp; &nbsp;<span class="sc3"><span class="re1">&lt;tr<span class="re2">&gt;</span></span></span>
<span class="sc3"><span class="re1">&lt;td</span> <span class="re0">style</span>=<span class="st0">&quot;width:80%&quot;</span><span class="re2">&gt;</span></span>
Текущее время PLC, секунды
&nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;/td<span class="re2">&gt;</span></span></span>
&nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;td<span class="re2">&gt;</span></span></span>
0050
&nbsp; &nbsp; &nbsp; <span class="sc3"><span class="re1">&lt;/td<span class="re2">&gt;</span></span></span>
&nbsp; &nbsp;<span class="sc3"><span class="re1">&lt;/tr<span class="re2">&gt;</span></span></span>
<span class="sc3"><span class="re1">&lt;/table<span class="re2">&gt;</span></span></span>
Total tag processing: 14
&nbsp; &nbsp;<span class="sc3"><span class="re1">&lt;/body<span class="re2">&gt;</span></span></span>
<span class="sc3"><span class="re1">&lt;/html<span class="re2">&gt;</span></span></span></pre></td></tr></table></div></td></tr></tbody></table></div>
				   </div>
			   </div><br />
<br />
<b>7. ИСПОЛЬЗОВАНИЕ</b><br />
<br />
Программа сделана и остаётся консольной в том числе и из-за удобства использования.<br />
<br />
Поясню.<br />
Удобство формируется за счёт упрощения командной строки при вызове. Получается это применением двух факторов - замена вызова программы на пакетный файл и взаимным расположением файла тегов и образов в папках.<ol style="list-style-type: decimal"><li>Замена вызова программы на пакетный файл<br />
У меня есть папка, в которой собраны необходимые для работы утилиты, под которыми подразумеваю или программы или пакетные файлы, упрощающие набор командной строки.<br />
Так и в этом случае, в папке для утилит содержатся два файла - &quot;VMemory.exe&quot; и &quot;VMem.bat&quot;<br />
<b>VMem.bat</b><br />
<div class="codeblock"><table class="winbatch"><thead><tr><td colspan="2" id="201829980"  class="head">Windows Batch file</td></tr></thead><tbody><tr class="li1"><td><div id="201829980" style="height: 94px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
</pre></td><td class="de1"><pre class="de1"><span class="kw1">if</span> <span class="st0">&quot;%1&quot;</span>==<span class="st0">&quot;&quot;</span> <span class="re1">goto</span> <span class="re0">:<span class="kw1">exit</span></span>
c:\My_DL\VMemory\VMemory.exe <span class="co101">/html</span> <span class="st0">&quot;%1&quot;</span> ..\..\tags.txt <span class="sy0">&gt;</span> ResultX.html
c:\My_DL\VMemory\VMemory.exe <span class="st0">&quot;%1&quot;</span> ..\..\tags.txt <span class="sy0">&gt;</span> ResultX.txt
<span class="co102">:exit</span></pre></td></tr></table></div></td></tr></tbody></table></div>Через &quot;Мой компьютер&quot; к переменной окружения <code class="inlinecode">PATH</code> добавлен путь к этой папке.</li>
<li>Взаимное расположение фала тегов и файлов образов<br />
Чтобы такой пакетный файл работал, размещаю образы памяти в структуре каталогов<br />
<div class="codeblock"><table class="unknown"><thead><tr><td colspan="2" id="268230399"  class="head">Code</td></tr></thead><tbody><tr class="li1"><td><div id="268230399" style="height: 190px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
</pre></td><td class="de1"><pre class="de1">\Doc\
&nbsp;|-----\FromPLC\
&nbsp;|----- |-----\<span class="nu0">20200107</span>\
&nbsp;|----- |----- |-----Image.dat
&nbsp;|----- |-----\<span class="nu0">20200209</span>\
&nbsp;|----- |----- |-----Image.dat
&nbsp;|----- |-----\<span class="nu0">20200310</span>\
&nbsp;|----- |----- |-----Image.dat
&nbsp;|
&nbsp;|-----\tags.txt</pre></td></tr></table></div></td></tr></tbody></table></div>Т.е. создаётся папка <code class="inlinecode">\Doc</code>, содержащая разные документы, папки, в том числе файл тегов <code class="inlinecode">tags.txt</code> и папку <code class="inlinecode">\FromPLC</code>, в которой по датам размещены считанные образы <code class="inlinecode">Image.dat</code>.</li>
<li>Запуск<br />
Захожу в папку с образом, из командной строки запускаю пакетный файл с единственным параметром -  именем образа<br />
<div class="codeblock"><table class="winbatch"><thead><tr><td colspan="2" id="202570350"  class="head">Windows Batch file</td></tr></thead><tbody><tr class="li1"><td><div id="202570350" style="height: 46px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
</pre></td><td class="de1"><pre class="de1">VMem image.dat</pre></td></tr></table></div></td></tr></tbody></table></div>В результате работы в той же папке получаю два файла с протоколами <code class="inlinecode">ResultX.txt</code> и <code class="inlinecode">ResultX.html</code></li>
</ol><br />
При работе с диалоговыми окнами открытия файла такой скорости бы не было.<br />
<br />
<b>8. ВЫВОДЫ И РЕКОМЕНДАЦИИ</b><br />
<br />
Программой пользовался с 2010 г., продолжаю использовать и сейчас для формирования протоколов настройки автоматики для технических отчётов о ПНР и РНИ (сейчас уже, конечно, только РНИ), при реверсе чужого кода, когда дополнительно уточняю значения переменных, используемых в программе.<br />
<br />
Острой нужды в улучшении программы не было с 2014 г., поэтому тогда же и работы над ней прекратил. Хотя недостатки и пути улучшения видел в следующем:<br />
<ol style="list-style-type: decimal"><li>Идеально было бы сделать программу, которая получив параметры при вызове отрабатывала в консоли, а без параметров - запуская графическую оболочку. Так получился бы значительно более удобный пользовательский интерфейс.</li>
<li>Так и не закончил обработку битов в ячейках V-памяти, именуемых с префиксом <code class="inlinecode">b</code>. Хотел выводить в двух форматах - значение 0 или 1, а также заменой числа строкой. Практический интерес замены строкой в выводе единиц измерения времени интегрирования ПИД-регулятора и других подобных случаях.</li>
<li>Программа работает только с образами памяти DL06 и DL260. В моём распоряжении были только ПЛК DL06, поэтому программа в значительной степени отлаживалась на них. Один раз достался образ и программа без комментариев для DL260. С появлением образа DL260 переделал программу, чтобы можно было обрабатывать и эти образы, но тщательной проверки не было, макрос информации по настройкам Port2 для DL260 не переделывал. Образ для ПЛК DL260 по сравнению с DL06 содержит ещё какой-то раздел данных, помню, что посчитал его несущественным и игнорировал. Для других ПЛК потребуется небольшие изменения в программе и проверка.</li>
</ol><br />
<b>9. ПРИЛОЖЕНИЕ</b><br />
<br />
Для загрузки доступны исполняемый файл <code class="inlinecode">bin.7z</code> и исходники <code class="inlinecode">Sources.7z</code>.<br />
Исполняемый файл для Windows 32-bit, сжат утилитой upx.<br />
В папке с исполняемым файлом присутствует папка с файлами для примера использования утилиты. Достаточно выполнить пакетный файл <code class="inlinecode">VMem.bat</code>, в результате чего будут созданы файлы <code class="inlinecode">Result.txt</code> и <code class="inlinecode">Result.html</code>.<br />
Исходники для компилятора Free Pascal 2.6.2.</div>


<!-- attachments -->
	<div style="margin-top:10px">

		
		
		
		
			<fieldset class="fieldset">
				<legend>Вложения</legend>
				<table cellpadding="0" cellspacing="3" border="0">
				<tr>
	<td><img loading="lazy" decoding="async" class="inlineimg" src="http://www.cyberforum.ru//cyberstatic.net/images/attach/7z.gif" alt="Тип файла: 7z" width="16" height="16" border="0" style="vertical-align:baseline" /></td>
	<td><a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=7802&amp;d=1668932354">bin.7z</a> (71.1 Кб, 226 просмотров)</td>
</tr><tr>
	<td><img loading="lazy" decoding="async" class="inlineimg" src="http://www.cyberforum.ru//cyberstatic.net/images/attach/7z.gif" alt="Тип файла: 7z" width="16" height="16" border="0" style="vertical-align:baseline" /></td>
	<td><a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=7803&amp;d=1668932354">Sources.7z</a> (16.7 Кб, 228 просмотров)</td>
</tr>
				</table>
			</fieldset>
		

	</div>
<!-- / attachments -->
]]></content:encoded>
			<dc:creator>ФедосеевПавел</dc:creator>
			<guid isPermaLink="true">https://www.cyberforum.ru/blogs/534277/7598.html</guid>
		</item>
		<item>
			<title>Реализация обработки кнопки сброса сигнализации в Owen Logic</title>
			<link>https://www.cyberforum.ru/blogs/534277/8233.html</link>
			<pubDate>Wed, 06 Sep 2023 17:16:07 GMT</pubDate>
			<description>*Реализация обработки кнопки сброса сигнализации в Owen Logic 
(перевод) 
* 
 
*ВВЕДЕНИЕ* 
 
По...</description>
			<content:encoded><![CDATA[<div><b><font size="+2"><div align="center">Реализация обработки кнопки сброса сигнализации в Owen Logic<br />
(перевод)</div></font></b><br />
<br />
<b>ВВЕДЕНИЕ</b><br />
<br />
По разным причинам заинтересовался программированием для предупредительной противоаварийной защиты (ПАЗ). Вышел на библиотеку PLCopen (набор алгоритмов, диаграмм состояния), которая реализована для различных ПЛК.<br />
<br />
Естественно, что для программируемых реле (ПР) библиотека избыточна и невостребована, особенно в части дублирования и троирования датчиков.<br />
<br />
Но, уже успел прочитать, перевести...<br />
<br />
Предлагаю вольный перевод статьи об одном из элементов библиотеки функциональных блоков (ФБ) для комплекса программирования CoDeSys.<br />
<br />
<a rel="nofollow noopener noreferrer" href="https://content.helpme-codesys.com/en/CODESYS%20Safety%20SIL2/sf_antivalent_fb.html" target="_blank" title="https://content.helpme-codesys.com/en/CODESYS%20Safety%20SIL2/sf_antivalent_fb.html">CODESYS Safety SIL2 - Library: PLCopen Safety FBs - SF_ResetButton</a><br />
<br />
Перевод дополнен изображениями и пояснениями из другого источника - редакции описания библиотеки безопасных элементов PLCOpen<br />
<a rel="nofollow noopener noreferrer" href="https://plcopen.org/system/files/downloads/plcopen_safety_part_1_version_2.01.pdf" target="_blank" title="https://plcopen.org/system/files/downloads/plcopen_safety_part_1_version_2.01.pdf">PLCopen - Technical Committee 5 – Safety Software Technical Specification Part 1: Concepts and Function Blocks Version 2.01 – Official Release</a><br />
<br />
Функциональный блок обработки кнопки сброса сигнализации, несомненно, возможно реализовать проще, чем предлагается в статье - подключив к дискретному входу последовательно функциональные блоки подавления &quot;дребезга&quot; контактов и выделения нарастающего фронта или, даже, одного функционального блока выделения нарастающего фронта.<br />
<br />

			   <div class="spoiler-wrap">
				   <div class="spoiler-head folded clickable">Варианты упрощённой реализации - скрин программы на FBD в среде Owen Logic</div>
				   <div class="spoiler-body">
					   <br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=8240&amp;d=1693753261" rel="Lightbox" id="attachment8240" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=8240&amp;thumb=1&amp;d=1693753261" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: ResetButton_AnyVariants_100.png
Просмотров: 511
Размер:	8.2 Кб
ID:	8240" style="margin: 5px" /></a><br />

				   </div>
			   </div><br />
Но, на примере простого, но достаточно полезного ФБ, было интересно познакомиться со стилем и миром &quot;безопасного&quot; программирования.<br />
<br />
Несмотря на то, что библиотека описывает состав, названия и диапазон значений обязательных входов и выходов, для ФБ SF_ResetButton эти правила не соблюдаются - вход Activate назван ResetRequested, вход S_AutoReset отсутствует (понятно, что с целью эффективной реализации без ненужных сущностей). В исходном тексте присутствуют ошибки - вход ResetRequested в части иллюстраций и текстов назван ResetRequest (как одноимённый выход сопряжённого ФБ).<br />
<br />
В оригинальной статье имеются ссылки на документы:<ul><li>PLCopen – Technical Committee 5 – Safety Software</li>
<li>ISO 13849-1:2015</li>
</ul>Они отсутствуют в свободном доступе и могут быть приобретены за оплату на сайтах соответствующих организаций.<br />
Кроме того, стандарт ISO 13849-1:2015 в настоящий момент уже не действует и заменён на ISO 13849-1:2023.<br />
<br />
<b>1. ПРИМЕНЁННЫЕ СТАНДАРТЫ БЕЗОПАСНОСТИ</b><br />
<br />
SF_ResetButton - это сертифицированный функциональный блок PLCopen. Подробнее о применяемых стандартах смотрите в разделе: &quot;PLCopen – Technical Committee 5 – Safety Software&quot;.<br />
<blockquote>Важно:<br />
Пользователь должен выполнять требования, перечисленные в стандартах.</blockquote>
			   <div class="spoiler-wrap">
				   <div class="spoiler-head folded clickable">Дополнение переводчика</div>
				   <div class="spoiler-body">
					   Стандарты изменяются и необходимо следить за соответствием библиотечных реализаций этим изменениям. По материалам из гл.5.6 PLCopen – Technical Committee 5 – Safety Software:<br />
<br />

<table width="95%"  class="bbcode_maincontainer"><tr><td>
	<div class="bbcode_container">
	  <div class="bbcode_quote">
	    <div class="btbtbt">
		<div class="quote_container">
	      	<div class="bbcode_quote_container"></div>
	       	<div class="bbcode_postedby">
	         	
	        </div>
	        <div class="message">В связи с принятием стандарта ISO 13849-1:2015 функциональность входа от кнопки ручного сброса определяется иначе, чем в спецификации PLCopen: ниспадающий фронт против нарастающего.<br />
Входной сигнал от кнопки сброса нисходящего фронта (F_TRIG) указан в ISO 13849-1, гл. 5.2.2, в то время как сигнал нарастающего фронта (R_TRIG) используется в спецификации PLCopen версии 1.0 (на основе EN 954 -1:1996).<br />
Хотя дальнейшие исследования, проведенные BG и TÜV, оценили эти различные функциональные возможности как эквивалентные с точки зрения безопасности, такого мнения придерживаются не все (новые) эксперты. Что приводит к проблемам при утверждении установок (алгоритмов обработки) в региональных спецификациях, к продолжению дискуссий по этому вопросу или даже к неодобрению установки.<br />
<br />
Чтобы упростить и ускорить принятие установки, что является одной из основных целей спецификации безопасности PLCopen, добавляется поведение &quot;задняя кромка&quot; (trailing edge), сохраняющее обратную совместимость с существующими реализациями и установками, при котором проверяется как нарастающий, так и ниспадающий фронты, в дополнение к двум другим возможностям:<ul><li>нарастающий фронт</li>
<li>ниспадающий фронт</li>
<li>задняя кромка</li>
</ul><br />
Схема изменения требований к обработке кнопки сброс<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=8239&amp;d=1693752177" rel="Lightbox" id="attachment8239" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=8239&amp;thumb=1&amp;d=1693752177" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: Таблица изменений требований к сбросу_ru.png
Просмотров: 315
Размер:	16.4 Кб
ID:	8239" style="margin: 5px" /></a><br />
Примечание: эта схема является неофициальным переводом немецкой версии документа &quot;Manuelle Rückstelleinrichtung&quot; as provided by DGUV Fachbereich Holz und Metall in DGUV-Information 02/2015<br />
<br />
Примечание: &quot;задняя кромка&quot; имеет минимальное и максимальное допустимое время нажатия кнопки<br />
Примечание: сброс при нарастающем фронте и обнаружение ошибки должны выполняться при ниспадающем фронте: перезапуск с ошибкой невозможен</div>
	      </div>
			</div> 
		</div>
	</div>
</td></tr></table>
				   </div>
			   </div><br />
<br />
<b>2. ОПИСАНИЕ ИНТЕРФЕЙСА</b><br />
<br />
Функциональный блок SF_ResetButton выделяет задний фронт сигнала от кнопки (отпускание кнопки) и формирует импульс, начинающийся с нарастающего фронта. Это делается в соответствии с EN ISO 13849-1:2015.<br />
<br />
Входные параметры<br />
<table cellspacing="1" cellpadding="5" class="stg_table tborder sortable" id="sorttable1767"><thead class="thead"><tr><th><div align="center">Имя</div></th><th><div align="center">Тип</div></th><th><div align="center">Начальное </div>значение</th><th>Назначение, описание значений</th></tr></thead><tbody><tr class="alt2"><td>ResetRequested</td><td>BOOL</td><td>TRUE</td><td>Вход, который должен быть подключен к выходу ResetRequest (запрос сброса) сопряженного ФБ<br /><br /> TRUE: требуется сброс<br /> FALSE: не требуется сброс / не проверять вход ResetIn</td></tr><tr class="alt1"><td>ResetIn</td><td>BOOL</td><td>FALSE</td><td>Вход от кнопки сброса<br /><br /> FALSE: кнопка отпущена<br /> TRUE: кнопка сброса нажата оператором.</td></tr><tr class="alt2"><td>TrailingMinimum</td><td>TIME</td><td>T#350ms</td><td>Константа<br /><br /> Минимальное время, в течение которого должна удерживаться нажатой кнопка сброса. Если кнопка удерживалась меньше времени - нажатие игнорируется.<br /><br /> Типовое значение: 350 мс<br /> Минимальное значение: 100 мс<br /> Минимальное значение: 2 цикла ПЛК</td></tr><tr class="alt1"><td>TrailingMaximum</td><td>TIME</td><td>T#2s</td><td>Константа<br /><br /> Максимальное время, в течение которого должна удерживаться кнопка сброса. Если кнопка удерживалась больше времени - нажатие игнорируется.<br /><br /> Типовое значение: 2 с</td></tr></tbody></table><blockquote>Важно:<br />
Выход ResetOut представляет собой импульс нарастающего фронта (R_TRIG) длительностью в один цикл, который может быть подключен к любому ФБ, имеющему вход сброса, и соответствующий соглашениям PLCopen Safety FBs version 1.0.<br />
<br />
Вход ResetRequested соединяется с выходом ResetRequest соответствующего ФБ. При этом контролируется временной интервал, в течение которого необходимо удерживать нажатой кнопку сброса.<br />
<br />
Этот ФБ неявно использует S_AutoReset - т.е. выход из ошибочных состояний выполняется без квитирования, сразу после устранения причины ошибки.</blockquote>Выходные параметры<br />
<table cellspacing="1" cellpadding="5" class="stg_table tborder sortable" id="sorttable3099"><thead class="thead"><tr><th><div align="center">Имя</div></th><th><div align="center">Тип</div></th><th><div align="center">Начальное </div>значение</th><th>Назначение, описание значений</th></tr></thead><tbody><tr class="alt2"><td>Ready</td><td>BOOL</td><td>FALSE</td><td>Состояние активности ФБ<br /><br /> TRUE: Указывает на то, что функциональный блок активирован и выходные результаты действительны (аналогично индикатору &quot;POWER&quot; реле безопасности).<br /> FALSE: Функциональный блок не активен, и программа не выполняется.<br /><br /> Полезно в режиме отладки или для активации / деактивации дополнительных функциональных блоков. Также полезно для дальнейшей обработки в функциональной программе.</td></tr><tr class="alt1"><td>ResetOut</td><td>BOOL</td><td>FALSE</td><td>Импульс для инициирования процедуры сброса у сопряжённого ФБ<br /><br /> Этот импульс генерируется по падению фронта входного сигнала ResetIn (по отпусканию кнопки). Импульс начинается с нарастающего фронта. Длительность -  не менее 1 цикла</td></tr><tr class="alt2"><td>Error</td><td>BOOL</td><td>FALSE</td><td>Индикатор ошибки (такой же, как светодиод &quot;K1/K2&quot; реле безопасности)<br /><br /> TRUE: Значение TRUE указывает на то, что произошла ошибка и ФБ находится в состоянии ошибки. Соответствующее состояние ошибки отражается на выходе диагностического кода DiagCode.<br /> FALSE: Указывает, что ошибки нет и ФБ находится в другом состоянии. Это также отражается в выходе DiagCode.<br /><br /> Полезен в режиме отладки, а также для дальнейшей обработки в функциональной программе.</td></tr><tr class="alt1"><td>DiagCode</td><td>WORD</td><td>16#0000</td><td>Диагностический код (состояния или ошибки). В общем случае, коды определяются по правилам из главы 5.2 PLCopen - Technical Committee 5 – Safety Software Technical Specification Part 1: Concepts and Function Blocks Version 2.01<br /><br />Для данного ФБ определены значения:<br /> 0000 - ожидание активации (Idle),<br /> 83E2 - ожидание нажатия кнопки сброса,<br /> 83F2 - ожидание отпускания кнопки сброса,<br /> 8000 - формирование выходного импульса сброса,<br /> C000 - ошибочные значения входных параметров,<br /> C001 - при активации кнопка сброс уже была нажата,<br /> C3E0 - ошибка (удержание кнопки нажатой превысило максимально разрешённое время),<br /> C3F0 - ошибка (кнопка сброс нажималась, но слишком кратковременно)</td></tr></tbody></table><br />
<b>3. ОПИСАНИЕ РАБОТЫ ФУНКЦИОНАЛЬНОГО БЛОКА</b><br />
<br />
Описание работы функционального блока см. в ISO 13849-1:2015, Section 5.6 &quot;Reset Behavior&quot;.<br />
<br />
Таблица истинности<br />
<table cellspacing="1" cellpadding="5" class="stg_table tborder sortable" id="sorttable9029"><thead class="thead"><tr><th colspan="4">Входы</th><th colspan="4">Выходы</th></tr></thead><tbody><tr class="alt2"><td>ResetRequested</td><td>ResetIn</td><td>TrailingMinimum</td><td>TrailingMaximum</td><td>Ready</td><td>ResetOut</td><td>Error</td><td>DiagCode</td></tr><tr class="alt1"><td>FALSE</td><td>---</td><td>---</td><td>---</td><td>FALSE</td><td>FALSE</td><td>FALSE</td><td>16#0000</td></tr><tr class="alt2"><td>TRUE</td><td>FALSE</td><td>&gt;= 100 мс</td><td>TrailingMaximum &gt;= TrailingMinimum</td><td>TRUE</td><td>FALSE</td><td>FALSE</td><td>16#83E2</td></tr><tr class="alt1"><td>TRUE</td><td>FALSE → TRUE</td><td colspan="2">Начало отсчёта времени удержания нажатой кнопки</td><td>TRUE</td><td>FALSE</td><td>FALSE</td><td>16#83F2</td></tr><tr class="alt2"><td>TRUE</td><td>TRUE</td><td colspan="2">TrailingMinimum &lt; время удержания &lt; TrailingMaximum</td><td>TRUE</td><td>FALSE</td><td>FALSE</td><td>16#83F2</td></tr><tr class="alt1"><td>TRUE</td><td>TRUE → FALSE</td><td colspan="2">TrailingMinimum &lt; время удержания &lt; TrailingMaximum</td><td>TRUE</td><td>TRUE</td><td>FALSE</td><td>16#8000</td></tr><tr class="alt2"><td>TRUE</td><td colspan="3">При следующем вызове происходит автоматический переход с 16#8000 на 16#83E2</td><td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td><td>16#83E2</td></tr></tbody></table><blockquote>Примечание<br />
В таблице истинности стрелка → обозначает переход. TRUE → FALSE означает, что значение переменной изменилось с TRUE на FALSE.</blockquote><b>4. ДИАГРАММА СОСТОЯНИЙ</b><br />
<br />
Диаграмма состояний ФБ SF_ResetButton<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=8241&amp;d=1693756439" rel="Lightbox" id="attachment8241" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=8241&amp;thumb=1&amp;d=1693756439" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: StateDiagram_200.png
Просмотров: 445
Размер:	42.5 Кб
ID:	8241" style="margin: 5px" /></a><blockquote><b>Примечания</b><br />
<br />
В главе 5.2.2 стандарта ISO 13849-1 упоминается только ниспадающий фронт. Таким образом, если ниспадающий фронт появляется одновременно с включением функции безопасности / защиты, сброс от кнопки может быть доступен.<br />
<br />
Переход из любого состояния в состояние ожидания активации (Idle) из-за Activate = FALSE не отображается. Однако эти переходы имеют наивысший приоритет.</blockquote><b>5. ТИПИЧНАЯ ВРЕМЕННАЯ ДИАГРАММА</b><br />
<br />
Типичная временная диаграмма с подключённым для примера библиотечным ФБ SF_ESPE<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=8242&amp;d=1693758169" rel="Lightbox" id="attachment8242" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=8242&amp;thumb=1&amp;d=1693758169" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: uuid-2d9cb87f-d84f-8d19-9983-17f0171bd89a.png
Просмотров: 313
Размер:	13.2 Кб
ID:	8242" style="margin: 5px" /></a><blockquote><b>Примечание</b><br />
<br />
(*) Safety Function / Safety Guard Operative (Функции безопасности / защиты) - это внутреннее состояние ФБ в соответствии со стандартом ISO 13849-1:2015, (глава 5.2.2) - доступно только в том случае, если все функции безопасности и защиты работают. В данном примере это представлено параметром S_ESPE_Out функционального блока SF_ESPE.</blockquote><b>6. ДИАГНОСТИРУЕМЫЕ ОШИБКИ</b><br />
<br />
Ошибка формируется в случаях:<ul><li>Если значение ResetIn = TRUE при переключении ResetRequested в TRUE.</li>
<li>Если ResetRequested = ResetIn = TRUE, а длительность удержания кнопки сброс в нажатом состоянии не достигла минимального значение задержки времени TrailingMinimum или превысила максимальное значение задержки времени TrailingMaximum.</li>
</ul><br />
<b>7. ОБРАБОТКА ОШИБКИ</b><br />
<br />
В случае статического сигнала TRUE на входе ResetIn, на выходе DiagCode отображается соответствующий код ошибки, и на выходе Error устанавливается значение TRUE.<br />
<br />
<b>8. СПЕЦИФИЧНЫЕ ДЛЯ ФБ КОДЫ ОШИБОК И СОСТОЯНИЙ</b><br />
<br />
Коды ошибок<br />
<table cellspacing="1" cellpadding="5" class="stg_table tborder sortable" id="sorttable7665"><thead class="thead"><tr><th><div align="center">Значение</div>DiagCode</th><th>Название состояния</th><th>Описание состояния и установок выходных сигналов</th></tr></thead><tbody><tr class="alt2"><td>16#C000</td><td>Parameter Error</td><td>Ошибочные значения входных параметров<br />TrailingMinimum &gt; TrailingMaximum OR TrailingMinimum &lt; 100 мс<br /><br />Ready = TRUE<br /><br />ResetOut = FALSE<br /><br />Error = TRUE</td></tr><tr class="alt1"><td>16#C001</td><td>Reset Error</td><td>При активации ФБ кнопка сброс уже была нажата<br />ResetIn = TRUE. Ожидание отпускания кнопки (NOT ResetIn)<br /><br />Ready = TRUE<br /><br />ResetOut = FALSE<br /><br />Error = TRUE</td></tr><tr class="alt2"><td>16#C3E0</td><td>Error Trailing Maximum</td><td>Удержание кнопки нажатой превысило максимально разрешённое время<br />Время удержания нажатой кнопки (ResetIn) достигло TrailingMaximum до прихода ниспадающего фронта (F_TRIG) от ResetIn. Ожидание нарастающего фронта (R_TRIG) от ResetIn (отпускания кнопки) для повторной попытки сформировать сигнал сброса<br /><br />Ready = TRUE<br /><br />ResetOut = FALSE<br /><br />Error = TRUE</td></tr><tr class="alt1"><td>16#C3F0</td><td>Error Trailing Minimum</td><td>Кнопка сброс нажималась, но слишком кратковременно<br />Ниспадающий фронт от ResetIn пришёл прежде, чем прошло время TrailingMinimum. Ожидание нарастающего фронта от ResetIn.<br /><br />Ready = TRUE<br /><br />ResetOut = FALSE<br /><br />Error = TRUE</td></tr></tbody></table><br />
Коды состояний<br />
<table cellspacing="1" cellpadding="5" class="stg_table tborder sortable" id="sorttable7469"><thead class="thead"><tr><th><div align="center">Значение</div>DiagCode</th><th>Название состояния</th><th>Описание состояния и установок выходных сигналов</th></tr></thead><tbody><tr class="alt2"><td>16#0000</td><td>Idle</td><td>The function block is not active (initial state)<br /><br />Ready = FALSE<br /><br />ResetOut = FALSE<br /><br />Error = FALSE</td></tr><tr class="alt1"><td>16#83E2</td><td>Wait for R_TRIG</td><td>The function block is enabled. Waiting for R_TRIG at ResetIn<br /><br />Ready = TRUE<br /><br />ResetOut = FALSE<br /><br />Error = FALSE</td></tr><tr class="alt2"><td>16#83F2</td><td>Wait for F_TRI</td><td>ResetIn = TRUE. Waiting for F_TRIG at ResetIn<br /><br />Ready = TRUE<br /><br />ResetOut = FALSE<br /><br />Error = FALSE</td></tr><tr class="alt1"><td>16#8000</td><td>Reset Detected</td><td>Valid reset behavior was detected. The state is valid for at least one cycle and will automatically transfer to 83E2.<br /><br />Ready = TRUE<br /><br />ResetOut = TRUE<br /><br />Error = FALSE<br /><br /></td></tr></tbody></table><br />
<b>9. ВАРИАНТЫ ИСПОЛЬЗОВАНИЯ (дополнение)</b><br />
<blockquote><b>Примечание</b><br />
Это дополнение - перевод раздела 5.6 из документа<br />
PLCopen - Technical Committee 5 – Safety Software Technical Specification Part 1: Concepts and Function Blocks Version 2.01 – Official Release<br />
</blockquote>Функциональный блок SF_ResetButton используется самостоятельно (раздельно от других ФБ), но при нормальной работе должен быть связан с соответствующим ФБ, которому необходим функционал сброса.<br />
Разделение упрощает задачу разработчику. Однако по отношению к пользователю лучше инкапсулировать каждую пару ФБ и,<br />
таким образом, предоставлять их как один объект. Это означает, что также необходимо выровнять диагностические коды DiagCode (на рисунке ниже это выравнивание DiagCodes показано не в деталях, а только через линию, ведущую к ФБ безопасности, на практике для их объединения будет достаточно сочетания AND и (исключающего) OR или их комбинации), выходы Error можно просто объединить с помощью функции AND.<br />
<br />
Вариант использования совместно с ФБ SF_ESPE<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=8243&amp;d=1693762925" rel="Lightbox" id="attachment8243" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=8243&amp;thumb=1&amp;d=1693762925" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: Sample.png
Просмотров: 423
Размер:	30.4 Кб
ID:	8243" style="margin: 5px" /></a><br />
<br />
В PLCopen - Technical Committee 5 – Safety Software Technical Specification показана возможность подачи константы TRUE на вход ResetRequested вместо выхода ResetRequest связанного блока. Но этот способ признавался спорным.<br />
<br />
<b>ВЫВОДЫ И РЕКОМЕНДАЦИИ</b><br />
<br />
Воспроизведён функциональный блок из библиотеки PLCopen в переложении на FBD реализации Owen Logic.<br />
<br />
Этот ФБ, являясь полезным для большинства приложений, при реализации позволяет начать знакомство с другим подходом к программированию, с готовой и хорошо продуманной, описанной библиотекой со множеством ФБ.<br />
<br />
После появления возможности создания ФБ на ST, хорошо бы вспомнить о существовании для CoDeSys таких библиотек, как OSCAT, PLCopen. Это сократит время на реализацию давно созданных элементов.<br />
<br />
<b>ПРИЛОЖЕНИЕ</b><br />
<br />
На FBD несколько неудобно реализовывать конечный автомат, поэтому при реализации состояния ошибок с автосбросом собственных триггеров не имеют.<br />
<br />
Уже после реализации ФБ на языке FBD в среде Owen Logic добавилась возможность реализации функциональных блоков на языке ST. Но эти ФБ на ST не поддерживают &quot;стандартные&quot; макросы (TON, TOF и т.д). Поэтому реализация остаётся на языке FBD.<br />
<br />
На настоящий момент среда программирования Owen Logic имеет ограничение на длину имени макроса (функционального блока) на языке FBD, поэтому при реализации пришлось его изменить на ResetButton.<br />

			   <div class="spoiler-wrap">
				   <div class="spoiler-head folded clickable">Скрины ФБ SafeReset на FBD</div>
				   <div class="spoiler-body">
					   Скриншот разделён на несколько частей из-за ограничений на размеры прикрепляемых изображений.<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=8244&amp;d=1693972234" rel="Lightbox" id="attachment8244" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=8244&amp;thumb=1&amp;d=1693972234" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: SafeReset_2.png
Просмотров: 335
Размер:	24.9 Кб
ID:	8244" style="margin: 5px" /></a><br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=8245&amp;d=1693972234" rel="Lightbox" id="attachment8245" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=8245&amp;thumb=1&amp;d=1693972234" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: SafeReset_3.png
Просмотров: 338
Размер:	35.6 Кб
ID:	8245" style="margin: 5px" /></a><br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=8251&amp;d=1694015569" rel="Lightbox" id="attachment8251" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=8251&amp;thumb=1&amp;d=1694015569" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: SafeReset_4.png
Просмотров: 357
Размер:	53.5 Кб
ID:	8251" style="margin: 5px" /></a><br />

				   </div>
			   </div><br />
Исходники макроса и тестовой программы для ПР205-230.1211.22.X.0 в OWEN Logic версия 2.4.334.0<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=8252&amp;d=1694016955" >Test_SF_ResetButton.owle.7z</a></div>

]]></content:encoded>
			<dc:creator>ФедосеевПавел</dc:creator>
			<guid isPermaLink="true">https://www.cyberforum.ru/blogs/534277/8233.html</guid>
		</item>
		<item>
			<title><![CDATA[Сохранение параметров в файле при помощи библиотеки OwenLibFileAsyn&shy;­­­­­­­­­­­­­­­&shy;­­­­­­­­­­­­­­­&shy;­­­­c]]></title>
			<link>https://www.cyberforum.ru/blogs/534277/7956.html</link>
			<pubDate>Sun, 05 Mar 2023 19:47:59 GMT</pubDate>
			<description>*Сохранение параметров в файле при помощи библиотеки OwenLibFileAsyn­c 
* 
 
*1. ВВЕДЕНИЕ* 
...</description>
			<content:encoded><![CDATA[<div><b><div align="center"><font size="+2">Сохранение параметров в файле при помощи библиотеки OwenLibFileAsyn­c</font></div></b><br />
<br />
<b>1. ВВЕДЕНИЕ</b><br />
<br />
Работа с файлами в документации к этой библиотеке описана крайне скудно и содержит ошибки:<ul><li>нет описания почему требуется каждую функцию вызывать дважды и ожидать разных результатов,</li>
<li>в списке описаний имена функций имеют вид &quot;OwenSysFileXXX&quot;, главы с подробным описанием функций &quot;SysFileXXX&quot;, а сама библиотека содержит &quot;OwenFileXXX&quot;,</li>
<li>нет описаний, что произойдёт при отсутствии открываемого файла.</li>
</ul>И множество других недочётов, которые предполагается устранить методом чтения нескольких тем на форуме, в которых присутствуют примеры, пояснения представителей технической поддержки, примеров программ, а также руководства пользователя по программированию ПЛК1хх[М02] в CODESYS. Всё очень разрозненно.<br />
<br />
Форум и документация вместе помогли разобраться с поставленной задачей сохранения и быстрого восстановления настроек параметров техпроцесса.<br />
<br />
Соберу в одном месте цитаты из нескольких тем, поясняющие детали работы с библиотекой, а также соберу минимальный тестовый пример, демонстрирующий ввод, контроль, сохранение и восстановление параметров настройки системы автоматики на основе Овен ПЛК1хх[M02].<br />
<br />
<b>2. ВОПРОСЫ И ОТВЕТЫ</b><br />
<br />
<u>Какое назначение библиотеки?</u><br />
Асинхронная библиотека это просто надстройка для асинхронного доступа над стандартной SysLibFile. И место на диске определяется аналогично как через SysLibFile<br />
И функции возвращают 2 значения, состояние асинхронной машины и *когда возвратится DONE) - ответ соответствующей функции SysLibFile, через указатель.<br />
<br />
Асинхронная библиотека нами сделана как аналог аналогичной библиотеки в CDS3<br />
Интерфейс отличается, т.к. мы не можем научить компилятор CDS2.3 создавать задачи.<br />
Для ввода/вывода небольших файлов на внутреннюю flash/ram диск асинхронная библиотека не даст суперпреимуществ, т.к. задержки в худшем случае будкт порядка единиц/десятков мс.<br />
Однако при использовании внешних накопителей USB, особенно в бытовом исполнении - на тестах наблюдались задержки до 4 секунд, из-за реализации обмена внутри USB-Flash.<br />
Т.о. для больших файлов и внешних накопителей - must have<br />
Для стабильного цикла в единицы мс- must have<br />
Во всех остальных случаях можно по старинке.<br />
P.S. CAA - это просто обертка SysLibFile и на 2-м кодесисе никакой асинхронности там нету.<br />
<br />
Библиотека асинхронного доступа нужна для ускорения работы цикла управления.<br />
Т.к. появились внешние флешки, а они могут тормозить операцию (например записи) до секунды спокойно, то при использовании обычной синхронной библиотеки весь цикл управления на 1 секунду замирает.<br />
Что неприемлемо.<br />
Доступ к внутренней Flash, конечно быстрее, но тоже возможны задержки при записи.<br />
Поэтому мы настоятельно рекомендуем в новых проектах использовать только функции асинхронного доступа к файлам.<br />
<br />
<u>Что вернет функция OwenFileOpenAsync(NAME, 'r', ADR(handle)), если на Flash не окажется файла с указанным в функции именем файла ?</u><br />
Вернет сначала ASYNC_WORKING а затем ASYNC_DONE. А handle будет равен NULL.<br />
<br />
Ф<u>ункция SysFileCopyAsync сама по себе не создает файлы? Т.е., для копирования при отсутствии файла приемника сначала нужно<br />
- создать файл OwenFileOpenAsync(NAME, 'w', ADR(handle));<br />
- закрыть файл OwenFileCloseAsync(handle, ADR(result));<br />
- скопировать один файл в другой OwenFileCopyAsync(NAME, NAME2, result)...<br />
или по-другому?</u><br />
Да, файл сначала надо создать.<br />
<br />
<u>Как просмотреть содержимое не только внутреннего диска, но и USB Flash, RAM-диск?</u><br />
если набрать <code class="inlinecode">filedir</code> - видим внутренний диск. Если набрать <code class="inlinecode">filedir usb</code> - то флешку, Если <code class="inlinecode">filedir ram</code> - то RAM-диск<br />
<br />
<u>ASYNC_BLOCK_ACCESS возвращается если есть доступ к библиотеке с др. запросом до окончания первого.<br />
Как же тогда возможность работы с 5 файлами?</u><br />
Тогда точно будет доступ к библиотеке с другим запросом, до окончания первого.<br />
<br />
<u>Что делать с этим ASYNC_BLOCK_ACCESS?<br />
Только перезагружать?</u><br />
5 одновременных запросов к РАЗНЫМ файлам<br />
<br />
<u>Как отформатировать диск?</u><br />
Для форматирование есть команда в ПЛК-Браузер formatFFS<br />
<br />
<u>Пояснения к примеру</u><br />

			   <div class="spoiler-wrap">
				   <div class="spoiler-head folded clickable">Пример: 110_60_click_asyncfile.pro из архива Пока ГТП спит.zip</div>
				   <div class="spoiler-body">
					   <div class="codeblock"><table class="pascal"><thead><tr><td colspan="2" id="447470791"  class="head">Pascal</td></tr></thead><tbody><tr class="li1"><td><div id="447470791" style="height: 222px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
</pre></td><td class="de1"><pre class="de1"><span class="kw1">PROGRAM</span> PLC_PRG
<span class="kw1">VAR</span>
&nbsp; &nbsp; b<span class="sy1">:</span> DWORD<span class="sy1">;</span>
&nbsp; &nbsp; state<span class="sy1">:</span>DWORD<span class="sy1">:</span><span class="sy3">=</span><span class="nu0">0</span><span class="sy1">;</span>
&nbsp; &nbsp; state_res<span class="sy1">:</span>DWORD<span class="sy1">:</span><span class="sy3">=</span><span class="nu0">0</span><span class="sy1">;</span>
&nbsp; &nbsp; handle<span class="sy1">:</span>DWORD<span class="sy1">:</span><span class="sy3">=</span><span class="nu0">0</span><span class="sy1">;</span>
&nbsp; &nbsp; res<span class="sy1">:</span> ASYNC_RET_VALUE<span class="sy1">;</span>
&nbsp; &nbsp; result<span class="sy1">:</span>DWORD<span class="sy1">;</span>
&nbsp; &nbsp; bufout<span class="sy1">:</span><span class="kw4">STRING</span><span class="br0">&#40;</span><span class="nu0">80</span><span class="br0">&#41;</span><span class="sy1">:</span><span class="sy3">=</span><span class="st0">'Hello, Kitty$L$N'</span><span class="sy1">;</span>
&nbsp; &nbsp; bufin<span class="sy1">:</span><span class="kw4">STRING</span><span class="br0">&#40;</span><span class="nu0">80</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; counter<span class="sy1">:</span>DWORD<span class="sy1">:</span><span class="sy3">=</span><span class="nu0">0</span><span class="sy1">;</span>
END_VAR</pre></td></tr></table></div></td></tr></tbody></table></div><div class="codeblock"><table class="pascal"><thead><tr><td colspan="2" id="568994609"  class="head">Pascal</td></tr></thead><tbody><tr class="li1"><td><div id="568994609" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
</pre></td><td class="de1"><pre class="de1">out<span class="sy1">:</span><span class="sy3">=</span> <span class="kw1">in</span><span class="sy1">;</span>
<span class="kw1">IF</span> <span class="br0">&#40;</span>b&gt;<span class="sy3">=</span><span class="nu0">300</span><span class="br0">&#41;</span> <span class="kw1">THEN</span>
b<span class="sy1">:</span><span class="sy3">=</span><span class="nu0">0</span><span class="sy1">;</span>
a<span class="sy1">:</span><span class="sy3">=</span>a <span class="kw1">XOR</span> <span class="nu0">1</span><span class="sy1">;</span>
END_IF
&nbsp;
b<span class="sy1">:</span><span class="sy3">=</span>b<span class="sy3">+</span><span class="nu0">1</span><span class="sy1">;</span>
<span class="kw1">CASE</span> state_res <span class="kw1">OF</span>
&nbsp;
&nbsp; &nbsp; <span class="nu0">0</span><span class="sy1">:</span>
&nbsp; &nbsp; res<span class="sy1">:</span><span class="sy3">=</span>OwenFileOpenAsync<span class="br0">&#40;</span><span class="st0">'usb:test.dat'</span><span class="sy1">,</span><span class="st0">'a'</span><span class="sy1">,</span>ADR<span class="br0">&#40;</span>handle<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; <span class="kw1">IF</span> res<span class="sy3">=</span>ASYNC_WORKING <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; state<span class="sy1">:</span><span class="sy3">=</span><span class="nu0">1</span><span class="sy1">;</span>
&nbsp; &nbsp; END_IF
&nbsp;
&nbsp;
&nbsp; &nbsp; <span class="nu0">1</span><span class="sy1">:</span>
&nbsp; &nbsp; res<span class="sy1">:</span><span class="sy3">=</span>OwenFileOpenAsync<span class="br0">&#40;</span><span class="st0">'test.dat'</span><span class="sy1">,</span><span class="st0">'a'</span><span class="sy1">,</span>ADR<span class="br0">&#40;</span>handle<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; <span class="kw1">IF</span> res<span class="sy3">=</span>ASYNC_DONE <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> handle&lt;&gt;<span class="nu0">0</span> <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; state<span class="sy1">:</span><span class="sy3">=</span><span class="nu0">2</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">ELSE</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; state<span class="sy1">:</span><span class="sy3">=</span><span class="nu0">0</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; END_IF
&nbsp; &nbsp; ELSIF res&lt;<span class="nu0">0</span> <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; state<span class="sy1">:</span><span class="sy3">=</span><span class="nu0">0</span><span class="sy1">;</span>
&nbsp; &nbsp; END_IF
&nbsp;
&nbsp;
&nbsp;
&nbsp; &nbsp; <span class="nu0">2</span><span class="sy1">:</span>
&nbsp; &nbsp; res<span class="sy1">:</span><span class="sy3">=</span>OwenFileWriteAsync<span class="br0">&#40;</span>handle<span class="sy1">,</span>ADR<span class="br0">&#40;</span>bufout<span class="br0">&#41;</span><span class="sy1">,</span><span class="nu0">14</span><span class="sy1">,</span>ADR<span class="br0">&#40;</span>result<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; <span class="kw1">IF</span> res<span class="sy3">=</span>ASYNC_WORKING <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; state<span class="sy1">:</span><span class="sy3">=</span><span class="nu0">3</span><span class="sy1">;</span>
&nbsp; &nbsp; <span class="kw1">ELSE</span>
&nbsp; &nbsp; &nbsp; &nbsp; state<span class="sy1">:</span><span class="sy3">=</span><span class="nu0">6</span><span class="sy1">;</span>
&nbsp; &nbsp; END_IF
&nbsp;
&nbsp;
&nbsp;
&nbsp; &nbsp; <span class="nu0">3</span><span class="sy1">:</span>
&nbsp; &nbsp; res<span class="sy1">:</span><span class="sy3">=</span>OwenFileWriteAsync<span class="br0">&#40;</span>handle<span class="sy1">,</span>ADR<span class="br0">&#40;</span>bufout<span class="br0">&#41;</span><span class="sy1">,</span><span class="nu0">14</span><span class="sy1">,</span>ADR<span class="br0">&#40;</span>result<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; <span class="kw1">IF</span> res<span class="sy3">=</span>ASYNC_DONE <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> result<span class="sy3">=</span><span class="nu0">14</span> <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; state<span class="sy1">:</span><span class="sy3">=</span><span class="nu0">4</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">ELSE</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; state<span class="sy1">:</span><span class="sy3">=</span><span class="nu0">6</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; END_IF
&nbsp; &nbsp; ELSIF res&lt;<span class="nu0">0</span> <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; state<span class="sy1">:</span><span class="sy3">=</span><span class="nu0">6</span><span class="sy1">;</span>
&nbsp; &nbsp; END_IF
&nbsp;
&nbsp;
&nbsp;
&nbsp; &nbsp; <span class="nu0">4</span><span class="sy1">:</span>
&nbsp; &nbsp; res<span class="sy1">:</span><span class="sy3">=</span>OwenFileReadAsync<span class="br0">&#40;</span>handle<span class="sy1">,</span>ADR<span class="br0">&#40;</span>bufin<span class="br0">&#41;</span><span class="sy1">,</span><span class="nu0">14</span><span class="sy1">,</span>ADR<span class="br0">&#40;</span>result<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; <span class="kw1">IF</span> res<span class="sy3">=</span>ASYNC_WORKING <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; state<span class="sy1">:</span><span class="sy3">=</span><span class="nu0">5</span><span class="sy1">;</span>
&nbsp; &nbsp; <span class="kw1">ELSE</span>
&nbsp; &nbsp; &nbsp; &nbsp; state<span class="sy1">:</span><span class="sy3">=</span><span class="nu0">6</span><span class="sy1">;</span>
&nbsp; &nbsp; END_IF
&nbsp;
&nbsp;
&nbsp;
&nbsp; &nbsp; <span class="nu0">5</span><span class="sy1">:</span>
&nbsp; &nbsp; res<span class="sy1">:</span><span class="sy3">=</span>OwenFileReadAsync<span class="br0">&#40;</span>handle<span class="sy1">,</span>ADR<span class="br0">&#40;</span>bufin<span class="br0">&#41;</span><span class="sy1">,</span><span class="nu0">14</span><span class="sy1">,</span>ADR<span class="br0">&#40;</span>result<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; <span class="kw1">IF</span> res<span class="sy3">=</span>ASYNC_DONE <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> result&gt;<span class="sy3">=</span><span class="nu0">0</span> <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; state<span class="sy1">:</span><span class="sy3">=</span><span class="nu0">6</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; counter<span class="sy1">:</span><span class="sy3">=</span>counter<span class="sy3">+</span><span class="nu0">1</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">ELSE</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; state<span class="sy1">:</span><span class="sy3">=</span><span class="nu0">6</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; END_IF
&nbsp; &nbsp; ELSIF res&lt;<span class="nu0">0</span> <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; state<span class="sy1">:</span><span class="sy3">=</span><span class="nu0">6</span><span class="sy1">;</span>
&nbsp; &nbsp; END_IF
&nbsp;
&nbsp;
&nbsp;
&nbsp; &nbsp; <span class="nu0">6</span><span class="sy1">:</span>
&nbsp; &nbsp; res<span class="sy1">:</span><span class="sy3">=</span>OwenFileCloseAsync<span class="br0">&#40;</span>handle<span class="sy1">,</span>ADR<span class="br0">&#40;</span>result<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; <span class="kw1">IF</span> res<span class="sy3">=</span>ASYNC_WORKING <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; state<span class="sy1">:</span><span class="sy3">=</span><span class="nu0">7</span><span class="sy1">;</span>
&nbsp; &nbsp; <span class="kw1">ELSE</span>
&nbsp; &nbsp; &nbsp; &nbsp; state<span class="sy1">:</span><span class="sy3">=</span><span class="nu0">0</span><span class="sy1">;</span>
&nbsp; &nbsp; END_IF
&nbsp;
&nbsp;
&nbsp;
&nbsp; &nbsp; <span class="nu0">7</span><span class="sy1">:</span>
&nbsp; &nbsp; res<span class="sy1">:</span><span class="sy3">=</span>OwenFileCloseAsync<span class="br0">&#40;</span>handle<span class="sy1">,</span>ADR<span class="br0">&#40;</span>result<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; <span class="kw1">IF</span> res<span class="sy3">=</span>ASYNC_DONE <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> result<span class="sy3">=</span><span class="nu0">0</span> <span class="kw1">THEN</span>
&nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; state<span class="sy1">:</span><span class="sy3">=</span><span class="nu0">0</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">ELSE</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; state<span class="sy1">:</span><span class="sy3">=</span><span class="nu0">0</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; END_IF
&nbsp; &nbsp; ELSIF res&lt;<span class="nu0">0</span> <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; state<span class="sy1">:</span><span class="sy3">=</span><span class="nu0">0</span><span class="sy1">;</span>
&nbsp; &nbsp; END_IF
&nbsp;
&nbsp;
<span class="kw1">ELSE</span>
&nbsp; &nbsp; state<span class="sy1">:</span><span class="sy3">=</span><span class="nu0">0</span><span class="sy1">;</span>
END_CASE
&nbsp;
state_res<span class="sy1">:</span><span class="sy3">=</span>state<span class="sy1">;</span></pre></td></tr></table></div></td></tr></tbody></table></div>
				   </div>
			   </div><br />
в case 2 Вы задаёте библиотеке следующую команду, в Вашем случае на запись.<br />
а в case 3 вы периодически опрашиваете библиотеку: &quot;Не выполнила ли ты мою команду&quot;<br />
Собственно запись в файл происходит асинхронно в промежутке между case2 и ответом ASYNC_DONE в case3.<br />
И подавать на вход функции записи строку надо всегда одну и ту же и не менять её значение до ASYNC_DONE! Стековые переменные для данных записи не использовать, только статические или глобальные переменные.<br />
Если Вы получили строку/массив через входные параметры функции/ФБ - это 95% стековая переменная.<br />
Если Вы получили переменную по указателю - её содержимое внезапно может поменяться где-то там и Вы получите трудноотлаживаемую ошибку.<br />
Если Вы объявили переменную в блоке VAR - она статическая<br />
Если Вы объявили переменную в блоке VAR_GLOBAL - она глобальная<br />
<br />
ASYNC_BLOCK_ACESS - Значит в пользовательском коде есть логическая ошибка<br />
ASYNC_PAUSED - подождать и повторить запрос<br />
<br />
<u>Заказчик жалуется на то, что файлы, которые он забирает с флешки всегда сохранены в 1980м году, хотя в ПЛК110 время выставлено актуальное и в названии файлов дата сохранения указанна верно.<br />
Неудобно ему пересохранять файлы. Если просто складывать есть трудности сортировки по дате.<br />
</u>Время создания файла всегда будет некорректное это ограничение файловой систем. Вопрос закрыт<br />
<br />
<b>3. ОПИСАНИЕ АЛГОРИТМА СОХРАНЕНИЯ И ВОССТАНОВЛЕНИЯ ПАРАМЕТРОВ НАСТРОЙКИ</b><br />
<br />
Программа в ПЛК содержит переменные, вводимые с панели оператора и буфер обмена с файлами, содержащий все эти переменные.<br />
По команде &quot;записать в файл&quot;, переменные копируются в буфер и содержимое буфера сохраняется в файле.<br />
По команде &quot;прочитать из файла&quot;, содержимое файла копируются в буфер, но переменные остаются неизменными.<br />
По команде &quot;установить в буфере настройки по умолчанию&quot;, в буфер записываются значения &quot;по умолчанию&quot;, но переменные остаются неизменными.<br />
По команде &quot;скопировать настройки из промежуточного буфера&quot;, содержимое буфера копируется в переменные.<br />
Таким образом, появляется возможность ознакомления с сохранёнными ранее настройками и сравнения их с актуальными.<br />
<br />
Параметры, вводимые с панели оператора, предлагаю подвергать проверке на соответствие (на диапазон, на соотношение) в отдельном процессе.<br />
<br />
<b>4. ТИПЫ ДАННЫХ И ПЕРЕМЕННЫЕ</b><br />
<br />
В документации нет описания типа ASYNC_RET_VALUE, но его можно посмотреть, открыв библиотеку OwenLibFileAsyn­c в CoDeSys. Приведу его, чтобы было проще понимать алгоритм:<br />
<div class="codeblock"><table class="pascal"><thead><tr><td colspan="2" id="880751790"  class="head">Pascal</td></tr></thead><tbody><tr class="li1"><td><div id="880751790" style="height: 238px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
</pre></td><td class="de1"><pre class="de1"><span class="kw1">TYPE</span> ASYNC_RET_VALUE <span class="sy1">:</span>
<span class="br0">&#40;</span>
&nbsp;
&nbsp; &nbsp; &nbsp; ASYNC_PAUSED<span class="sy1">:</span><span class="sy3">=</span> <span class="sy3">-</span><span class="nu0">1000</span><span class="sy1">,</span> <span class="coMULTI">(*Система по своим внутренним причинам приостановила обработку асинхронных запросов*)</span>
&nbsp; &nbsp; ASYNC_QUERY_FULL<span class="sy1">:</span><span class="sy3">=</span> <span class="sy3">-</span><span class="nu0">1001</span><span class="sy1">,</span> <span class="coMULTI">(*&gt;5 запросов в очереди*)</span>
&nbsp; &nbsp; ASYNC_BLOCK_ACCESS <span class="sy1">:</span><span class="sy3">=</span> <span class="sy3">-</span><span class="nu0">1002</span><span class="sy1">,</span><span class="coMULTI">(*Запрос к уже обрабатываемому объекту с другой функцией*)</span>
&nbsp; &nbsp; ASYNC_GENERAL_ERROR<span class="sy1">:</span><span class="sy3">=</span> <span class="sy3">-</span><span class="nu0">1003</span><span class="sy1">,</span>
&nbsp; &nbsp; ASYNC_INVALID_HANDLE_ERROR<span class="sy1">:</span><span class="sy3">=</span> <span class="sy3">-</span><span class="nu0">1004</span><span class="sy1">,</span> <span class="coMULTI">(*Запрос к неоткрытому/открытому не через асинхронную библиотеку файлу*)</span>
&nbsp; &nbsp; ASYNC_WORKING<span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">16</span><span class="re1">#7</span>FFE<span class="sy1">,</span>
&nbsp; &nbsp; ASYNC_DONE<span class="sy1">:</span><span class="sy3">=</span><span class="nu0">16</span><span class="re1">#7</span>FFF
&nbsp;
<span class="br0">&#41;</span><span class="sy1">;</span>
END_TYPE</pre></td></tr></table></div></td></tr></tbody></table></div>Для чтения и записи всех параметров одновременно удобно объявить пользовательский тип - структуру PARAMETERS. Переменная этого типа будет являться буфером для файлового обмена. В качестве полей она содержит все переменные, вводимые с операторской панели.<br />
<div class="codeblock"><table class="pascal"><thead><tr><td colspan="2" id="741536243"  class="head">Pascal</td></tr></thead><tbody><tr class="li1"><td><div id="741536243" style="height: 222px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
</pre></td><td class="de1"><pre class="de1"><span class="kw1">TYPE</span> PARAMETERS <span class="sy1">:</span>
STRUCT
&nbsp; &nbsp; tRevers_2<span class="sy1">,</span> tTimeOut_2<span class="sy1">,</span>
&nbsp; &nbsp; tRevers_3<span class="sy1">,</span> tTimeOut_3<span class="sy1">,</span>
&nbsp; &nbsp; tRevers_4<span class="sy1">,</span> tTimeOut_4<span class="sy1">,</span>
&nbsp; &nbsp; tRevers_5<span class="sy1">,</span> tTimeOut_5<span class="sy1">,</span>
&nbsp; &nbsp; tRevers_6<span class="sy1">,</span> tTimeOut_6<span class="sy1">:</span> <span class="kw4">WORD</span><span class="sy1">;</span>
&nbsp; &nbsp; tLowLevel<span class="sy1">,</span> tHighLevel<span class="sy1">,</span> tOrderLevel<span class="sy1">:</span> <span class="kw4">WORD</span><span class="sy1">;</span>
&nbsp; &nbsp; tT20<span class="sy1">:</span> <span class="kw4">WORD</span><span class="sy1">;</span>
&nbsp; &nbsp; tDelayStartFeed<span class="sy1">,</span> tDelayStopFeed<span class="sy1">:</span> <span class="kw4">WORD</span><span class="sy1">;</span>
END_STRUCT
END_TYPE</pre></td></tr></table></div></td></tr></tbody></table></div>Для чтения и записи файла потребуются следующие переменные:<br />
<div class="codeblock"><table class="pascal"><thead><tr><td colspan="2" id="838731505"  class="head">Pascal</td></tr></thead><tbody><tr class="li1"><td><div id="838731505" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
</pre></td><td class="de1"><pre class="de1"><span class="coMULTI">(* команды от панели *)</span>
&nbsp; &nbsp; bReadFile<span class="sy1">:</span> BOOL<span class="sy1">;</span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="coMULTI">(* команда - прочитать из файла в промежуточный буфер *)</span>
&nbsp; &nbsp; bWriteFile<span class="sy1">:</span> BOOL<span class="sy1">;</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="coMULTI">(* команда - записать настройки в файл *)</span>
&nbsp; &nbsp; bCopyBufferToParams<span class="sy1">:</span> BOOL<span class="sy1">;</span>&nbsp; <span class="coMULTI">(* команда - скопировать настройки из промежуточного буфера *)</span>
&nbsp; &nbsp; bSetDefaultParams<span class="sy1">:</span> BOOL<span class="sy1">;</span>&nbsp; &nbsp; <span class="coMULTI">(* команда - установить в буфере настройки &quot;по умолчанию&quot; *)</span>
<span class="coMULTI">(* переменные для работы с файлами *)</span>
&nbsp; &nbsp; sFileName<span class="sy1">:</span> <span class="kw4">STRING</span> <span class="sy1">:</span><span class="sy3">=</span> <span class="st0">'ram:configure.dat'</span><span class="sy1">;</span> &nbsp; <span class="coMULTI">(* имя файла *)</span>
&nbsp; &nbsp; parParameters<span class="sy1">:</span> PARAMETERS<span class="sy1">;</span>&nbsp; <span class="coMULTI">(* буферная переменная, содержашая все параметры настройки *)</span>
<span class="coMULTI">(* - чтение файла *)</span>
&nbsp; &nbsp; iFileReadState<span class="sy1">,</span>
&nbsp; &nbsp; iFileReadState_Next<span class="sy1">:</span> INT<span class="sy1">;</span> &nbsp; <span class="coMULTI">(* стадии конечного автомата чтения из файла *)</span>
&nbsp; &nbsp; iReadHandle<span class="sy1">:</span> DWORD <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">;</span>&nbsp; &nbsp; <span class="coMULTI">(* описатель файла *)</span>
&nbsp; &nbsp; iReadResult<span class="sy1">:</span> DWORD<span class="sy1">;</span> &nbsp; &nbsp; &nbsp; &nbsp; <span class="coMULTI">(* результат выполнения файловой функции - количество считанных байт *)</span>
&nbsp; &nbsp; arvReadResult<span class="sy1">:</span> ASYNC_RET_VALUE<span class="sy1">;</span> <span class="coMULTI">(* результат выполнения файловой функции *)</span>
&nbsp; &nbsp; bFileReadSuccess<span class="sy1">:</span> BOOL<span class="sy1">;</span> &nbsp; &nbsp; <span class="coMULTI">(* флаг успешного чтения из файла для отображения на панели *)</span>
<span class="coMULTI">(* - запись файла *)</span>
&nbsp; &nbsp; iFileWriteState<span class="sy1">,</span>
&nbsp; &nbsp; iFileWriteState_Next<span class="sy1">:</span> INT<span class="sy1">;</span>&nbsp; <span class="coMULTI">(* стадии конечного автомата записи в файла *)</span>
&nbsp; &nbsp; iWriteHandle<span class="sy1">:</span> DWORD <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">0</span><span class="sy1">;</span> &nbsp; <span class="coMULTI">(* описатель файла *)</span>
&nbsp; &nbsp; iWriteResult<span class="sy1">:</span> DWORD<span class="sy1">;</span>&nbsp; &nbsp; &nbsp; &nbsp; <span class="coMULTI">(* результат выполнения файловой функции - количество записанных байт *)</span>
&nbsp; &nbsp; arvWriteResult<span class="sy1">:</span> ASYNC_RET_VALUE<span class="sy1">;</span>&nbsp; &nbsp; <span class="coMULTI">(* результат выполнения файловой функции *)</span>
&nbsp; &nbsp; bFileWriteSuccess<span class="sy1">:</span> BOOL<span class="sy1">;</span>&nbsp; &nbsp; <span class="coMULTI">(* флаг успешной записи в файл для отображения на панели *)</span></pre></td></tr></table></div></td></tr></tbody></table></div>Переменные, вводимые с панели, перечислены на вкладке Ресурсы в пункте Конфигурация ПЛК среди настроек обмена.<br />
Покажу их условное описание - они для данного примера взяты из небольшого реального проекта:<br />
<div class="codeblock"><table class="pascal"><thead><tr><td colspan="2" id="212217385"  class="head">Pascal</td></tr></thead><tbody><tr class="li1"><td><div id="212217385" style="height: 94px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
</pre></td><td class="de1"><pre class="de1">&nbsp; &nbsp; wDumper_2_DelayReverse<span class="sy1">,</span> wDumper_2_TimeOut<span class="sy1">,</span> wDumper_3_DelayReverse<span class="sy1">,</span> wDumper_3_TimeOut<span class="sy1">,</span>
&nbsp; &nbsp; wDumper_4_DelayReverse<span class="sy1">,</span> wDumper_4_TimeOut<span class="sy1">,</span> wDumper_5_DelayReverse<span class="sy1">,</span> wDumper_5_TimeOut<span class="sy1">,</span>
&nbsp; &nbsp; wDumper_6_DelayReverse<span class="sy1">,</span> wDumper_6_TimeOut<span class="sy1">,</span> wDelayLowAlarmLevel<span class="sy1">,</span> wDelayHighAlarmLevel<span class="sy1">,</span>
&nbsp; &nbsp; wDelayOrderAlarmLevel<span class="sy1">,</span> i_T20<span class="sy1">,</span> wDelayStartFeed<span class="sy1">,</span> wDelayStopFeed<span class="sy1">:</span> <span class="kw4">WORD</span><span class="sy1">;</span></pre></td></tr></table></div></td></tr></tbody></table></div><br />
<b>5. ЗАПИСЬ ФАЙЛА</b><br />
<br />
Т.к. асинхронный процесс обмена с файлами - многостадийный, то напрашивается реализация конечного автомата на конструкции CASE.<br />
<br />
В целом, этот пример повторяет пример разработчиков ОВЕН, но снабжён более подробными комментариями, переменные имеют более осмысленные названия, отсутствуют общие с процессом чтения переменные.<br />
<br />
Добавлю, что из-за недостатка времени доступа к ПЛК не стал экспериментировать с предложенной структурой конечного автомата - текущее состояние хранится в переменной iFileWriteState, по результатам выполнения действия определяется номер состояния в переменной iFileWriteState_Next. После завершения CASE происходит присвоение iFileWriteState нового значения из iFileWriteState_Next.<br />
<br />
Контроль записи производится по размеру записанного блока памяти. При его равенстве размеру буфера, запись признаётся успешной и выставляется флаг bFileWriteSuccess.<br />
<br />
Т.к. это тестовый пример, то файл размещён в ОЗУ ПЛК (виртуальный RAM диск) и между отключениями электропитания файл и параметры в нём не сохраняются.<br />
Для изменения размещения файла нужно в строке с именем файла изменить название диска на одно из предлагаемых:<ul><li><code class="inlinecode">ffs:</code> для внутреннего Flash-диска;</li>
<li><code class="inlinecode">ram:</code> для внутреннего виртуального RAM диска (64 килобайт);</li>
<li><code class="inlinecode">usb:</code> для внешнего накопителя, подключенного к порту USB-Host (например, Flash-память, или жесткий диск).</li>
</ul><div class="codeblock"><table class="pascal"><thead><tr><td colspan="2" id="135363922"  class="head">Pascal</td></tr></thead><tbody><tr class="li1"><td><div id="135363922" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
</pre></td><td class="de1"><pre class="de1"><span class="kw1">CASE</span> iFileWriteState <span class="kw1">OF</span>
&nbsp; &nbsp; <span class="nu0">10</span><span class="sy1">:</span> <span class="coMULTI">(* ожидание команды записи в файл от панели оператора *)</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> bWriteFile <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; iFileWriteState_Next <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">20</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bWriteFile <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">FALSE</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; END_IF<span class="sy1">;</span>
&nbsp; &nbsp; <span class="nu0">20</span><span class="sy1">:</span> <span class="coMULTI">(* постановка задачи открытия и ожидание подтверждения начала работы с асинхронным процессом *)</span>
&nbsp; &nbsp; &nbsp; &nbsp; arvWriteResult <span class="sy1">:</span><span class="sy3">=</span> OwenFileOpenAsync<span class="br0">&#40;</span>sFileName<span class="sy1">,</span> <span class="st0">'w+'</span><span class="sy1">,</span> ADR<span class="br0">&#40;</span>iWriteHandle<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> arvWriteResult <span class="sy3">=</span> ASYNC_WORKING <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; iFileWriteState_Next <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">30</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; END_IF<span class="sy1">;</span>
&nbsp; &nbsp; <span class="nu0">30</span><span class="sy1">:</span> <span class="coMULTI">(* ожидание выполнения процесса открытия файла и обработка результатов открытия *)</span>
&nbsp; &nbsp; &nbsp; &nbsp; arvWriteResult <span class="sy1">:</span><span class="sy3">=</span> OwenFileOpenAsync<span class="br0">&#40;</span>sFileName<span class="sy1">,</span> <span class="st0">'w+'</span><span class="sy1">,</span> ADR<span class="br0">&#40;</span>iWriteHandle<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> arvWriteResult <span class="sy3">=</span> ASYNC_DONE <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> iWriteHandle &lt;&gt; <span class="nu0">0</span> <span class="kw1">THEN</span> &nbsp; <span class="coMULTI">(* если описатель не равен нулю, то открытие успешно *)</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; iFileWriteState_Next <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">40</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="coMULTI">(* копирование актуальных параметров в структуру (буферную переменную) *)</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; parParameters<span class="sy1">.</span><span class="me1">tRevers_2</span> &nbsp; &nbsp; &nbsp; &nbsp; <span class="sy1">:</span><span class="sy3">=</span> wDumper_2_DelayReverse<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; parParameters<span class="sy1">.</span><span class="me1">tTimeOut_2</span>&nbsp; &nbsp; &nbsp; &nbsp; <span class="sy1">:</span><span class="sy3">=</span> wDumper_2_TimeOut<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; parParameters<span class="sy1">.</span><span class="me1">tRevers_3</span> &nbsp; &nbsp; &nbsp; &nbsp; <span class="sy1">:</span><span class="sy3">=</span> wDumper_3_DelayReverse<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; parParameters<span class="sy1">.</span><span class="me1">tTimeOut_3</span>&nbsp; &nbsp; &nbsp; &nbsp; <span class="sy1">:</span><span class="sy3">=</span> wDumper_3_TimeOut<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; parParameters<span class="sy1">.</span><span class="me1">tRevers_4</span> &nbsp; &nbsp; &nbsp; &nbsp; <span class="sy1">:</span><span class="sy3">=</span> wDumper_4_DelayReverse<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; parParameters<span class="sy1">.</span><span class="me1">tTimeOut_4</span>&nbsp; &nbsp; &nbsp; &nbsp; <span class="sy1">:</span><span class="sy3">=</span> wDumper_4_TimeOut<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; parParameters<span class="sy1">.</span><span class="me1">tRevers_5</span> &nbsp; &nbsp; &nbsp; &nbsp; <span class="sy1">:</span><span class="sy3">=</span> wDumper_5_DelayReverse<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; parParameters<span class="sy1">.</span><span class="me1">tTimeOut_5</span>&nbsp; &nbsp; &nbsp; &nbsp; <span class="sy1">:</span><span class="sy3">=</span> wDumper_5_TimeOut<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; parParameters<span class="sy1">.</span><span class="me1">tRevers_6</span> &nbsp; &nbsp; &nbsp; &nbsp; <span class="sy1">:</span><span class="sy3">=</span> wDumper_6_DelayReverse<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; parParameters<span class="sy1">.</span><span class="me1">tTimeOut_6</span>&nbsp; &nbsp; &nbsp; &nbsp; <span class="sy1">:</span><span class="sy3">=</span> wDumper_6_TimeOut<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; parParameters<span class="sy1">.</span><span class="me1">tLowLevel</span> &nbsp; &nbsp; &nbsp; &nbsp; <span class="sy1">:</span><span class="sy3">=</span> wDelayLowAlarmLevel<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; parParameters<span class="sy1">.</span><span class="me1">tHighLevel</span>&nbsp; &nbsp; &nbsp; &nbsp; <span class="sy1">:</span><span class="sy3">=</span> wDelayHighAlarmLevel<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; parParameters<span class="sy1">.</span><span class="me1">tOrderLevel</span> &nbsp; &nbsp; &nbsp; <span class="sy1">:</span><span class="sy3">=</span> wDelayOrderAlarmLevel<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; parParameters<span class="sy1">.</span><span class="me1">tT20</span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="sy1">:</span><span class="sy3">=</span> i_T20<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; parParameters<span class="sy1">.</span><span class="me1">tDelayStartFeed</span> &nbsp; <span class="sy1">:</span><span class="sy3">=</span> wDelayStartFeed<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; parParameters<span class="sy1">.</span><span class="me1">tDelayStopFeed</span>&nbsp; &nbsp; <span class="sy1">:</span><span class="sy3">=</span> wDelayStopFeed<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">ELSE</span>&nbsp; &nbsp; <span class="coMULTI">(* открыть файл не удалось - перейти к стадии ожидания команды на запись *)</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; iFileWriteState_Next <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">10</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; END_IF<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; ELSIF arvWriteResult &lt; <span class="nu0">0</span> <span class="kw1">THEN</span> &nbsp; &nbsp; &nbsp; <span class="coMULTI">(* такое смелое игнорирование неотрицательных значений</span>
<span class="coMULTI">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; переменной arvWriteResult:ASYNC_RET_VALUE связано с тем,</span>
<span class="coMULTI">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; что положительные это только </span>
<span class="coMULTI">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ASYNC_WORKING=16#7FFE и ASYNC_DONE:=16#7FFF,</span>
<span class="coMULTI">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; а все остальные - только отрицательные *)</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; iFileWriteState_Next <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">10</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; END_IF<span class="sy1">;</span>
&nbsp; &nbsp; <span class="nu0">40</span><span class="sy1">:</span> <span class="coMULTI">(* запись *)</span>
&nbsp; &nbsp; arvWriteResult <span class="sy1">:</span><span class="sy3">=</span> OwenFileWriteAsync<span class="br0">&#40;</span>iWriteHandle<span class="sy1">,</span> ADR<span class="br0">&#40;</span>parParameters<span class="br0">&#41;</span><span class="sy1">,</span> SIZEOF<span class="br0">&#40;</span>parParameters<span class="br0">&#41;</span><span class="sy1">,</span> ADR<span class="br0">&#40;</span>iWriteResult<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; <span class="kw1">IF</span> arvWriteResult <span class="sy3">=</span> ASYNC_WORKING <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; iFileWriteState_Next <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">41</span><span class="sy1">;</span> <span class="coMULTI">(* если в записи не отказано, то выполнить её *)</span>
&nbsp; &nbsp; <span class="kw1">ELSE</span>
&nbsp; &nbsp; &nbsp; &nbsp; iFileWriteState_Next <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">50</span><span class="sy1">;</span> <span class="coMULTI">(* если отказано - закрыть файл *)</span>
&nbsp; &nbsp; END_IF<span class="sy1">;</span>
&nbsp; &nbsp; <span class="nu0">41</span><span class="sy1">:</span> <span class="coMULTI">(* запись - 2 этап *)</span>
&nbsp; &nbsp; &nbsp; &nbsp; arvWriteResult <span class="sy1">:</span><span class="sy3">=</span> OwenFileWriteAsync<span class="br0">&#40;</span>iWriteHandle<span class="sy1">,</span> ADR<span class="br0">&#40;</span>parParameters<span class="br0">&#41;</span><span class="sy1">,</span> SIZEOF<span class="br0">&#40;</span>parParameters<span class="br0">&#41;</span><span class="sy1">,</span> ADR<span class="br0">&#40;</span>iWriteResult<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> arvWriteResult <span class="sy3">=</span> ASYNC_DONE <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> iWriteResult <span class="sy3">=</span> SIZEOF<span class="br0">&#40;</span>parParameters<span class="br0">&#41;</span> <span class="kw1">THEN</span><span class="coMULTI">(* если записано требуемое количество байт, *)</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; iFileWriteState_Next <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">50</span><span class="sy1">;</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="coMULTI">(* то выставить флаг успеха записи и закрыть файл *)</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bFileWriteSuccess <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">TRUE</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">ELSE</span>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="coMULTI">(* иначе - выставить флаг отсутствия записи и закрыть файл *)</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bFileWriteSuccess <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">FALSE</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; iFileWriteState_Next <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">50</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; END_IF
&nbsp; &nbsp; &nbsp; &nbsp; ELSIF arvWriteResult &lt; <span class="nu0">0</span> <span class="kw1">THEN</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="coMULTI">(* при отказе в записи - выставить флаг отсутствия записи и закрыть файл *)</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bFileWriteSuccess <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">FALSE</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; iFileWriteState_Next <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">50</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; END_IF<span class="sy1">;</span>
&nbsp; &nbsp; <span class="nu0">50</span><span class="sy1">:</span> <span class="coMULTI">(* закрытие *)</span>
&nbsp; &nbsp; &nbsp; &nbsp; arvWriteResult <span class="sy1">:</span><span class="sy3">=</span> OwenFileCloseAsync<span class="br0">&#40;</span>iWriteHandle<span class="sy1">,</span> ADR<span class="br0">&#40;</span>iWriteResult<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> arvWriteResult <span class="sy3">=</span> ASYNC_WORKING <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; iFileWriteState_Next <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">51</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">ELSE</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; iFileWriteState_Next <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">10</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; END_IF<span class="sy1">;</span>
&nbsp; &nbsp; <span class="nu0">51</span><span class="sy1">:</span> <span class="coMULTI">(* закрытие *)</span>
&nbsp; &nbsp; &nbsp; &nbsp; arvWriteResult <span class="sy1">:</span><span class="sy3">=</span> OwenFileCloseAsync<span class="br0">&#40;</span>iWriteHandle<span class="sy1">,</span> ADR<span class="br0">&#40;</span>iWriteResult<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> arvWriteResult <span class="sy3">=</span> ASYNC_DONE <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> iWriteResult <span class="sy3">=</span> <span class="nu0">0</span> <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; iFileWriteState_Next <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">10</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">ELSE</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; iFileWriteState_Next <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">10</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; END_IF
&nbsp; &nbsp; &nbsp; &nbsp; ELSIF arvWriteResult &lt; <span class="nu0">0</span> <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; iFileWriteState_Next <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">10</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; END_IF<span class="sy1">;</span>
<span class="kw1">ELSE</span>
&nbsp; &nbsp; &nbsp; &nbsp; iFileWriteState_Next <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">10</span><span class="sy1">;</span>
END_CASE<span class="sy1">;</span>
&nbsp;
<span class="coMULTI">(* обновление стадии записи *)</span>
iFileWriteState <span class="sy1">:</span><span class="sy3">=</span> iFileWriteState_Next<span class="sy1">;</span></pre></td></tr></table></div></td></tr></tbody></table></div><b>6. ЧТЕНИЕ ФАЙЛА</b><br />
<br />
Код для чтения в целом повторяет код для записи.<br />
<div class="codeblock"><table class="pascal"><thead><tr><td colspan="2" id="589293623"  class="head">Pascal</td></tr></thead><tbody><tr class="li1"><td><div id="589293623" style="height: 350px" class="codeframe"><table><tr class="li1"><td class="ln" style="padding: 0px 10px 0px 5px;"><pre class="de1">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
</pre></td><td class="de1"><pre class="de1"><span class="coMULTI">(* конечный автомат чтения переменной *)</span>
<span class="kw1">CASE</span> iFileReadState <span class="kw1">OF</span>
&nbsp; &nbsp; <span class="nu0">10</span><span class="sy1">:</span> <span class="coMULTI">(* ожидание команды чтения из файла от панели оператора *)</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> bReadFile <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; iFileReadState_Next <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">20</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bReadFile <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">FALSE</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; END_IF<span class="sy1">;</span>
&nbsp; &nbsp; <span class="nu0">20</span><span class="sy1">:</span> <span class="coMULTI">(* постановка задачи открытия и ожидание подтверждения начала работы с асинхронным процессом *)</span>
&nbsp; &nbsp; &nbsp; &nbsp; arvReadResult <span class="sy1">:</span><span class="sy3">=</span> OwenFileOpenAsync<span class="br0">&#40;</span>sFileName<span class="sy1">,</span> <span class="st0">'r'</span><span class="sy1">,</span> ADR<span class="br0">&#40;</span>iReadHandle<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> arvReadResult <span class="sy3">=</span> ASYNC_WORKING <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; iFileReadState_Next <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">30</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; END_IF<span class="sy1">;</span>
&nbsp; &nbsp; <span class="nu0">30</span><span class="sy1">:</span> <span class="coMULTI">(* ожидание выполнения процесса открытия файла и обработка результатов открытия *)</span>
&nbsp; &nbsp; &nbsp; &nbsp; arvReadResult <span class="sy1">:</span><span class="sy3">=</span> OwenFileOpenAsync<span class="br0">&#40;</span>sFileName<span class="sy1">,</span> <span class="st0">'r'</span><span class="sy1">,</span> ADR<span class="br0">&#40;</span>iReadHandle<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> arvReadResult <span class="sy3">=</span> ASYNC_DONE <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> iReadHandle &lt;&gt; <span class="nu0">0</span> <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; iFileReadState_Next <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">40</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">ELSE</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; iFileReadState_Next <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">10</span><span class="sy1">;</span>&nbsp; <span class="coMULTI">(* описатель равен нулю при отсутствии файла *)</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; END_IF
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;ELSIF arvReadResult &lt; <span class="nu0">0</span> <span class="kw1">THEN</span> &nbsp; <span class="coMULTI">(* такое смелое игнорирование неотрицательных значений</span>
<span class="coMULTI">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; переменной arvReadResult:ASYNC_RET_VALUE связано с тем,</span>
<span class="coMULTI">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; что положительные это только </span>
<span class="coMULTI">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ASYNC_WORKING=16#7FFE и ASYNC_DONE:=16#7FFF,</span>
<span class="coMULTI">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; а все остальные - только отрицательные *)</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; iFileReadState_Next <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">10</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; END_IF<span class="sy1">;</span>
&nbsp; &nbsp; <span class="nu0">40</span><span class="sy1">:</span> <span class="coMULTI">(* чтение из файла - 1 этап *)</span>
&nbsp; &nbsp; &nbsp; &nbsp; arvReadResult <span class="sy1">:</span><span class="sy3">=</span> OwenFileReadAsync<span class="br0">&#40;</span>iReadHandle<span class="sy1">,</span> ADR<span class="br0">&#40;</span>parParameters<span class="br0">&#41;</span><span class="sy1">,</span> SIZEOF<span class="br0">&#40;</span>parParameters<span class="br0">&#41;</span><span class="sy1">,</span> ADR<span class="br0">&#40;</span>iReadResult<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> arvReadResult <span class="sy3">=</span> ASYNC_WORKING <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; iFileReadState_Next <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">41</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">ELSE</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; iFileReadState_Next <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">50</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; END_IF<span class="sy1">;</span>
&nbsp; &nbsp; <span class="nu0">41</span><span class="sy1">:</span> <span class="coMULTI">(* чтение из файла - 2 этап *)</span>
&nbsp; &nbsp; &nbsp; &nbsp; arvReadResult <span class="sy1">:</span><span class="sy3">=</span> OwenFileReadAsync<span class="br0">&#40;</span>iReadHandle<span class="sy1">,</span> ADR<span class="br0">&#40;</span>parParameters<span class="br0">&#41;</span><span class="sy1">,</span> SIZEOF<span class="br0">&#40;</span>parParameters<span class="br0">&#41;</span><span class="sy1">,</span> ADR<span class="br0">&#40;</span>iReadResult<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> arvReadResult <span class="sy3">=</span> ASYNC_DONE <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> iReadResult <span class="sy3">=</span> SIZEOF<span class="br0">&#40;</span>parParameters<span class="br0">&#41;</span> <span class="kw1">THEN</span> <span class="coMULTI">(* в iReadResult - количество считанных байт *)</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bFileReadSuccess <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">TRUE</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; iFileReadState_Next <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">50</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">ELSE</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; iFileReadState_Next <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">50</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bFileReadSuccess <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">FALSE</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; END_IF
&nbsp; &nbsp; &nbsp; &nbsp; ELSIF arvReadResult &lt; <span class="nu0">0</span> <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; bFileReadSuccess <span class="sy1">:</span><span class="sy3">=</span> <span class="kw2">FALSE</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; iFileReadState_Next <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">50</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; END_IF<span class="sy1">;</span>
&nbsp; &nbsp; <span class="nu0">50</span><span class="sy1">:</span> <span class="coMULTI">(* закрытие *)</span>
&nbsp; &nbsp; &nbsp; &nbsp; arvReadResult <span class="sy1">:</span><span class="sy3">=</span> OwenFileCloseAsync<span class="br0">&#40;</span>iReadHandle<span class="sy1">,</span> ADR<span class="br0">&#40;</span>iReadResult<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> arvReadResult <span class="sy3">=</span> ASYNC_WORKING <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; iFileReadState_Next <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">51</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">ELSE</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; iFileReadState_Next <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">10</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; END_IF<span class="sy1">;</span>
&nbsp; &nbsp; <span class="nu0">51</span><span class="sy1">:</span> <span class="coMULTI">(* закрытие *)</span>
&nbsp; &nbsp; &nbsp; &nbsp; arvReadResult <span class="sy1">:</span><span class="sy3">=</span> OwenFileCloseAsync<span class="br0">&#40;</span>iReadHandle<span class="sy1">,</span> ADR<span class="br0">&#40;</span>iReadResult<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> arvReadResult <span class="sy3">=</span> ASYNC_DONE <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">IF</span> iReadResult <span class="sy3">=</span> <span class="nu0">0</span> <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; iFileReadState_Next <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">10</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">ELSE</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; iFileReadState_Next <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">10</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; END_IF<span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; ELSIF arvReadResult &lt; <span class="nu0">0</span> <span class="kw1">THEN</span>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; iFileReadState_Next <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">10</span><span class="sy1">;</span>
&nbsp; &nbsp; &nbsp; &nbsp; END_IF<span class="sy1">;</span>
<span class="kw1">ELSE</span>
&nbsp; &nbsp; &nbsp; &nbsp; iFileReadState_Next <span class="sy1">:</span><span class="sy3">=</span> <span class="nu0">10</span><span class="sy1">;</span>
END_CASE<span class="sy1">;</span>
<span class="coMULTI">(* обновление стадии чтения *)</span>
iFileReadState <span class="sy1">:</span><span class="sy3">=</span> iFileReadState_Next<span class="sy1">;</span></pre></td></tr></table></div></td></tr></tbody></table></div><b>7. МИНИМАЛЬНАЯ ТЕСТОВАЯ ПРОГРАММА</b><br />
<br />
Минимальная тестовая программа написана для ПЛК110-32[M02] и панели оператора Овен СП307.<br />
Программы являются усечёнными до демонстрационного примера версиями реального проекта, поэтому остались рудименты в виде непоследовательной нумерации управляющих и статусных бит в словах управления и состояния, названия параметров настройки, цветовое выделение на экранах панели, нумерация экранов не последовательная, некоторое нагромождение элементов на экранах (т.к. сами параметры для программы играют второстепенную роль, то разместил их ввод на одном экране).<br />
<br />
Программа в ПЛК демонстрирует обработку параметров настройки, вводимых с панели оператора:<ul><li>проверку корректности каждого параметра,</li>
<li>сохранение в файл на внутреннем диске ПЛК (виртуальный RAM диск),</li>
<li>чтение параметров настройки из файла в промежуточный буфер,</li>
<li>занесение в буфер заводских установок,</li>
<li>копирование в параметры настройки из буфера содержащиеся в нём значения (заводские установки или полученные из файла).</li>
</ul><br />
Для подачи команд в ПЛК используется сенсорная панель оператора СП307.<br />
Её программа содержит три экрана:<ul><li>главный, содержащий кнопки формирования команд и визуализацию,</li>
<li>экран ввода параметров настройки,</li>
<li>справочный экран, содержащий параметры сетевых настроек ПЛК и панели.</li>
</ul>
			   <div class="spoiler-wrap">
				   <div class="spoiler-head folded clickable">Скриншоты экранов</div>
				   <div class="spoiler-body">
					   <br />
Главный экран.<br />
Из-за ошибок эмулятора, индикаторы успешных чтения и записи не переключаются в другое состояние, хотя на самой панели переключение происходит.<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=7958&amp;d=1678043287" rel="Lightbox" id="attachment7958" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=7958&amp;thumb=1&amp;d=1678043287" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: main.PNG
Просмотров: 302
Размер:	8.1 Кб
ID:	7958" style="margin: 5px" /></a><br />
<br />
Экран ввода параметров<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=7959&amp;d=1678043287" rel="Lightbox" id="attachment7959" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=7959&amp;thumb=1&amp;d=1678043287" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: params.PNG
Просмотров: 286
Размер:	7.0 Кб
ID:	7959" style="margin: 5px" /></a><br />
<br />
Экран справки по настройке параметров связи<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=7960&amp;d=1678043287" rel="Lightbox" id="attachment7960" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=7960&amp;thumb=1&amp;d=1678043287" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: help.PNG
Просмотров: 324
Размер:	4.0 Кб
ID:	7960" style="margin: 5px" /></a><br />

				   </div>
			   </div><br />
ПЛК и панель соединены по интерфейсу Ethernet.<br />
Настройки связи ПЛК необходимо ввести через ПЛК-Браузер:<br />
[FIELDSET=&quot;Настройки связи ПЛК&quot;]<font face="Courier New">IP 192.168.0.100<br />
GATE 192.168.0.1<br />
MASK 255.255.255.0</font>[/FIELDSET]<br />
<br />
<b>8. ВЫВОДЫ И РЕКОМЕНДАЦИИ</b><br />
<br />
На основе разрозненных данных собран минимальный тестовый пример, демонстрирующий работу с настройками системы:<ul><li>ограничение диапазона и коррекция параметров,</li>
<li>сохранение настроек в файл,</li>
<li>загрузка настроек на выбор или из файла или сброс на заводские значения</li>
<li>возможность сравнения актуальных параметров с параметрами из других источников.</li>
</ul><br />
Предполагаю придерживаться этого направления в следующих программах.<br />
<br />
<b>9. ЛИТЕРАТУРА</b><br />
<ol style="list-style-type: decimal"><li>Файл библиотеки OwenLibFileAsync.lib<br />
<a rel="nofollow noopener noreferrer" href="https://ftp.owen.ru/CoDeSys23/05_Library/02_Library/OwenLibFileAsync.lib" target="_blank" title="https://ftp.owen.ru/CoDeSys23/05_Library/02_Library/OwenLibFileAsync.lib">https://ftp.owen.ru/CoDeSys23/... eAsync.lib</a></li>
<li>Файл описания к библиотеке<br />
<a rel="nofollow noopener noreferrer" href="https://ftp.owen.ru/CoDeSys23/05_Library/01_Manuals/OwenLibFileAsync.pdf" target="_blank" title="https://ftp.owen.ru/CoDeSys23/05_Library/01_Manuals/OwenLibFileAsync.pdf">https://ftp.owen.ru/CoDeSys23/... eAsync.pdf</a></li>
<li>Руководство по программированию ПЛК110[M03] и ПЛК160[M02]<br />
<a rel="nofollow noopener noreferrer" href="https://owen.ru/uploads/313/rp_plk1hh_m02__1-ru-75044-1.33.pdf" target="_blank" title="https://owen.ru/uploads/313/rp_plk1hh_m02__1-ru-75044-1.33.pdf">https://owen.ru/uploads/313/rp... 4-1.33.pdf</a></li>
<li>Пример программы, предложенный специалистами Овен<br />
&quot;Пока ГТП спит.zip&quot;</li>
</ol><br />
<b>ПРИЛОЖЕНИЕ</b><br />
<br />
Архив с тестовыми программами<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=7961&amp;d=1678045460" >sample.7z</a></div>

]]></content:encoded>
			<dc:creator>ФедосеевПавел</dc:creator>
			<guid isPermaLink="true">https://www.cyberforum.ru/blogs/534277/7956.html</guid>
		</item>
		<item>
			<title>Заготовки протоколов настройки приборов автоматики</title>
			<link>https://www.cyberforum.ru/blogs/534277/7418.html</link>
			<pubDate>Mon, 01 Aug 2022 19:27:14 GMT</pubDate>
			<description>*Заготовки протоколов настройки приборов автоматики 
* 
 
*1. Цель* 
 
Воспользуюсь форматом блога...</description>
			<content:encoded><![CDATA[<div><b><font size="+2"><div align="center">Заготовки протоколов настройки приборов автоматики</div></font></b><br />
<br />
<b>1. Цель</b><br />
<br />
Воспользуюсь форматом блога в качестве файл-сервера, чтобы поделиться со своими коллегами по работе шаблонами протоколов настройки приборов.<br />
Если кто-то ещё воспользуется — буду только рад побочному эффекту :)<br />
<br />
<b>2. Немного литературной составляющей</b><br />
<br />
В состав технических отчётов о наладке автоматики уже давно стараюсь прикладывать протоколы настройки приборов.<br />
<br />
Возможные ситуации, при которых они окажутся востребованы:<ul><li>замена неисправного прибора службой эксплуатации, при технической невозможности копирования настроек из неисправного прибора;</li>
<li>несанкционированное изменение настроек прибора в результате вмешательства намеренного или случайного — так тоже бывает (комбинация кнопок для входа в режим настройки совпадает с комбинацией установки в заводские установки и при попытке входа в этот режим моргает свет — выполняется второе условие установки в исходное состояние — и это не шутка, а реальный случай);</li>
<li>неудачная конструкция самого прибора — в практике был случай, когда на лицевой панели декоративная плёнка, закрывающая кнопки, под действием температуры стягивалась и самопроизвольно «нажимала» на кнопки, изменяя настройки, причём дефект касался всей партии приборов определённого диапазона измерений, установленных на объекте;</li>
<li>обращение за помощью к производителю или экспертному сообществу с вопросом по настройке или явно ошибочной работе прибора.</li>
</ul>Признаюсь, нужда в существовании протоколов возникает крайне редко, но когда возникает — радуюсь, что не поленился составить их.<br />
<br />
Производители часто выкладывают для скачивания программные утилиты — конфигураторы, позволяющие конфигурировать прибор и формировать протокол его настройки. Обычно, эти программы бесплатны и доступны на сайтах производителей или официальных представителей.<br />

			   <div class="spoiler-wrap">
				   <div class="spoiler-head folded clickable">Пример отчёта о настройках прибора, сформированного конфигуратором «ОВЕН ТРМ2xx»</div>
				   <div class="spoiler-body">
					   <a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=7365&amp;d=1641364387" rel="Lightbox" id="attachment7365" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=7365&amp;thumb=1&amp;d=1641364387" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: ТРМ202.png
Просмотров: 601
Размер:	32.3 Кб
ID:	7365" style="margin: 5px" /></a>
				   </div>
			   </div><br />
<br />
В некоторых случаях, конфигураторы имеют ограниченную ценность — не формируют отчёт или для подключения к прибору требуется покупка уникального адаптера. Или же конфигуратора не существует. В таких случаях желательно протокол оформить самостоятельно.<br />

			   <div class="spoiler-wrap">
				   <div class="spoiler-head folded clickable">Пример протокола настроек прибора, сделанный самостоятельно в офисном пакете</div>
				   <div class="spoiler-body">
					   <a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=7516&amp;d=1651502363" rel="Lightbox" id="attachment7516" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=7516&amp;thumb=1&amp;d=1651502363" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: САУ-У.PNG
Просмотров: 585
Размер:	20.7 Кб
ID:	7516" style="margin: 5px" /></a>
				   </div>
			   </div><br />
За время работы накопились протоколы для некоторых приборов, которые и собрал в отдельных файлах — прикрепляю их к статье.<br />
<br />
Многие приборы выпускаются и совершенствуются — параметры настройки добавляются, удаляются или заменяется их назначение. Поэтому нужно внимательно сравнивать эти протоколы с актуальными данными из руководств на приборы. Именно по причине таких изменений для некоторых приборов приводится несколько вариантов протоколов.<br />
<br />
По оформлению добавлю, что сами протоколы являются приложением к техническому отчёту и часть данных (населённый пункт, заказчик, дата, фамилии, объект автоматизации, заводской и станционный номера) заполняются автоматически из исходных данных. Для получения такой «автоматизации» сам отчёт оформляется в электронной таблице Calc из состава Libre Office (аналог Exel из состава MS Office). Также, протоколы создавались на протяжении значительного времени и их оформление немного различается. При оформлении приходится идти на компромиссы и не всегда очевидные решения.<ul><li>В работе использую пакет Libre Office (LibOo), а не привычный многим MS Office. Это связано с особенностью работодателя — мы самостоятельно обеспечиваем себя инструментом. Я решил экономить и не покупать офисный пакет. Т.к. работая в Linux уже ознакомился с Open Office (OOo), то выбор был очевиден, а переход на LibOo связан с дальнейшей историей самого пакета OOo. Офисные пакеты лучше работают с собственными форматам файлов, поэтому формат файлов — ODF (OpenDocument Format).</li>
<li>Для согласования размеров ячеек «шапки» и таблицы параметров прибора решил сделать исходно маленькие размеры ячеек — хотел, как в тетради 0,50×0,50 см, но для шрифта Times New Roman размером 12 потребовалась высота 0,54 см. В итоге получил сетку 0,50×0,54 см.</li>
<li>Чтобы не зависеть от существования колонтитулов, пришлось уменьшить на одну строку высоту печатаемой области вставкой разрыва страницы — высота листа стала 46 строк. Таким образом, размер печатного листа принудительно ограничен вручную, установкой разрыва листа по строке.</li>
</ul><br />
<b>3. Содержимое файлов</b><ol style="list-style-type: decimal"><li>Приборы_датчики.ods<ul><li>АИР-20/М2-Н</li>
<li>ОВЕН ПД150</li>
<li>ОВЕН ПД200</li>
<li>ОВЕН НПТ-1, ОВЕН НПТ-3</li>
<li>АГАВА АДР</li>
<li>АГАВА АДН</li>
<li>KROHNE Optiswirl 4070</li>
<li>ЭМИС-ДИО 230</li>
<li>ПРОМА ИДМ-010</li>
</ul></li>
<li>Приборы.ods<ul><li>ОВЕН ТРМ1А</li>
<li>ОВЕН ТРМ1</li>
<li>ОВЕН 2ТРМ1</li>
<li>ОВЕН 2ТРМ1.У2</li>
<li>ОВЕН ТРМ12</li>
<li>ОВЕН ТРМ32</li>
<li>ОВЕН САУ-У (алгоритмы 11, 12, 14 и 15)</li>
<li>ОВЕН САУ-М7Е</li>
<li>Delta CTA4</li>
<li>RWF40</li>
<li>RWF50.2</li>
<li>KS40</li>
<li>Ascon KM3</li>
<li>Emko ESM-4435</li>
<li>Wilo SK-712/w</li>
<li>CAL 9500P</li>
</ul></li>
<li>Приборы_ПЧВ.ods<ul><li>Веспер EI-7011</li>
<li>Hyundai N700E</li>
</ul></li>
<li>Приборы_Управления_Печами_Котлами.ods<ul><li>Альфа-М XXI век — котёл Е</li>
<li>Альфа-М XXI век — котёл Е-Unigas</li>
<li>VITOTRONIC 100 type GC1</li>
<li>VITOTRONIC 100 type GC1B</li>
<li>KM628</li>
<li>E8.4401</li>
<li>ROC-A</li>
<li>Hortek XL</li>
</ul></li>
</ol><br />
Для некоторых приборов оставил оформление (списки параметров полные, но оформление недостаточно аккуратное) шаблонов в незаконченном состоянии, т.к. не предвижу их использования в обозримом будущем для вставки в отчёты, но они удобны для неофициального применения при эксплуатации — хранение настроек приборов в доступном распечатанном виде непосредственно в щите автоматики, сохранение настроек перед отправкой прибора в ремонт.<ul><li>Riello 5000 CL-M.ods</li>
</ul><br />
<div class="smallfont offtopic" onmouseover="this.style.color='#000000';" onmouseout="this.style.color='#888888';">
				<p><b>Не по теме:</b></p>
				<p>2022/09/05 Добавил ОВЕН ТРМ1А, ОВЕН 2ТРМ1.У2.<br />
2023/03/12 Добавил Hortek XL, ПРОМА ИДМ-010, CAL 9500P</p>
				</div></div>


<!-- attachments -->
	<div style="margin-top:10px">

		
		
		
		
			<fieldset class="fieldset">
				<legend>Вложения</legend>
				<table cellpadding="0" cellspacing="3" border="0">
				<tr>
	<td><img loading="lazy" decoding="async" class="inlineimg" src="http://www.cyberforum.ru//cyberstatic.net/images/attach/7z.gif" alt="Тип файла: 7z" width="16" height="16" border="0" style="vertical-align:baseline" /></td>
	<td><a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=7971&amp;d=1678612948">ПРОТОКОЛЫ_НАСТРОЙКИ_ПРИБОРОВ.7z</a> (287.4 Кб, 253 просмотров)</td>
</tr>
				</table>
			</fieldset>
		

	</div>
<!-- / attachments -->
]]></content:encoded>
			<dc:creator>ФедосеевПавел</dc:creator>
			<guid isPermaLink="true">https://www.cyberforum.ru/blogs/534277/7418.html</guid>
		</item>
		<item>
			<title>Шрифт, имитирующий символы семисегментного светодиодного индикатора</title>
			<link>https://www.cyberforum.ru/blogs/534277/6996.html</link>
			<pubDate>Tue, 23 Feb 2021 17:16:17 GMT</pubDate>
			<description>*Шрифт, имитирующий символы семисегментного светодиодного индикатора 
* 
 
*1. Введение* 
При...</description>
			<content:encoded><![CDATA[<div><b><font size="+2"><div align="center">Шрифт, имитирующий символы семисегментного светодиодного индикатора</div></font></b><br />
<br />
<b>1. Введение</b><br />
При создании разделов КИП и А для технических отчётов о ПНР или РНИ на оборудовании (как в инструкции, так и в протоколе настроек) сталкивался с необходимостью показывать названия параметров в том виде, в каком они высвечиваются на дисплее прибора.<br />
<br />
Необходимость возникает не всегда — чаще достаточно записать соответствующие символы латиницы (именно таким образом программы‑конфигураторы формируют протокол настроек прибора).<br />
<br />
<b>2. Исследование возможности единого шрифта для различных приборов</b><br />
Для нескольких приборов, с которыми неоднократно работал, составил сводную таблицу соответствия символов на цифровом индикаторе буквам латинского алфавита.<br />
Таблица составлена на основе документации производителя:<br />
- для приборов производства Овен в РЭ таблица соответствия приведена в готовом виде,<br />
- для таймера CTA4 соответствие было составлено из графических вставок названий параметров в РЭ, поэтому шрифт получился неполный — отсутствуют несколько символов.<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=6823&amp;d=1613941175" rel="Lightbox" id="attachment6823" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=6823&amp;thumb=1&amp;d=1613941175" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: СравнительнаяТаблица.PNG
Просмотров: 1098
Размер:	4.1 Кб
ID:	6823" style="margin: 5px" /></a><br />
В сводной таблице применены обозначения и выделения цветом.<br />
Обозначения приборов соответствуют:<ul><li>ТРМ148 (старый) — Овен ТРМ148 ранних выпусков,</li>
<li>ТРМ148 (новый) — Овен ТРМ148 новых выпусков,</li>
<li>ТРМ2xx — семейство приборов Овен ТРМ200, ТРМ201, ТРМ202, ТРМ212,</li>
<li>CTA4 — таймер Delta CTA4.</li>
</ul>Выделения цветом:<ul><li>желтым цветом выделены символы, различные для разных приборов,</li>
<li>красным цветом выделены символы, изображения которых остались неизвестными.</li>
</ul>Доводилось работать с ПИД-регуляторами производства Ascon моделей M3, M5, KM3, TLK94, производства Siemens моделей RWF40, RWF50, производства PMA модели KS40. В РЭ на эти приборы отсутствовала готовая таблица соответствия символов на цифровом индикаторе буквам латинского алфавита. Для этих приборов даже и не пытался её составить, т. к. беглого ознакомления было достаточно, чтобы увидеть различия в большом количестве глифов, в добавок, были различия в начертании строчных и прописных букв.<br />
<br />
<b>3. Выводы</b><br />
Из этих исследований следует неутешительный вывод — готового единого шрифта, подходящего для всех приборов не существует.<br />
<br />
<b>4. Возможные решения</b><br />
Т. к. к настройке приборов допускается подготовленный персонал, то достаточно разумным является запись параметров обычным шрифтом, без визуального соответствия изображению на цифровом индикаторе.<br />
<br />
Возможен вариант рисования в графическом редакторе заготовки текста из нужного количества символов, а для каждой надписи удалять ненужные сегменты и в виде изображения вставлять в документ.<br />
Пример такой заготовки и изображений с текстом на её основе показан на рисунках<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=6824&amp;d=1613941863" rel="Lightbox" id="attachment6824" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=6824&amp;thumb=1&amp;d=1613941863" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: template.png
Просмотров: 633
Размер:	630 байт
ID:	6824" style="margin: 5px" /></a>  <a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=6825&amp;d=1613941875" rel="Lightbox" id="attachment6825" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=6825&amp;thumb=1&amp;d=1613941875" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: cta4_Stage2.png
Просмотров: 559
Размер:	663 байт
ID:	6825" style="margin: 5px" /></a>  <a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=6826&amp;d=1613941885" rel="Lightbox" id="attachment6826" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=6826&amp;thumb=1&amp;d=1613941885" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: cta4_time.png
Просмотров: 439
Размер:	537 байт
ID:	6826" style="margin: 5px" /></a><br />
<br />
Однако, если «охота пуще неволи», имеется возможность создания собственного шрифта для каждого случая.<br />
<br />
<b>5. Создание собственного шрифта</b><br />
Несколько лет назад с целью создания шрифта подбирал редактор. На тот момент из бесплатных был доступен только FontForge, доступный по ссылке<br />
<a rel="nofollow noopener noreferrer" href="https://fontforge.org/en-US/" target="_blank" title="https://fontforge.org/en-US/">https://fontforge.org/en-US/</a><br />
На официальном сайте доступна справка на английском языке. Т. к. редактор широко известный, то в интернет находятся ответы на множество вопросов по работе с редактором на русском языке.<br />
<br />
Шрифт можно создать «с нуля», можно взять из интернет какой-нибудь близкий по начертанию бесплатный шрифт, открыть в редакторе шрифтов, скопировать символ цифры «8» в позицию нужного символа, удалить «лишние» сегменты. Перед сохранением зайти в свойства шрифта («Элемент» — «Информация о шрифте...») и изменить имя, с которым он будет виден в системе. Если редактировался не шрифт, а проект, то в меню «Файл» выбрать «Создать шрифты...». После этого установить шрифт штатным способом. Перезагрузить компьютер и пользоваться.<br />
<br />
В редакторе FontForge создал собственный шрифт для прибора Овен ТРМxx, чуть изменив получил другой — для Delta CTA4.<br />
Особенностью шрифтов является отсутствие перемещения курсора при вводе символов «точка» и «запятая» — как это и выглядит на реальных цифровых дисплеях.<br />
<br />
Шрифтом для Овен ТРМ2xx никогда не пользовался, т. к. конфигуратор формирует отчёт обычным шрифтом.<br />
Шрифтом для Delta CTA4 пользовался при составлении протокола настройки этого прибора.<br />
Получается, после создания шрифта, пользоваться им не стал, т. к. научился видеть текст в закорючках и в протоколах обходился латиницей.<br />
<br />
Прикрепляю архив с файлами проекта для FontForge и шрифтами в формате TTF.<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=6828&amp;d=1613946079" >LED-7.7z</a><br />
<br />
Ещё добавлю ссылки на бесплатные шрифты для имитации семисегментного индикатора<br />
<a rel="nofollow noopener noreferrer" href="https://www.dafont.com/7led.font" target="_blank" title="https://www.dafont.com/7led.font">https://www.dafont.com/7led.font</a><br />
<a rel="nofollow noopener noreferrer" href="http://ru.legionfonts.com/fonts/7-segment" target="_blank" title="http://ru.legionfonts.com/fonts/7-segment">http://ru.legionfonts.com/fonts/7-segment</a><br />
<br />
<b>5. Заключение</b><br />
Проведено исследование изображений символов на цифровом индикаторе для различных приборов, из которого получен вывод о невозможности существования единого шрифта.<br />
<br />
Для двух приборов созданы шрифты, имитирующие цифровой дисплей из семисегментных индикаторов.<br />
<br />
Даны рекомендации по модификации шрифтов для получения соответствия другим приборам.<br />
<br />
Как и во всех случаях работы с уникальными шрифтами, обязательно нужно учитывать вероятность их отсутствия на других компьютерах.<br />
Для меня приемлемым является встраивание шрифтов в документ (в свойствах документа).</div>

]]></content:encoded>
			<dc:creator>ФедосеевПавел</dc:creator>
			<guid isPermaLink="true">https://www.cyberforum.ru/blogs/534277/6996.html</guid>
		</item>
		<item>
			<title>Создание программы управления двумя повышающими насосами на основе программируемого реле ОВЕН ПР100</title>
			<link>https://www.cyberforum.ru/blogs/534277/6108.html</link>
			<pubDate>Sat, 12 Oct 2019 20:18:22 GMT</pubDate>
			<description>*Создание программы управления двумя повышающими насосами на основе программируемого реле ОВЕН...</description>
			<content:encoded><![CDATA[<div><b><font size="+2"><div align="center">Создание программы управления двумя повышающими насосами на основе программируемого реле ОВЕН ПР100</div></font></b><br />
<br />
<b>ВВЕДЕНИЕ</b><br />
<br />
Рассмотрим пример создания управляющей программы для работы системы подпитки с двумя повышающими насосами.<br />
Т.к. это учебный пример, то выбор оборудования в некоторой мере — условен. Выберем новое на сегодняшний день программируемое реле ОВЕН ПР100-230.0804.0. Количество и тип его входов и выходов, а также возможности самого реле в плане вычислений, позволяют решить поставленную задачу. Также способ достижения равномерности износа насосов выберем упрощённым — ротация будет производиться по каждому запросу подпитки, а не по реальной наработке.<br />
Решение будет создаваться в среде OWEN Logic версия 1.14.194.18756.<br />
Файлы для OWEN Logic прикладывать нет смысла, т.к. эта среда бурно развивается и не всегда осуществляется поддержка ранних форматов собственных файлов (так я потерял наработки для версии 1.7).<br />
<br />
<b>1. ТЕХНИЧЕСКОЕ ЗАДАНИЕ</b><br />
<br />
Для подпитки системы отопления установлен накопительный бак с химподготовленной водой. Вода из бака через повысительные насосы подпитывает систему отопления. Для определения необходимости подпитки в обратном трубопроводе системы отопления установлен датчик-реле давления - при снижении давления ниже уставки, датчик замыкает сухой контакт.<br />
<br />
Программа управления двумя повысительными насосами должна выполнять:<br />
1. Включение насоса после замыкания контакта датчика-реле понижения давления в системе.<br />
2. Чередование (ротацию) насосов для равномерного износа — чередование насосов при каждом запросе подпитки.<br />
3. Контроль работоспособности включённого насоса по датчику-реле протока (датчику перепада давления на насосах или другому типу датчика протока). При отсутствии протока в течение заданного времени, насос признаётся неисправным, отключается, включается аварийная (световая и звуковая) сигнализации.<br />
4. Аварийный ввод резерва (АВР). В случае неисправности одного из насосов, он исключается из дальнейшей работы, по запросу подпитки включается насос, оставшийся в рабочем состоянии.<br />
5. Сброс аварийного состояния насосов осуществляется кнопкой «СБРОС» со щита управления.<br />
6. Для каждого насоса предусмотрена электрическая схема с возможностью автоматического и ручного управления (переключатель «Р-О-А», кнопки «ПУСК» и «СТОП»).<br />
7. По состоянию переключателей «Р-О-А» программа должна определять исключение одного или обоих насосов из автоматического управления. Если один из насосов переведён в положение отличное от «А», то программа продолжает автоматическую работу с оставшимся насосом, при этом выведенный из работы насос рассматривается как аварийный, но без выдачи сигнала «АВАРИЯ» —  т.е. вступает в действие АВР.<br />
<br />
Схематично подключение к ПР будет таким<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=5626&amp;d=1570911399" rel="Lightbox" id="attachment5626" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=5626&amp;thumb=1&amp;d=1570911399" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: Pic_01_01.PNG
Просмотров: 6388
Размер:	48.5 Кб
ID:	5626" style="margin: 5px" /></a><br />
<br />
<b>2. АНАЛИЗ</b><br />
<br />
Начинаем с декомпозиции задачи.<br />
На главном холсте создадим единственный макрос «повысительные насосы» (UpperPumps), входы и выходы которого и подключим к физическим контактам программируемого реле. Других элементов на главном холсте не будет. Это сделано для удобства редактирования программы без привязки к аппаратной части и для возможности повторного использования на других программируемых реле производства ОВЕН.<br />
Главный холст будет выглядеть<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=5615&amp;d=1570905331" rel="Lightbox" id="attachment5615" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=5615&amp;thumb=1&amp;d=1570905331" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: Pic_02_01.PNG
Просмотров: 10350
Размер:	44.3 Кб
ID:	5615" style="margin: 5px" /></a><br />
<br />
Рассмотрим возможное содержимое макроса UpperPumps. Очевидно, что он состоит из трёх крупных блоков — два однотипных блока «состояние насоса» и координирующая их «схема управления».<br />
Таким образом, задача распалась на две соответствующие подзадачи.<br />
Повторяющийся код, описывающий состояние отдельного насоса, удобно заключить в макрос «состояние насоса» (Pump) и дважды разместить его на холсте. Получается, что в состав макроса «повысительные насосы» (UpperPumps) будут входить два однотипных макроса «состояние насоса» (Pump) и схема управления.<br />
<br />
Нельзя не упомянуть существующий в онлайн-библиотеке ОВЕН такого макроса как ActMech — «состояние насоса». Это сложный макрос, предназначенный для построения систем управления насосными группами.<br />
<a rel="nofollow noopener noreferrer" href="http://ftp-ow.owen.ru/softupdate/OWEN%20Logic/OnlineComponents/Macros/Additional/" target="_blank" title="http://ftp-ow.owen.ru/softupdate/OWEN%20Logic/OnlineComponents/Macros/Additional/">http://ftp-ow.owen.ru/softupda... dditional/</a><br />
<a rel="nofollow noopener noreferrer" href="http://ftp-ow.owen.ru/softupdate/OWEN%20Logic/OnlineComponents/Macros/Additional/ActMech_v1.05.pdf" target="_blank" title="http://ftp-ow.owen.ru/softupdate/OWEN%20Logic/OnlineComponents/Macros/Additional/ActMech_v1.05.pdf">http://ftp-ow.owen.ru/softupda... _v1.05.pdf</a><br />
Т.к. я с ним ещё не разобрался, то использую собственные более простые наработки.<br />
<br />
<b>3. МАКРОС «СОСТОЯНИЕ НАСОСА» — Pump</b><br />
<br />
Этот макрос формирует выход «Пуск/Останов» и состояние насоса «Исправен/Аварийный»:<br />
- по запросу включает или отключает насос;<br />
- по наличию протока следит за исправностью насоса;<br />
- при неисправности насоса — отключает его, устанавливает в лог.1. сигнал «Авария насоса»;<br />
- по сигналу «Сброс аварии» — сбрасывается состояние «Авария насоса».<br />
Кроме дискретных входных сигналов, на вход в данный блок (макрос) поступают также и три параметра (по аналогии с параметрами ОВЕН САУ-У):<br />
- время задержки включения насоса после поступления сигнала «Запрос»;<br />
- задержка включения контроля протока после формирования сигнала «Пуск насоса»;<br />
- длительность допустимых «провалов» от датчика протока.<br />
<br />
Названия входов и выходов макроса сделаем следующими:<br />
<table cellspacing="1" cellpadding="5" class="stg_table tborder sortable" id="sorttable9418"><thead class="thead"><tr><th><div align="center">Контакт</div></th><th><div align="center">Обозначение</div></th><th>&nbsp;</th><th>Назначение</th></tr></thead><tbody><tr class="alt2"><td>I1</td><td>Reset</td><td>&nbsp;</td><td>Сброс аварии</td></tr><tr class="alt1"><td>I2</td><td>Start</td><td>&nbsp;</td><td>Запрос «Пуск насоса»</td></tr><tr class="alt2"><td>I3</td><td>Stream</td><td>&nbsp;</td><td>Датчик протока</td></tr><tr class="alt1"><td>I4</td><td>T_delay_start</td><td>&nbsp;</td><td>Задержка включения насоса, с</td></tr><tr class="alt2"><td>I5</td><td>T_alarm_start</td><td>&nbsp;</td><td>Задержка включения контроля протока после пуска насоса, с</td></tr><tr class="alt1"><td>I6</td><td>T_alarm_delay</td><td>&nbsp;</td><td>Длительность допустимых провалов датчика протока, с</td></tr><tr class="alt2"><td>Q1</td><td>Alarm</td><td>&nbsp;</td><td>Авария насоса</td></tr><tr class="alt1"><td>Q2</td><td>Start</td><td>&nbsp;</td><td>Пуск насоса</td></tr></tbody></table><br />
Начнём с формирования состояний Пуск/Останов. Пуск и останов насоса управляются от входа «Запрос», но очевидно, что аварийный насос не должен включаться. На основе входов «Запрос», «Авария насоса» и выхода «Пуск» сформируем таблицу истинности<br />
<table cellspacing="1" cellpadding="5" class="stg_table tborder sortable" id="sorttable3428"><thead class="thead"><tr><th>Запрос</th><th><div align="center">Авария</div>насоса</th><th>&nbsp;</th><th><div align="center">Пуск</div>насоса</th><th>Примечание</th></tr></thead><tbody><tr class="alt2"><td>0</td><td>0</td><td>&nbsp;</td><td>0</td><td>&quot;Останов&quot;, т.к. нет &quot;Запроса&quot;</td></tr><tr class="alt1"><td>0</td><td>1</td><td>&nbsp;</td><td>0</td><td>&quot;Останов&quot;, т.к. нет &quot;Запроса&quot;</td></tr><tr class="alt2"><td>1</td><td>0</td><td>&nbsp;</td><td>1</td><td>&quot;Пуск&quot;, т.к. есть и &quot;Запрос&quot; и &quot;Исправен&quot;</td></tr><tr class="alt1"><td>1</td><td>1</td><td>&nbsp;</td><td>0</td><td>&quot;Останов&quot;, т.к. насос &quot;Аварийный&quot;</td></tr></tbody></table><br />
В этом случае функция очевидна и использовать карты Карно нет необходимости.<br />
Т.к. OWEN Logic разумно не позволяет соединять выход макроса со входом элемента, а сигнал «Авария насоса» является выходным для всего блока (макроса), то для формирования «Авария насоса» поставим на холст RS-триггер (с приоритетом сброса).<br />
Также сразу учтём задержку включения насоса — для этого масштабируем входную переменную и запишем её в таймер задержки включения. Получим схему<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=5616&amp;d=1570905568" rel="Lightbox" id="attachment5616" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=5616&amp;thumb=1&amp;d=1570905568" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: Pic_03_01.png
Просмотров: 7618
Размер:	131.9 Кб
ID:	5616" style="margin: 5px" /></a><br />
<br />
Соберём схему формирования сигнала «Авария насоса» на RS-триггере.<br />
Сигнал «Зафиксировать аварию» формируется при наступлении всех событий одновременно:<br />
1) насос включён,<br />
2) после включения прошло время задержки контроля при пуске,<br />
3) протока нет более времени длительности допустимых провалов.<br />
Очевидно, что событие 2 включает в себя и событие 1. Событие 2 формируется из события 1 через триггер задержки включения. Событие 3 формируется из сигнала «Проток», проходящего через таймер задержки отключения. Таким образом, в составлении таблицы истинности участвует два входных параметра — «разрешён анализ протока» и «проток отсутствует более допустимого времени».<br />
<table cellspacing="1" cellpadding="5" class="stg_table tborder sortable" id="sorttable9698"><thead class="thead"><tr><th><div align="center">Разрешён</div>анализ протока</th><th><div align="center">Проток</div>отсутствует долго</th><th>&nbsp;</th><th><div align="center">Зафиксировать</div>аварию</th></tr></thead><tbody><tr class="alt2"><td>0</td><td>0</td><td>&nbsp;</td><td>0</td></tr><tr class="alt1"><td>0</td><td>1</td><td>&nbsp;</td><td>0</td></tr><tr class="alt2"><td>1</td><td>0</td><td>&nbsp;</td><td>1</td></tr><tr class="alt1"><td>1</td><td>1</td><td>&nbsp;</td><td>0</td></tr></tbody></table>И в этом случае функция также очевидна.<br />
Итак, при помощи двух задержек формируем сигналы «Разрешён анализ» и «Проток отсутствует долго». Далее из этих сигналов формируем сигнал «Зафиксировать аварию», которую подадим на вход «S» RS-триггера «Авария насоса».<br />
Сброс этого триггера осуществим от сигнала «Сброс аварии».<br />
При доработке программы немного перемещаем компоненты. Получаем схему<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=5617&amp;d=1570905568" rel="Lightbox" id="attachment5617" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=5617&amp;thumb=1&amp;d=1570905568" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: Pic_03_02.png
Просмотров: 5813
Размер:	108.8 Кб
ID:	5617" style="margin: 5px" /></a><br />
<br />
При проверке в симуляторе видно, что даже в состоянии «Авария насоса» отсчёт времени в таймерах происходит при подаче сигнала «Запрос». Сделаем вход на таймер задержки включения насоса равным нулю при состоянии «Авария насоса» независимо от состояния входа «Запрос».<br />
Также запомним и в вышестоящем макросе «ПОВЫСИТЕЛЬНЫЕ НАСОСЫ» UpperPumps примем меры для предотвращения невозможности установить состояние «Авария насоса» при случайном или преднамеренном «залипании» входного сигнала «Сброс аварии» — ко входу Reset подключим триггер переднего фронта.<br />
В итоге получаем схему, которую и сохраним в виде макроса с названием «Pump».<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=5618&amp;d=1570905568" rel="Lightbox" id="attachment5618" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=5618&amp;thumb=1&amp;d=1570905568" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: Pic_03_03.png
Просмотров: 5941
Размер:	151.9 Кб
ID:	5618" style="margin: 5px" /></a><br />
<br />
<b>4. МАКРОС «ПОВЫСИТЕЛЬНЫЕ НАСОСЫ» — UpperPumps</b><br />
<br />
Макрос немного сложнее макроса «состояние насоса» поэтому при его создании вместо множества перекрёстных линий связи будут использоваться переменные.<br />
<br />
Итак, на входе в макрос имеются следующие входные сигналы:<br />
- Насос 1 в режиме «АВТОМАТИЧЕСКИЙ»;<br />
- Насос 2 в режиме «АВТОМАТИЧЕСКИЙ»;<br />
- Сброс аварии;<br />
- запрос включения насоса;<br />
- датчик протока.<br />
Кроме дискретных входных сигналов, на вход в данный блок (макрос) поступают также и три параметра (по аналогии с параметрами ОВЕН САУ-У):<br />
- время задержки включения насоса после поступления сигнала «Запрос»;<br />
- задержка включения контроля протока после формирования сигнала «Пуск насоса»;<br />
- длительность допустимых «провалов» от датчика протока.<br />
<br />
Названия входов и выходов макроса сделаем следующими:<br />
<table cellspacing="1" cellpadding="5" class="stg_table tborder sortable" id="sorttable9773"><thead class="thead"><tr><th><div align="center">Контакт</div></th><th><div align="center">Обозначение</div></th><th>&nbsp;</th><th>Назначение</th></tr></thead><tbody><tr class="alt2"><td>I1</td><td>InAuto1</td><td>&nbsp;</td><td>Насос 1 в режиме «АВТОМАТИЧЕСКИЙ»</td></tr><tr class="alt1"><td>I2</td><td>InAuto2</td><td>&nbsp;</td><td>Насос 2 в режиме «АВТОМАТИЧЕСКИЙ»</td></tr><tr class="alt2"><td>I3</td><td>Reset</td><td>&nbsp;</td><td>Сброс аварии</td></tr><tr class="alt1"><td>I4</td><td>Request</td><td>&nbsp;</td><td>Запрос включения насоса</td></tr><tr class="alt2"><td>I5</td><td>Stream</td><td>&nbsp;</td><td>Датчик протока</td></tr><tr class="alt1"><td>I6</td><td>T_delay_start</td><td>&nbsp;</td><td>Задержка включения насоса, с</td></tr><tr class="alt2"><td>I7</td><td>T_alarm_start</td><td>&nbsp;</td><td>Задержка включения контроля протока после пуска насоса, с</td></tr><tr class="alt1"><td>I8</td><td>T_alarm_delay</td><td>&nbsp;</td><td>Длительность допустимых провалов датчика протока, с</td></tr><tr class="alt2"><td>Q1</td><td>Pump_1</td><td>&nbsp;</td><td>Пуск насоса 1</td></tr><tr class="alt1"><td>Q2</td><td>Pump_1</td><td>&nbsp;</td><td>Пуск насоса 2</td></tr><tr class="alt2"><td>Q3</td><td>Alarm_1</td><td>&nbsp;</td><td>Авария насоса 1</td></tr><tr class="alt1"><td>Q4</td><td>Alarm_2</td><td>&nbsp;</td><td>Авария насоса 2</td></tr></tbody></table><br />
Очевидно, что некоторая часть сигналов со входов коммутируется на соответствующие входы макросов «состояние насоса» без какой либо обработки.  Для сигнала «Сброс аварии» можно выделить одиночный импульс по фронту, чтобы не получить эффект «залипания» кнопки. Получаем схему, где Pump1 и Pump2  — это макросы «состояние насоса»<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=5619&amp;d=1570908017" rel="Lightbox" id="attachment5619" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=5619&amp;thumb=1&amp;d=1570908017" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: Pic_04_01.png
Просмотров: 5352
Размер:	121.5 Кб
ID:	5619" style="margin: 5px" /></a><br />
<br />
В макросе решаются задачи чередования и АВР. На основании значения входов и состояний насосов:<br />
- Насос 1 в «АВТ»,<br />
- Насос 2 в «АВТ»,<br />
- Авария насоса 1,<br />
- Авария насоса 2,<br />
- Запрос включения насоса<br />
принимается решение о формировании всего двух сигналов:<br />
- Запрос включения насоса 1;<br />
- Запрос включения насоса 2.<br />
<br />
Очевидно, что из сигналов «Насос N в «АВТ» и «Авария насоса N» получается сигнал «Насос N доступен для работы».<br />
<br />
Сигнал «Запрос включения насоса» в схеме управления нужен не сам по себе, а как переключатель состояния очерёдности работы насосов. Т.е. при помощи счётного триггера (T-trig) сигнал «Запрос включения насоса» формирует сигналы «Очередь работы насоса 1» и «Очередь работы насоса 2». Для исключения ложного срабатывания счётного триггера от дребезга контактов перед триггером ставится схема подавления дребезга (Debounce).<br />
Способы построения T-trigger и Debounce рассмотри позже.<br />
Получаем схему<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=5620&amp;d=1570908017" rel="Lightbox" id="attachment5620" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=5620&amp;thumb=1&amp;d=1570908017" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: Pic_04_02.png
Просмотров: 5302
Размер:	140.9 Кб
ID:	5620" style="margin: 5px" /></a><br />
<br />
Таким образом для формирования сигналов запроса включения насосов имеем сигналы:<br />
- Насос 1 доступен,<br />
- Насос 2 доступен,<br />
- Очередь насоса 1,<br />
- Очередь насоса 2.<br />
На основании этих сигналов строим таблицу истинности для функций «Запрос включения насоса 1» и «Запрос включения насоса 2». Чтобы сократить объём статьи, объединю две таблицы в одну. Неопределённые состояния, которые недостижимы в работе — помечены символом «*»<br />
<table cellspacing="1" cellpadding="5" class="stg_table tborder sortable" id="sorttable2584"><thead class="thead"><tr><th><div align="center">Насос 1</div>доступен</th><th><div align="center">Насос 2</div>доступен</th><th><div align="center">Очередь</div>насоса 1</th><th><div align="center">Очередь</div>насоса 2</th><th>&nbsp;</th><th><div align="center">Запрос включения</div>насоса 1</th><th><div align="center">Запрос включения</div>насоса 2</th></tr></thead><tbody><tr class="alt2"><td>0</td><td>0</td><td>0</td><td>0</td><td>&nbsp;</td><td>0</td><td>0</td></tr><tr class="alt1"><td>0</td><td>0</td><td>0</td><td>1</td><td>&nbsp;</td><td>0</td><td>0</td></tr><tr class="alt2"><td>0</td><td>0</td><td>1</td><td>0</td><td>&nbsp;</td><td>0</td><td>0</td></tr><tr class="alt1"><td>0</td><td>0</td><td>1</td><td>1</td><td>&nbsp;</td><td>*</td><td>*</td></tr><tr class="alt2"><td>0</td><td>1</td><td>0</td><td>0</td><td>&nbsp;</td><td>0</td><td>0</td></tr><tr class="alt1"><td>0</td><td>1</td><td>0</td><td>1</td><td>&nbsp;</td><td>0</td><td>1</td></tr><tr class="alt2"><td>0</td><td>1</td><td>1</td><td>0</td><td>&nbsp;</td><td>0</td><td>1</td></tr><tr class="alt1"><td>0</td><td>1</td><td>1</td><td>1</td><td>&nbsp;</td><td>*</td><td>*</td></tr><tr class="alt2"><td>1</td><td>0</td><td>0</td><td>0</td><td>&nbsp;</td><td>0</td><td>0</td></tr><tr class="alt1"><td>1</td><td>0</td><td>0</td><td>1</td><td>&nbsp;</td><td>1</td><td>0</td></tr><tr class="alt2"><td>1</td><td>0</td><td>1</td><td>0</td><td>&nbsp;</td><td>1</td><td>0</td></tr><tr class="alt1"><td>1</td><td>0</td><td>1</td><td>1</td><td>&nbsp;</td><td>*</td><td>*</td></tr><tr class="alt2"><td>1</td><td>1</td><td>0</td><td>0</td><td>&nbsp;</td><td>0</td><td>0</td></tr><tr class="alt1"><td>1</td><td>1</td><td>0</td><td>1</td><td>&nbsp;</td><td>0</td><td>1</td></tr><tr class="alt2"><td>1</td><td>1</td><td>1</td><td>0</td><td>&nbsp;</td><td>1</td><td>0</td></tr><tr class="alt1"><td>1</td><td>1</td><td>1</td><td>1</td><td>&nbsp;</td><td>*</td><td>*</td></tr></tbody></table>Из этой сводной таблицы истинности создаются карты Карно<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=5621&amp;d=1570908017" rel="Lightbox" id="attachment5621" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=5621&amp;thumb=1&amp;d=1570908017" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: Pic_04_03.PNG
Просмотров: 4338
Размер:	44.5 Кб
ID:	5621" style="margin: 5px" /></a><br />
<br />
Из карт Карно получаем две функции<br />
<br />
Запрос насоса 1 = (Насос 1 доступен) *(Очередь насоса 1)+ /(Насос 2 доступен)*(Насос 1 доступен)*(Очередь насоса 2)<br />
<br />
Запрос насоса 2 = (Насос 2 доступен) *(Очередь насоса 2)+ /(Насос 1 доступен)*(Насос 2 доступен)*(Очередь насоса 1)<br />
<br />
Эти функции и реализуем в схеме<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=5622&amp;d=1570909824" rel="Lightbox" id="attachment5622" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=5622&amp;thumb=1&amp;d=1570909824" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: Pic_04_04.png
Просмотров: 5409
Размер:	119.7 Кб
ID:	5622" style="margin: 5px" /></a><br />
<br />
<b>5. ВСПОМОГАТЕЛЬНЫЕ МАКРОСЫ</b><br />
<br />
Счётный триггер реализуется схемой<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=5623&amp;d=1570910529" rel="Lightbox" id="attachment5623" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=5623&amp;thumb=1&amp;d=1570910529" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: Pic_05_01.PNG
Просмотров: 4202
Размер:	39.4 Кб
ID:	5623" style="margin: 5px" /></a><br />
<br />
Макрос подавления дребезга реализуется схемой<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=5624&amp;d=1570910529" rel="Lightbox" id="attachment5624" ><img src="https://www.cyberforum.ru/blog_attachment.php?attachmentid=5624&amp;thumb=1&amp;d=1570910529" class="thumbnail" border="0" alt="Нажмите на изображение для увеличения
Название: Pic_05_02.PNG
Просмотров: 5355
Размер:	36.7 Кб
ID:	5624" style="margin: 5px" /></a><br />
<br />
<b>ВЫВОДЫ И РЕКОМЕНДАЦИИ</b><br />
<br />
Рассмотрен способ построения программы на языке FBD в среде OWEN Logic для программируемых реле семейств ПР100/ПР110/ПР114/ПР200. Решена практическая задача написания программы для повысительных насосов подпитки системы отопления.<br />
<br />
Подобный способ (макросы «состояние насоса» и комбинационная схема управления) подходит для создания программ управления двумя насосами. Но для управления большим количеством насосов требуется иной подход — добавление в состав логики «состояние насоса» некоего подобия счётчика и схемы трансляции (распространения) счётного импульса при неисправности.<br />
<br />
К функционалу макроса «Повысительные насосы» (UpperPumps) можно добавить ещё следующие возможности:<br />
- формирование сигнала «Внимание» при полном отсутствии насосов в «АВТОМАТИЧЕСКОМ» режиме,<br />
- формирование сигнала «Утечка в теплосети» (Leak) при чересчур длительной работе повысительного насоса,<br />
- сделать счётный триггер (T-trigger) энергонезависимым.<br />
<br />
<b>ПРИЛОЖЕНИЕ</b><br />
<br />
Исходники для OWEN Logic версия 1.14.194.18756<br />
<a href="https://www.cyberforum.ru/blog_attachment.php?attachmentid=5625&amp;d=1570910732" >Soft20191012.owl.7z</a></div>

]]></content:encoded>
			<dc:creator>ФедосеевПавел</dc:creator>
			<guid isPermaLink="true">https://www.cyberforum.ru/blogs/534277/6108.html</guid>
		</item>
	</channel>
</rss>
