PDA

Просмотр полной версии : Упаковка переменных BOOL, INT, REAL в Modbus String



ПРОЕКТ-П
16.04.2015, 10:06
Здравствуйте.

Возможно данный вопрос уже поднимался на форме, но я что-то ни как не могу найти подходящий ответ.

Задача такая:
К ПЛК110 подключен 3G-роутер посредством которого должна осуществляется связь по Modbus-TCP с удаленным компьютером. На компьютере установлена самописная программа, которая работает в режиме Modbus-slave.
К программе планируется подключить с несколько десятков устройств. Для того чтобы разгрузить программу и канал 3G хотелось бы сделать так, чтоб контроллеры писали в Modbus-slave групповыми запросами.
Групповой запрос в ПЛК110 в Universal Modbus Device реализуется через переменные STRING.
Вопрос следующий:
Как упаковать переменные в STRING?

Пытался решить следующим способом:

--------------------------------------------------------------
VAR

str1: STRING[8];
str2: STRING[44];
str3: STRING[36];

ptr1: POINTER TO ARRAY[0..7] OF BYTE;
ptr2: POINTER TO ARRAY[0..21] OF INT;
ptr3: POINTER TO ARRAY[0..17] OF INT;

END_VAR

ptr1:=ADR(str1);
ptr2:=ADR(str2);
ptr3:=ADR(str3);

ptr1^[0].0 := parametr11;
ptr1^[0].1 := parametr12;
ptr1^[0].2 := parametr13;
ptr1^[0].3 := parametr14;
ptr1^[0].4 := parametr15;
ptr1^[0].5 := parametr16;
ptr1^[0].6 := parametr17;
ptr1^[0].7 := parametr18;
и так далее для строки str1
(parametr11...parametr18 - переменные типа BOOL)

ptr2^[0] := parametr21;
ptr2^[1] := parametr22;
и так далее для строки str2
(parametr21, parametr22 - переменные типа INT)

ptr3^[0] := REAL_TO_INT(parametr31 * 100);
ptr3^[1] := REAL_TO_INT(parametr32 * 100);
и так далее для строки str3
(paremetr31,parametr32 - переменные типа REAL)

modbus_group1 := str1;
modbus_group2 := str2;
modbus_group3 := str3;

(modbus_group1,modbus_group2,modbus_group3- переменные типа STRING Universal Modbus Device)
--------------------------------------------------------------

После заливки такого кода программа зависает, через некоторое время перезагружается контроллер (наверно watchdog срабатывает)

Что не так в коде? И может какое другое решение есть?
Подскажите пожалуйста.

Yegor
16.04.2015, 10:52
Это происходит когда не соблюдается выравнивание (доступ к REAL по некратному 4 адресу или к WORD по некратному 2 адресу, например), либо от неудачного вылета за границы массива. Скорее всего, первое, потому что строки попадают на «кривые» адреса, которые вы потом разыменовываете. Можете попробовать SysLibMem — там есть SysMemCpy.


SysMemSet(ADR(modbus_group1), PACK(parameter11, parameter12...), 1);
SysMemCpy(ADR(modbus_group2) + 0, ADR(parameter21), SIZEOF(parameter21));
SysMemCpy(ADR(modbus_group2) + 2, ADR(parameter22), SIZEOF(parameter22));
tmp := REAL_TO_INT(parameter31 * 100);
SysMemCpy(ADR(modbus_group3) + 0, ADR(tmp), SIZEOF(tmp));
tmp := REAL_TO_INT(parameter32 * 100);
SysMemCpy(ADR(modbus_group3) + 2, ADR(tmp), SIZEOF(tmp));

Трофимов Артем
16.04.2015, 10:52
сделайте ptr1: POINTER TO BYTE;
ptr2: POINTER TO array [0..1] of BYTE;
далее в коде
ptr1:=ADR(str1);
все бул переменные переприсваивайте в переменную байт или ворд. ( т.к. 8 бит то лучше в байт)
ptr1^:=переменная типа байт;
ptr1:=ptr1+1; перешли к следующему символу строки.
тут пошли данные больше одного байта
ptr2:=ADR(инт_переменная);
ptr1^:=ptr2^[0];
ptr1:=ptr1+1; перешли к следующему символу строки.
ptr1^:=ptr2^[1];
ptr1:=ptr1+1; перешли к следующему символу строки.

и т.д.

ПРОЕКТ-П
16.04.2015, 15:02
Какие-то чудеса с указателями.

Код тестовой программы следующий:

VAR

i: INT;
j: INT;

str1: STRING[8];

p1: POINTER TO BYTE;
p2: POINTER TO ARRAY [0..1] OF BYTE;

END_VAR

p1 := ADR(str1);
p2 := ADR( i);
i := 124;
p1^ := p2^[0];
p1 := p1 + 1;
p1^ := p2^[1];
p1 := p1 + 1;

p1 := ADR(str1);
p2 := ADR(j);
p2^[0] := p1^;
p1 := p1 + 1;
p2^[1] := p1^;

Запускаю программу на контроллере и вижу:
i = 124
j = 124

Далее изменяю имена переменных в редакторе p1 -> ptr1, p2 -> ptr2
Запускаю программу на контроллере и вижу:
i = -28924
j = -18564

Что за чудеса не понятно. Прошу помощи и разъяснений.

amn
16.04.2015, 17:52
Тип INT занимает 2 байта, поэтому учитываем это, и там где надо пишем p1:=p1+2

ПРОЕКТ-П
19.04.2015, 13:03
Тип INT занимает 2 байта, поэтому учитываем это, и там где надо пишем p1:=p1+2

p1^ := p2^[0];
p1 := p1 + 1;
p1^ := p2^[1];
p1 := p1 + 1;

На каждую переменную INT сдвиг осуществляется на два байта,
Вопрос не в этом. А в том что имена переменных влияют на результат, что вообще не должно быть.
Меняю имена переменных в редакторе p1 -> ptr1, p2 -> ptr2 (нажимаю Ctrl+H, меняю ptr на p) результат программы разный, вот в чем вопрос. Причем делаю Reset (Original) перед загрузкой каждого варианта программы.

Валенок
19.04.2015, 16:26
+ "проект/очистить все" перед каждой загрузкой. Если не поможет - ищите ошибку в 22й строке
С указателями всегда и везде - нормуль. Только руки портят. И вообще - какой-то сложный способ упаковки.

amn
19.04.2015, 22:58
Вот, что я имел в виду:


VAR
i: INT:=257;
j: INT;
k: REAL:=12.5;
m: REAL;
b: BYTE:=120;
a: BYTE;
str1: STRING;

ptr1: POINTER TO BYTE;
ptr2: POINTER TO INT;
ptr3: POINTER TO REAL;
END_VAR

--------------------

(*записываем в строку переменные разных типов*)
ptr2 := ADR(str1); (*ставим указатель на нашу стоку*)
ptr2^ := i; (*пишем переменную типа INT*)
ptr3:=ptr2+2; (*смещаем указатель на 2 байта*)

ptr3^ := k; (*пишем переменную типа REAL*)
ptr1:=ptr3+4; (*смещаем указатель на 4 байта*)

ptr1^ := b; (*пишем переменную типа BYTE*)

(*читаем из строки переменные разных типов*)
ptr2 := ADR(str1); (*ставим указатель на нашу стоку*)
j:=ptr2^; (*читаем переменную типа INT*)
ptr3 := ptr2 + 2; (*смещаем указатель на 2 байта*)

m:=ptr3^; (*читаем переменную типа REAL*)
ptr1:=ptr3+4; (*смещаем указатель на 4 байта*)

a:=ptr1^; (*читаем переменную типа BYTE*)

Дмитрий Артюховский
20.04.2015, 09:14
(*читаем из строки переменные разных типов*)
ptr2 := ADR(str1); (*ставим указатель на нашу стоку*)
j:=ptr2^; (*читаем переменную типа INT*)
ptr3 := ptr2 + 2; (*смещаем указатель на 2 байта*)

j := ptr2^;
вот здесь "собака" - если str1 начинается с четного адреса - будет работать, если с нечетного - ватчдог! т.е. как фишка ляжет -50 на 50 ))) поработал код, добавили переменную и абзац! Егор правду глаголет! проверил сам.

ПРОЕКТ-П
20.04.2015, 10:59
(*читаем из строки переменные разных типов*)
ptr2 := ADR(str1); (*ставим указатель на нашу стоку*)
j:=ptr2^; (*читаем переменную типа INT*)
ptr3 := ptr2 + 2; (*смещаем указатель на 2 байта*)

j := ptr2^;
вот здесь "собака" - если str1 начинается с четного адреса - будет работать, если с нечетного - ватчдог! т.е. как фишка ляжет -50 на 50 ))) поработал код, добавили переменную и абзац! Егор правду глаголет! проверил сам.

У меня данная проблема как раз по этой причины и возникла (см. первое сообщение в теме).
Поэтому в дальнейшем я использую решение предложенное Трофимовым Артемом - упаковываю и читаю по байтно, а не целыми INT или REAL.

ПРОЕКТ-П
20.04.2015, 11:02
+ "проект/очистить все" перед каждой загрузкой. Если не поможет - ищите ошибку в 22й строке
С указателями всегда и везде - нормуль. Только руки портят. И вообще - какой-то сложный способ упаковки.

Чистить не пробовал.
Паковать сразу INT - не получиться из-за возможного смещения адреса (уже описана проблема), поэтому упаковка по байтно.

Валенок
20.04.2015, 15:50
Сами себе сложности создаете. Стринг в umd расположен как нужно (адрес кратен 4).
На кой вам еще где-то стринги ? А сами данные в стринге выровнять сложно ? И причем тут стринг вообще. Забудьте. Нет стринга, есть буфер с нужного адреса




fb my
var_input
k : real;
i : int;
b : byte;
x : byte; //неявная
.......
end;
----
;
----


var
p : pointer to my;
k : real;
i : int;
b : byte;
m : my;
----
p := adr(строка_прям_в_umd);
p^(i:=i,k:=k,b:=b); //упаковали&положили

m := p^; //куда-то переложили все
j := p^.j; //взяли часть
..


Общий случай (где могут гулять адреса)

var
k : real;
i : int;
b : byte;
m : my;
----
m(i:=i,k:=k,b:=b); //упаковали
sysmemcpy(..., adr(m),sizeof(m)); //переложили хоть куда


sysmemcpy(adr(m),adr(...),sizeof(m)); //переложили хоть откуда
j := m.j; //взяли часть



Заставьте машину думать по максимуму. А вам - только структуру/фб грамотно описать - и все

Валенок
20.04.2015, 15:58
т.е. как фишка ляжет -50 на 50 .
Фишки всегда кладутся как нужно если понимать как их класть ))

Дмитрий Артюховский
20.04.2015, 16:50
Фишки всегда кладутся как нужно если понимать как их класть ))

Я для себя - объявил массив из DWORDов ( чтобы он "правильно" в память лег ) и лазаю в него уже любыми указателями, правильно разместив переменные ))) это если "разогнать" код нужно, а для понимания лучше через обычные переменные, сильно время на отладке экономится!

amn
20.04.2015, 21:33
(*читаем из строки переменные разных типов*)
ptr2 := ADR(str1); (*ставим указатель на нашу стоку*)
j:=ptr2^; (*читаем переменную типа INT*)
ptr3 := ptr2 + 2; (*смещаем указатель на 2 байта*)

j := ptr2^;
вот здесь "собака" - если str1 начинается с четного адреса - будет работать, если с нечетного - ватчдог! т.е. как фишка ляжет -50 на 50 ))) поработал код, добавили переменную и абзац! Егор правду глаголет! проверил сам.

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

capzap
20.04.2015, 22:09
ни кто разве не замечал в оскат бибке, что присвоив последний раз указатель, он смещается еще раз, может в этом "собака порылась"