Category:Plugins

From Heureka Wiki
Jump to navigation Jump to search

It is possible to replace some of Heureka's built-in functions with your own plugins. You need to program such functions in C#. When you have built a plugin-function, you can place it in My Dcouments > Heureka > Common > Plugins-folder. After that, when you start a Heureka application (StandWise, PlanWise or RegWise), the program will scan this folder for functions that implements a certain so called interface. For example, if there is a plugin (dll) that implements the cost function interface, it will be imported to the application in run-time, and be available as an option the control tables interface.

Plugins are available for the following models:

  • Single-tree growth
  • Stand-level growth
  • Sapling growth
  • Tree volume
  • Tree height
  • Tree height growth
  • Bark thickness
  • Mortality
  • Age to breast height
  • Forwarder and harvester cost (time consumption)
  • Optimization solvers

Writing plugins for Heureka

The easiest way to start writing a plugin is to use a template that we provide as a starting point PluginTemplateProject.zip. Either open the included project in Visual Studio 2015 or later, or start a new project and include only the required templates. All templates need a reference to 'Slu.Heureka.BaseLayer.dll' and to 'Slu.Heureka.DomainLayer.dll'. This library can be found with all installations of Heureka in the root of the installation directory.

Comments in the templates will tell you what Heureka expects as a return value from the functions. If a plugin needs to read settings that the user has made to, for example, the ProductionModel control table, Heureka will supply this to the plugin constructor if it finds a matching constructor that accepts a single ProductionModelControlTable as an argument. Otherwise the parameterless constructor will be used. At least one of these two constructors MUST be available.

If you want to supply custom arguments to the time consumption model, you can use the property CustomFunctionParameters, which is similar to a C# dictionary consisting of key-value pairs, see below.


IMPORTANT Plugins must have a unique Name string among plugins of the same type. Heureka uses the name to identify which plugin is currently selected.

After building the template project, the plugin .dll files are copied to the solution directory.

Custom cost functions

Creating plugins for cost functions is done a bit differently. There is a plugin interface, but it is not really intended for implementing from scratch. Instead, you should either create a class that derives from *Slu.Heureka.DomainLayer.ValueModel.CostCalculatorSkogforskAdvanced, or

  • Slu.Heureka.DomainLayer.ValueModel.CostCalculatorSkogforskSimple, or from the abstract class
  • Slu.Heureka.DomainLayer.ValueModel.CostCalculatorBase (which the two above derive from).

Each of these implements IPlugin, so you do not have to worry about anything but overriding for example CostCalculatorSkogforskAdvanced properly. Below is a dummy dode example to calculate cost for a imagined cable logging system.

Code example for harvester and forwarder cost function

 
using System.Collections.Generic;
using Slu.Heureka;
using Slu.Heureka.BaseLayer;
using Slu.Heureka.BaseLayer.ConfigurationHandling;
using Slu.Heureka.DomainLayer.CommonForestModels;
using Slu.Heureka.DomainLayer.Forest;
using Slu.Heureka.DomainLayer.TreatmentModel;
using Slu.Heureka.DomainLayer.ValueModel;

namespace MyCostFunctions
{
    public class MyLoggingCostFunction : CostCalculatorSkogforskAdvanced
    {
        private double _area;
        private double _ayd;
        private GenericPropertyArray<double> _functionParameters = new GenericPropertyArray<double>("CableLoggingCoeffs",
            propertyLabel:new string[]
            {
                "A","B","SystemUtilization"
            },
            values:new double[] {0.25d, 3.0d, 0.9d},
            defaultValues:new double[] { 0.25d, 3.0d, 0.9d },
            descriptions:new string[] {"Param A (min/tree)",
                "Param B (min/m3)",
                "Utilization degree expressed as proportion"
            });

               /// <summary>
        /// Empty constructor required for IPlugin handler
        /// </summary>
        public MyLoggingCostFunction () { }

        public MyLoggingCostFunction (IControlCategory cc) : base(cc)
        {
        }

        public override string Name { get { return "MyLoggingCostFunction1"; } }

        public override GenericPropertyArray<double> CustomFunctionParameters
        {
            get { return _functionParameters; }
            set { _functionParameters = value; }
        }

        /// <summary>
        /// Initializes data needed by the models
        /// </summary>
        /// <param name="tUnit"></param>
        /// <param name="period"></param>
        /// <param name="treatment"></param>
        /// <param name="results"></param>
        /// <param name="value">Felling value</param>
        protected override void Initialize(TreatmentUnit tUnit, int period, Treatment treatment, IList<Result> results, FellingValue value)
        {
            base.Initialize(tUnit, period, treatment, results, value);
            int periodIndex = period == 0 ? 0 : period - tUnit.StartPeriod;
            object[] treatmentInfo = (object[])results[periodIndex].Treatment(typeof(TreatmentData));
            int index = FindFirstCuttingTreatment(treatmentInfo);
            CalculateBasicVariables((TreatmentData)treatmentInfo[index], value);
            
            if (Configuration.CustomHarvesterCostParameters != null &&
               _functionParameters.Label == Configuration.CustomHarvesterCostParameters.Label)
            {
                _functionParameters = Configuration.CustomHarvesterCostParameters;  
            }

        }

        protected override double ForwarderCostFinalFelling()
        {
            return 0;
        }

        protected override double HarvesterCostFinalFelling()
        {
            // No cable logging required: use ordinaary function
            if (Slope < SlopeType.ThirtyThreeToFifty)
                return base.HarvesterCostFinalFelling();

            // Add some code for calulating the cost...
            // var cost = ... _functionParameters["B"]*AverageVolumeOfFelledTrees ...

            return _functionParameters["SystemUtilization"] > 0 ? cost / _functionParameters["SystemUtilization"] : cost;

        }

    }
}



Getting Heureka to load the plugins

Plugins are loaded from the current user's \Documents\Heureka\Common\Plugins directory. If successful, the plugin will show up in the drop-down box for that particular feature.

If a plugin failed to load, an error will show up in the 'General' output window with information of what went wrong.

This category currently contains no pages or media.