Category Archives: Codesys

Пример Dll для ActiveX Control

Ниже приведен исходный текст примера Dll, использующийся для вызова методов ActiveX элемента в визуализации CoDeSys.

В данном примере dll будет вызывать методы GoBack или GoForward, только если элемент поддерживает интерфейс IWebBrowser.

Метод выбирается параметром pszId.

#include «stdafx.h»

#include <unknwn.h>

#include <exdisp.h>

BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call,

LPVOID lpReserved)

{

return TRUE;

}

extern «C» __declspec (dllexport) void ExecuteActiveXCall(IUnknown* pUnk,

char* pszId, char* pszParam, char* pszReturnBuffer, int nReturnBufferSize,

DWORD* pdwReturnFlag)

{

if (strcmp(pszId, «IWebBrowser|GoBack») == 0)

{

IUnknown* pNewUnk;

IWebBrowser* pwb;

pUnk->QueryInterface(IID_IWebBrowser, (void**) &pNewUnk);

pwb = (IWebBrowser*) pNewUnk;

if (pwb)

{

pwb->GoBack();

pwb->Release();

}

}

else if (strcmp(pszId, «IWebBrowser|GoForward») == 0)

{

IUnknown* pNewUnk;

IWebBrowser* pwb;

pUnk->QueryInterface(IID_IWebBrowser, (void**) &pNewUnk);

pwb = (IWebBrowser*) pNewUnk;

if (pwb)

{

pwb->GoForward();

pwb->Release();

}

Мой блог находят по следующим фразам

Аппаратные часы времени в CoDeSys

Пример работы с аппаратными часами реального времени с помощью библиотеки syslibtime. Получение данных о текущем времени и изменение времени из программы.

 

PROGRAM PLC_PRG                      (*PLC100 RL core 2-07-2 target 2-05-7*)

VAR

GetTime: CurTimeEx;

TimeAndDate: SystemTimeDate;

Sys_Time: SysTime64;

set_time: BOOL;

END_VAR

 

IF set_time THEN

 

TimeAndDate.Minute;

TimeAndDate.Second;

TimeAndDate.Hour;

GetTime (SystemTime:=Sys_Time , TimeDate:= TimeAndDate);

ELSE

 

TimeAndDate.Day :=0;

TimeAndDate.DayOfWeek :=0;

TimeAndDate.dwHighMsec :=0;

TimeAndDate.dwLowMSecs :=0;

TimeAndDate.Milliseconds :=0;

TimeAndDate.Minute :=0;

TimeAndDate.Second :=0;

TimeAndDate.Hour :=0;

TimeAndDate.Year :=0;

TimeAndDate.Month :=0;

Sys_time.ulHigh :=0;

Sys_time.ulLow :=0;

GetTime (SystemTime:=Sys_Time , TimeDate:= TimeAndDate);

END_IF

 

Мой блог находят по следующим фразам

Температурный график с заданием от панели

Пример формирования графика температуры отопления в зависимости от температуры наружнего воздуха с реализацией ночного режима работы.

Функциональный блок экспортирован, для вставки в проект необходимо сделать импорт.

FUNCTION_BLOCK Graph_Temp

VAR_INPUT

Temp_in:REAL;    (*Температура наружнего воздуха*)

Temp_NV_A:WORD;         (*температура воздуха первой точки графика (с панели)*)

Temp_NV_B:WORD;         (*температура воздуха второй точки графика (с панели)*)

Temp_otopl_A:WORD;         (*температура отопления , первая точка графика (с панели)*)

Temp_otopl_B:WORD;         (*температура отопления , вторая точка графика (с панели)*)

tmp_N: WORD;  (*снижение температуры ночью на Х градусов (с панели)*)

In_night_on: BOOL; (*разрешение включения ночного режима*)

time_night_in: BOOL; (*время включения ночного режима настало*)

END_VAR

VAR_OUTPUT

Zad: REAL;           (*вычисленное по графику задание температуры отопления*)

END_VAR

VAR

koef1:REAL;          (*коэффициент уравнения прямой*)

Temp_otopl_B_: REAL;

Temp_otopl_A_: REAL;

Temp_NV_A_: REAL;

Temp_NV_B_: REAL;

tmp_N_: REAL;

zad_N: REAL; (*снижение задания на ночное время*)

zad_G: REAL; (*задание по графику*)

END_VAR

Temp_otopl_B_:=WORD_TO_REAL(Temp_otopl_B)/10; (*преобразование форматов данных полученных с ИП320*)

Temp_otopl_A_:=WORD_TO_REAL(Temp_otopl_A)/10;

Temp_NV_A_:=WORD_TO_REAL(Temp_NV_A)/10;

Temp_NV_B_:=WORD_TO_REAL(Temp_NV_B)/10;

tmp_N_:=WORD_TO_REAL(tmp_N)/10;

koef1 :=  (Temp_otopl_B_ — Temp_otopl_A_)/(Temp_NV_B — Temp_NV_A_)  ; (*коэфициенты уравнения*)

(*формирование графика*)

IF (Temp_in >= Temp_NV_A_) THEN

zad_G := Temp_otopl_A_;

END_IF

IF ((Temp_in <Temp_NV_A_) AND (Temp_in >=Temp_NV_B)) THEN

zad_G := Temp_otopl_A_ + koef1 * (Temp_in — Temp_NV_A_);

END_IF

IF (Temp_in <Temp_NV_B) THEN

zad_G := Temp_otopl_B;

END_IF
(*формирование ночного графика*)

IF (In_night_on=TRUE AND time_night_in=TRUE AND (zad_G- tmp_N_<= zad_G-zad_N) ) THEN

zad_N:= zad_N +0.00001;

END_IF

(*снижение и увеличение температуры происходит со скоростью 0.2-0.3 градуса в минуту и зависит от цикла ПЛК, кому не нравиться переписывайте на внешнее время*)

IF (In_night_on=TRUE AND time_night_in=FALSE AND zad<=zad_G  ) THEN

zad_N:= zad_N -0.000001;

END_IF
(*общее формирование задания*)

IF In_night_on=TRUE THEN

zad:=zad_G-zad_N;

ELSE

zad:=zad_G;

zad_N:=0;

END_IF

END_FUNCTION_BLOCK

Управление 3 насосами

Алгоритм предназначен для управления 3-мя насосами (или другими обьектами требующими чредование времени работы,например тремя водогрейными котлами),  каждый из которых снабжен своим датчиком   (или иным сигналом обратной связи). Одновременно работают два объекта из трех. По истечении  заданного времени  происходит переключение работающих  насосов.

Листинг

VAR_INPUT

Run: BOOL; (*сигнал начала работы программы ротации*)

Time_Rotat_in:WORD; (*время работы насосов, в часах*)

Time_zad_on_in: WORD; (*время задержки перед началом работы алгоритма , в секундах*)

Time_Nech_in: WORD; (*время, в течение которого при запуске двигателя насоса  не анализируются показания датчика давления, в секундах*)

Time_Nech_on_work_in: WORD; (*время, в течение которого при работе двигателя насоса допускаются «провалы» показаний датчика давления, в секундах*)

Time_Zad_in: WORD; (*время задержки между отключением одного насоса и включением другого, в секундах*)

p1_on: BOOL; (*сигнал обратной связи (датчик давления, контакт пускателя итд.) насоса №1*)

p2_on: BOOL; (*сигнал обратной связи (датчик давления, контакт пускателя итд.) насоса №2*)

p3_on: BOOL; (*сигнал обратной связи (датчик давления, контакт пускателя итд.) насоса №3*)
END_VAR

VAR_OUTPUT

p1_out: BOOL; (*сигнал включения насоса №1*)

p2_out: BOOL; (*сигнал включения насоса №2*)

p3_out: BOOL; (*сигнал включения насоса №3*)

err_p1: BOOL; (*авария насоса №1*)

err_p2: BOOL; (*авария насоса №2*)

err_p3: BOOL; (*авария насоса №3*)

END_VAR

VAR

i: WORD; (*переменная которая определяет запуск насосов*)

Time_zad_on: TIME; (*время задержки перед началом работы алгоритма*)

Time_Nech: TIME; (*время, в течение которого при запуске двигателя насоса  не анализируются показания датчика давления;*)

Time_Nech_on_work: TIME; (*время, в течение которого при работе двигателя насоса допускаются «провалы» показаний датчика давления*)

Time_Zad: TIME; (*время задержки между отключением одного насоса и включением другого*)

Time_rotat: TIME;  (*время работы насосов*)

Tim_off: BOOL;

Trig1: R_TRIG;

C_Tim: TON; (*таймер чредования насосов*)

Time_Rotat_DW: DWORD;

p1_out_: BOOL;

p2_out_: BOOL;

p3_out_: BOOL;

TON_Zad_Pump1: TON;

TON_Pump1_Nech: TON;

Run_Pump: BOOL;

Run_Trig: R_TRIG;

Sbros_Trig: F_TRIG;

TOF_Pump1_Nech: TOF;

TON_Zad_Pump2: TON;

TON_Pump2_Nech: TON;

TOF_Pump2_Nech: TOF;

TON_Zad_Pump3: TON;

TON_Pump3_Nech: TON;

TOF_Pump3_Nech: TOF;

T_zad2: TIME; (*задержка включения второго насоса при холодном старте*)

END_VAR

(*преобразование переменных времени полученых с панели *)

Time_Rotat_DW:=WORD_TO_DWORD(Time_Rotat_in);(*перевод в часы*)

Time_rotat:=DWORD_TO_TIME((Time_Rotat_DW*3600000)/2);

Time_zad_on:=WORD_TO_TIME(Time_zad_on_in*1000); (*перевод в секунды*)

Time_Nech:=WORD_TO_TIME(Time_Nech_in*1000);

Time_Nech_on_work:= WORD_TO_TIME(Time_Nech_on_work_in*1000);

Time_Zad:=WORD_TO_TIME(Time_Zad_in*1000);(*инициализация и востановление начальных условий*)

IF Run=TRUE THEN

Run_Trig(CLK:=TRUE);

Sbros_Trig(CLK:=TRUE) ;

ELSE

Run_Trig(CLK:=FALSE);

Sbros_Trig(CLK:=FALSE);

END_IF

IF Run_Trig.Q=TRUE THEN

Run_Pump:=TRUE;

T_zad2:=T#5s;

END_IF

IF  Trig1.Q=TRUE THEN

T_zad2:=T#0s ;

END_IF

IF Sbros_Trig.Q= TRUE THEN

err_p1:=FALSE;

err_p2:=FALSE;

err_p3:=FALSE;

END_IF

(*запуск таймера ротации насосов*)

IF Run_Pump=TRUE  THEN

C_Tim(IN:=TRUE , PT:=T#1m );

END_IF

(*сброс таймера ротации насосов*)

IF Run_Pump =FALSE THEN

C_Tim(IN:=FALSE , PT:=T#0s );

END_IF

(*детектирование импульса перехода с насоса на насос*)

Trig1(CLK:= C_Tim.Q );

Tim_off:=Trig1.Q;

(*востановление начальных установок, если идет холодный старт в автомате*)

IF (Run_Pump=FALSE AND (p3_on= FALSE  AND p1_on=FALSE AND p2_on=FALSE)) THEN

i:=0;

END_IF

IF Run=FALSE THEN

p1_out_:= FALSE;

p2_out_:=FALSE;

p3_out_:=FALSE;

Run_Pump:=FALSE;

END_IF

(*подхват насосов на «лету»*)

IF (Run_Pump=FALSE AND (p1_on= TRUE AND p2_on=TRUE)) THEN

i:=0;

END_IF

IF (Run_Pump=FALSE AND (p2_on= TRUE AND p3_on=TRUE)) THEN

i:=2;

END_IF

IF (Run_Pump=FALSE AND (p3_on= TRUE AND p1_on=TRUE)) THEN

i:=1;

END_IF

(*счетчик*)

IF (Tim_off=TRUE AND Run_Pump=TRUE ) THEN

i:=i+1;

END_IF

IF i>=3 THEN

i:=0;

END_IF

(*включение насосов*)

IF (Run_Pump=TRUE AND i=0) THEN

p1_out_:= TRUE;

p2_out_:=TRUE;

p3_out_:=FALSE;

END_IF

IF (Run_Pump=TRUE AND i=1) THEN

p1_out_:= TRUE;

p2_out_:=FALSE;

p3_out_:=TRUE;

END_IF

IF (Run_Pump=TRUE AND i=2) THEN

p1_out_:= FALSE;

p2_out_:=TRUE;

p3_out_:=TRUE;

END_IF

(*перезапуск таймера*)

IF (Run_Pump=TRUE AND Tim_off=TRUE) THEN

C_Tim(IN:=FALSE , PT:=T#1m );

END_IF

(*управление насосами*)

(*насос №1*)

IF p1_out_=TRUE THEN

TON_Zad_Pump1(IN:=TRUE , PT:=Time_Zad );

ELSE

TON_Zad_Pump1(IN:=FALSE , PT:=Time_Zad );

END_IF

IF (TON_Zad_Pump1.Q=TRUE AND err_p1=FALSE) THEN

p1_out:=TRUE;

END_IF (*формирование задержки включения и защиты от ошибочного включения*)

IF (p1_out_=TRUE AND p1_out=TRUE) THEN

TON_Pump1_Nech(IN:=TRUE , PT:=Time_Nech );

ELSE

TON_Pump1_Nech(IN:=FALSE , PT:=Time_Nech );

END_IF

(*задержка контроля обратной связи после пуска насоса*)

IF  (p1_out=TRUE AND p1_on=FALSE) THEN

TOF_Pump1_Nech(IN:=FALSE, PT:=Time_Nech_on_work);

ELSE

TOF_Pump1_Nech(IN:=TRUE, PT:=Time_Nech_on_work);

END_IF

(*задержка при пропадании сигнала обратной связи при работе насоса*)

IF (Run=TRUE AND p1_out_=TRUE AND  TON_Pump1_Nech.Q=TRUE AND p1_on=FALSE AND TOF_Pump1_Nech.Q=FALSE) THEN

err_p1:=TRUE ;

Run_Pump:=FALSE ;

p1_out:=FALSE;

p2_out_:=TRUE;

p3_out_:=TRUE;

END_IF

(*определение аварии насоса, производиться остановка ротации и принудительное включение оставшихся насосов*)

IF p1_out_=FALSE THEN

p1_out:=FALSE;

END_IF

(*остановка насоса по истесению времени или аварии*)

(*насос №2*)

IF p2_out_=TRUE THEN

TON_Zad_Pump2(IN:=TRUE , PT:=Time_Zad+T_zad2 );

ELSE

TON_Zad_Pump2(IN:=FALSE , PT:=Time_Zad );

END_IF

(*здесь все тоже самое*)

IF (TON_Zad_Pump2.Q=TRUE AND err_p2=FALSE) THEN

p2_out:=TRUE;

END_IF

IF (p2_out_=TRUE AND p2_out=TRUE) THEN

TON_Pump2_Nech(IN:=TRUE , PT:=Time_Nech );

ELSE

TON_Pump2_Nech(IN:=FALSE , PT:=Time_Nech );

END_IF

IF  (p2_out=TRUE AND p2_on=FALSE) THEN

TOF_Pump2_Nech(IN:=FALSE, PT:=Time_Nech_on_work);

ELSE

TOF_Pump2_Nech(IN:=TRUE, PT:=Time_Nech_on_work);

END_IF

IF (Run=TRUE AND p2_out_=TRUE AND  TON_Pump2_Nech.Q=TRUE AND p2_on=FALSE AND TOF_Pump2_Nech.Q=FALSE) THEN

err_p2:=TRUE ;

Run_Pump:=FALSE ;

p2_out:=FALSE;

p1_out_:=TRUE;

p3_out_:=TRUE;

END_IF

IF p2_out_=FALSE THEN

p2_out:=FALSE;

END_IF

(*насос №3*)

IF p3_out_=TRUE THEN

TON_Zad_Pump3(IN:=TRUE , PT:=Time_Zad );

ELSE

TON_Zad_Pump3(IN:=FALSE , PT:=Time_Zad );

END_IF

IF (TON_Zad_Pump3.Q=TRUE AND err_p3=FALSE) THEN

p3_out:=TRUE;

END_IF

IF (p3_out_=TRUE AND p3_out=TRUE) THEN

TON_Pump3_Nech(IN:=TRUE , PT:=Time_Nech );

ELSE

TON_Pump3_Nech(IN:=FALSE , PT:=Time_Nech );

END_IF

IF  (p3_out=TRUE AND p3_on=FALSE) THEN

TOF_Pump3_Nech(IN:=FALSE, PT:=Time_Nech_on_work);

ELSE

TOF_Pump3_Nech(IN:=TRUE, PT:=Time_Nech_on_work);

END_IF

IF (Run=TRUE AND p3_out_=TRUE AND  TON_Pump3_Nech.Q=TRUE AND p3_on=FALSE AND TOF_Pump3_Nech.Q=FALSE) THEN

err_p3:=TRUE ;

Run_Pump:=FALSE ;

p3_out:=FALSE;

p2_out_:=TRUE;

p1_out_:=TRUE;

END_IF

IF p3_out_=FALSE THEN

p3_out:=FALSE;

END_IF

END_PROGRAM

Пожелания к работе программы и описание найденных ошибок можно направлять по адресу a_vit76@mail.ru Автор — Виктор Алексеев

График с заданием точек элементами массива

Пример: график уставки по пяти точкам. Каждая из координат задается массивом из 5 элементов, которые сначала сортируются по возрастанию, а затем по ним определяется уставка регулирования.

При выходе за пределы линейной зависимости выходная переменная принимает значение  крайних точек зависимости.

Листинг

FUNCTION_BLOCK obr_gr (*сортировка элементов массивов по возрастанию*)

VAR_INPUT

x,y:ARRAY[1..5] OF REAL;

END_VAR

VAR_OUTPUT

x1,y1:ARRAY[1..5] OF REAL;

END_VAR

VAR i: INT;

xp,yp: REAL;

END_VAR

FOR i:=1 TO 4 DO

IF x[i+1]<x[i] THEN

xp:=x[i];

x[i]:=x[i+1];

x[i+1]:=xp;
yp:=y[i];

y[i]:=y[i+1];

y[i+1]:=yp;
END_IF;

END_FOR;
FOR i:=1 TO 5 DO

x1[i]:=x[i];

y1[i]:=y[i];

END_FOR;

FUNCTION_BLOCK opr_ust

VAR_INPUT

x,y:ARRAY[1..5] OF REAL; (*массивы входных значений*)

vh_znach:REAL; (*сигнал с датчика*)

END_VAR

VAR_OUTPUT

ust:REAL; (*требуемая уставка регулирования*)

END_VAR

VAR

i: INT;

k: REAL;

END_VAR

FOR i:=1 TO 4 DO;

IF (x[i+1]>vh_znach) AND (x[i]<vh_znach) THEN

k:=(y[i+1]-y[i])/(x[i+1]-x[i]);

ust:=y[i]+k*(vh_znach-x[i]);

END_IF;

END_FOR;

Простой график в CoDeSys

Простейший пример: график уставки по двум точкам. При желании по аналогии можете сделать столько точек, сколько вам нужно.

x1, y1 — координаты первой точки

x2, y2 — координаты второй точки
При выходе за пределы линейной зависимости выходная переменная принимает значение или y1, или y2.

Листинг:

FUNCTION_BLOCK graph

VAR_INPUT

par:REAL;            (*входной параметр*)

x1, y1:REAL;         (*координаты первой точки*)

x2, y2:REAL;         (*координаты второй точки*)

END_VAR

VAR_OUTPUT

zad: REAL;           (*задание, определенное по графику*)

END_VAR

VAR

koef1:REAL;          (*коэффициент уравнения прямой*)
END_VAR

koef1 := (y2 — y1) / (x2 — x1);
IF (par <= x1) THEN

zad := y1;

END_IF;
IF ((par > x1) AND (par < x2)) THEN

zad := y1 + koef1 * (par — x1);

END_IF;
IF (par >= x2) THEN

zad := y2;

END_IF;

Работа с таймерами на языке ST

Пример работы с дискретными входами и выходами ПЛК, а также с таймерами ton и tof. Пример на языке ST.

При замыкании первого входа замыкается первый выход.

При замыкании первого и второго входа замыкается второй выход.

Через 5 секунд после замыкания третьего входа замыкается третий выход.

Через 10 секунд после размыкания четвертого входа размыкается четвертый выход.

Листинг:

PROGRAM PLC_PRG

VAR

tim1:TON;

tim2:TOF;

END_VAR

(*plc100-24.K-L core 2-03 target 2-02-4*)

out1:=in1;

Out2:=in1 AND in2;

(*задержка включения*)

tim1(IN := in3, PT := T#5s);

out3:=tim1.Q;

(*задержка выключения*)

tim2(IN := in4, PT := T#10s);

out4:=tim2.Q;

Сетевые переменные в CoDeSys.

Данная операция имеет смысл, когда у Вас на одном объекте стоит несколько контроллеров, и необходимо обмениваться данными между этими контроллерами без создания дополнительного межсетевого обмена.

Необходимо помнить, что данная опция необходима, когда у нас на одном объекте как минимум 2 контроллера.

Обмен переменными может вестись только по интерфейсу Ethernet. (На данном этапе).

Пример создан на базе контроллера ПЛК150-220.И-L

Target 2-02.3

Переходим на вкладку Resources

Выбираем папку «Target settings», вкладку «Network functionality»

В строчке «Name of supported network interfaces» задаем в ручную – UDP

Во вкладке Resources выбираем вкладку «Library Manager» и добавляем библиотеку (правой кнопкой мыши в поле библиотек) – Additional Library.

Необходимо подключить следующие библиотеки:

SysLibCallback.lib

SysLibSockets.lib

NetVarUdp_LIB_V23.lib

Библиотеки вложены в архиве, либо на диске с ПЛК, либо на сайте ОВЕН.

Рекомендуется перед подключением перенести библиотеки в директорию, созданную CoDeSys, по умолчанию: c:Program Files3S SoftwareCoDeSys V2.3Library

Выбираем папку «Global variables»

Выбираем команду Add Object

Всплывает следующее окно.

В строчке «Name of the global list» задаем имя, например PRIMER.

!Важно:

  1. В обоих (нескольких) контроллерах имя данной папки должно быть идентичным.
  2. Регистр (большие или маленькие буквы) важен.

Далее в этом окне нажимаем на кнопку «Add network» — окно видоизменяется следующим образом:

Значение параметра «Network type» — необходимо выставить UDP. Вкладку Settings не редактируем.

Значение параметра  «List identifier (COB-ID)» так же должно быть общим для всех контроллеров, участвующих в обмене. Значение выставляется произвольно. Для примера – 1.

Далее ставим галочку у необходимого параметра.

Для того, чтобы контроллер получал данные из сети необходимо поставить галочку у параметра Read. Для передачи данных с данного контроллера необходимо поставить галочку у параметра Write.

Если выбраны Write – становятся доступными три последующие строчки, которые описывают по какому принципу необходимо передавать значение: «Циклически», «по изменению значения», «по команде».

!Важно: Рекомендуется не ставить галочки чтение и запись для одной и той же группы «PRIMER» в одном контроллере. То есть только в одном контроллере переменные одной папки должны быть Write, в остальных контроллерах типа Read.

Если необходимо, чтобы у каждого контроллера были переменные, которые и опрашиваются, и задаются – необходимо сделать несколько таких папок.

После этого добавляем (объявляем) непосредственно во вновь созданной папке переменные, которые нам необходимы.

И уже в программе работаем с ними, как со стандартными глобальными переменными.

Сложные типы данных CoDeSys

Массивы

Элементарные типы данных могут образовывать одно-, двух-, и трехмерные массивы. Массивы  могут быть объявлены в разделе объявлений POU или в списке глобальных переменных. Путем вложения массивов можно получить многомерные массивы, но не более 9 мерных ( «ARRAY[0..2] OF ARRAY[0..3] OF …» ).

Синтаксис:

<Имя_массива>:ARRAY [<ll1>..<ul1>,<ll2>..<ul2>] OF <базовый тип>.

где ll1, ll2, ll3 указывают нижний предел индексов; ul1, ul2 и ul3 указывают верхние пределы.

Индексы должны быть целого типа. Нельзя использовать отрицательные индексы.

Пример:

Card_game: ARRAY [1..13, 1..4] OF INT;

Пример инициализации простых массивов:

arr1 : ARRAY [1..5] OF INT := 1,2,3,4,5;

arr2 : ARRAY [1..2,3..4] OF INT := 1,3(7); (* сокращение для 1,7,7,7 *)

arr3 : ARRAY [1..2,2..3,3..4] OF INT := 2(0),4(4),2,3;

(* сокращение для 0,0,4,4,4,4,2,3 *)

Пример инициализации массива структур:

TYPE STRUCT1

STRUCT

p1:int;

p2:int;

p3:dword;

END_STRUCT

ARRAY[1..3] OF STRUCT1:= (p1:=1,p2:=10,p3:=4723),(p1:=2,p2:=0,p3:=299),
(p1:=14,p2:=5,p3:=112);

Пример инициализации части массива:

arr1 : ARRAY [1..10] OF INT := 1,2;

Не инициализированные явно элементы массива принимают значения по умолчанию. Так, в данном примере оставшиеся элементы примут значение 0.

Доступ к элементам массива:

Для доступа к элементам двухмерного массива используется следующий синтаксис:

<Имя_массива>[Индекс1,Индекс2]

Пример:

Card_game [9,2]

Функция CheckBounds

Определив в проекте функцию с именем CheckBounds, вы сможете использовать её для контроля за соблюдением границ индексов массивов. Имя функции фиксировано, изменять его нельзя.

Пример функции CheckBounds:

FUNCTION CheckBounds : INT
VAR_INPUT
index, lower, upper: INT;
END_VAR

IF  index < lower THEN
CheckBounds := lower;
ELSIF  index > upper THEN
CheckBounds := upper;
ELSE  CheckBounds := index;
END_IF

В этом примере CheckBounds ограничивает индекс массива заданными границами. Если запрашивается элемент, отсутствующий в массиве, функция  CheckBounds возвращает ближайший элемент.

Проверка работы функции CheckBounds:

PROGRAM PLC_PRG
VAR
a: ARRAY[0..7] OF BOOL;
b: INT:=10;
END_VAR

a[b]:=TRUE;

Указатели

Указатели позволяют работать с адресами переменных или функциональных блоков.

Синтаксис:

<Имя_указателя>: POINTER TO <Тип данных/Функциональный блок>;

Указатели применимы для всех базовых типов данных или функциональных блоков, включая  определяемые пользователем.

Адреса переменных и функциональных блоков можно получить во время исполнения программы при помощи оператора ADR. Для обращения через указатель необходимо добавить оператор «^»  (content) после его имени.

Обратите внимание: Указатели инкрементируются побайтно! Для увеличения указателя, как это принято в C-компиляторах, используйте инструкцию p=p+SIZEOF(p^);.

Пример:

pt:POINTER TO INT;

var_int1:INT := 5;

var_int2:INT;

pt := ADR(var_int1);

var_int2:= pt^;  (* var_int2 теперь равна 5 *)

Функция CheckPointer:

Данная функция позволяет контролировать обращение к допустимой области памяти через указатели. Если определена функция CheckPointer, то она будет автоматически вызываться при любом обращении через указатель. Функция должна быть определена в проекте (непосредственно или в библиотеке). Ее имя (CheckPointer) изменять нельзя.  Функция имеет следующие параметры:

Для систем с 32-х битными указателями:

FUNCTION CheckPointer : DWORD

VAR_INPUT

dwAddress : DWORD;

iSize : INT;

bWrite: BOOL;

END_VAR

Для систем с 16-и битными указателями:

FUNCTION CheckPointer : WORD

VAR_INPUT

dwAddress : WORD;

iSize : INT;

bWrite: BOOL;

END_VAR

Функция возвращает адрес, который будет использоваться как указатель. Если все хорошо то, это будет входной параметр — dwAddress.

Перечисление

Перечисление — это определяемый пользователем тип данных, задающий несколько строковых псевдонимов для числовых констант.

Перечисление доступно в любой части проекта, даже при локальном его объявлении внутри POU. Поэтому наиболее разумно создавать все перечисления на вкладке Типы данных Организатора Объектов. Объявление должно начинаться с ключевого слова TYPE и заканчиваться строкой END_TYPE.

Синтаксис:

TYPE <Имя_перечисления>:(<Элемент_0> ,< Элемент _1>, …< Элемент _n>);END_TYPE

Переменная типа  <Имя_перечисления> может принимать только перечисленные значения. При инициализации переменная получает первое из списка значение. Если числовые значения элементов перечисления не указаны явно, им присваиваются последовательно возрастающие числа, начиная с 0. Фактически элемент перечисления — это число типа INT и работать с ними можно точно так же. Можно напрямую присвоить число переменной типа перечисление.

Пример:

TYPE TRAFFIC_SIGNAL: (Red, Yellow, Green:=10); (*Каждому цвету

соответствует свое значение, для red — это 0, для yellow — 1 и для green — 10 *)

END_TYPE

TRAFFIC_SIGNAL1 : TRAFFIC_SIGNAL;

TRAFFIC_SIGNAL1:=0; (* Переменная получила значение red*)

FOR i:= Red TO Green DO

i := i + 1;

END_FOR;

Элемент, уже включенный в перечисление, нельзя повторно включать в другое перечисление.

Пример:

TRAFFIC_SIGNAL: (red, yellow, green);

COLOR: (blue, white, red);

Ошибка: попытка повторного использования элемента TRAFFIC_SIGNAL red  в COLOR.

Структуры

Структуры создаются на вкладке Типы данных Организатора Объектов. Объявление должно начинаться с ключевых слов TYPE и STRUCT и заканчиваться строками END_STRUCT и END_TYPE.

Синтаксис:

TYPE <Имя _структуры>:

STRUCT

<Объявление переменной 1>

.

.

<Объявление переменной n>

END_STRUCT

END_TYPE

<Имя _структуры> образует новый тип данных, который может быть использован в любой части проекта наряду с базовыми типами.

Вложенные структуры допускаются. Единственное ограничение заключается в запрете размещения элементов структуры по прямым адресам (AT объявления недопустимы!).

Пример объявления структуры по имени Polygonline:

TYPE Polygonline:

STRUCT

Start:ARRAY [1..2] OF INT;

Point1:ARRAY [1..2] OF INT;

Point2:ARRAY [1..2] OF INT;

Point3:ARRAY [1..2] OF INT;

Point4:ARRAY [1..2] OF INT;

End:ARRAY [1..2] OF INT;

END_STRUCT

END_TYPE

Пример инициализации структуры:

Poly_1:polygonline := ( Start:=3,3, Point1 =5,2, Point2:=7,3, Point3:=8,5, Point4:=5,7, End := 3,5);

Для доступа к элементам структуры используется следующий синтаксис:

<Имя_структуры>.<Имя_компонента>

Например, структура «Week» содержит компонент «Monday», обращение к которому будет выглядеть так:

Week.Monday

Псевдонимы типов

Псевдонимы типов нужны для создания альтернативных пользовательских наименований типов данных. Это удобно при работе с большим числом однотипных констант, переменных и функциональных блоков.

Псевдонимы типов определены на вкладке Типы данных Организатора Объектов. Объявление должно начинаться с ключевого слова TYPE и заканчиваться строкой  END_TYPE.

Синтаксис:

TYPE <Имя псевдонима>: <Исходное имя>;
END_TYPE

Пример:

TYPE message:STRING[50];

END_TYPE;

Ограничение диапазона значений

Ограничение диапазона позволяет объявить переменную, значения которой ограничены в определенных пределах. Существует возможность создать в проекте новые типы данных с ограниченным диапазоном значений либо задать диапазон непосредственно при объявлении переменной.

Создание нового типа выглядит так:

TYPE < Имя > : < Целый тип > (<от>..<до>) END_TYPE;

<Имя> любой допустимый МЭК идентификатор,
<IЦелый тип> один из типов SINT, USINT, INT, UINT, DINT, UDINT, BYTE, WORD,DWORD (LINT, ULINT, LWORD).
<от> константа, определяющая начало диапазона значений включительно
<до> константа, определяющая конец диапазона значений включительно.

Пример:

TYPE

SubInt : INT (-4095..4095);

END_TYPE

Ограничение диапазона при объявлении переменной:

i : INT (-4095..4095);

ui : UINT (0..10000);

END_VAR

При попытке присвоить переменной с ограниченным диапазоном константы, не попадающей в заданный диапазон (например i := 5000;) CoDeSys даст сообщение об ошибке.

Для контроля значений «ограниченных» переменных в процессе исполнения, применяются функции CheckRangeSigned или CheckRangeUnsigned. Они позволяют обрабатывать ошибки произвольным образом. Например, ограничить присваиваемое значение или сформировать флаги ошибки. Первая функция работает для переменных со знаком, вторая для переменных без знака (unsigned). Изменять идентификаторы этих функций нельзя.

Пример:

Здесь применяется функция CheckRangeSigned, контролирующая переменные со знаком (как, например, объявленная выше i). Функция «обрезает» присваиваемые значения по границам диапазона.

FUNCTION CheckRangeSigned : DINT

VAR_INPUT

value, lower, upper: DINT;

END_VAR

IF (value < lower) THEN

CheckRangeSigned := lower;

ELSIF(value > upper) THEN

CheckRangeSigned := upper;

ELSE

CheckRangeSigned := value;

END_IF

Функция вызывается автоматически при соответствующих операциях присваивания. Она получает три параметра: присваиваемое значение (value) и обе границы диапазона (lower, upper). Фактически присваивается возвращаемое CheckRangeSigned значение.

Например, при присваивании i:=10*y происходит неявный вызов:

i := CheckRangeSigned(10*y, -4095, 4095);

В результате, даже если y > 1000, i не получит значение более 4095.

Аналогично объявляется и функция CheckRangeUnsigned:

FUNCTION CheckRangeUnsigned : UDINT

VAR_INPUT

value, lower, upper: UDINT;

END_VAR

Важно: Если функции CheckRangeSigned или CheckRangeUnsigned не включены в проект, ограничение границ диапазонов во время исполнения работать не будет.

Внимание: Функции CheckRange…, содержащащиеся в библиотеке Check.Lib представляют собой пример реализации. Прежде чем использовать эту библиотеку, убедитесь, что данные функции работает  так, как нужно в вашем случае, либо создайте собственные функции непосредственно в вашем проекте.

Пример. Переменная ui не превысит 10000, и цикл FOR никогда не закончится:
VAR
ui : UINT (0..10000);
END_VAR

FOR ui:=0 TO 10000 DO

END_FOR

Элементарные типы данных CoDeSys

Типы данных

Тип данных определяет род информации и методы ее обработки и хранения, количество выделяемой памяти. Программист может непосредственно использовать элементарные (базовые) типы данных или создавать собственные (пользовательские) типы на их основе.

Логический (BOOL)

BOOL логический тип данных. Переменная может принимать 2 значения ИСТИНА (TRUE) или ЛОЖЬ (FALSE). Занимает 8 бит памяти, если не задан прямой битовый адрес.

Целочисленные

BYTE, WORD, DWORD, SINT, USINT, INT, UINT, DINT, и UDINT — все это целочисленные типы.

Они отличаются различным диапазоном сохраняемых данных и, естественно, различными требованиями к памяти. Подробно данные характеристики представлены в следующей таблице:

Тип Нижний предел Верхний предел Размер памяти
BYTE 0 255 8 Бит
WORD 0 65535 16 Бит
DWORD 0 4294967295 32 Бит
SINT: -128 127 8 Бит
USINT: 0 255 8 Бит
INT: -32768 32767 16 Бит
UINT: 0 65535 16 Бит
DINT: -2147483648 2147483647 32 Бит
UDINT: 0 4294967295 32 Бит

Очевидно, присвоение данных большего типа переменной меньшего типа может приводить к потере информации.

Рациональные

REAL и LREAL данные в формате с плавающей запятой, используются для сохранения рациональных чисел. Для типа REAL необходимо 32 бита памяти и 64 для LREAL.

Диапазон значений REAL от: 1.175494351e-38 до 3.402823466e+38

Диапазон значений LREAL от: 2.2250738585072014e-308 до 1.7976931348623158e+308

Строки

Строковый тип STRING представляет строки символов. Максимальный размер строки определяет количество резервируемой памяти и указывается при объявлении переменной. Размер задается в круглых или квадратных скобках. Если размер не указан, принимается размер по умолчанию — 80 символов.

Длина строки не ограничена в CoDeSys, но строковые функции способны обращаться со строками от  1 до 255 символов!

Пример объявления строки размером до 35 символов:

str:STRING(35):=’Просто строка»;

Время и дата

TIME представляет длительность интервалов времени в миллисекундах. Максимальное значение для типа TIME : 49d17h2m47s295ms (4194967295 ms).

TIME, TIME_OF_DAY (сокр. TOD) содержит время суток, начиная с 0 часов (с точностью до миллисекунд). Диапазон значений TOD от: 00:00:00 до 23:59:59.999.

DATE содержит календарную дату, начиная с 1 января 1970 года. Диапазон значений от: 1970-00-00 до 2106-02-06.

DATE_AND_TIME (сокр. DT) содержит время в секундах, начиная с 0 часов 1 января 1970 года. Диапазон значений от: 1970-00-00-00:00:00 до 2106-02-06-06:28:15.

Типы TIME, TOD, DATE и DATE_AND_TIME (сокр. DT) сохраняются физически как DWORD.

Формат представления данных описан в разделе Константы: