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

Сразу могу сказать что REAL в ПЛК - 32-bit, поэтому кастовать в С# нужно не к double (64-bit), а к float (32-bit).
Если возможно, хотелось бы видеть весь текст скрипта.
 
denzhigDate: Wednesday, 13.09.2017, 14:00 | Message # 3
Sergeant
Group: Users
Messages: 32
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
Sergeant
Group: Users
Messages: 32
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: 275
Reputation: 1
Status: Offline
Постараюсь сегодня посмотреть.
 
denzhigDate: Thursday, 14.09.2017, 11:09 | Message # 6
Sergeant
Group: Users
Messages: 32
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: 275
Reputation: 1
Status: Offline
Да, я могу это всё объяснить. Постараюсь вечером написать развёрнутый ответ, почему оно так работает.
 
AlexorDate: Thursday, 14.09.2017, 20:39 | Message # 8
Major general
Group: Administrators
Messages: 275
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
Sergeant
Group: Users
Messages: 32
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: 275
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# не делает это автоматически.
 
Forum » Process Simulator » Russian » CSharp
Page 1 of 11
Search: