CSharp
|
|
denzhig | Date: Wednesday, 13.09.2017, 10:35 | Message # 1 |
Lieutenant
Group: Users
Messages: 46
Status: Offline
| День добрый.
Что-то глюки всплывают при работе скриптов. Вчера написал скрипт, проверил - работает. Сегодня почему-то начал ругаться вот на эту строку: if((double)'SS.open_NA' > 100.0) Если меняю её на if (Convert.ToDouble('SS.open_NA') > 100.0) - то всё работает. Казалось бы вот он выход, ан нет - перед этой строкой ещё 5 таких же переменных так же обрабатываются и на них не ругается. Причём если переменная 'SS.open_NA' не меняется - то преобразование работает, как только начинает меняться - вываливает ошибку каста. Сами переменные одинаковые: берутся из DB-шника, REAL-овского типа. Единственное отличие в обработке переменных в скрипте - перед строкой на которую ругается проверяется ещё одно условие: if ((bool)'Loc.test_115_NA_stopped' == false) { 'SS.open_NA' = Convert.ToDouble('SS.open_NA') + (MSFromLastCall / 12000.0 * (double)'SS.pos_GZ_NA'); } ругаться на строку после этого условия начинает как раз когда переменная 'Loc.test_115_NA_stopped' становится = true, т.е. это условие опускается и сразу переходит на if((double)'SS.open_NA' > 100.0) и тут вылетает если SS.open_NA изменяется (если стоит на месте - не вылетает).
Message edited by denzhig - Wednesday, 13.09.2017, 10:47 |
|
| |
Alexor | Date: Wednesday, 13.09.2017, 11:46 | Message # 2 |
Major general
Group: Administrators
Messages: 311
Status: Offline
| Добрый день,
Сразу могу сказать что REAL в ПЛК - 32-bit, поэтому кастовать в С# нужно не к double (64-bit), а к float (32-bit). Если возможно, хотелось бы видеть весь текст скрипта.
|
|
| |
denzhig | Date: Wednesday, 13.09.2017, 14:00 | Message # 3 |
Lieutenant
Group: Users
Messages: 46
Status: Offline
| С float вообще не работает скрипт - ругается сразу на первые же строки, хотя чек проходит без ошибок. Сам скрипт выглядит вот так:
Code // Модель ГЗ НА 'SS.pos_GZ_NA' = Convert.ToDouble('SS.pos_GZ_NA') + (MSFromLastCall / 70.0 * (double)'SS.dir_PGR_NA'); if ((bool)'Loc.test_115_GZ_NA_open' && ((double)'SS.pos_GZ_NA' < -10.0)){'SS.pos_GZ_NA' = -10.0;} if ((double)'SS.pos_GZ_NA' > 90.0){'SS.pos_GZ_NA' = 90.0;} if ((double)'SS.pos_GZ_NA' < -90.0){'SS.pos_GZ_NA' = -90.0;}
// Модель ГЗ РК 'SS.pos_GZ_RK' = Convert.ToDouble('SS.pos_GZ_RK') + (MSFromLastCall / 70.0 * (double)'SS.dir_PGR_RK'); if ((double)'SS.pos_GZ_RK' > 90.0){'SS.pos_GZ_RK' = 90.0;} if ((double)'SS.pos_GZ_RK' < -90.0){'SS.pos_GZ_RK' = -90.0;}
// Модель с/м НА if ((bool)'Loc.test_115_NA_stopped' == false) { 'SS.open_NA' = Convert.ToDouble('SS.open_NA') + (MSFromLastCall / 12000.0 * (double)'SS.pos_GZ_NA'); } if (Convert.ToDouble('SS.open_NA') > 100.0){'SS.open_NA' = 100.0;} if (Convert.ToDouble('SS.open_NA') < 0.001){'SS.open_NA' = 0.001;} // Точный "0" нельзя писать из-за особенностей алгоритма сглаживания в хэндлере 'SS.open_NA_2' = 'SS.open_NA';
// Модель с/м РК // Здесь считается разность т.к. датчик ГЗ РК на реальной машине "перевёрнут" 'SS.open_RK' = Convert.ToDouble('SS.open_RK') - (MSFromLastCall / 45000.0 * (double)'SS.pos_GZ_RK'); if ((double)'SS.open_RK' > 100.0){'SS.open_RK' = 100.0;} if ((double)'SS.open_RK' < 0.001){'SS.open_RK' = 0.001;} // Точный "0" нельзя писать из-за особенностей алгоритма сглаживания в хэндлере 'SS.open_RK_2' = 'SS.open_RK';
// Переключение скорости и мощности if ((bool)'Gov.VG_on') { 'Loc.bf_lag_power' = 'Loc.Power_calc'; 'Loc.bf_lag_speed' = 'Gov.Speed_sim_value'; } else { 'Loc.bf_lag_power' = 0.0; 'Loc.bf_lag_speed' = 'Loc.Speed_calc'; if ((bool)'Loc.test_115_speed_112' && ((double)'Loc.bf_lag_speed' < 112.0)) { 'Loc.bf_lag_speed' = 112.0; } } 'Gov.P_val' = Convert.ToUInt16((double)'Loc.tmp_power' * 276.48); 'Gov.DCHV_TG2' = Convert.ToUInt16((double)'Loc.tmp_speed' * 138.24);
В данном варианте скрипт работает, применено "Convert.ToDouble('SS.open_NA') ".
Message edited by denzhig - Thursday, 14.09.2017, 07:56 |
|
| |
denzhig | Date: Thursday, 14.09.2017, 07:54 | Message # 4 |
Lieutenant
Group: Users
Messages: 46
Status: Offline
| Сделал отдельный тест данного глюка. В PLCSim-е находится только один DB-шник, в котором одна переменная REAL-овского типа. скрипт имеет вид:
Code if ((bool)'Loc.test_115_NA_stopped' == false) { 'SS.open_NA' = Convert.ToDouble('SS.open_NA') + MSFromLastCall / 12000.0D * (double)'Loc.Item1'; } if ((double)'SS.open_NA' > 100.0){'SS.open_NA' = 100.0;} if ((double)'SS.open_NA' < 0.001){'SS.open_NA' = 0.001;} Если слайдер стоит на "0.0" - то установка чек-бокса не приводит к вылету скрипта. Расчётное значение просто стоит, при этом как я понимаю проверки на выход за диапазон продолжают выполняться. Как только смещаем слайдер в какую-то сторону и значение, что вычисляем и пишем в PLCSim, начнёт бежать - если в этот момент выставить крыж, то имеем вылет скрипта. И да - тип float вызывает перманентный останов выполнения скрипта, хотя ошибок при проверке нет.
Проект для PS вот здесь: https://yadi.sk/d/TJ04S30e3MsRxj
Message edited by denzhig - Thursday, 14.09.2017, 08:01 |
|
| |
Alexor | Date: Thursday, 14.09.2017, 08:04 | Message # 5 |
Major general
Group: Administrators
Messages: 311
Status: Offline
| Постараюсь сегодня посмотреть.
|
|
| |
denzhig | Date: Thursday, 14.09.2017, 11:09 | Message # 6 |
Lieutenant
Group: Users
Messages: 46
Status: Offline
| Кажется нашёл взаимосвязь с вылетами скрипта и последовательностью действий: если в текущем цикле симулятора в скрипте к переменной ранее применялся "Convert.To..." - то и далее касты будут происходить нормально, а если не было конверта, то и касты выдадут ошибку. Вот такой код заработал:
Code 'SS.open_NA' = Convert.ToDouble('SS.open_NA'); if ((bool)'Loc.test_115_NA_stopped' == false) { 'SS.open_NA' = (double)'SS.open_NA' + MSFromLastCall / 12000.0 * (double)'Loc.Item1'; } if ((double)'SS.open_NA' > 100.0){'SS.open_NA' = 100.0;} if ((double)'SS.open_NA' < 0.001){'SS.open_NA' = 0.001;} Насчёт того что глюк это или фича - не берусь судить. Хотя мне кажется что дело не в C#.
Message edited by denzhig - Thursday, 14.09.2017, 11:10 |
|
| |
Alexor | Date: Thursday, 14.09.2017, 11:52 | Message # 7 |
Major general
Group: Administrators
Messages: 311
Status: Offline
| Да, я могу это всё объяснить. Постараюсь вечером написать развёрнутый ответ, почему оно так работает.
|
|
| |
Alexor | Date: Thursday, 14.09.2017, 20:39 | Message # 8 |
Major general
Group: Administrators
Messages: 311
Status: Offline
| Как я уже говорил Real в контроллере – 32 bit. В С# это float (он же Single). Если взять значение с типом double и записать в Item то PS преобразует тип автоматически и запишет в переменную контроллера.
При работе скрипта запись переменных происходит только после его исполнения, а во время исполнения скрипт работает со значениями из своего кеш.
1 'SS.open_NA' = Convert.ToDouble('SS.open_NA'); 2 if((bool)'Loc.test_115_NA_stopped' == false) 3 { 4 'SS.open_NA'= (double)'SS.open_NA' + MSFromLastCall / 12000.0 * (double)'Loc.Item1'; 5 } 6 if((double)'SS.open_NA' > 100.0){'SS.open_NA' = 100.0;} 7 if((double)'SS.open_NA' < 0.001){'SS.open_NA' = 0.001;}
В первой строке текущее значение 'SS.open_NA' (float) из кеш скрипта преобразуется в double и помещается обратно в кеш. Все дальнейшие манипуляции с этим значением происходят там. После завершения скрипта значение из кеш записывается в Item 'SS.open_NA' с автоматическим преобразованием в float. Поэтому при следующем цикле скрипта в кеш опять значение float.
Каст (double)'SS.open_NA' будет работать только, если в этой переменной действительно тип double. В противномслучае нужно делать конвертацию Convert.ToDouble. И так для любого типа.
Скрипт можно переписать следующим образом:
if ((bool)'Loc.test_115_NA_stopped' == false) { 'SS.open_NA' = (float)'SS.open_NA' +MSFromLastCall / 12000.0f * (float)'Loc.Item1'; } if ((float)'SS.open_NA' > 100.0f){'SS.open_NA' = 100.0f;} if ((float)'SS.open_NA' < 0.001f){'SS.open_NA' = 0.001f;}
При этом нужно учитывать, что 12000.0 это double и использовать 12000.0f. Иначе компилятор все вычисления приведёт опять к этому типу. Кстати в 'Loc.Item1' нужно тоже записать float (Single), иначе будет ошибка.
Мой совет – использовать исходный тип данных. Само собой, если нужна двойная точность в расчётах можно их проводить с double, но записывать в Item лучше float.
|
|
| |
denzhig | Date: Friday, 15.09.2017, 07:57 | Message # 9 |
Lieutenant
Group: Users
Messages: 46
Status: Offline
| Спасибо за объяснение. Я так и предполагал что дело где-то в кеше с типами данных. Видимо меня сбило с толку что при объявлении переменных в PS они типа Single, а в C# такого типа в касте нет.
На этом фоне выглядит странным что переменная MSFromLastCall не требует каста/конвертации не смотря на то что она 64-битная и при этом всё выражение не переводится "под её размер".
Message edited by denzhig - Friday, 15.09.2017, 08:12 |
|
| |
Alexor | Date: Friday, 15.09.2017, 09:20 | Message # 10 |
Major general
Group: Administrators
Messages: 311
Status: Offline
| Если в арифметике используются целые и дробные типы то результат будет дробное.
https://docs.microsoft.com/en-us....e
"You can mix numeric integral types and floating-point types in an expression. In this case, the integral types are converted to floating-point types."
MSFromLastCall это long (целое).
Кстати, все переменные которые Item это object. Поэтому их приходится кастовать и конвертировать явно. C# не делает это автоматически.
|
|
| |
an1977drej7345 | Date: Saturday, 20.01.2018, 11:49 | Message # 11 |
Private
Group: Users
Messages: 9
Status: Offline
| а как работать с переменными Item, которые массивы? я например считываю в одну переменную через modbus несколько десятков регистров, а потом хочу в коде использовать значения нужных мне элементов массива.
|
|
| |
Alexor | Date: Saturday, 20.01.2018, 14:28 | Message # 12 |
Major general
Group: Administrators
Messages: 311
Status: Offline
| Два варианта:
1) Напрямую в скрипте. В примере инкрементируется 0-ой элемент массива Connection1.Item1 ( Int32[] ). Code var lArray = 'Connection1.Item1' as Array; if (lArray != null) { int lInt = (int)lArray.GetValue(0); lInt = lInt + 1; lArray.SetValue(lInt, 0); } 2) Использовать сим. объект Item.ArraySplitter - разбить массив по внутренним переменным, а из скрипта работать уже с ними.
|
|
| |
an1977drej7345 | Date: Saturday, 20.01.2018, 16:00 | Message # 13 |
Private
Group: Users
Messages: 9
Status: Offline
| Спасибо за быстрый ответ! по второму варианту с Item.ArraySplitter уже пробывал, но почему-то не получается записать значения в отдельный элемент массива переменной, которая создана для соединения Modbus. Читается все с этого массива хорошо, а запись не получается.
С элементами массива внутренней переменной все работает с объектом Item.ArraySplitter хорошо в оба направления.
|
|
| |
an1977drej7345 | Date: Saturday, 20.01.2018, 16:49 | Message # 14 |
Private
Group: Users
Messages: 9
Status: Offline
| со скрипта тоже не получается записать в элемент массива переменной Modbus. Видно пытается, значение моргает но возвращается сразу же на старое, которое в ПЛК. Хотя в Communication Manager с вкладки Value запись элементов массива этой переменной вручную (write) проходит нормально.
|
|
| |
Alexor | Date: Sunday, 21.01.2018, 22:16 | Message # 15 |
Major general
Group: Administrators
Messages: 311
Status: Offline
| Да, действительно есть ошибка и вроде нашёл как её исправить - Process Simulator 2.8.6595. Будет время, посмотрите пожалуйста. Если все работает, выложу для всех.
|
|
| |