Часть 2, приоритеты и базовые сигналы / программирование :: длиннопост :: реактор образовательный :: АСУ ТП :: geek (Прикольные гаджеты. Научный, инженерный и айтишный юмор)

песочница geek АСУ ТП программирование реактор образовательный длиннопост 

Часть 2, приоритеты и базовые сигналы

Часть 1,введение (отправил в политоту из-за двусмысленных высказываний, ибо было сложно удержаться)


Приоритеты команды
Итак, вы решили взяться за благое дело и сэкономить пару дефицитных рупей на найме программиста и запилить всё своими руками. Что же для этого надо?
1) скачайте среду разработки
2) откройте среду разработки

3) закройте среду разработки и идите искать программиста

Для начала, давайте проясним одну вещь. То что я рассказываю не является аксиомой. Это концепция архитектуры, позволяющая сделать максимально гибкое и устойчивое решение, позволяющее безболезненно изменять различные прослойки кода внутри проекта, не роняя завод.

Итак, помните, я говорил что всё есть сигнал? Я наврал. Всё есть объект.
Вход - это объект.
Выход - это объект.
Бутылка - тоже объект, но более высокого порядка.

А дальше мы начинаем собирать пирамидку из кубиков:
- базовые сигналы нижнего уровня, дискретные и аналоговые
- простые устройства
- сложные устройства
- технологические участки
- технологические линии
- производственные участки
...
- планирование производства

Вот всё что лежит до троеточия и будем рассматривать. Начнём с базиса, но перед этим - а кто здесь главный? Ты, я? оператор? директор? да пошёл ты в жопу, директор! Главная здесь ОНА:

песочница,geek,Прикольные гаджеты. Научный, инженерный и  айтишный юмор,АСУ ТП,программирование,реактор образовательный,длиннопост


Мне лень рисовать пирамидки маслоу, поэтому обойдёмся блок-схемами. ГОСТы - для слабаков! Главное, чтобы было понятно...

ооооо
Глобальные переменные
Внутренние переменные Управление из алгоритма
Управление с НМ1 Внешние сигналы
Ограничение уровней доступа на НМ1
[а] Администратор 0 Оператор [п~] Пользователь Без ограничений
Приоритетность команд
Приоритет управляющих сигналов и режимов работы от наивысшего


УХ, ебать! Но это лёгкий вариант, извините.

Глобальные переменные
Обратите внимание на "Глобальные переменные". Что в них входит? А всё, что проходит красной нитью через весь код и существует в единственном экземпляре:
- Аварийный останов
- Общий сброс аварий
- Общий режим эмуляции
- Общий перевод в автоматический режим всех устройств (очень полезно, если оператор любит пошарить ручками где надо и забывает что делал в недрах нашей системы)

Как оно выглядит в объявлении переменных? Вот так:

All_Block:BOOL; //аварийная остановка
All_Reset :BOOL; //сброс всех аварий
All_Sim :BOOL; //общий режим эмуляции
All_Auto :BOOL; //перевести всё в автоматический режим
sys :system; //системные переменные
sec :REAL; //длительность одной секунды

Обратите внимание, на две последние переменные. Это наша опора и поддержка, которую мы будем гонять вместе с первой четвёркой по всем проектам.
sec - длительность секунды в часах, да, я знаю что считать в REAL повышает нагрузку на CPU, но пардон, у вас мощности позволяют то в 2к2+ году
sys - структура системных переменных, которая собирает в себя базовые компоненты, реализация которых отличает у разных производителей:
- флаг TRUE
- флаг FALSE
- импульсы с разным весом, которые живут ровно 1цикл программы
- прочая мелочёвка

Опс, опять что-то новенькое. 1 цикл программы. Да-да, ваш код всегда исполняется от начала и до конца сверху вниз, слевана право. За всякие go_to я лично вырываю руки, ибо нехер.

Итого, на текущий момент у меня в sys лежит:

