PDA

Просмотр полной версии : POINTER TO what?



drvlas
29.11.2010, 23:39
ВНИМАНИЕ: тема рассмотрена с многочисленными заблуждениями и отклонениями. Можешь посмотреть на 4 страницу, там резюме. Читать все подряд позволено только совершеннолетним :)

Пробую использовать указатели на переменные. В принципе, получается. Но удивляет (а значит, может когда-то подставить "подножку") такая вольность: я могу сделать

POINTER TO INT
или
POINTER TO DINT

- а ничегошеньки не изменится. Это и понятно: ведь сказано, что, цитирую

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

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

Тогда вопрос: почему же так странно сделано? Вроде как лишнее вот это самое

TO INT
TO DINT

сказали POINTER - и все дела...

drvlas
30.11.2010, 10:04
уважаемые знатоки,

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

итак, есть типы данных

Word и Uint
Dword и Udint

из описания в кодесис, а также из известной книги товарища петрова я вижу, что использовать (d)word можно всегда, когда используется U(d)int, но не наоборот. разница, если я правильно понял, в том, что с (d)word можно выполнять битовые операции, а с U(d)int - нельзя.

означает ли это, что использование U(d)int - исключительно дело вкуса программиста? или, иначе говоря, если у меня со вкусом все "параллельно", то я могу просто забыть о существовании типов U(d)int - и никогда не пострадаю?

спасибо!

Валенок
30.11.2010, 20:47
1.В паскале есть нетипизированный указатель. Просто pointer.
Он нужон, если я не запамятовал, как буфер для жонглирования типизироваными указателями.
В племянике паскаля (st) указатели можно перекидывать без буфера, а для работы с заранее неопределенным типом можно использовать dword, по этому "просто поинтер" не нужен.

По поводу указателя на тип.

W : word := 345;
B : byte;
StartMotor : bool := FALSE; (* Михалыч сказал не трогать на время отладки *)
pW : pointer to word;
--------------------------------------
pW := adr(B);
pW^ := W;

Угадайте, сколько будет в B ? Некоторые могут сказать что 89. Наверное. Но Михалыч может сказать вам : Ё.. мать !! Студент бип-бип-бип !!!! Я же сказал тебе – НЕ ВКЛЮЧАТЬ !!.

2. Если отбросить словесную шелуху, то использование uint или word – по барабану, при условии что вы четко понимаете как они работают на уровне битов.

DARNER
30.11.2010, 22:47
...
угадайте, сколько будет в B ? некоторые могут сказать что 89. наверное. ...

поменьше загадок, с овеном их и так хватает! ;)

Валенок
30.11.2010, 23:16
Если наберется достаточная группа для кого это - загадка,
прочитаю лекцию.Небезвозмездно.Дитям на д.Мороза надоть.:rolleyes:

PS
Овен тут не причем.

Малышев Олег
01.12.2010, 10:24
Валенок -респект и уважение. Пример работы с указателями - просто зачет. Классическая ошибка даже для опытного программиста.
Бесплатное разъяснение. st - не java и не .net. Он не сечет неправильной работы с областями памяти.
Если мы пересылаем в b(byte) двухбайтовое по указателю то записывется и переменная старт_мотор. В результате студент от Михалыча получает по шее.

drvlas
01.12.2010, 14:00
Пробую осмыслить заковыристый стиль уважаемого Учителя. Начнем с простого:

2. Если отбросить словесную шелуху, то использование uint или word – по барабану, при условии что вы четко понимаете как они работают на уровне битов.
Ну непонятна мне фраза "если вы четко понимаете". Насколько четко?

На уровне размещения в памяти побайтно - трудно вообразить, что для uint и word используется разные правила. Значит, думаем. что старший байт uint и старший байт word будут лежать одинаково. Так?
Побитно - дык, в байте уж никак не развернут наоборот.
Что еще нужно понимать, чтобы забыть вообще про существование типа uint?
Вопрос даже не в практической плоскости, а в понимании "странности" стандарта.

Теперь к главному.

для работы с заранее неопределенным типом можно использовать dword, по этому "просто поинтер" не нужен.
Похоже, что есть разница, что использовать. Я провел эксперименты, запутался безнадежно, потом сузил поле до 2-х типов: WORD и DWORD. И что получилось.

Готовим несколько переменных типа WORD и DWORD, причем из W и DW будем только черпать инфо, их не изменяем. А для измены есть переменные с цифрами 1...4.
Также есть по 2 указателя, одна пара будет использоваться для указание на источник данных, другая - на приемник.


VAR
DW: DWORD := 16#10104;
DW1: DWORD;
DW2: DWORD;
DW3: DWORD;
DW4: DWORD;
pDWsour: POINTER TO DWORD;
pDWdest: POINTER TO DWORD;
W: WORD := 16#102;
W1: WORD;
W2: WORD;
W3: WORD;
W4: WORD;
pWsour: POINTER TO WORD;
pWdest: POINTER TO WORD;
END_VAR

В программе 4 шага. В каждом из них рассматриваетм передачу данных, указываемых пойнтерами, в ячейки, адресуемые тоже через указатели. При этом каждый из шагов отличается тем, что присвоение указателям значений (адресов ячеек) производтся с "вывихами":


pDWsour := ADR(DW); (* Source is right *)
pWsour := ADR(W);
pDWdest := ADR(DW1); (* Destin is right *)
pWdest := ADR(W1);
pDWdest^ := pDWsour^ +1; (* DW <= DW *)
pWdest^ := pWsour^ +1; (* W <= W *)

pDWsour := ADR(W); (* Source is wrong *)
pWsour := ADR(DW);
pDWdest := ADR(DW2); (* Destin is right *)
pWdest := ADR(W2);
pDWdest^ := pDWsour^ +1; (* DW <= W, fetch throu pDW *)
pWdest^ := pWsour^ +1; (* W <= DW, fetch throu pW *)

pDWsour := ADR(DW); (* Source is right *)
pWsour := ADR(W);
pWdest := ADR(DW3); (* Destin is wrong *)
pDWdest := ADR(W3);
pDWdest^ := pDWsour^ +1; (* W <= DW, written throu pDW *)
pWdest^ := pWsour^ +1; (* DW <= W, written throu pW *)

pDWsour := ADR(W); (* Source is wrong *)
pWsour := ADR(DW);
pWdest := ADR(DW4); (* Destin is wrong *)
pDWdest := ADR(W4);
pDWdest^ := pDWsour^ +1; (* W <= W throu pDW *)
pWdest^ := pWsour^ +1; (* DW <= DW throu pW *)

Результаты таковы. Во-первых, компилятор не ругается. Хотя, когда я пробовал приемник данных указывать явно - требовал иногда преобразования типов.

А дальше вот что. При правильном использовании пойнтеров все хорошо, но это ясно. В остальных случаях некоторые присвоения правильны, некоторые нет. Более того, выполнение последнего оператора (передача двойного слова через 2 пойнтера на слово) - прерывает исполнение программы

Доступ по несуществующему адресу. ПЛК остановлен

(Слава Богу, что мы не управляли котельней!)

Грустный вывод. Хоть компилятор и пропустит, но жизнь поправит. Причем, уважаемый Учитель, хочу заметить, что даже при использовании указателя высшего типа на (шаге 2)

pDWdest^ := pDWsour^ +1; (* DW <= W, fetch throu pDW *)

результат неверен :(
Т.е., использование DWORD при выборке из переменной типа WORD "прихватывает" еще несколько байт и пихает их в приемник (в нашем случае это приемник типа DWORD, поэтому мы и увидели всеь ужас прихватки).

Не знаю, наскольок мои путанные мысли что-то продвинули, но теперь думаю, что придется все переменные, на которые буду указывать DWORD-пойнтером, искусственно повышать до типа DWORD. А там еще и переменные TIME есть... Пойду удавлюсь.

Валенок
01.12.2010, 18:49
Начнем с простого:

... Пойду удавлюсь.

Когда лопнет веревка, открываем литературу по ЛЮБЫМ языкам программирования, и изучаем типы, типытипы(и особенно - их размеры)
Не забываем что pointer - сам по себе тоже тип.

А pointer to что-то :

1.Директива компилятору - как использовать бессмысленный набор байтов по указанному адресу.
2.Вытекает из 1. и (ВНИМАНИЕ !) какое кол-во этих байтов


Ошибка 1. (Толстая)

pDWsour := ADR(W); (* Source is wrong *)
...
pDWdest := ADR(DW2); (* Destin is right *)
...
pDWdest^ := pDWsour^ +1; (* DW <= W, fetch throu pDW *)

pDWdest^:= word_to_dword( dword_to_word( pDWsour^)) +1;
(word_to_dword конечно же избыточен,но показывает ход преобразований)
или проще
:= ( pDWsour^ and 16#0000_FFFF ) + 1;


Ошибка 2. (Тоньше 1.)


VAR
......
W4: WORD;
pWsour: POINTER TO WORD;
pWdest: POINTER TO WORD;
END_VAR

pDWsour := ADR(W); (* Source is wrong *)
pWsour := ADR(DW);
pWdest := ADR(DW4); (* Destin is wrong *)
pDWdest := ADR(W4);
pDWdest^ := pDWsour^ +1; (* W <= W throu pDW *)


Опс. Здесь мы гробим pWsour. (Понятно как ?)
И в :

pWdest^ := pWsour^ +1; (* DW <= DW throu pW *)

Пытаемся прочитать х.з. откуда, что иногда приводит к :


Доступ по несуществующему адресу. ПЛК остановлен


(Слава Богу, что мы не управляли котельней!)

Её-то Михалыч отремонтирует. А если б а.реактором ?
И если приспичило :


pDWdest^[/U] := pDWsour^ +1

то придется применять проктологические методы

pDWdest^ := ( pDWdest^ and 16#FFFF_0000 ) or ( ( pDWsour^ + 1 ) and 16#0000_FFFF );

Сможете пояснить смысл этого действа ?



PS
Взяли на понт. Остались детки без подарков .......

drvlas
02.12.2010, 10:30
Спасибо, уважаемый Учитель! Проясняется... Или это похмел проходит?


Ошибка 1. (Толстая)
...
Ошибка 2. (Тоньше 1.)
Смысл ошибок понятен, хотя натолкал-то я их специально, в тщетной надежде, что прокатит использование пойнтера на длинный тип данных при обращении и к коротким типам (о своей задаче ЩАС расскажу чуть подробнее).

И если приспичило :
то придется применять проктологические методы

pDWdest^ := ( pDWdest^ and 16#FFFF_0000 ) or ( ( pDWsour^ + 1 ) and 16#0000_FFFF );

Сможете пояснить смысл этого действа ?
Думаю, что да, Учитель. Надеясь на определенный способ расположения многобайтных данных в памяти, мы пер анус обращаемся к ним "по частям" и "склеиваем" части. Только ты впиндюрил ошибочку, ИМХО. Надо

pDWdest^ := ( pDWsour^ and 16#FFFF_0000 ) or ( ( pDWsour^ + 1 ) and 16#0000_FFFF );

Верно?
И, конечно же, это не есть гут. Ибо в хороших переносимых программах не привязываются к таким особенностям реализации (расположения в памяти).


PS
Взяли на понт. Остались детки без подарков .......
Это судьба Русского Учителя, уважаемый. Космонавтов учит, премьер-министрам сопли вытирает, а сам в рваных штанах :)
Неси свой крест! Мы тобой восхищены!

А теперь постараюсь пояснить, почему мне изощрения, ИМХО, не помогут. Дело в том, что я собираюсь не знать при исполнении, каковы же реально типы данных под пойнтером... Но это заслуживает отдельного поста. ЩА СДЕЛАЕМ...
...барабанная дробь...

drvlas
02.12.2010, 11:05
У меня есть куча параметров работы (далее - параметров), которые имеют одно общее свойство: их может читать оператор и-или комп сверху.

Сами по себе, это данные разных типов:
- флаги отдельных свойств (ну, там можно BYTE юзать);
- короткие пременные, тоже однобайтных хватает;
- длинные переменные, как со знаком, так и без, некоторые требуют аж 4-х байт;
- временнЫе данные, такие как время, дата, длительность разных пауз и задержек (до десятков секунд);
- флоаты тоже. по сути работают. но я их постараюсь убрать и в качестве параметров не использовать.

Эти данные имеют разные изменяемости:
- неизменяемые переменные (типа номер версии ПО, задается при компилировании);
- изменяемые программой, но не оператором (например, результат измерения или фаза работы);
- изменяемые оператором и-или программой, но с разной "разрешительной системой", например, изменяемые только с кодом доступа или как попало;

Эти данные еще имеют и разные свойства в смысле сохраняемости. Часть из них должна быть RETAIN, для остальных это бессмысленно. Часть параметров сохраняется вообще не в ПЛК, а в другом узле сети.
Ну, и другие мелкие различия в порядке их редактирования, автоматического изменения и прочее - этим, скорее всего, придется пожертвовать ради великой Цели.

А Цель такова: сделать работу с просмотром и редактированием параметров в одном участке кода. То есть, работать с параметром при его редактировании по его индексу. И все. Остальное предполагается извлекать из структуры PARSCRIPT, в которой есть вся необходимая инфо.
В старом проекте (не на ПЛК) были просто огромные SWITCH-и и на каждый параметр свой CASE. Но там хоть привычная форма представления программ, в человеческих редакторах. В КОДЕСИС же (при всем уважении!) работать с огромными текстами неудобно.
Да и вообще, считайте это моим капризом. "Хочу иконки, несмотря на социализм" :)

Для реализации Цели я создаю такие элементы:

1. Описание параметров.


TYPE PARSCRIPT :
STRUCT
Numb: BYTE; (* Номер параметра по ЭД, 0...255 *)
Point: BYTE; (* Формат вывода: 0, 1, 2, 3 знака справа от точки *)
Mdf: BYTE; (* 0 prog RO, 1 user RO, 2 RW, 3 R(W в СТОПЕ), 4 R(W с паролем) *)
Node: BYTE; (* Место хранения параметра, 0 - в ПЛК, 1 - в ТП *)
Mini: DINT; (* Границы значения параметра *)
Maxi: DINT;
Ptr: POINTER TO DINT;(* Указатель на параметр *)
Name: STRING(10); (* Текст имени *)
END_STRUCT
END_TYPE

2. Массив структур размером с количество параметров:


VAR_GLOBAL
apsPar: ARRAY [0..MAX_PAR_NMB] OF PARSCRIPT;
END_VAR

Этот массив с помощью функции set, подсказанной Учителем, заполняю при старте константами. В том числе, на место Ptr записываю адреса переменных-параметров:

в фунции set есть строка


apsPar[ iParIndx].Name := Name;

и функция вызывается многократно, на все параметры:


set( 101, 2, 0, 0, 0, 9999, ADR(Dummy), 'Версия ПЛК');
set( 102, 2, 0, 0, 0, 9999, ADR(Dummy), 'Версия ТП');
set( 103, 0, 4, 0, 1, 31, ADR(_103_Addr_IP), 'Адр с ИП');
set( 104, 0, 4, 0, 0, 5, ADR(_104_Baud_IP), 'Скор с ИП');


3. Собственно параметры, разных типов, сохраняемые и нет:


VAR_GLOBAL RETAIN
(* Сохраняемые П А Р А М Е Т Р Ы работы ПЛК *)
_103_Addr_IP: BYTE := 1; (*Адрес для работы с ИП320 *)
_104_Baud_IP: BYTE := 5; (*Код 0-5 скорости работы *)
...
(* Конец области задания П А Р А М Е Т Р О В *)
END_VAR

VAR_GLOBAL
(* Не сохраняемые П А Р А М Е Т Р Ы работы ПЛК *)
_120_ADC: DINT; (* Код АЦП *)
_170_Phase: BYTE; (* Фаза работы *)
...
_185_Time2Unld: TIME; (* Время до разгрузки *)
_186_Errors: WORD; (* Слово ощибок (аварий) *)
(* Конец области задания П А Р А М Е Т Р О В *)
END_VAR

Дальше я еще не написал, т.е. все неприятности по поводу чтения и редактирования параметров оператором - впереди.
Но уже сейчас понятно, что объявление в PARSCRIPT


Ptr: POINTER TO DINT; (* Указатель на параметр *)

вызовет проблемы для тех параметров, которые вовсе не DINT.

Как же быть? Как доступаться к параметрам, имея на руках только указатель, если параметры разные по типам?

Валенок
02.12.2010, 11:50
Понесу свой крест.

Не-а, милок. Как проктолог проктологу :

pDWdest^ := ( pDWdest^ and 16#FFFF_0000 ) or ( ( pDWsour^ + 1 ) and 16#0000_FFFF );

Именно pDWdest.

Прежде чем что-то влить по адресу pDWdest^, а там ведь своё - только первое слово, ведь :

pDWdest := ADR(W4)) ,

а вливаться по pDWdest^ := будет дабл-слово,

И чтоб не запороть там чужие данные ( (pDWdest+2)^ ), надоть оттуды их выковырнуть (старшее слово) :

( pDWdest^ and 16#FFFF_0000 )....

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

pDWsour^ + 1

т.е. :

.... or ( ( pDWsour^ + 1 ) and 16#0000_FFFF );

(Ессно, or можно поменять на +, характер образуемых здесь битовых полей никогда не приведет к переполнению)

PS
Пока незачот.

Остальное посмотрю попозже. Щас спешу.

drvlas
02.12.2010, 12:06
пока незачот.

котельне 3.14-здец... ждем михалыча :)

Валенок
02.12.2010, 12:13
Шнурую ботинки ...



Как доступаться к параметрам, имея на руках только указатель, если параметры разные по типам?


-Петька, прибор !!
-100 !!
-Чё 100 ?
-А чё прибор.


Сообщите про тип, например :


struct tType
( tReal, t.... );


function ...
var_input
typ : tType;
...


А как вы думаете работает, например бейсик там, или лисп.

Валенок
02.12.2010, 22:46
to drvlas

По поводу Вашей задумки.
Идите дальше. На кой Вам массив ? Сделайте односвязный список. Данные будут реально лежать не линейно в одном месте, а там, где кому надо, и сами нижние владельцы данных не будут знать от том что их данные на учете, а верхний смотрящий будет с ними лехко и непринужденно работать хоть с данными, хоть с владельцами.
И тут реально удобней будет сделать не структуру, а ф.б.

И чисто совет.
Используйте перечисления. Чем меньше чисел, тем меньше ошибок.
Зачем вам :

.......Point:BYTE; (* Формат вывода: 0, 1, 2, 3 знака справа от точки *)

Пишите, например :


......OutFormat : ( Format0, Format1, Format2......);

Во-первых меньше дурацких комментариев (не при объявлении, а далее по коду), без потери наглядности.
Во-вторых, присвоение
OutFormat := Format2 - более осмысленно, чем Point := 2; (хотя - дело вкуса)

В третьих, код легче модифицировать

drvlas
02.12.2010, 23:57
советы по поводу перечислений - понятно. иногда делаю, иногда нет. но согласен со всеми аргументами.

а вот это


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

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

в этом своем подходе я запнулся на том, что пойнтеры на разные по типам данные будут брать-записывать параметры неправильно. так как я не понял и совет "сообщать про тип", то пока на этом затыке и сижу.

и думаю: что это учитель мне про список говорит? это он на русском языке вообще? моя твоя не панимай...

Игорь Петров
03.12.2010, 16:43
По определению в стандарте МЭК WORD – это битовая строка из 16 элементов, которые можно адресовать раздельно. UINT – это целое без знака. По стандарту их нельзя смешивать, но CoDeSys допускает такую вольность.

Указатели в CoDeSys изначально понадобились для сериализации, при написании протоколов связи. Указатель тут понимается как физический адрес в памяти. Получил указатель на что угодно и погнал побайтно в com порт. Бывает в текстовую строчку надо залезть как в массив или сложный тип разобрать. Предполагается, что указатель использует человек разумный и сам знает чего он там адресует. Для его удобства контроль вырублен.

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

drvlas
03.12.2010, 18:47
Если подпрограмма получает указатель и не знает что это, то сообщить ей тип логично. Она может по CASE для возможных типов взять переменную правильно, если ей это надо.

Да, я именно так и понял то, что советовал уважаемый Валенок. В функциях чтения и записи значения параметра (там, где доступ к нему идет по индексу) будет CASE на варианты

BYTE
WORD
DWORD
STRING
TIME
DATE

При этом в структуре PARSCRIPT, описывающей интимности каждого параметра, добавляется элемент


Typ: PARTYPE;

В нем и будет указываться тип переменной для данного параметра. И достаточно функции доступа указать индекс параметра, чтобы она извлекла из глобального массива структур ту, что описывает наш параметр и по элементу .Type сориентироваться, как доступаться к параметру.

Я вот думаю: если пойнтер указывает, сколько извлекать байт (и сколько записывать), то не будет ли достаточно иметь разные указатели только на те типы данных, которые различаются количеством байт?
Тогда у нас будет:

BYTE
WORD
DWORD
STRING

- и все. А всякие там INT, UINT, DINT, TIME, DATE - отдыхают. И обращение к переменной типа TIME будут происходить так же, как к переменной DWORD. Выдернутся 4 байта, а потом программа сделает с ними чего надо - и они обратно запихнутся так же. Даже порядок сохранения байтов не имеет значения, ИМХО.
Можно так?

Спасибо за подсказки! Постараюсь это проверить и сообщить результат.

Валенок
03.12.2010, 20:45
Последняя идея имхо не лучшая.
Тип - это тип.

i : int := -15000;
w : word := 50000;

if ( i or w ) < 0 then ?

Лично я нехочу заморачиваться , как конкретно данный язык будет расширять данные, до longinta все расширит или i пихнет в word или w в int


К тому же типы - это не только стандартные.

Например TTemperatura.
Свойства :
1.вывод на экран : с одним знаком после запятой, с суффиксом '*С' или текстовое сообщение 'Обрыв датчика' .......
2.время от последнего опроса.
....

Если тип - ф.блок, обрабатывающий температуру и с кучей своих данных ( таймеров и др.. ) ?



У вас жестко определенная структура PARSCRIPT
А зачем жестко ? Кому-то нужен min-max, а кому-то нет….
Ведь кол-во свойств и их самоё определяет сам тип.
Да и данные у Вас хранятся отдельно от своих свойств.
А вдруг владельцы данных захотят поменять некоторые свойства ?
Со списком это было бы проще (имхо)

И жаль, конечно, что в кдс-2 нет хотя бы типа-функции.

PS
А вдруг тип : pointer to pointer to ...... ;)

drvlas
03.12.2010, 22:26
К тому же типы - это не только стандартные.

Например TTemperatura.
...
Со списком это было бы проще (имхо)

Вот он, профессорский подход! Но я сейчас так мелко плаваю, что не готов решать задачу в таком общем виде. И список меня пугает.

А решая все же только свою задачу, я говорю: да,

У вас жестко определенная структура PARSCRIPT
жесткая. В нее входит то, что мне в этом проекте нужно. Что, кстати, не мешает в другом проекте внести иную (тоже жесткую) структуру.


Да и данные у Вас хранятся отдельно от своих свойств.
А вдруг владельцы данных захотят поменять некоторые свойства?
Те данные, которые у меня (с Вашей легкой руки) хранятся в коде, это всего лищь константы, описывающие структуру каждого параметра. Сами же параметры разбросаны как угодно, не являясь элементом никакого массива или структуры. Мне кажется удобным...

Более того, порядок размещения описателей данных в массиве структур apsPar[]вообще не имеет значения, ибо я собираюсь доступаться к элементам массива поиском номера параметра - перебирая индекс, просматривать массив apsPar[] до совпадения с номером параметра в apsPar[i].Numb
То есть я в любой фазе разработки могу выбросить пару строк или добавить, переставить строки как угодно - и проект не поломается. Я имею в виду строки

set(...);
set(...);
Сейчас играюсь с примером доступа по описанным принципам - уже мажется :) Чуть позже предоставлю пример на строгий суд Учителей. Зачет-то нужен :)

drvlas
03.12.2010, 22:51
Сейчас играюсь с примером доступа по описанным принципам

Вот и работающий пример. Параметры - очень разные. от логических до даты. Указывая на все параметры пойнтером


POINTER TO DWORD

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

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

З.Ы. И как-то странно у меня складывается с ФБ. Пока не стал записывать

GetPar( by1:=5, X => dw);
ничего не работало. Т.е. конструкции вида

dw := GetPar( by1:=5).X;
ну не выполняются и все тут! Вижу нулевое значение вместо Х... Что за Х...

Валенок
04.12.2010, 03:00
GetPar( by1:=5, X => dw);
ничего не работало. Т.е. конструкции вида

dw := GetPar( by1:=5).X;
ну не выполняются и все тут! Вижу нулевое значение вместо Х... Что за Х...

К сожалению проза жизни. Программа не может быть операндом. А фб - это все-таки программа, хоть и с неявной передачей указателя на текущую структуру. Лобби от функций проплатили этот запрет.:)
Иначе кому они нужны были б.

