[ New messages · Members · Forum rules · Search · RSS ]
  • Page 1 of 2
  • 1
  • 2
  • »
Forum moderator: Alexor  
Forum » Process Simulator » Russian » CSharp
CSharp
denzhigDate: Wednesday, 13.09.2017, 10:35 | Message # 1
Lieutenant
Group: Users
Messages: 46
Reputation: 0
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
 
AlexorDate: Wednesday, 13.09.2017, 11:46 | Message # 2
Major general
Group: Administrators
Messages: 311
Reputation: 1
Status: Offline
Добрый день,

Сразу могу сказать что REAL в ПЛК - 32-bit, поэтому кастовать в С# нужно не к double (64-bit), а к float (32-bit).
Если возможно, хотелось бы видеть весь текст скрипта.
 
denzhigDate: Wednesday, 13.09.2017, 14:00 | Message # 3
Lieutenant
Group: Users
Messages: 46
Reputation: 0
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
 
denzhigDate: Thursday, 14.09.2017, 07:54 | Message # 4
Lieutenant
Group: Users
Messages: 46
Reputation: 0
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
 
AlexorDate: Thursday, 14.09.2017, 08:04 | Message # 5
Major general
Group: Administrators
Messages: 311
Reputation: 1
Status: Offline
Постараюсь сегодня посмотреть.
 
denzhigDate: Thursday, 14.09.2017, 11:09 | Message # 6
Lieutenant
Group: Users
Messages: 46
Reputation: 0
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
 
AlexorDate: Thursday, 14.09.2017, 11:52 | Message # 7
Major general
Group: Administrators
Messages: 311
Reputation: 1
Status: Offline
Да, я могу это всё объяснить. Постараюсь вечером написать развёрнутый ответ, почему оно так работает.
 
AlexorDate: Thursday, 14.09.2017, 20:39 | Message # 8
Major general
Group: Administrators
Messages: 311
Reputation: 1
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.
 
denzhigDate: Friday, 15.09.2017, 07:57 | Message # 9
Lieutenant
Group: Users
Messages: 46
Reputation: 0
Status: Offline
Спасибо за объяснение. Я так и предполагал что дело где-то в кеше с типами данных.
Видимо меня сбило с толку что при объявлении переменных в PS они типа Single, а в C# такого типа в касте нет.

На этом фоне выглядит странным что переменная MSFromLastCall не требует каста/конвертации не смотря на то что она 64-битная и при этом всё выражение не переводится "под её размер".


Message edited by denzhig - Friday, 15.09.2017, 08:12
 
AlexorDate: Friday, 15.09.2017, 09:20 | Message # 10
Major general
Group: Administrators
Messages: 311
Reputation: 1
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# не делает это автоматически.
 
an1977drej7345Date: Saturday, 20.01.2018, 11:49 | Message # 11
Private
Group: Users
Messages: 9
Reputation: 0
Status: Offline
а как работать с переменными Item, которые массивы? 
я например считываю в одну переменную через modbus несколько десятков регистров,
а потом хочу в коде использовать значения нужных мне элементов массива.
 
AlexorDate: Saturday, 20.01.2018, 14:28 | Message # 12
Major general
Group: Administrators
Messages: 311
Reputation: 1
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 - разбить массив по внутренним переменным, а из скрипта работать уже с ними.
 
an1977drej7345Date: Saturday, 20.01.2018, 16:00 | Message # 13
Private
Group: Users
Messages: 9
Reputation: 0
Status: Offline
Спасибо за быстрый ответ!
по второму варианту с  Item.ArraySplitter уже пробывал,
но почему-то не получается записать значения в отдельный элемент массива переменной, которая создана для соединения Modbus. Читается все с этого массива хорошо, а запись не получается.

С элементами массива внутренней переменной все работает с объектом Item.ArraySplitter хорошо в оба направления.
 
an1977drej7345Date: Saturday, 20.01.2018, 16:49 | Message # 14
Private
Group: Users
Messages: 9
Reputation: 0
Status: Offline
со скрипта тоже не получается записать в элемент массива переменной Modbus. Видно пытается, значение моргает но возвращается сразу же на старое, которое в ПЛК. Хотя в Communication Manager с вкладки Value запись  элементов массива этой переменной вручную (write) проходит нормально.
 
AlexorDate: Sunday, 21.01.2018, 22:16 | Message # 15
Major general
Group: Administrators
Messages: 311
Reputation: 1
Status: Offline
Да, действительно есть ошибка и вроде нашёл как её исправить - Process Simulator 2.8.6595.
Будет время, посмотрите пожалуйста. Если все работает, выложу для всех.
 
Forum » Process Simulator » Russian » CSharp
  • Page 1 of 2
  • 1
  • 2
  • »
Search: