EcoSUI
Python Scripting
F. Birke - Schneider Electric - 2015
EcoSUI – Scripting Engine – Why ?
’
Benefits of a scripting engine :
“The compiled part of the system is optimized for speed of execution, but
is harder to modify (because it must be compiled and reloaded), whereas
the scripted part of the system is optimized for flexibility, it will run slower
but can be quickly and easily modified by anyone with a text editor.”
In EcoSUI, Scripting Engine lets you add
- Slow Automatisms
- Reporting Facilities
- Additional Slow Acquisition
- Additional Slow Scada Interface
-…
Schneider Electric 2
EcoSUI – Scripting Engine – How ?
’
EcoSUI is developed with .NET Framework. Iron Python is an open-source
implementation of the Python programming language which is tightly
integrated with .NET Framework
An additional package ‘IronPython-2.7.3.msi’ must be installed
Schneider Electric 3
EcoSUI – Scripting Engine – Where ?
’
Python Scripts can be used in
a- HMI, using action ‘EXECUTESCRIPT “PathToScript”’ (Script
is executed asynchronously. There is no return value)
b- SBUS Server, ‘Scripts’ Tab
c- In Response to a xPC from a Virtual Ring (DefaultActions.py)
Schneider Electric 4
EcoSUI – Scripting Engine – !! WARNING !!
’
Python Scripts are very powerful and therefore dangerous if used in an
inappropriate way.
- Scripts are not compiled and can be changed by anyone using a
Text Editor
- Scripts have access to all EcoSUI datapoints (xPS, xPC , MV)
- Scripts have access to all computer ressources
- Scripts have access to all network ressources
=> If you don’t need Python scripts, do not install Python
Schneider Electric 5
EcoSUI – Python Overview
Python source code is located in .py files
’
Python source code can be edited by any Text Editor (ie. Notepad++)
Schneider Electric 6
EcoSUI – Python Overview
!! WHITE SPACE MATTERS !!
’
YOUR CODE WILL NOT RUN CORRECTLY IF YOU USE IMPROPER
INDENTATION.
DON’T MIX WHITESPACE AND TABS FOR INDENTATION
Python is Case-Sensitive
Use # for Comments
Use ; for Multiple Statements on a Single Line
Integer, Float, Boolean,Complex Number, String, List, Dictionary, Tuple,
File, Set
Python is not strongly typed
Python does not require declaration of variables before their use
Schneider Electric 7
EcoSUI – Python Overview – Variables 1/3
BOOLEAN (bool)
’
•True
•False
NUMBERS
•Decimal integer and Lonf integer: 1234, 1234567890546378940L (or l)
•Binary integer: 0b10, 0B10, 0b10101010101010101010101010101010L (begins with a 0b or 0B)
•Octal integer: 0177, 0o177, 0O177, 0177777777777777777L (begins with a 0 , 0o, or 0O)
•Hex integer: 0xFF, 0XFFFFffffFFFFFFFFFFL (begins with 0x or 0X)
•Float (double precision): 3.14e-10, .001, 10., 1E3
•Complex: 1J, 2+3J, 4+5j (ends with J or j, + separates (float) real and imaginary parts)
Schneider Electric 8
EcoSUI – Python Overview – Variables 2/3
’
SEQUENCES
•Strings
'', '1', "12", 'hello\n'
•Tuples (type tuple) of length 0, 1, 2, etc: (cannot be changed after initialization ‘=immutable’)
() (1,) (1,2) # parentheses are optional if len > 0
•Lists (type list) of length 0, 1, 2, etc:
[] [1] [1,2]
Indexing is 0-based. Negative indices (usually) mean count backwards from end of sequence.
Sequence slicing [starting-at-index : but-less-than-index [ : step]]. Start defaults to 0, end to
len(sequence), step to 1. a = (0,1,2,3,4,5,6,7)
a[3] == 3
a[-1] == 7
a[2:4] == (2, 3)
a[1:] == (1, 2, 3, 4, 5, 6, 7)
a[:3] == (0, 1, 2)
a[:] == (0,1,2,3,4,5,6,7) # makes a copy of the sequence.
a[::2] == (0, 2, 4, 6) # Only even numbers.
a[::-1] = (7, 6, 5, 4, 3 , 2, 1, 0) # Reverse order.
Schneider Electric 9
EcoSUI – Python Overview – Variables 3/3
’
Dictionary
•dict = {'Name': 'Zara', 'Age': 7, 'Class': 'First'};
print "dict['Name']: ", dict['Name']; # Gives « dict['Name']: Zara »
print "dict['Age']: ", dict['Age']; # Gives « dict['Age']: 7 »
Schneider Electric 10
EcoSUI – Python Overview – Basic Operators
’
Operator Description Example
+ Addition Adds values on either side of the operator. a + b = 30
- Subtraction Subtracts right hand operand from left hand operand. a – b = -10
* Multiplication Multiplies values on either side of the operator a * b = 200
/ Division Divides left hand operand by right hand operand b/a=2
% Modulus Divides left hand operand by right hand operand and returns b%a=0
remainder
** Exponent Performs exponential (power) calculation on operators a**b =10 to the power 20
// Floor Division - The division of operands where the result is 9//2 = 4 and 9.0//2.0 = 4.0
the quotient in which the digits after the decimal point are
removed.
Schneider Electric 11
EcoSUI – Python Overview - Comparisons
’
Operator Description Example
== If the values of two operands are equal, then the condition becomes true. (a == b) is not true.
!= If values of two operands are not equal, then condition becomes true.
<> If values of two operands are not equal, then condition becomes true. (a <> b) is true. This is
similar to != operator.
> If the value of left operand is greater than the value of right operand, then (a > b) is not true.
condition becomes true.
< If the value of left operand is less than the value of right operand, then (a < b) is true.
condition becomes true.
>= If the value of left operand is greater than or equal to the value of right (a >= b) is not true.
operand, then condition becomes true.
<= If the value of left operand is less than or equal to the value of right (a <= b) is true.
operand, then condition becomes true.
Schneider Electric 12
EcoSUI – Python Overview - Assignment
Operator Description Example
= Assigns values from right side operands to left c = a + b assigns value of a + b into c
’
side operand
+= Add It adds right operand to the left operand and c += a is equivalent to c = c + a
assign the result to left operand
-= Subtract It subtracts right operand from the left operand c -= a is equivalent to c = c - a
and assign the result to left operand
*= Multiply It multiplies right operand with the left operand c *= a is equivalent to c = c * a
and assign the result to left operand
/= Divide It divides left operand with the right operand and c /= a is equivalent to c = c / ac /= a is
assign the result to left operand equivalent to c = c / a
%= Modulus It takes modulus using two operands and assign c %= a is equivalent to c = c % a
the result to left operand
**= Exponent Performs exponential (power) calculation on c **= a is equivalent to c = c ** a
operators and assign value to the left operand
//= Floor It performs floor division on operators and assign c //= a is equivalent to c = c // a
Division value to the left operand
Schneider Electric 13
EcoSUI – Python Overview – Bitwise Operators
With a=0011 1100 and b=0000 1101 ’
Operator Description Example
& Binary AND Operator copies a bit to the result if it exists in (a & b) (means 0000 1100)
both operands
| Binary OR It copies a bit if it exists in either operand. (a | b) = 61 (means 0011 1101)
^ Binary XOR It copies the bit if it is set in one operand but not (a ^ b) = 49 (means 0011 0001)
both.
~ Binary Ones It is unary and has the effect of 'flipping' bits. (~a ) = -61 (means 1100 0011 in 2's
Complement complement form due to a signed binary
number.
<< Binary Left The left operands value is moved left by the a << = 240 (means 1111 0000)
Shift number of bits specified by the right operand.
>> Binary Right The left operands value is moved right by the a >> = 15 (means 0000 1111)
Shift number of bits specified by the right operand.
Schneider Electric 14
EcoSUI – Python Overview – IF…ELIF…ELSE
Syntax: ’
if expression1:
statement(s)
elif expression2:
statement(s)
elif expression3:
statement(s)
else:
statement(s)
Ex:
if n == 0:
print("None")
elif n < 0:
print("Low")
else: # Handle values greater than zero.
if n > 100:
print("Out of range")
else:
print("High“)
Schneider Electric 15
EcoSUI – Python Overview – Loops
’
Syntax:
for iterating_var in sequence:
statements(s)
Ex:
fruits = ['banana', 'apple', 'mango']
for fruit in fruits:
print 'Current fruit :', fruit
Schneider Electric 16
EcoSUI – Python Overview – Loops
’
Syntax:
while expression:
statement(s)
Ex:
while (count < 9):
print 'The count is:', count
count = count + 1
Schneider Electric 17
EcoSUI – Script – Add a new script in SBUSServer
’
Edit C:\MCIS\Scripts\[ComputerName]\Scripts_SBUSServer.xml
<?xml version="1.0" encoding="utf-8"?>
<Scripts>
<Script filename="c:\mcis\scripts\scriptonchange.py" enabled="1" when="0" description="" />
<Script filename="c:\mcis\scripts\scriptcyclic.py" enabled="1" when="0" description="" />
<Script filename="c:\mcis\scripts\scriptgetmvarchives.py" enabled="0" when="0" description="" />
</Scripts>
Schneider Electric 18
EcoSUI – Special Python Functions
’
Logging Functions
Log message in DbgView and SBUS Server Scripts Tab
MCISLog(string message)
Ex:
MCISLog("Script Startup")
REMARK : Don’t forget to enable Script logs in DbgView (see
c:\Mcis\Debug\Debug.ini [Script] Log=1)
Schneider Electric 19
EcoSUI – Special Python Functions
’
EcoSUI Qualities (based on IEC61850)
0 – Valid
1 – Disconnected
2 – Invalid
3 – Forced
4 – Questionable
5 – Overflow
6 – OutOfRange
7 – BadReference
8 – Oscillatory
9 – Failure
10 – OldData
11 – Inconsistent
12 – Inaccurate
13 – Substituted
14 – Test
15 – Operator Blocked
Schneider Electric 20
EcoSUI – Special Python Functions
’
Variable Status Functions
Get the Current Value of a Datapoint
Res=MCISGetValue(string mnemonic)
Res[0] -> boolean, datapoint found
Res[1] -> double, value of datapoint
Res[2] -> integer, quality of datapoint
Res[3] -> datetime, last update of datapoint
Ex:
Res = MCISGetValue("SUBSTATION / VL / BAY / Q0 / CB POSITION")
MCISLog("Value=" + str( int( Res[1] ) ) + ", Quality=" +
str(Res[2]) + ", Found=" + str(Res[0]) ))
Schneider Electric 21
EcoSUI – Special Python Functions
’
Variable Attributes Functions
Get Attribute of a Datapoint (See Attributes in signalList)
Res=MCISGetAttribute(string mnemonic, int attributeIdx)
Res -> string, attribute of datapoint
Ex:
Res = MCISGetAttribute("SUBSTATION / VL / BAY / Q0 / CB POSITION",5)
MCISLog("Attribute 5 =" + Res)
Schneider Electric 22
EcoSUI – Special Python Functions
’
Variable Acquisition Functions 1/2
Set Value of a Datapoint
MCISSetValue(mnemonic, double value, int quality)
Ex:
MCISSetValue("SUBSTATION / VL / BAY / VOLTAGE",110,0)
Schneider Electric 23
EcoSUI – Special Python Functions
’
Variable Acquisition Functions 2/2
Force Quality of a Datapoint
MCISForceQuality(string mnemonic, int quality)
Ex:
MCISForceQuality("SUBSTATION / VL / BAY / CURRENT",5)
Unforce Quality of a Datapoint
MCISUnforceQuality(string mnemonic)
Ex:
MCISUnforceQuality("SUBSTATION / VL / BAY / CURRENT")
Schneider Electric 24
EcoSUI – Special Python Functions
’
Command Functions 1/2
Send Select Command
MCISSendSelectCommand(string mnemonic, bool value)
Ex:
MCISSendSelectCommand("SUBSTATION / VL / BAY / Q0 /
CONTROL",true)
Send Execute Command
MCISSendDECommand(string mnemonic, bool value)
Ex:
MCISSendDECommand("SUBSTATION / VL / BAY / Q0 /
CONTROL",true)
Schneider Electric 25
EcoSUI – Special Python Functions
’
Command Functions 2/2
Send SetPoint
MCISSendSetPoint(string mnemonic, double value)
Ex:
MCISSendSetPoint("SUBSTATION / VL / BAY / TAP",15)
Schneider Electric 26
EcoSUI – Special Python Functions
’
Command Functions 2/2
Send FreeCommand (Modbus only)
MCISSendFreeCommand(string eqptName, byte typeCmd, int dataAddr, int
uniqueID, byte[] freemCmd)
Schneider Electric 27
EcoSUI – Special Python Functions - Callbacks
’
MCISContinueAfterExit()
MCISRegisterSignal(mnemonic, callbackFunctionName)
MCISRegisterTime_Cyclically(seconds, callbackFunctionName)
MCISRegisterTime_EveryMinute(second, callbackFunctionName)
MCISRegisterTime_EveryHour(minute,second,callbackFunctionName)
MCISRegisterTime_EveryDay(hour,minute,second,callbackFunctionName)
MCISRegisterTime_EveryWeek(dayOfWeek,hour,minute,second,callbackFuntionName)
MCISRegisterTime_EveryMonth(day,hour,minute,second,callbackFunctionName)
Schneider Electric 28
EcoSUI – Special Python Functions - Callbacks
’
Callbacks are used when you want EcoSUI to call a function of your
scripts when
- A specific time/date or duration has been reached
- Value of a Datapoint has been updated
To tell EcoSUI when to call back your script, you will use functions to
register datapoint, cycle/duration.
EcoSUI, please call
EcoSUI
EcoSUI Python Execute
myFunction every
Scripting Engine myFunction
minute
Every Minute
Schneider Electric 29
EcoSUI – Special Python Functions – On Change
’
Ex:
def Main():
MCISLog("Start of ScriptOnChange")
RegisterSignals()
MCISContinueAfterExit(True)# Mandatory when using Registered signals or time.
def RegisterSignals():
MCISLog("Registering Signals")
MCISRegisterSignal("STATION / SEC1 / OUTCOMER1 / CLOSE", "OnEvent" ) # When
"STATION / SEC1 / OUTCOMER1 / CLOSE" is updated, OnEvent function is called
MCISRegisterSignal("STATION / SEC1 / OUTCOMER2 / CLOSE", "OnEvent" ) # When
"STATION / SEC1 / OUTCOMER2 / CLOSE" is updated, OnEvent function is called
def OnEvent(mnemonic):
MCISLog("" + mnemonic + " has been updated")
DoMyWork()
def DoMyWork():
MCISLog("DoMyWork when a signal is updated")
Main()
Schneider Electric 30
EcoSUI – Special Python Functions – On Change
Two different way of coding – Loop or OnChange (Callback) ’
Loop (Unefficient)
import time
while 1:
debarras=MCISGetValue('Débarras Haut / Volet Roulant / Switch')
sejour=MCISGetValue('Séjour / Volet Roulant / Switch')
if (debarras[1]!=sejour[1]) & (debarras[2]==0) & (sejour[2]==0):
if sejour[1]==0:
MCISSendDECommand('Débarras Haut / Volet Roulant / Descendre',1)
else:
MCISSendDECommand('Débarras Haut / Volet Roulant / Monter',1)
time.sleep(30)
Schneider Electric 31
EcoSUI – Special Python Functions – On Change
Two different way of coding – Loop or OnChange (Callback) ’
OnChange using Callback (efficient)
def Main():
MCISLog("Start of ScriptOnChange")
RegisterSignals()
MCISContinueAfterExit(True) # Mandatory when using Registered signals or time.
def RegisterSignals():
MCISLog("Registering Signals")
MCISRegisterSignal('Débarras Haut / Volet Roulant / Switch', "OnEvent" )
MCISRegisterSignal('Séjour / Volet Roulant / Switch', "OnEvent" )
def OnEvent(mnemonic):
MCISLog("" + mnemonic + " has been updated")
debarras=MCISGetValue('Débarras Haut / Volet Roulant / Switch')
sejour=MCISGetValue('Séjour / Volet Roulant / Switch')
if (debarras[1]!=sejour[1]) & (debarras[2]==0) & (sejour[2]==0):
if sejour[1]==0:
MCISSendDECommand('Débarras Haut / Volet Roulant / Descendre',1)
else:
MCISSendDECommand('Débarras Haut / Volet Roulant / Monter',1)
Main()
Schneider Electric 32
EcoSUI – Special Python Functions - Archives
MCISGetMVArchives(mnemonic, startdate, enddate) ’
Ex:
def Main():
MCISLog("Start of MCISGetMVArchives")
res=MCISGetMVArchives("STATION / SEC1 / MEASURE","2015/01/20","2015/01/24")
v=res[0]
t=res[1]
q=res[2]
for i in range(0,len(v)-1):
MCISLog(str(i) + ' v=' + str(v[i]) + ', t=' + str(t[i]) + ', q=' +
str(q[i]))
Main()
Schneider Electric 33
EcoSUI – Interfaces with a DLL 1/2
Use of cdll (from ctypes) to Load Libraries
’
Ex:
from ctypes import *
def Main():
os.chdir( "C:\\MCIS\\Scripts" )
global TopoDll
TopoDll = cdll.LoadLibrary( "C:\\MCIS\\Scripts\\Topology.dll" )
TopoDll.Topology_StartDebug()
DllVersion = TopoDll.Topology_Init()
if DllVersion == -1:
MCISLog( "Main: INIT TOPOLOGY, *** ERROR: Run time exception in Topology_Init ***" )
else:
MCISLog( "Main: INIT TOPOLOGY, Topology.dll version is %.3f" %(float(DllVersion)/1000 ))
RefreshTopo()
AssignTopoEvents()
MCISLog( "Main: Waiting for values" )
MCISContinueAfterExit(True)
#OnChangeLoop()
return
In that example, TopoDll contains all functions exposed by
Topology.dll
Schneider Electric 34
EcoSUI – Interfaces with a DLL 2/2
Use of create_string_buffer to create a string, byref() to get pointer ’
Ex:
def PopActions():
MCISLog( "PopActions:" )
Ptr = create_string_buffer( 256 )
Value = c_int(0)
while TopoDll.Topology_GetAction( Ptr, byref( Value ) ):
StoreVoltageValue( str(Ptr.value)[1:], Value.value )
return
Schneider Electric 35
EcoSUI – Interface with .NET functions
In py code, you can use any .NET function (Network/WEB, File, ’
Computer, Mathematics, Registry, Windows Forms, …)
Ex:
import clr
clr.AddReference("System.Windows.Forms")
from System.Windows.Forms import Application, Form
class HelloWorldForm(Form):
def __init__(self):
self.Text = 'Hello World'
self.Name = 'Hello World'
form = HelloWorldForm()
Application.Run(form)
Schneider Electric 36
EcoSUI – Virtual Ring
’
Automatic Feedback
When two signals (xPS/xPC or MV/SetPoint) have same names, there
is an automatic feedback.
-> If we send a command on xPC, associated xPS will get the new
value
-> If we send a setpoint, associated MV will get the new value
Schneider Electric 37
EcoSUI – Virtual Ring
’
Initial Values
If Ring.ini [InitialValues] LoadInitialValuesFromLastValues is 1,
values are stored in file. At next startup, values are reloaded from
this file
If this virtual ring is redundant, initial values can also be read from
other computer (newest value is read)
Schneider Electric 38
EcoSUI – Virtual Ring
’
Python Script
Each time a command/setpoint is sent to a datapoint belonging to a
virtual ring, DefaultActions.py is started.
command variable describes the type (command or setpoint)
commandvalue variable gives command value (0 or 1)
setpointvalue variable gives setpoint value
Ex:
if commandtype=='command':
MCISLog('Command '+str(commandvalue)+' on '+mnemonic)
elif commandtype=='setpoint':
MCISLog('SetPoint '+str(setpointvalue)+' on '+mnemonic)
Schneider Electric 39
EcoSUI – Exercises
’
- Create an operation counter. Each time a command is send to a
virtual DPC, a counter is increased
- Create a keepalive counter. Each minute, a counter is increased
Schneider Electric 40
End of Document