====Creating a Model in Python====
===Building the Basics===
First, create a new blank Python file. You can name it anything you like, but it should end with the **.py** extension if you're running on a Windows machine.
import ardimodel
host = ardimodel.ModelHost()
# Add Models Here
host.ardiurl = "localhost"
host.ardisite="default"
host.Run()
The code above will put in the framework we need. It...
* Imports the ardimodel Python module
* Creates a 'host' object to hold all of our models.
* Is given the ARDI server hostname and site name.
* Is told to start running
===Define the Model===
We're going to create a calculation that figures out **how many minutes of water we have left in the tank**.
Each model is created by its own Python function. This function is called with one parameter - an empty model that you can add [[data points|data points]] to. So let's create a function called //TankModel//.
def TankModel(mod):
pass
Once we've created the model function, we can add it to the [[host]] object we created earlier, at the bottom of our code.
# Add Models Here
host.Add(ardimode.Create("Tank",TankModel))
This defines our (currently empty) model, and adds that model to our running system with the name 'Tank'.
===Filling in the Inputs===
Currently, we have a **flow rate** from our flow sensor, and a 0-100% gauge of **tank level**.
These are out [[input|inputs]].
Inputs are created with the [[AddInput]] function of the model class.
Outflow = mod.AddInput("Outgoing Flow",10)
LevelPerc = mod.AddInput("Tank Level Perc",50)
This creates two new //input// data points. At the moment they aren't connected to anything - they'll be created with the given default values (10 for flow, 50% for tank level) and remain that way until changed.
===Creating a Constant===
In this case, we are trying to figure out how much time is left before the tank is empty. To do that, we'll need to know **how much water the tank is currently holding**.
We need to take the //level// of the tank and turn that into a //volume//. Assuming that this is a regularly-shaped, vertical tank, we'd just need to know the total capacity of the tank.
tank volume = tank_capacity * tank_level
That capacity is a value that won't change unless they make physical changes to the tank, so we can store it as a [[constant|constant]].
Constants are created with the [[AddConstant]] function of the model class.
Capacity = mod.AddConstant("Tank Max Volume",12000)
This defines a constant saying that the maximum capacity of the tank is //12000 litres//.
===Creating Calculations and Outputs===
Outputs are created with the [[AddOutput]] function of the model class and calculations with [[AddCalc]].
In this case, the value of exactly how much is left in the tank is probably useful, so we will [[published|publish]] it by having it as an output of our model too. If we didn't think it was very interesting or important, we could make the tank volume a //calculation// rather than an //output//.
Outputs and calculations are a little more difficult to create than inputs. They need a name (just like other data points), but they also need a //function// and a //list of used points//.
==Function==
The //function// is a Python function that calculates a value. As well as containing numbers, it can include any inputs, constants, calculations and other outputs you wish.
For example, the function here will be...
(LevelPerc.num() / 100) * Capacity.num()
//Note that the 'num' function converts a DataPoint to a floating-point number. Some points will be strings, so it's important to always cast the value to a number if you're using it as one.//.
==Used Points==
This is a list of points that are used in the function. So in the example above, you'd include...
[LevelPerc,Capacity]
This tells the system to automatically update the value of our calculation/output when changes occur in the level percentage or capacity.
==Our First Output==
Volume = mod.AddOutput("Tank Volume", lambda: (LevelPerc.num() / 100) * Capacity.num(), [LevelPerc,Capacity])
And that's our first output created. We now have a calculated tank volume.
Now, to calculate the time-to-empty.
===Outputs based on Outputs===
Outputs work just like other data points, so you can use them in output formulas.
mod.AddOutput("Time-To-Empty", lambda: Volume.num() / Outflow.num(), [Outflow,Volume])
An interesting thing to note here is that the 'used points' contains only those points that are being //directly used by the equation//. When an update to tank level arrives, the volume will be re-calculated, and the volume being re-calculated will trigger the Time-To-Empty to be re-calculated too.
===Our Simple Model===
import ardimodel
host = ardimodel.ModelHost()
def TankModel(mod):
Outflow = mod.AddInput("Outgoing Flow",10)
LevelPerc = mod.AddInput("Tank Level Perc",50)
Capacity = mod.AddConstant("Tank Max Volume",12000)
Volume = mod.AddOutput("Tank Volume", lambda: (LevelPerc.num() / 100) * Capacity.num(), [LevelPerc,Capacity])
mod.AddOutput("Time-To-Empty", lambda: Volume.num() / Outflow.num(), [Outflow,Volume])
# Add Models Here
host.Add(ardimodel.Create("Tank",TankModel))
host.ardiurl = "localhost"
host.ardisite="default"
host.Run()
We can now run the Python script and the model will be available through OPC-UA.
===Results===
{{ :model:modeloutput_basic.png?400|}}
.On the right you'll see an example of the model in [[OPC-UA]].
You can drag any of the points from the tree on the left into the //Data Access View// to get their current values.
Double-clicking on the value of any of the inputs or constants allows you to change them. The output values will immediately change to match those changes.
Note that if your inputs are connected to other data points (such as live data from ARDI), you would be able to change their values through OPC-UA.
===More Changes===
Let's try [[Fixing and Upgrading the Model]]