1
2
3
4
5
€
7
3
3
10
11
12
13
14
15
16
17
13
13
20
21
22
23
24
TYPE system :
STRUCT
pulse_01s	:BOOL,
pulse_ls	:BCDL,
xON	:BOOL,
xOFF	:BCXDL,
Hand_DI	:BOOL,
Hand_D0	:BCDL,
Hand_AI	:BOOL,
Hand_A0	:BOOL,
Hand_M	:BOOL,
Hand_V	:BOOL,
Hand_Axis	:BOOL,
Hand_Any	:BOOL,

Дану ёб твою мать, скажете вы, что это за херня? И будете правы!
- x - не потому что хуй, а потому что ON и OFF зарезервированы системой и их нельзя просто так применять;
- Hand - флаги, что хотя бы одно устройство данного типа переведено в ручной режим;
- Alm - флаги, что хоть одно устройство данного типа находится в аварии;
- Any - сборный флаг для всех флагов, привязанных к типам устройств;
- V - Valve, а не то что вы подумали. Клапан, заслонка, задвижка;
- M - не мудак, к сожалению, а Motor. Вентилятор, транспортёр, всё что вращается - всё Motor. Я иногда делю на MD и MDA, дикрестное и дискретно-аналоговое управления, но это личные заморочки, не более;
- DTL - DataTimeL.. хз что за L, типовая структура формата год-месяц-дата-час-минута-секунда, которую мы будем использовать при работе с расписанием и всем, что требует проверки системного времени.

Ну наконец-то! А где код-то? Хрена вам, а не код. Рано.
На очереди ещё одна структура, которая будет всегда и везде, обеспечивая работу вон той страшной картинки наверху, обычно я называю её CMD:

1
2
3
4
S
€
7
3
s
10
11
12
TYPE CMD : STRUCT

	Start_A	:BOOL,
	Start_M	:BOOL.
	Hand	:BOOL.
	Lock	:BOOL.
	Force	:BOOL.
	Vector	:BOOL.
	ResetOp	:BOOL.
	Reset	:BOOL,
END_	STRUCT	
END	TYPE	
//управление в автоматическом режиме //управление в ручном режиме
//режим управления, 0


УправлениеУра! Наконец-то! Мы добрались! И теперь...

FUNCTION drv_CMD : BOOL VAR_INPUT
Alarm :BOOL; END_VAR VAR_IN_OUT
CHD :CHD; END_VAR VAR
END VAR
// Опредление режима работы драйвера IF NOT All_Block THEN
//He принудительный режим IF NOT CMD.Force THEN
IF NOT CHD.Lock AND NOT Alarm THEN //Автоматический режим IF NOT CHD.Hand THEN

песочница,geek,Прикольные гаджеты. Научный, инженерный и  айтишный юмор,АСУ ТП,программирование,реактор образовательный,длиннопост

Видите вон там странный Alarm, которого раньше не было? Видите? И он есть!
А всё почему? Да потому что...
1) мы делаем универсальный код, который будет применим для всего и вся
2) программа - это не только программа, это ещё функциональный блоки функции

В чём же разница? Если кратко - функциональный блок может иметь свои внутренние, приватные, переменные, а функции - работает только с внешними и временными.
СтопЭ. временные и внутренние - какая в пень разница?
Внутренняя - сохраняет своё значения при переходе на новый цикл программы.
Временная - теряет своё значение может содержать случайную величину при переходе на новый цикл программы.
Таким образом, если вы внутри функции делаете А+Б=С и по значению С принимаете решение КАЖДЫЙ раз вызывая функцию, то можно использовать временную. А если делаете А+Б = С один раз и больше к этому не возвращаетесь, только читая С, то нужна внутренняя переменная. И опять таки... если вы хотите сохранить значение С при перезапуске контроллера - будьте добрый, сделать её энергонезависимой, т.е. Retain.

Вернёмся к Alarm. Для функциональных блоков (FB) и функций (FC) есть следующие виды переменных:
- входные - можно только читать
- выходные - можно только записывать
- входные-выходные - как тугая попка трапа, можно и читать и изменять значение
Это очень важно, когда у вас одна и та же внешняя переменная передаётся разныеFC/FB а вы сидите и хлопаете глазками, почему значение теряется посреди программы. Да потому что вы проебались и привязали не к тому типу. Наиболее наглядно сие видно в графических языках:

"«•оссл/и^рв1* "SseLCChjOt f eFE " n9Zjr*Lbu*m wrCAetualJ5p««dr
—	1ч» a Lu ii On
—Switch OÍÍ “
—	P9ÜUT« .Actuad Sp«M»
“Pebiule
"Eag¿.ae*
Huyauc_Or.
— rK Oa'
Prc5C5_3í>eed_?.síic "pl!_Pr**et_Sp*ed ? bsd	" -eftohftd*
SBC,песочница,geek,Прикольные гаджеты. Научный, инженерный и  айтишный

Слева- входные (input) и входно-выходные (input-output) переменные, справа -выходные.

Фух, разобрались, теперь вызовем нашу страшную функцию:

2S
30
31
// Управлявшее слово
drv_CMD(Alarm := stare.Alarm, CMD := CMD); IF CMD.Hand THEN sys.Hand_V := TRUE; END IF,песочница,geek,Прикольные гаджеты. Научный, инженерный и  айтишный юмор,АСУ ТП,программирование,реактор образовательный,длиннопост


Драйвера, устройства и состояния
Расширим очко овертона терминологию: драйвер. Нет, это не виндовый драйвер. Но близко. Это некий алгоритм и набор переменных, описывающий работу базового или типового устройства. Входа, выхода, насоса, клапана. Оно беспечивает автомномный контроль:
- безопасности
- аварий
- ручного и автоматического управления
- наработки
Включает в себя все необходимые для жизни параметры и настройки, коих дофига и больше. Но это мы рассмотрим потом. Или не рассмотрим. Если микроскоп вдруг сломается.

Выглядит управление дискретным входом следующим образом:

ооооо
Глобальные переменные	Ограничение уровней доступа на НМ1	Сигнал	
Внутренние переменные	[а] Администратор	0 У	BOOL = неопределён
Управление из алгоритма	0 Оператор	0 У	BOOL = TRUE
Управление с НМ1	(ГГ) Пользователь	0 У	BOOL = FALSE
Внешние сигналы	Без ограничений	0 У	Значение
Дискретный


Итак, для работы драйвера нам необходимы:
- CMD - структура команд, рассмотрена выше
- cfg - структура параметров
- state - структура состояний

Дискретный вход слишком прост и отдельный набор состояний ему не нужен. Состояния включают в себя коды аварий, подсчёт наработки, вспомогательные плюшки для удобства отображения оператору "что тут происходит, мамочки, почему оно встало?!"

TYPE cfg_DI :
STRUCT
use_NC	:BOOL;	//выбор	типа сигнала, 0 - нормально открытый, 1 -	нормально закрытый
T_ON	:INT;	//время	фильтрации дребезга на включение сигнала
T_OFF	:INT;	//время	фильтрации дребезга на выключение сигнала
END_STRUCT END TYPE,песочница,geek,Прикольные гаджеты. Научный,

use_NC...
NC - нормальной закрытый сигнал
NO - нормально открытый сигнал
Например, у вас есть дверь. Стоит датчик, контролирующий что "дверь закрыта". 

Есликогда она закрыта сигнал = 0, а при открытии двери сигнал = 1, это нормально открытый сигнал.
Если когда она закрыта сигнал = 1, а при закрытии двери сигнал = 0, то это нормально закрытый сигнал.

