7.5 脚本生成控制策略
为解决测点多、控制策略简单重复、配置文件用户手动书写繁琐等问题,可采用脚本生成控制策略(包括测点文件、通信通道文件和AOE文件)。
控制策略的配置文件可写为CSV格式,即由字符串组成的文本文件,所以任何能语言、软件都能实现脚本生成控制策略。
本章节以python语言和Matlab脚本为例,介绍脚本生成控制策略的实现过程。
基于python实现脚本生成控制策略
python实现脚本生成控制策略,主要用到了open()
、write()
等函数,需要注意的是低代码控制器的控制策略配置文件为CSV格式时,需要设置编码为UTF-8
或UTF-8(BOM)
,因此open()
函数中加入参数encoding='utf-8-sig'
。
import math
ProcessNum = 1000;
PointNum = 5;
SET = 10;
TimeSet = 2000;
ModbusCycle = 2000;
ClientIP='192.168.1.130';
ServerIP='192.168.1.151';
# AOE配置文件脚本
f=open('aoe-py-'+str(ProcessNum)+'-'+str(PointNum)+'.csv','w',encoding='utf-8-sig');
f.write('AOE ID,是否启用,名称,触发条件,触发条件参数,变量初始值\n');
for k in range(1,ProcessNum+1):
f.write(str(70000+k)+',TRUE,压力测试'+str(k)+',Event_Drive,,SET='+str(SET)+';\n');
f.write(',,,,,\n');
f.write('AOE ID,变量定义,,,,\n');
f.write(',,,,,\n');
f.write(',,,,,\n');
f.write('AOE ID,节点ID,名称,节点类型,超时时间(ms),表达式\n');
for k in range(1,ProcessNum+1):
f.write(str(70000+k)+',1,超出阈值事件,Condition,10,D'+str((k-1)*PointNum+1)+' > SET || 0-D'+str((k-1)*PointNum+1)+' > SET\n');
f.write(str(70000+k)+',2,超过下阈值事件,Condition,10,0-D'+str((k-1)*PointNum+1)+' > SET\n');
f.write(str(70000+k)+',3,超过上阈值事件,Condition,10,D'+str((k-1)*PointNum+1)+' > SET\n');
f.write(str(70000+k)+',4,结束节点,Condition,0,1\n');
f.write(',,,,,\n');
f.write('AOE ID,首尾节点,动作名称,失败模式,动作类型,动作参数\n');
for k in range(1,ProcessNum+1):
f.write(str(70000+k)+',1;2,进入判断是否超过下阈值的节点,Default,NONE,\n');
f.write(str(70000+k)+',1;3,进入判断是否超过上阈值的节点,Default,NONE,\n');
f.write(str(70000+k)+',2;4,递增器,Default,Set_Points,I'+str(k)+': 1;\n');
f.write(str(70000+k)+',3;4,递减器,Default,Set_Points,I'+str(k)+': -1;\n');
f.close
f=open('aoe-2-py-'+str(ProcessNum)+'-'+str(PointNum)+'.csv','w',encoding='utf-8-sig');
f.write('AOE ID,是否启用,名称,触发条件,触发条件参数,变量初始值\n');
for k in range(1,ProcessNum+1):
f.write(str(80000+k)+',TRUE,压力测试slave'+str(k)+',Simple_Repeat,'+str(TimeSet)+',\n');
f.write(',,,,,\n');
f.write('AOE ID,变量定义,,,,\n');
f.write(',,,,,\n');
f.write(',,,,,\n');
f.write('AOE ID,节点ID,名称,节点类型,超时时间(ms),表达式\n');
for k in range(1,ProcessNum+1):
f.write(str(80000+k)+',1,方向判断,Switch,10,I'+str(k)+' == 1\n');
f.write(str(80000+k)+',2,结束节点,Condition,100,0\n');
f.write(str(80000+k)+',3,结束节点,Condition,100,0\n');
f.write(',,,,,\n');
f.write('AOE ID,首尾节点,动作名称,失败模式,动作类型,动作参数\n');
for k in range(1,ProcessNum+1):
f.write(str(80000+k)+',1;2,递增,Default,Set_Points,');
for kk in range(1,PointNum+1):
f.write('D'+str((k-1)*PointNum+kk)+':D'+str((k-1)*PointNum+kk)+'+1;');
f.write('\n');
f.write(str(80000+k)+',1;3,递减,Default,Set_Points,');
for kk in range(1,PointNum+1):
f.write('D'+str((k-1)*PointNum+kk)+':D'+str((k-1)*PointNum+kk)+'-1;');
f.write('\n');
f.close
# 测点配置文件脚本
f=open('points-py-'+str(ProcessNum)+'-'+str(PointNum)+'.csv','w',encoding='utf-8-sig');
f.write('序号,点号,名称,别名,是否离散,是否计算点,计算公式,变换公式,逆变换公式,变化公式,判零公式,单位,上限值,下限值,最大变化,最小变化,是否实时点,是否SOE,默认值,备注\n');
for k in range(1,ProcessNum*PointNum+1):
f.write(str(k)+','+str(100000+k)+',测点'+str(k)+',D'+str(k)+',TRUE,FALSE,,,,,,,9999,-9999,0,0,TRUE,FALSE,0,\n');
for k in range(1,ProcessNum+1):
f.write(str(k+ProcessNum*PointNum)+','+str(80000+k+ProcessNum*PointNum)+',测点'+str(k+ProcessNum*PointNum)+',I'+str(k)+',TRUE,FALSE,,,,,,,9999,-9999,0,0,FALSE,FALSE,0,\n');
f.close
# 通信通道配置文件脚本
if PointNum*ProcessNum < 12:
print('transport manu');
else:
AllNum = (PointNum+1)*ProcessNum;
f=open('tcp-mbd-transport-py-'+str(ProcessNum)+'-'+str(PointNum)+'.csv','w',encoding='utf-8-sig');
f.write('通道名称,server测试通道,,连接名称,测试通道1,序号,寄存器类型,起始地址,数据类型,新请求标志,轮询周期,点号\n');
f.write('连接个数,1,,测点个数,'+str(AllNum)+',1,HOLDING,1,EightByteFloat,FALSE,'+str(ModbusCycle)+',100001\n');
f.write('服务端口,502,,客户端IP,'+ClientIP+',2,HOLDING,5,EightByteFloat,FALSE,'+str(ModbusCycle)+',100002\n');
f.write(',,,客户端端口,9999,3,HOLDING,9,EightByteFloat,FALSE,'+str(ModbusCycle)+',100003\n');
f.write(',,,slave id,1,4,HOLDING,13,EightByteFloat,FALSE,'+str(ModbusCycle)+',100004\n');
f.write(',,,通信协议,XA,5,HOLDING,17,EightByteFloat,FALSE,'+str(ModbusCycle)+',100005\n');
f.write(',,,一次读寄存器数上限,250,6,HOLDING,21,EightByteFloat,FALSE,'+str(ModbusCycle)+',100006\n');
f.write(',,,一次读开关数上限,2000,7,HOLDING,25,EightByteFloat,FALSE,'+str(ModbusCycle)+',100007\n');
f.write(',,,一次写寄存器数上限,250,8,HOLDING,29,EightByteFloat,FALSE,'+str(ModbusCycle)+',100008\n');
f.write(',,,一次写开关数上限,1968,9,HOLDING,33,EightByteFloat,FALSE,'+str(ModbusCycle)+',100009\n');
f.write(',,,轮询周期(ms),5000,10,HOLDING,37,EightByteFloat,FALSE,'+str(ModbusCycle)+',100010\n');
f.write(',,,超时(ms),1000,11,HOLDING,41,EightByteFloat,FALSE,'+str(ModbusCycle)+',100011\n');
f.write(',,,通道状态测点号,,12,HOLDING,45,EightByteFloat,FALSE,'+str(ModbusCycle)+',100012\n');
for k in range(13,AllNum+1):
f.write(',,,,,'+str(k)+',HOLDING,'+str(4*k-3)+',EightByteFloat,FALSE,'+str(ModbusCycle)+','+str(100000+k)+'\n');
f.close;
f=open('xa-mbc-transport-py-'+str(ProcessNum)+'-'+str(PointNum)+'.csv','w',encoding='utf-8-sig');
f.write('通道名称,测试通道1,序号,寄存器类型,起始地址,数据类型,新请求标志,轮询周期,点号\n');
f.write('测点个数,'+str(AllNum)+',1,HOLDING,1,EightByteFloat,FALSE,'+str(ModbusCycle)+',100001\n');
f.write('服务端ip,'+ServerIP+',2,HOLDING,5,EightByteFloat,FALSE,'+str(ModbusCycle)+',100002\n');
f.write('服务端端口,502,3,HOLDING,9,EightByteFloat,FALSE,'+str(ModbusCycle)+',100003\n');
f.write('slave id,1,4,HOLDING,13,EightByteFloat,FALSE,'+str(ModbusCycle)+',100004\n');
f.write('通信协议,XA,5,HOLDING,17,EightByteFloat,FALSE,'+str(ModbusCycle)+',100005\n');
f.write('一次读寄存器数上限,250,6,HOLDING,21,EightByteFloat,FALSE,'+str(ModbusCycle)+',100006\n');
f.write('一次读开关数上限,2000,7,HOLDING,25,EightByteFloat,FALSE,'+str(ModbusCycle)+',100007\n');
f.write('一次写寄存器数上限,2500,8,HOLDING,29,EightByteFloat,FALSE,'+str(ModbusCycle)+',100008\n');
f.write('一次写开关数上限,1968,9,HOLDING,33,EightByteFloat,FALSE,'+str(ModbusCycle)+',100009\n');
f.write('轮询周期(ms),5000,10,HOLDING,37,EightByteFloat,FALSE,'+str(ModbusCycle)+',100010\n');
f.write('超时(ms),1000,11,HOLDING,41,EightByteFloat,FALSE,'+str(ModbusCycle)+',100011\n');
for k in range(12,AllNum+1):
f.write(',,'+str(k)+',HOLDING,'+str(4*k-3)+',EightByteFloat,FALSE,'+str(ModbusCycle)+','+str(100000+k)+'\n');
f.close;
基于Mablab实现脚本生成控制策略
Matlab实现脚本生成控制策略,主要用到了fopen()
、fprintf()
等函数,需要注意的是低代码控制器的控制策略配置文件为CSV格式时,需要设置编码为UTF-8
或UTF-8(BOM)
。
下面以交流潮流为例展示基于Mablab实现脚本生成控制策略,Matpower提供的潮流计算案例由于节点较多,手动配置会非常繁琐,因此需要脚本生成控制策略。
具体脚本如下所示:
%数据读入
[dfile,pathname]=uigetfile('*.m','Select Data File');
if pathname == 0
error(' you must select a valid data file')
else
data=loadcase(dfile);
end
%数据预处理
[baseMVA, bus, gen, branch] = deal(data.baseMVA, data.bus, data.gen, data.branch);
ref = find(bus(:, 2) == 3);%平衡节点序号
nb = size(bus, 1);
ng = size(gen, 1);
%matpower计算对比输出
result = runpf(data);
solution = [result.bus(:,8);result.bus(:,9)*pi/180;result.gen(:,3)/baseMVA;result.gen(find(result.gen(:,1)==ref,1),2)/baseMVA];
equation_filname= strcat(dfile(1:length(dfile)-2),'_pf_result','.txt');
testf1=fopen(equation_filname,'w+','n','UTF-8');
fprintf(testf1, '%.8f\n',solution);
%创建直流潮流计算方程文件
equation_filname= strcat(dfile(1:length(dfile)-2),'_pf_polar','.txt');
testf2=fopen(equation_filname,'w+','n','UTF-8');
PFequation(data,testf2);
%创建测点文件
point_filname= strcat('points-',dfile(1:length(dfile)-2),'-pf.csv');
testf3=fopen(point_filname,'w+','n','UTF-8');
fprintf(testf3, '序号,点号,名称,别名,是否离散,是否计算点,计算公式,变换公式,逆变换公式,变化公式,判零公式,单位,上限值,下限值,最大变化,最小变化,是否实时点,是否SOE,默认值,备注\n');
fprintf(testf3,'1,100001,测点1,DoCal_POINT,TRUE,FALSE,,,,,,,9999,-9999,0,0,FALSE,FALSE,0,\n');
for k=1:nb
fprintf(testf3,'%s,%s,测点%s,v%s,FALSE,FALSE,,,,,,,99999,-99999,0,0,FALSE,FALSE,1,\n',int2str(k+1),int2str(k+100001),int2str(k+1),int2str(k));
end
for k=1:nb
fprintf(testf3,'%s,%s,测点%s,theta%s,FALSE,FALSE,,,,,,,99999,-99999,0,0,FALSE,FALSE,0,\n',int2str(k+1+nb),int2str(k+100001+nb),int2str(k+1+nb),int2str(k));
end
for k=1:ng
fprintf(testf3,'%s,%s,测点%s,qg%s,FALSE,FALSE,,,,,,,99999,-99999,0,0,FALSE,FALSE,0,\n',int2str(k+1+2*nb),int2str(k+100001+2*nb),int2str(k+1+2*nb),int2str(k));
end
fprintf(testf3,'%s,%s,测点%s,pg_balancenode,FALSE,FALSE,,,,,,,99999,-99999,0,0,FALSE,FALSE,0,\n',int2str(2+2*nb+ng),int2str(100002+2*nb+ng),int2str(2+2*nb+ng));
%创建AOE文件
AOE_filname= strcat('aoe-matlab-',dfile(1:length(dfile)-2),'-pf.csv');
testf4=fopen(AOE_filname,'w+','n','UTF-8');
fprintf(testf4,'AOE ID,是否启用,名称,触发条件,触发条件参数,变量初始值,,\n');
BL='';%变量初始值
for k=1:nb
BL=strcat(BL,'V',int2str(k),':1;');
end
for k=1:nb
BL=strcat(BL,'THETA',int2str(k),':0;');
end
for k=1:ng
BL=strcat(BL,'QG',int2str(k),':0;');
end
BL=strcat(BL,'PG_balancenode:0');
fprintf(testf4,'70001,TRUE,交流潮流计算,Event_Drive,,%s,,\n',BL);
fprintf(testf4,',,,,,,,\n');
fprintf(testf4,'AOE ID,变量定义,,,,,,\n');
fprintf(testf4,',,,,,,,\n');
fprintf(testf4,',,,,,,,\n');
fprintf(testf4,'AOE ID,节点ID,名称,节点类型,超时时间(ms),表达式,,\n');
fprintf(testf4,'70001,1,首节点,Condition,10,DoCal_POINT > 0.5,,\n');
fprintf(testf4,'70001,2,计算节点,Condition,10,1,,\n');
fprintf(testf4,'70001,3,赋值,Condition,10,1,,\n');
fprintf(testf4,'70001,4,结束节点,Condition,1000,DoCal_POINT==0,,\n');
fprintf(testf4,',,,,,,,\n');
fprintf(testf4,'AOE ID,首尾节点,动作名称,失败模式,动作类型,动作参数,,\n');
fprintf(testf4,'70001,1;2,进入交流潮流计算,Default,Set_Points,DoCal_POINT:0;,,\n');
fprintf(testf4,'70001,2;3,计算交流潮流计算,Default,NLSOLVE,');
fprintf(testf4,'"');
PFequation(data,testf4);
fprintf(testf4,'",,\n');
fprintf(testf4,'70001,3;4,赋值,Default,Set_Points,,');
fprintf(testf4,'"');
for k=1:nb
fprintf(testf4,'v%s:V%s;\n',int2str(k),int2str(k));
end
for k=1:nb
fprintf(testf4,'theta%s:THETA%s;\n',int2str(k),int2str(k));
end
for k=1:ng
fprintf(testf4,'qg%s:QG%s;\n',int2str(k),int2str(k));
end
fprintf(testf4,'pg_balancenode:PG_balancenode",\n');