drvlas
04.12.2010, 09:26
Во всяком случае, по GetPar Михалыч не дал по шее.

Видать, для зачета нужно еще и SetPar показать...

Я как понимаю на сегодняшний день. Указатели сами по себе есть 32-битные переменные (ну, для ПЛК100 так, во всяком случае). В зависимости от объявления указателя, контроллер вытаскивает данные строго указанной длины -

1 бит для BOOL
1 байт для BYTE, SINT, USINT
1 слово для WORD, INT, UINT
2 слова для DWORD, DINT, DUINT
строка для STRING (окончание по нулю?)
иное, если пользовательский тип данных
При этом способ размещения данных в получаемом значении как-то зафиксирован: по указанном уадресу - младший байт, а затем по возрастающей.
Аналогично следует размещать данные при записи. Потанцевав, ясен перец, с неразрушением данных в чужих полях (ибо наш универсальный пойнтер указывает на 4-байтный простор, который нужно засеять).

Хочу еще попробовать просто присваивать пойнтеры разных типов. Если получится, то танцев будет меньше.

З.Ы. Удивлен экспериментом с битовой переменной. Если пойнтер направить на некое битовое поле, в котором записать "1" не в младший разряд, то по GetPar выдернется, естественно, целый байт. А вот явное преобразование DWORD_TO_BOOL отсекает безжалостно все, кроме младшего бита
Я-то думал, всякое ненулевое значение целого байта по указанному адресу считается TRUE. А вот нет... Берем на заметку.