Применение того или иного типа сигнала должно быть продиктовано соображениями безопасности. Например, аварийный стоп - должен быть нормально закрытым сигналом. Есть сигнал - всё хорошо, пропал - всё плохо. Почему так? Потому что Вася уронил топор на кабель и сигнал пропал - вы должны отключить всю систему, чтобы бедного Васю не намотало, превратив в инвалида пожизненно. Да-да, безопасность Васи - Ваша ответственность!

А если это не критичный сигнал, требующий контроля целостности сигнальной линии, то можно смело ставить нормально открытый датчик. Но это теория, а на практике ныне - что найдёте, то и поставите. Сорян.

УУУ, сука, сколько буков то! А это только начало! И я не ответил на главный вопрос - на кой хер нам вообще различать на уровне драйвера тип сигнала? В алгоритме основном поправим, да и делов то. НЕТ! Хватит! Не усложняйте себе жизнь!

Вам, как программисту, должно быть глубочайше насрать какой там тип датчика -сработал это всегда = 1, не сработал это всегда = 0. И именно эту задачу унификации решает драйвер дискретного входа. УНИ-ФИ-КА-ЦИИ, а не загрузки процессорного времени бесполезным хламом. Так менеджеру и ответите, что вы ускоряете дальнейший цикл разработки, ага.

ТаймерыАвтор, ты заебал, где код драйвера? Да вот он, только работать он у вас не будет:

1
2
3
4
5
€
7
3
5
10
11
12
13
14
15
FUNCTION BLOCK drv DI
VAR_INPÜT Signal ENDJVAR VÄR_IN_OUT	:BOOL;
CMD cfg END_VAR VÄR_OUTPUT	:CHD; :cfg_DI;
Result END_VÄR VAR	:BOOL;
Clock	:ARRAY [0..1] OF Time_S;
END VAR,песочница,geek,Прикольные гаджеты. Научный, инженерный и  айтишный

1
2
3
4
5
€
7
3
5
10
11
12
13
14
15
16
17
13
13
20
21
22
23
24
25
2€
27
23
23
30
31
32
33
34
35
3€
37
33
35
40
41
42
43
44
45
4€
47
43
45
50
51
52
53
54
55
5€
57
// ДИСКРЕТНЫЙ ВХОД
// Сигналы от датчиков, например:
//	- уровень
//	-

А почему - угадаете? Не, вы не тупые, вы умные. Просто я хитрожопый и вместо системных таймеров использую самописные. Нахуа-хуа? Да потому что системные кривые. И их мало. И каждый системный таймер можно использовать только один раз(если они аппаратные, как в старых ПЛК).
Почему использовать системный таймеры, это грех:
- если вы измените во время работы таймера уставку времени и она окажется меньше, чем уже прошедшее время с момента запуска таймера, то он встанет раком
- нет паузы
- нет сброса
- нет контроля % отсчитанного времени
- и, самое главное, время в формате Time, которое очень неудобно выводить на панель оператора
Самописный таймер решает все эти задачи. Как вы уже догадались, там потребуется структура переменных для него и FC, вот они для самого простого таймера, который используется в драйвере выше:

1	ТУРЕ Т1те_3	:		
2	БТЮТСТ			
3	Бгагг	:ВООЬ;	//управление таймером	
4	0	:ВООЬ;	//выход таймера	
8	Раизе	:ВООЬ;	//пауза	
€	Иезег	:ВООЬ;	//сброс текущего времени	
7	БР	:ШГ;	//ЗебРотпб - уставка, время, которое надо отсчитать	запуска таймер^
8	АР	:ЮТ;	//ActualPoint - аутальное время, которое


FUNCTION drv_Time_S : bool VAR_IN_OUT
T	:|liine_S;
END_VAR VAR
END VAR
// Таймер с краткостью 1 секунда IF T.Start AND NOT T.Reset THEN // Работа таймера IF T.AP < T.SP THEN
IF NOT T.Pause AND sys.pulse_ls THEN T.AP := T.AP + 1;
END_IF;
T.Q := FALSE;
ELSE
// Заданное время достигнуто


