Проблемы при назначении единиц измерения результату функции плагина и возврате строки - Сообщения
Описал проблему в английской ветке. По предложению Вячеслава дублирую тему здесь.
При возврате из плагина результата в виде 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\" (должна существовать).
Заранее спасибо за подсказки и указание направления!
Может потребовать пояснение использование метода 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 КиБ) скачан 99 раз(а).
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#, несмотря на то, что лично мне этот язык не нравится. Но для данной платформы это - то, что нужно.
Использовать их обёртку не хочу - я в ней не разобрался, пусть сами используют, я просто сделал для них старт. Она какая-то слишком избыточная. Мне кажется, тут всё проще, логика каждой функции чётко прописана в своём классе.
Что касается IPluginLowLevelEvaluation. При всех его недочётах я практически во всех своих дополнениях использовал именно этот обработчик, даже в чисто численных (MathcadFileAccess). Можно взять любой мой плагин и посмотреть как работать в IPluginLowLevelEvaluation. В MathcadFileAccess я тоже сначала решил, что с IPluginMathNumericEvaluation будет проще, но потом весь код переделал под IPluginLowLevelEvaluation. Этот обработчик даёт большую степень свободы для программиста.
Твой код (давай на ты) как-то неправильно работает со строками. Функция, которая возвращает версию вываливается с ошибкой. Честно говоря, я так и не понял пока почему.
Меня смутило также использование StringBuilder в качестве типа параметра для работы с нативными функциями из библиотеки CoolProp.dll. Обычно для этого используются более примитивные классы. Я потому и рекомендовал использовать их вариант обёртки, т.к. у них эта переходная часть должна была быть записана правильно.
ЦитатаИспользовать их обёртку не хочу - я в ней не разобрался, пусть сами используют, я просто сделал для них старт.
Они о "старте" мыслят немного по-другому. Вот ссылка на консольный проект, который я собрал для пробы их обёртки: CoolPropConsole-x32.zip. В проекте есть файл CoolProp.dll - это специальная 32-разрядная сборка библиотеки для использования с обёрткой (насколько я понял). Этот файл можно скачать с sf.net, там есть папка CoolProp\Csharp\Windows32. Исходники из platform-independent.7z нужно положить в отдельную папку проекта. Далее я просто скопировал содержимое Example.cs в свой проект. Всё достаточно просто.
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 есть проблемы?
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;
В смысле, это одно и то же, или есть побочные эффекты/разница в результате? Я вроде не вижу разницы в результатах, но кто его знает, может, при определённых исходных...
По поводу использования того или иного метода. Я иногда, если не знаю что делает метод и как им пользоваться, делаю поиск по всем исходникам плагинов (они у меня закачаны из репозитория). Посмотрев на примеры можно сделать определённые выводы.
-
Новые сообщения
-
Нет новых сообщений