Просмотр полной версии : Упаковка переменных 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 срабатывает)
Что не так в коде? И может какое другое решение есть?
Подскажите пожалуйста.
Это происходит когда не соблюдается выравнивание (доступ к 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
Что за чудеса не понятно. Прошу помощи и разъяснений.
Тип 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) перед загрузкой каждого варианта программы.
+ "проект/очистить все" перед каждой загрузкой. Если не поможет - ищите ошибку в 22й строке
С указателями всегда и везде - нормуль. Только руки портят. И вообще - какой-то сложный способ упаковки.
Вот, что я имел в виду:
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 - не получиться из-за возможного смещения адреса (уже описана проблема), поэтому упаковка по байтно.
Сами себе сложности создаете. Стринг в 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; //взяли часть
Заставьте машину думать по максимуму. А вам - только структуру/фб грамотно описать - и все
т.е. как фишка ляжет -50 на 50 .
Фишки всегда кладутся как нужно если понимать как их класть ))
Дмитрий Артюховский
20.04.2015, 16:50
Фишки всегда кладутся как нужно если понимать как их класть ))
Я для себя - объявил массив из DWORDов ( чтобы он "правильно" в память лег ) и лазаю в него уже любыми указателями, правильно разместив переменные ))) это если "разогнать" код нужно, а для понимания лучше через обычные переменные, сильно время на отладке экономится!
(*читаем из строки переменные разных типов*)
ptr2 := ADR(str1); (*ставим указатель на нашу стоку*)
j:=ptr2^; (*читаем переменную типа INT*)
ptr3 := ptr2 + 2; (*смещаем указатель на 2 байта*)
j := ptr2^;
вот здесь "собака" - если str1 начинается с четного адреса - будет работать, если с нечетного - ватчдог! т.е. как фишка ляжет -50 на 50 ))) поработал код, добавили переменную и абзац! Егор правду глаголет! проверил сам.
С Егором согласен, у меня были проблемы когда структуру через указатели юзал, в симуляторе работало, а вот в реальном железе были проблемы. После выравнивания проблемы закончились.
ни кто разве не замечал в оскат бибке, что присвоив последний раз указатель, он смещается еще раз, может в этом "собака порылась"
Powered by vBulletin® Version 4.2.3 Copyright © 2024 vBulletin Solutions, Inc. All rights reserved. Перевод: zCarot