И весь этот охреневший объём кода вам нужен в промышленном контроллере только для того, что проверить нажатие кнопочки или срабатывание маленького геркончика. Очень мило, не правда ли?)

Благодарю за терпение, забыл важную картинку и не смог отредактировать пост. В прошлом варианте поста были вопрос на тему, почему pulse_01s и pulse_1s имеют тип BOOL и как вообще эта мерзость должна работать. Не зря в начале было сказано об архитектуре, именно её мы затронем третьей части историй после кружечки кофе и предложим своё видение "как оно должно работать", что, естественно, не претендует на истину и даже не носит рекомендательного характера, потому что код каждого программиста индивидуален и уникален, но если мы сможем писать хотя бы так, чтобы сосед слева мог его прочитать - это уже хорошо.


Подробнее

ооооо Глобальные переменные Внутренние переменные Управление из алгоритма Управление с НМ1 Внешние сигналы Ограничение уровней доступа на НМ1 [а] Администратор 0 Оператор [п~] Пользователь Без ограничений Приоритетность команд Приоритет управляющих сигналов и режимов работы от наивысшего (7) к низшему (0 - автоматический режим). Аварийный Останов НЕТ - А Принудительный режим ДА НЕТ НАЛИЧИЕ ЛЮБОГО ИЗ УСЛОВИИ 1 1 НЕТ _А ]_ 1 1 | Сервисный выключатель по месту | Местный ! установки оборудования. : 1 1 режим ДА- В автоматическом режиме транслируется в команду ручного режима для обеспечения безударного перехода в ручной режим. -нет АВТО ПУСК ПУСК- -ДА—I Датчик Сервисное Технологическая Неисправность безопаности обслуживание блокировка оборудования — 6 — 5 4 -стоп- I------------------------------------ ; Цифровой код режима работы. -{Используется при построении иерархии команд в коде. : Режим управления с игнорированием ] аварий и блокировок. Используется для 1 отладки и тестирования оборудования. I Работает только с ручным режимом. Технологическая блокировка -1 алгоритмическая защита от запуска \ исполнительного механизма при риске "¡повреждения оборудования. Например. I включения насоса на закрытую задвижку. Местный режим - управление физическим выключателем с дверцы шкафа или пульта по месту установки обордования. _• Ручной режим - управление по команде 1 оператора с НМ1. ОСТАНОВ <- <■ -стоп—
1 2 3 4 5 € 7 3 3 10 11 12 13 14 15 16 17 13 13 20 21 22 23 24 TYPE system : STRUCT pulse_01s :BOOL, pulse_ls :BCDL, xON :BOOL, xOFF :BCXDL, Hand_DI :BOOL, Hand_D0 :BCDL, Hand_AI :BOOL, Hand_A0 :BOOL, Hand_M :BOOL, Hand_V :BOOL, Hand_Axis :BOOL, Hand_Any :BOOL, Alm_AI :BOOL, Alm_M :BCDL, Alm_V :BOOL, Alm_Axis :BCDL, Alm_Logick :BOOL, Alm_Any :BOOL, Start :BOOL, DTL :DTL; END_STRUCT END TYPE //импульс весом //импульс весом //всегда TRUE //всегда FALSfJ 100 мс 1 секунда
1 2 3 4 S € 7 3 s 10 11 12 TYPE CMD : STRUCT Start_A :BOOL, Start_M :BOOL. Hand :BOOL. Lock :BOOL. Force :BOOL. Vector :BOOL. ResetOp :BOOL. Reset :BOOL, END_ STRUCT END TYPE //управление в автоматическом режиме //управление в ручном режиме //режим управления, 0 - автоматический, 1 - ручной //технологическая блокировка //принудительный режим с игнорированием аварий //выбор направления, 0 - вперёд, 1 - назад //Reset Operation Time - сброс времени межсервисного интервала //сброс аварии конкретно этого устройств
FUNCTION drv_CMD : BOOL VAR_INPUT Alarm :BOOL; END_VAR VAR_IN_OUT CHD :CHD; END_VAR VAR END VAR // Опредление режима работы драйвера IF NOT All_Block THEN //He принудительный режим IF NOT CMD.Force THEN IF NOT CHD.Lock AND NOT Alarm THEN //Автоматический режим IF NOT CHD.Hand THEN CHD.Start_H := CHD.Start_A; END_IF; ELSE // Технологическая блокировка или авария CHD.Start_H := FALSE; END_IF; END_IF; ELSE // Аварийная блокировка CHD.StartJi := FALSE; END IF;

"«•оссл/и^рв1* "SseLCChjOt f eFE " n9Zjr*Lbu*m wrCAetualJ5p««dr — 1ч» a Lu ii On —Switch OÍÍ “ — P9ÜUT« .Actuad Sp«M» “Pebiule "Eag¿.ae* Huyauc_Or. — rK Oa' Prc5C5_3í>eed_?.síic "pl!_Pr**et_Sp*ed ? bsd " -eftohftd* SBC
2S 30 31 // Управлявшее слово drv_CMD(Alarm := stare.Alarm, CMD := CMD); IF CMD.Hand THEN sys.Hand_V := TRUE; END IF
ооооо Глобальные переменные Ограничение уровней доступа на НМ1 Сигнал Внутренние переменные [а] Администратор 0 У BOOL = неопределён Управление из алгоритма 0 Оператор 0 У BOOL = TRUE Управление с НМ1 (ГГ) Пользователь 0 У BOOL = FALSE Внешние сигналы Без ограничений 0 У Значение Дискретный вход Общий алгоритм обработки сигнала с дискретного входа ПЛК с учётом следующих сценариев: - выбор типа датчика N0/1^0 - фильтрация дребезга - задержка срабатывания
TYPE cfg_DI : STRUCT use_NC :BOOL; //выбор типа сигнала, 0 - нормально открытый, 1 - нормально закрытый T_ON :INT; //время фильтрации дребезга на включение сигнала T_OFF :INT; //время фильтрации дребезга на выключение сигнала END_STRUCT END TYPE
1 2 3 4 5 € 7 3 5 10 11 12 13 14 15 FUNCTION BLOCK drv DI VAR_INPÜT Signal ENDJVAR VÄR_IN_OUT :BOOL; CMD cfg END_VAR VÄR_OUTPUT :CHD; :cfg_DI; Result END_VÄR VAR :BOOL; Clock :ARRAY [0..1] OF Time_S; END VAR
1 2 3 4 5 € 7 3 5 10 11 12 13 14 15 16 17 13 13 20 21 22 23 24 25 2€ 27 23 23 30 31 32 33 34 35 3€ 37 33 35 40 41 42 43 44 45 4€ 47 43 45 50 51 52 53 54 55 5€ 57 // ДИСКРЕТНЫЙ ВХОД // Сигналы от датчиков, например: // - уровень // - проток // - etc //глобальные команды IF All_Auto THEN CMD.Hand := FALSE; END_IF; // Фильтрация сигнала от датчика - включение IF cfg.T_ON > 0 AND Signal THEN Clock[1].Start := TRUE; Clock[0].Start := FALSE; IF Clock[1].Q THEN CMD.Start_A := Signal; END_IF; ELSIF Signal THEN CMD.Start_A := TRUE; Clock[1].Start := FALSE; Clock[0].Start := FALSE; END_IF; // Фильтрация сигнала от датчика - отключение IF cfg.T_0FF > 0 AND NOT Signal THEN Clock[1].Start := FALSE; Clock[0].Start := TRUE; IF Clock[0].Q THEN CMD.Start_A := Signal; END_IF; ELSIF NOT Signal THEN CMD.Start_A := FALSE; Clock[1].Start := FALSE; Clock[0].Start := FALSE; END_IF; // Нормально замкнутный контакт IF cfg.use_NC THEN CMD.Start_A := NOT CMD.Start_A; END_IF; // Спредление режима работы драйвера (контроль входа не должен прерываться при нажатии аварийной остановки, //его нельзя заблокировать или сбросить аварию которой нет, поэтому используется упрощённый вариант управления) IF NOT CMD.Hand THEN CMD.Start_M := CMD.Start_A; END_IF;| IF CMD.Hand THEN sys.Hand_DI := TRUE; END_IF; // Результат работы блока Result := CMD.Start_M; // Бызов и обработка таймеров Clock[l].SP := cfg.T_0N; Clock[0].SP := cfg.T_0FF; drv_Time_S {Clock[0]); drv_Time_S (Clock[1]);
1 ТУРЕ Т1те_3 : 2 БТЮТСТ 3 Бгагг :ВООЬ; //управление таймером 4 0 :ВООЬ; //выход таймера 8 Раизе :ВООЬ; //пауза € Иезег :ВООЬ; //сброс текущего времени 7 БР :ШГ; //ЗебРотпб - уставка, время, которое надо отсчитать запуска таймер^ 8 АР :ЮТ; //ActualPoint - аутальное время, которое прошло с момента 5 ЕЖ)_5ТИиСТ 10 £N0 ТУРЕ
 FUNCTION drv_Time_S : bool VAR_IN_OUT T :|liine_S; END_VAR VAR END VAR // Таймер с краткостью 1 секунда IF T.Start AND NOT T.Reset THEN // Работа таймера IF T.AP < T.SP THEN IF NOT T.Pause AND sys.pulse_ls THEN T.AP := T.AP + 1; END_IF; T.Q := FALSE; ELSE // Заданное время достигнуто T.Q := TRUE; END_IF; ELSE // Выключение и сброс таймера T.AP := 0; T.Q := FALSE; T.Reset := FALSE; END IF;
песочница,geek,Прикольные гаджеты. Научный, инженерный и айтишный юмор,АСУ ТП,программирование,реактор образовательный,длиннопост
Еще на тему
Развернуть
А где учился? Может посоветуешь что-то почитать по этому поводу?
МГУПИ, кафедра "Автоматизация технологических процессов и производств", ныне переведена в МИРЭА и очень хорошо укомплектована материально-технически, чего не могу сказать о квалификации персонала кафедры с точки зрения работы с реальным железом и применением теории к практике.
Так какой контроллер ты в итоге выбрал?
Сейчас сложно что-то выбрать да и не от меня зависит. На скришнотах и примерах будет код для Schneider Electric SoMachine 4.3 LMC058, но он совместим с OMRON NX, NJ, всеми системами на базе CoDeSyS, а также с небольшими доработками для Siemens.
Только зарегистрированные и активированные пользователи могут добавлять комментарии.
Похожие темы

Похожие посты
Как делают пластиковые бутылки. Производство пластиковых бутылок,People & Blogs,Как делают пластиковые бутылки,из +чего делают пластиковые бутылки +для воды,как делают пластиковые бутылки видео,производство пластиковых бутылок,производство бутылок пэт,производство пластиковых бутылок видео,Как делаю
подробнее»

песочница АСУ ТП программирование geek,Прикольные гаджеты. Научный, инженерный и айтишный юмор автоматика напитки завод политика,политические новости, шутки и мемы длиннопост реактор образовательный

Как делают пластиковые бутылки. Производство пластиковых бутылок,People & Blogs,Как делают пластиковые бутылки,из +чего делают пластиковые бутылки +для воды,как делают пластиковые бутылки видео,производство пластиковых бутылок,производство бутылок пэт,производство пластиковых бутылок видео,Как делаю
0.5 сек <-----►
<---->
0.5 сек
------>
время