rovki
04.12.2010, 10:32
почти не чего не понял,но как приятно послушать -это как хорошая песня на иностранном языке:)

drvlas
04.12.2010, 11:27
Каюсь, многословен... Тут ведь идет мучительный процесс научения меня, сухой остаток стараюсь выкладывать время от времени, но он все равно получается мокрый. Ну, думаю, что пример (скоро уже с SetPar) покажет все понятое.

Валенок
04.12.2010, 12:05
1 бит для BOOL..

Байт


Если пойнтер направить на некое битовое поле, в котором записать "1" не в младший разряд, то по GetPar выдернется, естественно, целый байт.

Может где и есть железки адресующие непосредственно бит, но я не слыхал.


А вот явное преобразование DWORD_TO_BOOL отсекает безжалостно все, кроме младшего бита

Не отсекает, а сравнивает с нулем весь DWORD. Улавливаете ?

Игорь Петров
04.12.2010, 12:29
Т.е. конструкции вида

dw := GetPar( by1:=5).X;
ну не выполняются и все тут!

Не путайте прынципиально разные штуки 1 и2:

1. Вызов экземпляра с одновременным присваиванием данных:


GetPar(by1:=5);

или просто вызов (со старыми данными)


GetPar();

2. Чтение/запись переменных экземпляра,
без вызова его кода:


