Проблемы при назначении единиц измерения результату функции плагина и возврате строки

Проблемы при назначении единиц измерения результату функции плагина и возврате строки - Messages

#1 Posted: 2/12/2015 4:57:54 AM
Mike Kaganski

Mike Kaganski

184 likes in 434 posts.

Group: User

Здравствуйте!

Описал проблему в английской ветке. По предложению Вячеслава дублирую тему здесь.

При возврате из плагина результата в виде TNumber и присвоении ему единиц измерения через его obj.Units результат прекрасно виден, но ведёт себя как будто эти единицы неизвестны программе. Как сделать назначение единиц правильно?

При возврате из функции строкового значения через

new SMath.Math.Numeric.TDouble("\"" + String + "\"" );

текст возвращается нормально, но использовать такую функцию внутри функций SMath, которым нужна строка, нельзя - возникает ошибка "Требуется строка!"
Как это делать правильно?

В архиве проект плагина C#, созданный в VS2013. Там же тестовый файл debug.sm с демонстрацией проблемы. При компиляции отладочной версии DLL плагина копируются в папку "%appdata%\SMath\extensions\plugins\ca92ef03-c7da-4888-98ad-528482733e2f\1.0.0.0\" (должна существовать).

Заранее спасибо за подсказки и указание направления!
С уважением, Михаил Каганский
#2 Posted: 2/12/2015 5:29:08 PM
уни

уни

156 likes in 355 posts.

Group: User

Я посмотрел исходники, которые выложил в репозиторий Давид. Если этот вариант рабочий (не пробовал), то от себя могу порекомендовать использовать вместо NumericEvaluation обработчик ExpressionEvaluation. Странно, что понадобилась какая-то дополнительная работа с размерностями. Насколько я понимаю, нужно обезразмерить параметры функции на входе и добавить размерность на выходе. Почему так это сложно получилось? Весь код для работы с размерностями должен быть на стороне SMath Studio.

Может потребовать пояснение использование метода UnitsManager.GetCurrentUnitName(). В SMath Studio принято соглашение, что "твёрдая" копия файла расчёта не должна зависеть от локализации. Поэтому существуют стандартные (для программы) идентификаторы для названий математических функций, а также соглашения для разделителей и некоторых других символов. По-видимому, то же касается единиц измерения. Поэтому после численного расчёта к результату в виде строки достаточно добавить (буквально) нужную по документации размерность. Проще всего (и понятней) это делать "вручную" в обработчике ExpressionEvaluation, где доступен экземпляр Store.

Проект копировался или создавался мастером? GUID должен быть уникальным. Если копировать шаблон из другого проекта, то нужно заново генерировать GUID, который находится в файле AssemblyInfo.cs. Мастер делает это автоматически.

П.С. А вообще, лучше использовать обёртку, которая написана самими авторами (platform-independent.7z). Там есть и пример как ей пользоваться (Example.cs). Наворотили они там предостаточно. Не понимаю зачем нужен такой сложный код, но авторам видней. Достаточно взять их обёртку и совместить с кодом плагина для SMath Studio. Я и автору это же посоветовал, так как видел у них на сайте описание обёртки на c#. Не знаю почему он так долго код ваяет, видимо он решил делать нативную обёртку на C++/CLI по моим примерам, а она в два (и более) раза сложнее, чем то же на c#.
platform-independent.zip (12 KiB) downloaded 99 time(s).
Россия навсегда! Вячеслав Мезенцев
1 users liked this post
Mike Kaganski 2/12/2015 7:24:00 PM
#3 Posted: 2/12/2015 9:47:02 PM
Mike Kaganski

Mike Kaganski

184 likes in 434 posts.

Group: User

Вячеслав, огромное спасибо за анализ!

Wrote

от себя могу порекомендовать использовать вместо NumericEvaluation обработчик ExpressionEvaluation.


По правде говоря, сначала я и сам пошёл этим путём. Я хотел сначала реализовать только IPluginLowLevelEvaluation, без IPluginMathNumericEvaluation. Тем более что, по моим наблюдениям, НЕ реализовывать IPluginLowLevelEvaluation совсем нельзя, нужна хотя бы заглушка: SMath ругается, когда пытаешься добавить единицы измерения в выражении, где есть функция плагина "чистого IPluginMathNumericEvaluation". Но меня напрягало, во-первых, усложнение подготовки численного значения параметров (параметры передаются не как готовое число/строка, а как массив Term), и во-вторых, то, что это может как-то сказаться на работе SMath: ведь я заставляю SMath безальтернативно производить численное решение подвыражений в аргументах, независимо от режима оптимизации выражения, выбранного пользователем. Ведь, судя по информации о последовательности вычислений, представленной Андреем в теме о разработке плагинов, ExpressionEvaluation выполняется перед другими, и независимо от метода оптимизации? Я впервые делаю плагин для SMath, да и вообще моё знакомство с .NET исключительно маргинальное, так что я подумал: пусть математикой занимается сама программа, ведь у меня фактически используется просто численная библиотека, а IPluginMathNumericEvaluation по логике как раз предназначен для этого?
Wrote

