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

Недавно на работе столкнулись с интересной ситуацией, о которой захотелось написать тут, потому что случай довольно интересный, хотя как и оказалось простой. На одном из агрегатов, управляемым контроллером от 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, потому как исключив из программы вызов блока, ошибки прекратились совсем

a32e2f42a08572125f9a8913a3aa7514.PNG

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

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 от других переменных в том, что это число отрицательное. Как только меняем его на положительное число, то ошибок нет, отрицательное — сразу валятся.

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

© Habrahabr.ru