dw := GetPar.X;

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

Валенок
04.12.2010, 12:49
Drvlas наверное имел ввиду что на нижнем уровне что ф.б., что прог,что функ. :

..
CALL ...
...

И что для ф.б. не сделали из

X := FB(A := ?).X

релиз типа :


..
mov FB.A, ? ______________________;A := ?

push @FB _______________________;FB()
call привязаная_к_FB_прога

mov X, FB.X ______________________;X := FB.Х


А было б неплохо, а ?



PS
Замучил этот автосборщик пробелов

drvlas
04.12.2010, 13:09
не отсекает, а сравнивает с нулем весь Dword. улавливаете ?
проверил в отдельном примере. да, весь бай (все слово). где-то раньше я напутал... ну, так и должно быть! почему я и удивился. спасибо!

drvlas
04.12.2010, 13:15
не путайте прынципиально разные штуки 1 и2:

ага! теперь ясно. что ж, логика фб вообще как-то до меня сложно доходит.

перечел ваш классический труд, п.п. 5.3.2 и 5.3.3. то, что я понял сейчас, никак не противоречит изложенному там, конечно. но там неочевидна разница - обратиться к переменным экземпляра до вызова или после вызова.
спасибо!

drvlas
04.12.2010, 13:50
Готовлю я себе примерчик, отрабатываю даже то, что сейчас не нужно... И наткнулся на такую ругню компилятора:


toda: TOD;
dat: DATE;
...
toda := toda + TOD#00:01;
dat := dat + DATE#0000-00-01;

На первое присвоение пишет:

Несоответствие операнда 2 в "+": невозможно преодразовать TOD в TOD
А на второе:

Неверный формат даты
Несоответствие операнда 2 в "+": невозможно преодразовать DATE в DATE

Вопрос: как прибавить к дате 1 день? Ко времени суток 1 минуту?
ИСПРАВЛЕНО: Ко времени дня добавить можно


toda := toda + T#1m;

А вот к дате... Даже 24 часа не помогают - дает из 2010-12-03 2013-08-29...

Определил, что


dat := TIME_TO_DATE(T#1d);

Компилируется нормально и дает 2 января 1970 года. А вот прибавить так 1 день НИЗЗЯ:


dat := dat + TIME_TO_DATE(T#1d);

Так что вопрос открытый

Малышев Олег
04.12.2010, 14:28
конвертируйте в DWord и добавляйте количество миллисекунд. потом обратно в дату.

Валенок
04.12.2010, 14:54
Тут с форматами намутено дай боже.

Time - в мсек
Date -в сек


К Date прибавить Date нельзя. Это логично. Ведь Date - это конкретная точка. У неё нет размерности.
А прибавить Time можно. Ведь Time - это интервал.

А вот почему при КДС не могет фразу :

DATE := DATE + TIME нормально преобразовать со скрытым преобразованием мсек в сек, это вопрос к Петрову И.

И потому приходится извращатся типа :

DATE := DATE + dword_to_time( time_to_dword( TIME ) / 1000 );

PS
Отвечал и открывал пивко - уже ответили ...

drvlas
04.12.2010, 15:35
С вашим пивком или без него, но оба ошиблись, уважаемые. В форматах дат (DATE и DT) время считается в секундах, а не в миллисиекндах. И слава Богу, потому что в миллисекундах дата 2010-12-31 выглядит где-то 13-разрядным десятичным числом и 4 байта DWORD не помещается.

Поэтому правидьный ответ будет таким: прибавить 1 день к дате следует через прибаление 86400 секунд к ее выражению в виде DWORD:


dat := DWORD_TO_DATE(DATE_TO_DWORD( dat) + 86400);

Валенок
04.12.2010, 16:42
Одно очко отыграли :


DATE := DATE + TIME нормально преобразовать со скрытым преобразованием мсек в сек, это вопрос к Петрову И.

И потому приходится извращатся типа :

DATE := DATE + dword_to_time( time_to_dword( TIME ) / 1000 );

Петрову И. отбой. КДС оказался умен - мс в сек преобразовывает.

DATE := DATE + dword_to_time( time_to_dword( TIME ));

а значит просто

DATE := DATE + TIME; Работает. Только мс. теряет.

и Ваше

dat := DWORD_TO_DATE(DATE_TO_DWORD( dat) + 86400);

Заменяется на :

dat := dat + T#1d;

Что же касается DATE + DATE
Это скорее философия. Между датами есть интервал. ( в сек)
И DATE1 минус DATE2 - имеет смысл. т.к. результат - интервал. (в сек)

А вот DATE1 + DATE2 - бессмысленно в принципе т.к. дата - точка осчета. а время - интервал.

Интервал можно умножить, разделить и др..

а дату нельзя. т.к. конечной результат будет зависеть от начальной точки.
толи Р.Х. , то ли 1970-1-1-1, толи 1961-4-12

drvlas
04.12.2010, 23:46
Итак. спасибо учителям, в вопросе разобрался.

Моя задача была такова (отбрасываю оффтопиковые детали):

Есть ряд переменных разного типа, разбросанных по областям CONSTANT, RETAIN и обычным VAR. Их свойства описываются структурой PARSCRIPT и существует массив таких структур. В этот массив входят все те переменные, которые я называю параметрами. В параметры я включаю те и только те переменные, для которых будет иногда использоваться инструмент единообразного доступа по чтению и записи этих переменных.

В прилагаемом проекте показано возможное решение, основанное на использовании указателей на параметры и информации о типах. В ФБ чтения и записи GetPar и SetPar происходит выбор по CASE участка кода, где точно известно, какого типа извлекаемые-записываемые данные. И там, конечно, без всяких проблем это чтение-запись происходит.
В программе PLC_PRG продемонстрировано, что значения правильно извлекаются, затем они для понту модифицируются и записываются назад.

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

Кстати, для моей задачи множественные выходы ФБ GetPar и множественные входы SetPar вовсе и не нужны. Они скорее хороши для того, чтобы в PLC_PRG показать, что извлеклось правильно. А я буду использовать один выход Х (см. закомментированные участки GetPar) и аналогичный вход Х у SetPar (еще не написано). Ведь мне вся эта байда нужна для обмена с ИП320 через регистры, а туда вся инфо по значению параметра пойдет в виде 4-хбайтного регистра. И в момент передачи на или с ИП320 до лампочки различия в формате INT, DWORD, TIME, DATE. Все равно ИП320 ни хрена не понимает, кроме бита и ворда :(
И красивое бортовое время придется нарезать на голы-очки-секунды, тьфу! на часы-минуты-секунды и передавать их туды через 3 регистра...

Но это уже отдельная песнь.

Так что еще раз хочу поблагодарить Учителя и всех остальных!