Арифметическое переполнение в ПЛК AllenBradley

в 1:34, , рубрики: allen-bradley, арифметика, АСУТП, Промышленное программирование

Недавно на работе столкнулись с интересной ситуацией, о которой захотелось написать тут, потому что случай довольно интересный, хотя как и оказалось простой. На одном из агрегатов, управляемым контроллером от Allen Bradley Compact Logix L33ER, в контроллере постоянно сыпались предупреждения, а точнее даже минорный ошибки (Minor Faults) - которые на функциональность никак не влияют, но раздражают своим присутствием. В секунду по нескольку десятков таких ошибок без перерыва: Type 04 Program fault (Code 04) Arithmetic overflow. Result of an arithmetic instruction out of range, что переводится примерно как "Арифметическое переполнение. Результат арифметической инструкции вышел за предел."

Окно с ошибками
Окно с ошибками

Проблема возникает в одной подпрограмме, в двух разных местах (Rung 0 и 37), где вызывается один и тот же блок, то есть проблема именно в этом блоке Penetration Control, потому как исключив из программы вызов блока, ошибки прекратились совсем

Арифметическое переполнение в ПЛК AllenBradley - 2

Внутри этого блока скрывается формула:

Bend_Radius := ((2.0*Penetration*Material_Gage)-(Penetration **2.0)-(Material_Gage **2.0)-(Roll_Spacing **2.0/4.0))/(4.0*(Penetration-Material_Gage));

Yield_Percent :=1.0-((Bend_Radius*2.0*Material_Yield)/(Modulus_Of_Elasticity*Material_Gage));

IFBend_Radius > 0.0
  THEN
    Bending_Loss :=(3.0*Material_Width*Material_Yield*Material_Gage**2.0)/(2.0*Bend_Radius)*(1.0+(1.0/(86.3-(88.0*Yield_Percent))));
  ELSE
    Bending_Loss := 0.0;
END_IF;

Первоначальное предположение было, что слишком много знаков после запятой у всех этих Material_Gage или Material_Width, а после возведения в квадрат так и вообще количество знаков становится еще больше. Конечно же REAL тип данных позволяет хранить очень много знаков, и более того, как я понимаю ему все-равно сколько там будет этих знаков, он сохранит число, просто с потерей точности, но вызывать ошибки это не должно было бы. Но так как проверить эту гипотезу проще всего, то попробовали немного уменьшить точность вычислений, округлив числа до 2-3 знаков после запятой. Кстати такие странные числа, что на скрине, получаются по причине того, что миллиметры переводятся в дюймы, а другие метрические единицы переводятся в неметрические. В итоге фокус с уменьшением точности ни к чему не привел, ошибки так и продолжали сыпаться.

Тут просто еще одна безуспешная попытка

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

А вот идея с выяснением на каком этапе вычислений происходит переполнение, дала свои плоды. Выяснилось что проблема в самой первой формуле, где как раз много возведений во вторую степень. После замены всех Х **2.0 на Х * Х, в первой формуле - ошибки перестали сыпаться. Дальше было уже интересно а какой именно параметр дает такой сбой. И в итоге получилось что Penetration ** 2.0 вызывает переполнение, а Penetration * Penetration нет. Отличие Penetration от других переменных в том, что это число отрицательное. Как только меняем его на положительное число, то ошибок нет, отрицательное - сразу валятся.

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

UPD Благодаря наводке из комментариев, за что очень сильно благодарен комментаторам, я решил все-таки обратиться к мануалу, хотя с этого наверно и стоило начать. В мануале описания для инструкции возведения в квадрат нет, зато есть инструкция для возведения в степень. В блочном виде (Ladder Diagram) это блок XPY (X to the Power of Y), в структурном тексте (ST) как раз и есть та самая инструкция dest := sourceX ** sourceY; И на следующей странице мануала так и пишется If Source X is negative, Source Y must be an integer value or a minor fault will occur.

Арифметическое переполнение в ПЛК AllenBradley - 3

Автор: Alexey

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js