Библиотека котроля.
Далее опишем вызов функций контроля наших агрегатов. Их всего 2, т.к. в работе присутствует 2 основных логики работы – для вентилей и для мотора. Переменные valve и motor являются ссылочными и будут хранить адреса каждого агрегата во время работы функции (внутри неё), в переменные data передаётся адрес во время вызова функции.
Рис. 5
Опишем вызов функций.
Рис. 6
Рис. 7
Логика обоих агрегатов проста: в зависимости от кода поступившей команды, его статус изменится. Теперь перейдём к библиотеке симуляции.
Библиотека симуляции.
Рис. 8
Создадим аналогично функциям управления 2 функции симуляции. На вход функции Valve_control_sim подаются команды open/close вентиля, выходными параметрами являются статусы opened/closed, из названий очевиден их смысл. На вход функции Motor_control_sim подаются команды start/close мотора, выходным параметром является статус runned. Рассмотрим код функций симуляции.
Рис. 9
Рис. 10
Перейдём к рассмотрению программ.
Программы.
Начнём с программы симуляции. Здесь запускаются соответствующие функции симуляции из созданной ранее библеотеки, передавая в них параметры соответствующих агрегатов.
Рис. 11
Далее рассмотрим основную функцию модели.
Рис. 12
Код программы:
(********************************************************************
* COPYRIGHT --
********************************************************************
* Program: Control
* File: ControlCyclic.ab
* Author: Илья
* Created: December 16, 2022
********************************************************************
* Implementation of program Control
********************************************************************)
PROGRAM _CYCLIC
IF (qo.status = 0) THEN
qo.command := 1
ENDIF
IF (qc.status = 0) THEN
qc.command := 1
ENDIF
IF (qo.oil_steam = 1) THEN
qo.command := 1
qc.command := 2
ELSE IF (qo.oil_steam = 0) THEN
qo.command := 2
qc.command := 1
ENDIF
IF ((motor1.status = 3) OR (valve1.status = 3) OR (valve2.status = 3)) THEN
motor1.command := 2
motor1.role := 0
valve1.command := 2
valve2.command := 2
ENDIF
IF ((motor2.status = 3) OR (valve3.status = 3) OR (valve4.status = 3)) THEN
motor2.command := 2
motor2.role := 0
valve3.command := 2
valve4.command := 2
ENDIF
IF ((motor3.status = 3) OR (valve5.status = 3) OR (valve6.status = 3)) THEN
motor3.command := 2
motor3.role := 0
valve5.command := 2
valve6.command := 2
ENDIF
IF (qc.barrel_level = 1) THEN
IF ((motor1.status <> 3) AND (valve1.status <> 3) AND (valve2.status <> 3)) THEN
IF (motor2.status = 1) THEN
motor2.command := 2
motor2.role := 0
valve3.command := 2
valve4.command := 2
ENDIF
IF (motor3.status = 1) THEN
motor3.command := 2
motor3.role := 0
valve5.command := 2
valve6.command := 2
ENDIF
IF (motor1.status <> 1) THEN
motor1.command := 1
valve1.command := 1
valve2.command := 1
ENDIF
motor1.role := 1
IF (motor1.work_time >= MAX_TIME_IN_ROLE) THEN
motor1.work_time := 0
ENDIF
ELSE IF ((motor2.status <> 3) AND (valve3.status <> 3) AND (valve4.status <> 3)) THEN
IF (motor3.status = 1) THEN
motor3.command := 2
motor3.role := 0
valve5.command := 2
valve6.command := 2
ENDIF
IF (motor2.status <> 1) THEN
motor2.command := 1
valve3.command := 1
valve4.command := 1
ENDIF
motor2.role := 1
IF (motor2.work_time >= MAX_TIME_IN_ROLE) THEN
motor2.work_time := 0
ENDIF
ELSE
IF (motor3.status <> 1) THEN
motor3.command := 1
valve5.command := 1
valve6.command := 1
ENDIF
motor3.role := 1
IF (motor3.work_time >= MAX_TIME_IN_ROLE) THEN
motor3.work_time := 0
ENDIF
ENDIF
ELSE IF (qc.barrel_level >= 2) THEN
IF ((motor1.status = 3) OR (valve1.status = 3) OR (valve2.status = 3)) THEN
IF ((motor2.status <> 1) AND (motor3.status <> 1)) THEN
motor2.role := 1
ENDIF
IF (motor2.role <> 1) THEN
motor2.role := 2
motor2.command := 2
valve3.command := 2
valve4.command := 2
ELSE IF (motor2.role = 1) THEN
motor2.command := 1
valve3.command := 1
valve4.command := 1
ENDIF
IF (motor3.role <> 1) THEN
motor3.role := 2
motor3.command := 2
valve5.command := 2
valve6.command := 2
ELSE IF (motor3.role = 1) THEN
motor3.command := 1
valve5.command := 1
valve6.command := 1
ENDIF
IF (motor2.work_time >= MAX_TIME_IN_ROLE) THEN
motor2.work_time := 0
motor2.role := 0
motor3.role := 1
ELSE IF (motor3.work_time >= MAX_TIME_IN_ROLE) THEN
motor3.work_time := 0
motor3.role := 0
motor2.role := 1
ENDIF
ELSE IF ((motor2.status = 3) OR (valve3.status = 3) OR (valve4.status = 3)) THEN
IF ((motor1.status <> 1) AND (motor3.status <> 1)) THEN
motor1.role := 1
ENDIF
IF (motor1.role <> 1) THEN
motor1.role := 2
motor1.command := 2
valve1.command := 2
valve2.command := 2
ELSE IF (motor1.role = 1) THEN
motor1.command := 1
valve1.command := 1
valve2.command := 1
ENDIF
IF (motor3.role <> 1) THEN
motor3.role := 2
motor3.command := 2
valve5.command := 2
valve6.command := 2
ELSE IF (motor3.role = 1) THEN
motor3.command := 1
valve5.command := 1
valve6.command := 1
ENDIF
IF (motor1.work_time >= MAX_TIME_IN_ROLE) THEN
motor1.work_time := 0
motor1.role := 0
motor3.role := 1
ELSE IF (motor3.work_time >= MAX_TIME_IN_ROLE) THEN
motor3.work_time := 0
motor3.role := 0
motor1.role := 1
ENDIF
ELSE
IF ((motor1.status <> 1) AND (motor2.status <> 1)) THEN
motor1.role := 1
ENDIF
IF (motor1.role <> 1) THEN
motor1.role := 2
motor1.command := 2
valve1.command := 2
valve2.command := 2
ELSE IF (motor1.role = 1) THEN
motor1.command := 1
valve1.command := 1
valve2.command := 1
ENDIF
IF (motor2.role <> 1) THEN
motor2.role := 2
motor2.command := 2
valve3.command := 2
valve4.command := 2
ELSE IF (motor2.role = 1) THEN
motor2.command := 1
valve3.command := 1
valve4.command := 1
ENDIF
IF (motor3.status = 1) THEN
motor3.command := 2
motor3.role := 0
valve5.command := 2
valve6.command := 2
ENDIF
IF (motor1.work_time >= MAX_TIME_IN_ROLE) THEN
motor1.work_time := 0
motor1.role := 0
motor2.role := 1
ELSE IF (motor2.work_time >= MAX_TIME_IN_ROLE) THEN
motor2.work_time := 0
motor2.role := 0
motor1.role := 1
ENDIF
ENDIF
ELSE IF (qc.barrel_level = 0) THEN
motor1.command := 2
valve1.command := 2
valve2.command := 2
motor1.work_time := 0
motor1.role := 0
motor2.command := 2
valve3.command := 2
valve4.command := 2
motor2.work_time := 0
motor2.role := 0
IF (motor3.status <> 0) THEN
motor3.command := 2
valve5.command := 2
valve6.command := 2
motor3.work_time := 0
motor3.role := 0
ENDIF
ENDIF
IF ((motor1.role = 1) AND (motor1.status = 1)) THEN
motor1.work_time := motor1.work_time + 1
ELSE IF ((motor2.role = 1) AND (motor2.status = 1)) THEN
motor2.work_time := motor2.work_time + 1
ELSE IF ((motor3.role = 1) AND (motor3.status = 1)) THEN
motor3.work_time := motor3.work_time + 1
ENDIF
Valve_control(ADR(qo))
Valve_control(ADR(qc))
Valve_control(ADR(valve1))
Valve_control(ADR(valve2))
Valve_control(ADR(valve3))
Valve_control(ADR(valve4))
Valve_control(ADR(valve5))
Valve_control(ADR(valve6))
Motor_control(ADR(motor1))
Motor_control(ADR(motor2))
Motor_control(ADR(motor3))
END_PROGRAM
Описание:
Код начинается с проверки состояния каналов qo и qc и выдачи команд запуска (значение команды 1), если они в данный момент не запущены. Затем он проверяет значение переменной oil_steam для канала qo. Если значение равно 1, он выдает команды запуска как для каналов qo, так и для контроля качества. Если значение равно 0, он выдает команды остановки (значение команды 2) для обоих каналов.
Затем код проверяет состояние каждого двигателя и подключенных к нему клапанов. Если какой-либо из них находится в состоянии ошибки (значение состояния 3), он выдает команды остановки для двигателя и подключенных клапанов.
Затем код проверяет значение переменной barrel_level для канала контроля качества. Если значение равно 1, он проверяет состояние каждого двигателя и подключенных клапанов, чтобы определить, какой двигатель доступен для запуска. Если ни один из двигателей не находится в состоянии ошибки и еще не запущен, он выдает команды запуска для доступного двигателя и подключенных к нему клапанов и устанавливает переменную роли для этого двигателя в 1 (указывая, что это основной двигатель). Если двигатель превысил максимальное значение времени (MAX_TIME_IN_ROLE), он сбрасывает переменную work_time в 0 и устанавливает переменную role в 0, указывая, что двигатель больше не является основным двигателем. Если ни один из двигателей не доступен для запуска, он ничего не делает.
Если переменная barrel_level не равна 1, код проверяет состояние каждого двигателя и подключенных клапанов, чтобы определить, какой двигатель доступен для запуска. Если ни один из двигателей не находится в состоянии ошибки и еще не запущен, он выдает команды запуска для доступного двигателя и подключенных к нему клапанов. Если двигатель превысил максимальное значение времени, он сбрасывает переменную work_time на 0. Если ни один из двигателей не доступен для запуска, он ничего не делает.
Рис. 13
В окне визуализации я изобразил:
3 мотора (М1 - М3)
6 вентилей системы откачки (V1 - V6)
резервуар с уровнями (контрольными точками) заполнения (3 не используется, т.к. предполагается что мощности основного и вспомогательного насоса хватит, чтобы его не достичь)
вентиль QO для сброса в отчистные сооружения
вентиль QC для сбора конденсата
детектор для визуализации датчика наличия нефтяных паров
различные трубы – линии
Чтобы взаимодействовать с объектами и менять их цвет были созданы цветовые карты.
Рис. 14
Отвечает за цвет уровня 1 ёмкости в зависимости от значения переменной barrel_level.
Отвечает за цвет уровня 2 ёмкости в зависимости от значения переменной barrel_level.
Отвечает за цвет уровня 3 ёмкости в зависимости от значения переменной barrel_level.
Отвечает за цвет детектора нефтяных паров.
Отвечает за цвет выходной общей трубы (справа от насосов) в зависимости от значения barrel_level.
Отвечает за цвет труб в зависимости от статуса вентилей рядом с ними.
Отвечает за цвет ролей моторов в зависимости от значения их роли.
Отвечает за цвет моторов и вентилей (всех) в зависимости от значения их статуса.