Странно, что понадобилась какая-то дополнительная работа с размерностями. Насколько я понимаю, нужно обезразмерить параметры функции на входе и добавить размерность на выходе. Почему так это сложно получилось? Весь код для работы с размерностями должен быть на стороне SMath Studio.

Может потребовать пояснение использование метода UnitsManager.GetCurrentUnitName(). В SMath Studio принято соглашение, что "твёрдая" копия файла расчёта не должна зависеть от локализации. Поэтому существуют стандартные (для программы) идентификаторы для названий математических функций, а также соглашения для разделителей и некоторых других символов. По-видимому, то же касается единиц измерения. Поэтому после численного расчёта к результату в виде строки достаточно добавить (буквально) нужную по документации размерность.


Меня тоже это удивило. По-видимому, в NumericEvaluation это именно так.
Вначале в этом обработчике я пытался просто использовать выражения типа new MItem("'J/'mol" ). Но это оказалось неправильным, потому что результат оказывался для программы "непонятным", в локализованной версии отображались английские названия единиц, и вели они себя просто как невстроенные и непривязанные единицы. При использовании для единиц GetCurrentUnitName() отображение их выправилось, но поведение осталось: программа не воспринимала их как встроенные единицы, если только они не оказывались "базовыми" единицами SI (такими как Кельвины или метры, в отличие от, скажем, Джоулей), да и те не работали, если выражение было сложным (скажем, м^3 или кг/м). И только после последовательного разбиения составных единиц SI на простые, использования UnitsManager.GetCurrentUnitName() для каждой единицы по отдельности, формирования строки-выражения составной единицы, Converter.ToMItem() и Expression.SimplifyEx() получилось то, чего хотелось достичь. При этом в "твёрдой" копии сохраняются именно стандартные единицы, не зависящие от локализации.

Для того, чтобы понять, что так вроде бы "правильно", я посмотрел, как SMath передаёт образмеренные параметры в функцию. Там для любой сложной единицы получается внутреннее разложенное представление в локализованных именах. Я так и сделал.

Я считаю, что просто в UnitsManager не хватает метода, создающего сразу нормальный MItem из языконезависимого базового представления, ведь у него есть весь необходимый контекст из файла. Было бы здорово написать что-то типа UnitsManager.MakeLocaleIndependentUnit("'J/'mol" ), ну, или (если так уж необходимо ограничится только встроенными единицами, учитывая разложение [J] = [kg*m^2/s^2]) UnitsManager.MakeLocaleIndependentUnit("'kg*'m^2/{'s^2*'mol}" ). Это обеспечило бы возможность нормально работать с единицами в NumericEvaluation() без плясок с бубном. Но это - вопрос расширения функционала к Андрею.
Wrote

Проще всего (и понятней) это делать "вручную" в обработчике ExpressionEvaluation, где доступен экземпляр Store.


Может быть, но с другой стороны, там труднее с числами (я писал выше, что у меня нет полного представления о возможных побочных явлениях этого подхода, а пляски с единицами в NumericEvaluation, при всей их экзотичности, выглядят "стабильными" без побочных эффектов). Может быть, я ошибаюсь.
Wrote

Проект копировался или создавался мастером? GUID должен быть уникальным. Если копировать шаблон из другого проекта, то нужно заново генерировать GUID, который находится в файле AssemblyInfo.cs. Мастер делает это автоматически.


Мастером. Я использовал в качестве отправной точки Ваш VBNetPlugin, но только как источник кода, а не проекта. Кстати, огромное спасибо! Без этого было бы гораздо труднее получить первое работающее приближение.
Wrote

А вообще, лучше использовать обёртку, которая написана самими авторами (platform-independent.7z). Там есть и пример как ей пользоваться (Example.cs). Наворотили они там предостаточно. Не понимаю зачем нужен такой сложный код, но авторам видней. Достаточно взять их обёртку и совместить с кодом плагина для SMath Studio. Я и автору это же посоветовал, так как видел у них на сайте описание обёртки на c#. Не знаю почему он так долго код ваяет, видимо он решил делать нативную обёртку на C++/CLI по моим примерам, а она в два (и более) раза сложнее, чем то же на c#.


Я думаю, что им нужно делать на C#, несмотря на то, что лично мне этот язык не нравится. Но для данной платформы это - то, что нужно.
Использовать их обёртку не хочу - я в ней не разобрался, пусть сами используют, я просто сделал для них старт. Она какая-то слишком избыточная. Мне кажется, тут всё проще, логика каждой функции чётко прописана в своём классе.
С уважением, Михаил Каганский
#4 Posted: 2/13/2015 3:05:47 AM
уни

уни

156 likes in 355 posts.

Group: User

Похоже, что авторам было лень самим разбираться в том как работает маршаллинг и прочие премудрости использования нативного кода в c#. Они использовали универсальную обёртку с сайта http://www.swig.org , которая обёртывает код автоматически. Поэтому он получается такой избыточный. Я могу попробовать сделать пример с использованием их обёртки.

Что касается IPluginLowLevelEvaluation. При всех его недочётах я практически во всех своих дополнениях использовал именно этот обработчик, даже в чисто численных (MathcadFileAccess). Можно взять любой мой плагин и посмотреть как работать в IPluginLowLevelEvaluation. В MathcadFileAccess я тоже сначала решил, что с IPluginMathNumericEvaluation будет проще, но потом весь код переделал под IPluginLowLevelEvaluation. Этот обработчик даёт большую степень свободы для программиста.

Твой код (давай на ты) как-то неправильно работает со строками. Функция, которая возвращает версию вываливается с ошибкой. Честно говоря, я так и не понял пока почему.

Меня смутило также использование StringBuilder в качестве типа параметра для работы с нативными функциями из библиотеки CoolProp.dll. Обычно для этого используются более примитивные классы. Я потому и рекомендовал использовать их вариант обёртки, т.к. у них эта переходная часть должна была быть записана правильно.

Quote

Использовать их обёртку не хочу - я в ней не разобрался, пусть сами используют, я просто сделал для них старт.


Они о "старте" мыслят немного по-другому. Вот ссылка на консольный проект, который я собрал для пробы их обёртки: CoolPropConsole-x32.zip. В проекте есть файл CoolProp.dll - это специальная 32-разрядная сборка библиотеки для использования с обёрткой (насколько я понял). Этот файл можно скачать с sf.net, там есть папка CoolProp\Csharp\Windows32. Исходники из platform-independent.7z нужно положить в отдельную папку проекта. Далее я просто скопировал содержимое Example.cs в свой проект. Всё достаточно просто.
Россия навсегда! Вячеслав Мезенцев
#5 Posted: 2/13/2015 4:08:16 AM
Mike Kaganski

Mike Kaganski

184 likes in 434 posts.

Group: User

Wrote

Что касается IPluginLowLevelEvaluation. При всех его недочётах я практически во всех своих дополнениях использовал именно этот обработчик, даже в чисто численных (MathcadFileAccess). Можно взять любой мой плагин и посмотреть как работать в IPluginLowLevelEvaluation. В MathcadFileAccess я тоже сначала решил, что с IPluginMathNumericEvaluation будет проще, но потом весь код переделал под IPluginLowLevelEvaluation. Этот обработчик даёт большую степень свободы для программиста.


Спасибо, я посмотрю.
Wrote


Твой код (давай на ты) как-то неправильно работает со строками. Функция, которая возвращает версию вываливается с ошибкой. Честно говоря, я так и не понял пока почему.


Я тоже не понял. Я её и выделил в тестовом файлике. Может быть, это как раз из-за численной природы вычисления...
Wrote

Меня смутило также использование StringBuilder в качестве типа параметра для работы с нативными функциями из библиотеки CoolProp.dll.


Я не силён в .NET. Посмотрел здесь: https://msdn.microsoft.com/en-us/library/e8w969hb.aspx
Там третья строка - "By reference. Passes strings as In/Out parameters using StringBuilder."
И оттуда - пример: https://msdn.microsoft.com/en-us/library/x3txb6xc.aspx
В GetSystemDirectory передаются буфер и его размер - как раз мой случай. Я не стал с низкоуровневыми структурами мудрить, раз этот класс специально для этого реализован. Если бы пришлось туда передавать структуру - тогда да, тогда IntPtr, Marshal.AllocHGlobal() etc... Или у варианта со StringBuilder есть проблемы?
С уважением, Михаил Каганский
#6 Posted: 2/13/2015 6:16:15 PM
Mike Kaganski

Mike Kaganski

184 likes in 434 posts.

Group: User

Вячеслав, скажи, а можно ли код

var arg1 = SMath.Math.Decision.Preprocessing(arg, ref context);
return SMath.Math.Numeric.Expression.Calculate(arg1, context).obj as SMath.Math.Numeric.TDouble;


сократить до

return SMath.Math.Decision.NumericCalculation(arg, ref context).obj as SMath.Math.Numeric.TDouble;

В смысле, это одно и то же, или есть побочные эффекты/разница в результате? Я вроде не вижу разницы в результатах, но кто его знает, может, при определённых исходных...
С уважением, Михаил Каганский
#7 Posted: 2/14/2015 3:09:31 AM
уни

уни

156 likes in 355 posts.

Group: User

Честно говоря, не знаю тонкостей NumericCalculation(). Подсказать не могу. Тут нужно у Андрея спросить. Вообще, рекомендую скачать с рутрекера Resharper 9.0, это специальный плагин для студии, который позволяет упрощать конструкции (он делает подсказки в коде, кроме прочего). Он может убрать рудиментарный код, т.е. тот, который писать не обязательно. Всё делается автоматически.

По поводу использования того или иного метода. Я иногда, если не знаю что делает метод и как им пользоваться, делаю поиск по всем исходникам плагинов (они у меня закачаны из репозитория). Посмотрев на примеры можно сделать определённые выводы.
Россия навсегда! Вячеслав Мезенцев
  • New Posts New Posts
  • No New Posts No New Posts