Commit 4547d1ea authored by Scott Sun's avatar Scott Sun

Merge branch 'master' of http://gitlab.topibd.net/scott.sun/scott

parents afa89f64 77aea4bb
......@@ -7,7 +7,7 @@ var items = {
{
name: "num1",
type: "LineEdit",
pack: { label: "参数1", column_span: 1, label_alignment:"Left" },
pack: { label: "参数1", column_span: 1},
validate: function(obj,val,title,moment,self){
if(moment === "EDIT"){
// return [title+'必须小于10','Error']
......@@ -68,7 +68,7 @@ var items = {
property: {
text: "计算"
},
pack: { column_span: 1, label_alignment:"Left" },
pack: { column_span: 3, layout_type: 1 },
callback: function(obj, check, self) {
var tempData = this.getAllValues();
var num1 = tempData.num1
......@@ -117,7 +117,7 @@ var items = {
{
name: "res",
type: "label",
pack: { label: "结果", column_span: 2, label_alignment:"Left" },
pack: { label: "结果", column_span: 3, label_alignment:"Left" },
},
{
type: 'stretch'
......
// D:\work\TopDFM_JNS\qt5.6.3-win32-msvc2015\topcam\topjs\3.4.0\bin\topjs3.exe
// 从tl解密脚本
var fs = require('fs');
var crypto = require('topsin.crypto');
var files = fs.listDir("./source")
files.forEach(function (file) {
var baseName = file.baseName; //
var path = file.path; //
var content = fs.readFile(path)
var encrypt = 2;
if(content && content != "") {
var retC = ""
if (encrypt == 1) {
retC = crypto.e(content);
}
else if (encrypt == 2) {
retC = crypto.e2(content);
}
else if (encrypt == 3) {
retC = crypto.e3(content);
}
if(!fs.exists("./dist")){
fs.mkdir("./dist")
}
fs.writeFile("./dist/" + baseName + ".TL2", retC)
}
})
// D:\work\TopDFM_JNS\qt5.6.3-win32-msvc2015\topcam\topjs\3.4.0\bin\topjs3.exe
// 从数据库解密脚本
var fs = require('fs');
var db = require("topsin.database");
var crypto = require('topsin.crypto');
var script_name = "JNS_ss_font_fill" // 脚本名称
db.addConnection({ // 数据库信息
databse_type:db.DbType.pg,
database_host:'222.191.240.214:5432', // 139.196.104.13:5433
database_name:'TOPDFM_JNS_V6',
database_user:'toplinker',
database_pwd:'TopLinker0510'
});
// 从数据库获取脚本
var ret = db.query("", function (d) {
return d.selectMap({
table:'pdm_workprocess_script',
field: ['id', 'md5', 'type', 'encrypt', 'content'], // , 'content'
where: { name: script_name, status: 'released' }
})
});
if(ret && ret.content) {
var content = ""
if (ret.encrypt == 1) {
content = crypto.d(ret.content);
}
else if (ret.encrypt == 2) {
content = crypto.d2(ret.content);
}
else if (ret.encrypt == 3) {
content = crypto.d3(ret.content);
}
if(!fs.exists("./src")){
fs.mkdir("./src")
}
fs.writeFile("./src/" + script_name + "." + (ret.type=="javascript"?"js":"pl"), content)
}
// D:\work\TopDFM_JNS\qt5.6.3-win32-msvc2015\topcam\topjs\3.4.0\bin\topjs3.exe
// 从tl解密脚本
var fs = require('fs');
var crypto = require('topsin.crypto');
var files = fs.listDir("./tl")
files.forEach(function (file) {
var baseName = file.baseName; //
var suffix = file.suffix; //
var path = file.path; //
if(/^tl\d?$/i.test(suffix)) {
var content = fs.readFile(path)
var encrypt = 1;
if(/2/.test(suffix)){encrypt = 2}
else if (/3/.test(suffix)) {encrypt = 3}
if(content && content != "") {
var retC = ""
if (encrypt == 1) {
retC = crypto.d(content);
}
else if (encrypt == 2) {
retC = crypto.d2(content);
}
else if (encrypt == 3) {
retC = crypto.d3(content);
}
if(!fs.exists("./src")){
fs.mkdir("./src")
}
fs.writeFile("./src/" + baseName, retC)
}
}
})
=head
NAME:
DESCRIPTION:
PARAMETER:
[
]
VERSION_HISTORY:
V1.00 2013-2-21 Cody Yu
1.新版本
V1.01 2020-7-15 许仕斌
1.内外层层名增加t/b标识
V1.02 2020-8-6 许仕斌
1.增加了根据叠构信息自动排列T/B标识
V1.03 2022-2-28 fsy
1.假层下一层带入时增加屏蔽条件
HELP:
<html><body bgcolor="#DDECFE">
<font size="3" color="#003DB2"><p>功能简介</p></font>
<p> 根据预审指示工具类型更改层名 </p>
<br>
<font size="3" color="#003DB2"><p>参数配置</p></font>
<font color="#008000"><p> ● </p></font>
<p> </p>
<font color="#008000"><p> ● </p></font>
<p> </p>
<font size="3" color="#003DB2"><p>注意事项</p></font>
<p> ● 无 </p>
<br>
</body></html>
=cut
#use strict;
#my ($JOB,$GEN,$GUI,$DB,$JOB_ID,$PAR);
use utf8;
use JSON;
use Data::Dump 'dump';
my ($Job,$Step) = ($JOB,undef);
my $Return = 'finish';
my @Report;
my (%stack_rows,$n,$y);
try {
my $json = JSON->new->allow_nonref;
###检查Genesis料号是否存在并打开
if (! $GEN->isJobExists(job=>$Job) ){
$GUI->msgbox(-icon=>'error',-text=>"料号 $Job 在Genesis中不存在, 请检查!");
return 'Error';
}
$GEN->openJob(job=>$Job) unless ($GEN->isJobOpen(job=>$Job));
my %matrix = $GEN->getMatrix(job=>$Job,type=>'hash');
my $inn_film_type = $IKM->get_jobinfo(-jobname=>$JOB,-jobcategory=>'work',-jobinfo=>'JNS_inn_tooling_type');
my $out_film_type = $IKM->get_jobinfo(-jobname=>$JOB,-jobcategory=>'work',-jobinfo=>'JNS_out_tooling_type');
my $silk_type = $IKM->get_jobinfo(-jobname=>$JOB,-jobcategory=>'work',-jobinfo=>'JNS_ss_procss_type');
my $layer_count = $GEN->getLayerCount(job=>$Job);
my $lamination_method = $IKM->get_jobinfo(-jobname=>$JOB,-jobcategory=>'work',-jobinfo=>'JNS_lamination_method');
my $spec3 = $IKM->get_jobinfo(-jobname=>$JOB,-jobcategory=>'work',-jobinfo=>'JNS_pcb_speciality3');
if ($layer_count > 16){
$y = 16*31+120;
}
else{
$y = $layer_count*31+120;
}
my $nameJOB = substr($Job,0,9);
$GEN->openMatrix(job=>$Job);
foreach my $layer (sort {$matrix{$a}{row} <=> $matrix{$b}{row}} keys %matrix) {
if ( ($matrix{$layer}{context} eq 'board')and ($matrix{$layer}{layer_type} eq 'silk_screen') ){
unless( $layer =~ /^(p|n)/ ){
my $tmp;
if( $silk_type =~ /^spray$/i ){
$tmp = 'p-';
}
elsif( $silk_type =~ /^silkscreen$/i ){
$tmp = 'n-';
}
else{
next;
}
$GEN->COM( 'matrix_rename_layer',job=>$Job,matrix=>'matrix',layer=>$layer,new_name=>$tmp.$layer);
}
}
elsif ( $matrix{$layer}{context} eq 'board' and ($matrix{$layer}{tl_name} =~ /^(top|bottom)$/ ) ){
if ($layer =~ m/(t|b)/){
$GUI->msgbox(-icon=>'info',-text=>"层已经被定义了上、下面,如需变更,请手动修正!",-gen => $GEN);
# return 'Cancel'
}
push @stackup_layers,$layer;
}
elsif ( $matrix{$layer}{context} eq 'board' and ($matrix{$layer}{tl_name} =~ /^l\d+$/ ) ){
if ($layer =~ m/(t|b)/){
$GUI->msgbox(-icon=>'info',-text=>"层已经被定义了上、下面,如需变更,请手动修正!",-gen => $GEN);
# return 'Cancel'
}
push @stackup_layers,$layer;
}
}
my $toplinker_layers = $IKM->select_arrayhash(-table=>'pdm_job_stack_layers',-where=>{job_id=>$JOB_ID});
my @cust_layer;
foreach my $item (sort{$a->{layer_num} <=> $b->{layer_num}} @$toplinker_layers) {
if($item->{attr_data} && $item->{layer_type} ne "Silk Screen" && $item->{layer_type} ne "Solder Mask"){
my $json_attr = $json->decode($item->{attr_data});
my $cust_ly = $json_attr->{customized_layer_name};
if($cust_ly && $cust_ly ne "") {
if($cust_ly =~ /jia/) {
push @cust_layer, $cust_ly;
} else {
if($cust_ly =~ /(\d+)/) {
push @cust_layer, ($1).($item->{layer_side} eq "Top"? "t" : "b");
} else {
push @cust_layer, $cust_ly;
}
}
}
}
}
my $test;
foreach my $layer (@stackup_layers){
my ($worklyrn) = $layer =~ /(\d+)/;
$ope_boolean = 1 if ($layer =~ m/^ope-/);
$n ++;
$stack_rows{$layer}{sequence} = $n;
$stack_rows{$layer}{gen_name} = $layer;
#enum_tl_side
if (scalar(@cust_layer) > 1 ){
foreach $l (@cust_layer) {
my ($lyrn) = $l =~ /(\d+)/;
# $GEN->PAUSE($l);
$l =~ s/-//;
#$GUI->msgbox(-icon=>'info',-text=>"$l $layer ");
if ($l =~ /^$layer/ ){
$lm = substr($l ,-2,1);
if ( $lm eq 't'){
$stack_rows{$layer}{enum_tl_side} = 'top';
last;
}
else{
$stack_rows{$layer}{enum_tl_side} = 'bottom';
last;
}
}
else{
if($worklyrn == $lyrn and $layer !~ /^jia/ and $l !~ /^jia/){
if ($l =~ /([a-zA-Z]+)/){
if ($1 eq 't'){
$stack_rows{$layer}{enum_tl_side} = 'top';
last;
}
else{
$stack_rows{$layer}{enum_tl_side} = 'bottom';
last;
}
}
}
}
}
$test = 'auto';
}
else{
if ( defined $matrix{$layer}{enum_tl_side} ){
$stack_rows{$layer}{enum_tl_side} = $matrix{$layer}{enum_tl_side};
}
else{
if(defined $matrix{$layer}{tl_type} and $matrix{$layer}{tl_type} eq 'outer'){#外层
if ( defined $matrix{$layer}{tl_num} and $matrix{$layer}{tl_num}%2 == 1 ){
$stack_rows{$layer}{enum_tl_side} = 'top';
}else{
$stack_rows{$layer}{enum_tl_side} = 'bottom';
}
}else{#内层
if ($spec3 eq 'through_board'){#通孔板
if ( defined $matrix{$layer}{tl_num} and $matrix{$layer}{tl_num}%2 == 0 ){
if ($lamination_method eq 'cu'){#覆箔法
$stack_rows{$layer}{enum_tl_side} = 'top';
}elsif ($lamination_method eq 'core'){#覆板法
if ($ope_boolean){#包含ope层
$stack_rows{$layer}{enum_tl_side} = 'top';
}else{
$stack_rows{$layer}{enum_tl_side} = 'bottom';
}
}
}else{
if ($lamination_method eq 'cu'){#覆箔法
$stack_rows{$layer}{enum_tl_side} = 'bottom';
}elsif ($lamination_method eq 'core'){#覆板法
if ($ope_boolean){#包含ope层
$stack_rows{$layer}{enum_tl_side} = 'bottom';
}else{
$stack_rows{$layer}{enum_tl_side} = 'top';
}
}
}
}else{
$stack_rows{$layer}{enum_tl_side} = '';
}
}
}
$test = 'work';
}
my $facefile = "E:/$nameJOB.txt";
# if (-e $facefile){
# open (LAYERMYFILE,$facefile);
# while (<LAYERMYFILE>) {
# chomp;
# my ($lyrn) = $_ =~ /(\d+)/;
# $_ =~ s/-//;
# #$GUI->msgbox(-icon=>'info',-text=>"$_ $layer ");
# if ($_ =~ /^$layer/ ){
# $lm = substr($_ ,-2,1);
# if ( $lm eq 't'){
# $stack_rows{$layer}{enum_tl_side} = 'top';
# last;
# }
# else{
# $stack_rows{$layer}{enum_tl_side} = 'bottom';
# last;
# }
# }
# else{
# if($worklyrn == $lyrn and $layer !~ /^jia/ and $_ !~ /^jia/){
# if ($_ =~ /([a-zA-Z]+)/){
# if ($1 eq 't'){
# $stack_rows{$layer}{enum_tl_side} = 'top';
# last;
# }
# else{
# $stack_rows{$layer}{enum_tl_side} = 'bottom';
# last;
# }
# }
# }
# }
# }
# close LAYERMYFILE;
# $test = 'auto';
# }
# else{
# if ( defined $matrix{$layer}{enum_tl_side} ){
# $stack_rows{$layer}{enum_tl_side} = $matrix{$layer}{enum_tl_side};
# }
# else{
# if(defined $matrix{$layer}{tl_type} and $matrix{$layer}{tl_type} eq 'outer'){#外层
# if ( defined $matrix{$layer}{tl_num} and $matrix{$layer}{tl_num}%2 == 1 ){
# $stack_rows{$layer}{enum_tl_side} = 'top';
# }else{
# $stack_rows{$layer}{enum_tl_side} = 'bottom';
# }
# }else{#内层
# if ($spec3 eq 'through_board'){#通孔板
# if ( defined $matrix{$layer}{tl_num} and $matrix{$layer}{tl_num}%2 == 0 ){
# if ($lamination_method eq 'cu'){#覆箔法
# $stack_rows{$layer}{enum_tl_side} = 'top';
# }elsif ($lamination_method eq 'core'){#覆板法
# if ($ope_boolean){#包含ope层
# $stack_rows{$layer}{enum_tl_side} = 'top';
# }else{
# $stack_rows{$layer}{enum_tl_side} = 'bottom';
# }
# }
# }else{
# if ($lamination_method eq 'cu'){#覆箔法
# $stack_rows{$layer}{enum_tl_side} = 'bottom';
# }elsif ($lamination_method eq 'core'){#覆板法
# if ($ope_boolean){#包含ope层
# $stack_rows{$layer}{enum_tl_side} = 'bottom';
# }else{
# $stack_rows{$layer}{enum_tl_side} = 'top';
# }
# }
# }
# }else{
# $stack_rows{$layer}{enum_tl_side} = '';
# }
# }
# }
# $test = 'work';
# }
#gen_ldi
if(defined $matrix{$layer}{tl_type} and $matrix{$layer}{tl_type} eq 'outer'){#外层
if( $out_film_type =~ /^ldi$/i){
$stack_rows{$layer}{gen_ldi} = 'ldi';
}
else{
$stack_rows{$layer}{gen_ldi} = 'film';
}
}
else{
if( $inn_film_type =~ /^ldi$/i ){
$stack_rows{$layer}{gen_ldi} = 'ldi';
}
else{
$stack_rows{$layer}{gen_ldi} = 'film';
}
}
}
if ($test eq 'work'){
$GUI->msgbox(-icon=>'error',-text=>"没有检测到叠构信息,请注意人工核对信息!");
}
my $stackup_new = $GUI->show_tableform (
-defaultsize=>[450,$y],
-title => "请确认料号的叠构信息--------------" ,
-rows => \%stack_rows,
-showcheck => 1,-gen => $GEN,
-columns => [
{
column_name=>'gen_name',
label=>'层别名称',
width=>150,
type=>'string',
validate_func=>sub{
my %par = @_;
my $layer = $par{formpanel}->get_value($par{row},'gen_name');
$par{formpanel}->get_widget($par{row},'gen_name')->set_editable(0);
if ($matrix{$layer}{context} eq 'board' and $matrix{$layer}{layer_type} eq 'signal' and $matrix{$layer}{polarity} eq 'positive' and $layer !~ m/jia/){
return {
bgcolor => '#FDC74D',
};
}
elsif ($matrix{$layer}{context} eq 'board' and $matrix{$layer}{layer_type} eq 'power_ground' and ($matrix{$layer}{polarity} eq 'negative' or $matrix{$layer}{polarity} eq 'positive') and $layer !~ m/jia/){
return {
bgcolor => '#DBAD00',
};
}
return {};
}
},
{
column_name=>'enum_tl_side',
label=>'层别面次',
type=>'enum',
must_field=>1,
width=>120,
#sensitive =>0,
property=>{
tl_field=>[name=>'scalar',display_name=>'text'],
tl_value_field=>'name',
tl_data=>[
{name=>'top',display_name=>'top'},
{name=>'bottom',display_name=>'bottom'},
]
},
validate_func=>sub{
my %par = @_;
my $layer = $par{formpanel}->get_value($par{row},'gen_name');
my $tl_side = $par{formpanel}->get_value($par{row},'enum_tl_side');
if ($matrix{$layer}{tl_name} eq 'top' and $tl_side eq 'bottom' ){
return {
bgcolor => 'red',
error_msg=>'外层层别面次选择错误',
};
}
elsif ($matrix{$layer}{tl_name} eq 'bottom' and $tl_side eq 'top' ){
return {
bgcolor => 'red',
error_msg=>'外层层别面次选择错误',
};
}
if ($tl_side eq 'top'){
return {
#bgcolor => '#CDDD00',
bgcolor => 'light green',
};
}
return {};
}
},
{
column_name=>'gen_ldi',
label=>'曝光方式',
type=>'enum',
must_field=>1,
width=>120,
#sensitive =>0,
property=>{
tl_field=>[name=>'scalar',display_name=>'text'],
tl_value_field=>'name',
tl_data=>[
{name=>'ldi',display_name=>'ldi'},
{name=>'film',display_name=>'film'},
]
},
validate_func=>sub{
my %par = @_;
my $tl_ldi = $par{formpanel}->get_value($par{row},'gen_ldi');
if ($tl_ldi eq 'film'){
return {
#bgcolor => '#0DBA00',
bgcolor => '#88777B',
};
}
return {};
}
},
],
);
return 'Cancel' unless ($stackup_new);
foreach my $layer (sort {$matrix{$a}{row} <=> $matrix{$b}{row}} keys %{$stackup_new}){
if($stackup_new->{$layer}{enum_tl_side} eq "top"){
if ($stackup_new->{$layer}{gen_ldi} eq "ldi"){
if ($layer =~ /ldi$/i){
$layerold = $layer;
$layer =~ s/-ldi/t-ldi/;
$GEN->COM( 'matrix_rename_layer',job=>$Job,matrix=>'matrix',layer=>$layerold,new_name=>$layer);
}
else{
$GEN->COM( 'matrix_rename_layer',job=>$Job,matrix=>'matrix',layer=>$layer,new_name=>$layer.'t-ldi');
}
}
else{
if ($layer =~ /ldi$/i){
$layerold = $layer;
$layer =~ s/-ldi/t/;
$GEN->COM( 'matrix_rename_layer',job=>$Job,matrix=>'matrix',layer=>$layerold,new_name=>$layer);
}
else{
$GEN->COM( 'matrix_rename_layer',job=>$Job,matrix=>'matrix',layer=>$layer,new_name=>$layer.'t');
}
}
}
else{
if ($stackup_new->{$layer}{gen_ldi} eq "ldi"){
if ($layer =~ /ldi$/i){
$layerold = $layer;
$layer =~ s/-ldi/b-ldi/;
$GEN->COM( 'matrix_rename_layer',job=>$Job,matrix=>'matrix',layer=>$layerold,new_name=>$layer);
}
else{
$GEN->COM( 'matrix_rename_layer',job=>$Job,matrix=>'matrix',layer=>$layer,new_name=>$layer.'b-ldi');
}
}
else{
if ($layer =~ /ldi$/i){
$layerold = $layer;
$layer =~ s/-ldi/b/;
$GEN->COM( 'matrix_rename_layer',job=>$Job,matrix=>'matrix',layer=>$layerold,new_name=>$layer);
}
else{
$GEN->COM( 'matrix_rename_layer',job=>$Job,matrix=>'matrix',layer=>$layer,new_name=>$layer.'b');
}
}
}
}
$GEN->COM( 'matrix_page_close',job=>$Job,matrix=>'matrix');
#$GUI->msgbox(-icon=>'info',-text=>"$layer - ",-gen => $GEN);
###output and return status, if genesis error, it will output genesis error command
unless ($GEN->{STATUS}){
return $Return;
}
else{
$GUI->msgbox(-icon=>'error',-text=>join("\n",@{$GEN->{STATUS}}));
$IKM->update_job_workflow_log(-notes=>" Genesis Error:\n ".join("\n ",@{$GEN->{STATUS}}));
return 'Error';
}
}
catch Error::Simple with {
my $error = shift;
$GUI->msgbox(-icon=>'error',-text=>$error);
return 'Error';
}
finally{
};
This source diff could not be displayed because it is too large. You can view the blob instead.
=head
NAME:
DESCRIPTION:
PARAMETER:
[
]
VERSION_HISTORY:
V1.00 2013-2-21 Cody Yu
1.新版本
V1.01 2020-7-15 许仕斌
1.内外层层名增加t/b标识
V1.02 2020-8-6 许仕斌
1.增加了根据叠构信息自动排列T/B标识
V1.03 2022-2-28 fsy
1.假层下一层带入时增加屏蔽条件
HELP:
<html><body bgcolor="#DDECFE">
<font size="3" color="#003DB2"><p>功能简介</p></font>
<p> 根据预审指示工具类型更改层名 </p>
<br>
<font size="3" color="#003DB2"><p>参数配置</p></font>
<font color="#008000"><p></p></font>
<p> </p>
<font color="#008000"><p></p></font>
<p> </p>
<font size="3" color="#003DB2"><p>注意事项</p></font>
<p> ● 无 </p>
<br>
</body></html>
=cut
#use strict;
#my ($JOB,$GEN,$GUI,$DB,$JOB_ID,$PAR);
use utf8;
use JSON;
use Data::Dump 'dump';
my ($Job,$Step) = ($JOB,undef);
my $Return = 'finish';
my @Report;
my (%stack_rows,$n,$y);
try {
my $json = JSON->new->allow_nonref;
###检查Genesis料号是否存在并打开
if (! $GEN->isJobExists(job=>$Job) ){
$GUI->msgbox(-icon=>'error',-text=>"料号 $Job 在Genesis中不存在, 请检查!");
return 'Error';
}
$GEN->openJob(job=>$Job) unless ($GEN->isJobOpen(job=>$Job));
my %matrix = $GEN->getMatrix(job=>$Job,type=>'hash');
my $inn_film_type = $IKM->get_jobinfo(-jobname=>$JOB,-jobcategory=>'work',-jobinfo=>'JNS_inn_tooling_type');
my $out_film_type = $IKM->get_jobinfo(-jobname=>$JOB,-jobcategory=>'work',-jobinfo=>'JNS_out_tooling_type');
my $silk_type = $IKM->get_jobinfo(-jobname=>$JOB,-jobcategory=>'work',-jobinfo=>'JNS_ss_procss_type');
my $layer_count = $GEN->getLayerCount(job=>$Job);
my $lamination_method = $IKM->get_jobinfo(-jobname=>$JOB,-jobcategory=>'work',-jobinfo=>'JNS_lamination_method');
my $spec3 = $IKM->get_jobinfo(-jobname=>$JOB,-jobcategory=>'work',-jobinfo=>'JNS_pcb_speciality3');
if ($layer_count > 16){
$y = 16*31+120;
}
else{
$y = $layer_count*31+120;
}
my $nameJOB = substr($Job,0,9);
$GEN->openMatrix(job=>$Job);
foreach my $layer (sort {$matrix{$a}{row} <=> $matrix{$b}{row}} keys %matrix) {
if ( ($matrix{$layer}{context} eq 'board')and ($matrix{$layer}{layer_type} eq 'silk_screen') ){
unless( $layer =~ /^(p|n)/ ){
my $tmp;
if( $silk_type =~ /^spray$/i ){
$tmp = 'p-';
}
elsif( $silk_type =~ /^silkscreen$/i ){
$tmp = 'n-';
}
else{
next;
}
$GEN->COM( 'matrix_rename_layer',job=>$Job,matrix=>'matrix',layer=>$layer,new_name=>$tmp.$layer);
}
}
elsif ( $matrix{$layer}{context} eq 'board' and ($matrix{$layer}{tl_name} =~ /^(top|bottom)$/ ) ){
if ($layer =~ m/(t|b)/){
$GUI->msgbox(-icon=>'info',-text=>"层已经被定义了上、下面,如需变更,请手动修正!",-gen => $GEN);
# return 'Cancel'
}
push @stackup_layers,$layer;
}
elsif ( $matrix{$layer}{context} eq 'board' and ($matrix{$layer}{tl_name} =~ /^l\d+$/ ) ){
if ($layer =~ m/(t|b)/){
$GUI->msgbox(-icon=>'info',-text=>"层已经被定义了上、下面,如需变更,请手动修正!",-gen => $GEN);
# return 'Cancel'
}
push @stackup_layers,$layer;
}
}
my $toplinker_layers = $IKM->select_arrayhash(-table=>'pdm_job_stack_layers',-where=>{job_id=>$JOB_ID});
my @cust_layer;
foreach my $item (sort{$a->{layer_num} <=> $b->{layer_num}} @$toplinker_layers) {
if($item->{attr_data} && $item->{layer_type} ne "Silk Screen" && $item->{layer_type} ne "Solder Mask"){
my $json_attr = $json->decode($item->{attr_data});
my $cust_ly = $json_attr->{customized_layer_name};
if($cust_ly && $cust_ly ne "") {
if($cust_ly =~ /jia/) {
push @cust_layer, $cust_ly;
} else {
if($cust_ly =~ /(\d+)/) {
push @cust_layer, ($1).($item->{layer_side} eq "Top"? "t" : "b");
} else {
push @cust_layer, $cust_ly;
}
}
}
}
}
my $test;
foreach my $layer (@stackup_layers){
my ($worklyrn) = $layer =~ /(\d+)/;
$ope_boolean = 1 if ($layer =~ m/^ope-/);
$n ++;
$stack_rows{$layer}{sequence} = $n;
$stack_rows{$layer}{gen_name} = $layer;
#enum_tl_side
if (scalar(@cust_layer) > 1 ){
foreach $l (@cust_layer) {
my ($lyrn) = $l =~ /(\d+)/;
# $GEN->PAUSE($l);
$l =~ s/-//;
#$GUI->msgbox(-icon=>'info',-text=>"$l $layer ");
if ($l =~ /^$layer/ ){
$lm = substr($l ,-2,1);
if ( $lm eq 't'){
$stack_rows{$layer}{enum_tl_side} = 'top';
last;
}
else{
$stack_rows{$layer}{enum_tl_side} = 'bottom';
last;
}
}
else{
if($worklyrn == $lyrn and $layer !~ /^jia/ and $l !~ /^jia/){
if ($l =~ /([a-zA-Z]+)/){
if ($1 eq 't'){
$stack_rows{$layer}{enum_tl_side} = 'top';
last;
}
else{
$stack_rows{$layer}{enum_tl_side} = 'bottom';
last;
}
}
}
}
}
$test = 'auto';
}
else{
if ( defined $matrix{$layer}{enum_tl_side} ){
$stack_rows{$layer}{enum_tl_side} = $matrix{$layer}{enum_tl_side};
}
else{
if(defined $matrix{$layer}{tl_type} and $matrix{$layer}{tl_type} eq 'outer'){#外层
if ( defined $matrix{$layer}{tl_num} and $matrix{$layer}{tl_num}%2 == 1 ){
$stack_rows{$layer}{enum_tl_side} = 'top';
}else{
$stack_rows{$layer}{enum_tl_side} = 'bottom';
}
}else{#内层
if ($spec3 eq 'through_board'){#通孔板
if ( defined $matrix{$layer}{tl_num} and $matrix{$layer}{tl_num}%2 == 0 ){
if ($lamination_method eq 'cu'){#覆箔法
$stack_rows{$layer}{enum_tl_side} = 'top';
}elsif ($lamination_method eq 'core'){#覆板法
if ($ope_boolean){#包含ope层
$stack_rows{$layer}{enum_tl_side} = 'top';
}else{
$stack_rows{$layer}{enum_tl_side} = 'bottom';
}
}
}else{
if ($lamination_method eq 'cu'){#覆箔法
$stack_rows{$layer}{enum_tl_side} = 'bottom';
}elsif ($lamination_method eq 'core'){#覆板法
if ($ope_boolean){#包含ope层
$stack_rows{$layer}{enum_tl_side} = 'bottom';
}else{
$stack_rows{$layer}{enum_tl_side} = 'top';
}
}
}
}else{
$stack_rows{$layer}{enum_tl_side} = '';
}
}
}
$test = 'work';
}
my $facefile = "E:/$nameJOB.txt";
# if (-e $facefile){
# open (LAYERMYFILE,$facefile);
# while (<LAYERMYFILE>) {
# chomp;
# my ($lyrn) = $_ =~ /(\d+)/;
# $_ =~ s/-//;
# #$GUI->msgbox(-icon=>'info',-text=>"$_ $layer ");
# if ($_ =~ /^$layer/ ){
# $lm = substr($_ ,-2,1);
# if ( $lm eq 't'){
# $stack_rows{$layer}{enum_tl_side} = 'top';
# last;
# }
# else{
# $stack_rows{$layer}{enum_tl_side} = 'bottom';
# last;
# }
# }
# else{
# if($worklyrn == $lyrn and $layer !~ /^jia/ and $_ !~ /^jia/){
# if ($_ =~ /([a-zA-Z]+)/){
# if ($1 eq 't'){
# $stack_rows{$layer}{enum_tl_side} = 'top';
# last;
# }
# else{
# $stack_rows{$layer}{enum_tl_side} = 'bottom';
# last;
# }
# }
# }
# }
# }
# close LAYERMYFILE;
# $test = 'auto';
# }
# else{
# if ( defined $matrix{$layer}{enum_tl_side} ){
# $stack_rows{$layer}{enum_tl_side} = $matrix{$layer}{enum_tl_side};
# }
# else{
# if(defined $matrix{$layer}{tl_type} and $matrix{$layer}{tl_type} eq 'outer'){#外层
# if ( defined $matrix{$layer}{tl_num} and $matrix{$layer}{tl_num}%2 == 1 ){
# $stack_rows{$layer}{enum_tl_side} = 'top';
# }else{
# $stack_rows{$layer}{enum_tl_side} = 'bottom';
# }
# }else{#内层
# if ($spec3 eq 'through_board'){#通孔板
# if ( defined $matrix{$layer}{tl_num} and $matrix{$layer}{tl_num}%2 == 0 ){
# if ($lamination_method eq 'cu'){#覆箔法
# $stack_rows{$layer}{enum_tl_side} = 'top';
# }elsif ($lamination_method eq 'core'){#覆板法
# if ($ope_boolean){#包含ope层
# $stack_rows{$layer}{enum_tl_side} = 'top';
# }else{
# $stack_rows{$layer}{enum_tl_side} = 'bottom';
# }
# }
# }else{
# if ($lamination_method eq 'cu'){#覆箔法
# $stack_rows{$layer}{enum_tl_side} = 'bottom';
# }elsif ($lamination_method eq 'core'){#覆板法
# if ($ope_boolean){#包含ope层
# $stack_rows{$layer}{enum_tl_side} = 'bottom';
# }else{
# $stack_rows{$layer}{enum_tl_side} = 'top';
# }
# }
# }
# }else{
# $stack_rows{$layer}{enum_tl_side} = '';
# }
# }
# }
# $test = 'work';
# }
#gen_ldi
if(defined $matrix{$layer}{tl_type} and $matrix{$layer}{tl_type} eq 'outer'){#外层
if( $out_film_type =~ /^ldi$/i){
$stack_rows{$layer}{gen_ldi} = 'ldi';
}
else{
$stack_rows{$layer}{gen_ldi} = 'film';
}
}
else{
if( $inn_film_type =~ /^ldi$/i ){
$stack_rows{$layer}{gen_ldi} = 'ldi';
}
else{
$stack_rows{$layer}{gen_ldi} = 'film';
}
}
}
if ($test eq 'work'){
$GUI->msgbox(-icon=>'error',-text=>"没有检测到叠构信息,请注意人工核对信息!");
}
my $stackup_new = $GUI->show_tableform (
-defaultsize=>[450,$y],
-title => "请确认料号的叠构信息--------------" ,
-rows => \%stack_rows,
-showcheck => 1,-gen => $GEN,
-columns => [
{
column_name=>'gen_name',
label=>'层别名称',
width=>150,
type=>'string',
validate_func=>sub{
my %par = @_;
my $layer = $par{formpanel}->get_value($par{row},'gen_name');
$par{formpanel}->get_widget($par{row},'gen_name')->set_editable(0);
if ($matrix{$layer}{context} eq 'board' and $matrix{$layer}{layer_type} eq 'signal' and $matrix{$layer}{polarity} eq 'positive' and $layer !~ m/jia/){
return {
bgcolor => '#FDC74D',
};
}
elsif ($matrix{$layer}{context} eq 'board' and $matrix{$layer}{layer_type} eq 'power_ground' and ($matrix{$layer}{polarity} eq 'negative' or $matrix{$layer}{polarity} eq 'positive') and $layer !~ m/jia/){
return {
bgcolor => '#DBAD00',
};
}
return {};
}
},
{
column_name=>'enum_tl_side',
label=>'层别面次',
type=>'enum',
must_field=>1,
width=>120,
#sensitive =>0,
property=>{
tl_field=>[name=>'scalar',display_name=>'text'],
tl_value_field=>'name',
tl_data=>[
{name=>'top',display_name=>'top'},
{name=>'bottom',display_name=>'bottom'},
]
},
validate_func=>sub{
my %par = @_;
my $layer = $par{formpanel}->get_value($par{row},'gen_name');
my $tl_side = $par{formpanel}->get_value($par{row},'enum_tl_side');
if ($matrix{$layer}{tl_name} eq 'top' and $tl_side eq 'bottom' ){
return {
bgcolor => 'red',
error_msg=>'外层层别面次选择错误',
};
}
elsif ($matrix{$layer}{tl_name} eq 'bottom' and $tl_side eq 'top' ){
return {
bgcolor => 'red',
error_msg=>'外层层别面次选择错误',
};
}
if ($tl_side eq 'top'){
return {
#bgcolor => '#CDDD00',
bgcolor => 'light green',
};
}
return {};
}
},
{
column_name=>'gen_ldi',
label=>'曝光方式',
type=>'enum',
must_field=>1,
width=>120,
#sensitive =>0,
property=>{
tl_field=>[name=>'scalar',display_name=>'text'],
tl_value_field=>'name',
tl_data=>[
{name=>'ldi',display_name=>'ldi'},
{name=>'film',display_name=>'film'},
]
},
validate_func=>sub{
my %par = @_;
my $tl_ldi = $par{formpanel}->get_value($par{row},'gen_ldi');
if ($tl_ldi eq 'film'){
return {
#bgcolor => '#0DBA00',
bgcolor => '#88777B',
};
}
return {};
}
},
],
);
return 'Cancel' unless ($stackup_new);
foreach my $layer (sort {$matrix{$a}{row} <=> $matrix{$b}{row}} keys %{$stackup_new}){
if($stackup_new->{$layer}{enum_tl_side} eq "top"){
if ($stackup_new->{$layer}{gen_ldi} eq "ldi"){
if ($layer =~ /ldi$/i){
$layerold = $layer;
$layer =~ s/-ldi/t-ldi/;
$GEN->COM( 'matrix_rename_layer',job=>$Job,matrix=>'matrix',layer=>$layerold,new_name=>$layer);
}
else{
$GEN->COM( 'matrix_rename_layer',job=>$Job,matrix=>'matrix',layer=>$layer,new_name=>$layer.'t-ldi');
}
}
else{
if ($layer =~ /ldi$/i){
$layerold = $layer;
$layer =~ s/-ldi/t/;
$GEN->COM( 'matrix_rename_layer',job=>$Job,matrix=>'matrix',layer=>$layerold,new_name=>$layer);
}
else{
$GEN->COM( 'matrix_rename_layer',job=>$Job,matrix=>'matrix',layer=>$layer,new_name=>$layer.'t');
}
}
}
else{
if ($stackup_new->{$layer}{gen_ldi} eq "ldi"){
if ($layer =~ /ldi$/i){
$layerold = $layer;
$layer =~ s/-ldi/b-ldi/;
$GEN->COM( 'matrix_rename_layer',job=>$Job,matrix=>'matrix',layer=>$layerold,new_name=>$layer);
}
else{
$GEN->COM( 'matrix_rename_layer',job=>$Job,matrix=>'matrix',layer=>$layer,new_name=>$layer.'b-ldi');
}
}
else{
if ($layer =~ /ldi$/i){
$layerold = $layer;
$layer =~ s/-ldi/b/;
$GEN->COM( 'matrix_rename_layer',job=>$Job,matrix=>'matrix',layer=>$layerold,new_name=>$layer);
}
else{
$GEN->COM( 'matrix_rename_layer',job=>$Job,matrix=>'matrix',layer=>$layer,new_name=>$layer.'b');
}
}
}
}
$GEN->COM( 'matrix_page_close',job=>$Job,matrix=>'matrix');
#$GUI->msgbox(-icon=>'info',-text=>"$layer - 上",-gen => $GEN);
###output and return status, if genesis error, it will output genesis error command
unless ($GEN->{STATUS}){
return $Return;
}
else{
$GUI->msgbox(-icon=>'error',-text=>join("\n",@{$GEN->{STATUS}}));
$IKM->update_job_workflow_log(-notes=>" Genesis Error:\n ".join("\n ",@{$GEN->{STATUS}}));
return 'Error';
}
}
catch Error::Simple with {
my $error = shift;
$GUI->msgbox(-icon=>'error',-text=>$error);
return 'Error';
}
finally{
};
/*
NAME: JNS_ss_font_fill
DESCRIPTION: 空心文字填实
PARAMETER:
[
{
name : 'step',
title : 'step',
type : 'LineEdit',
property : {tool_tip : '目标step,默认是orig'}
},
{
name : 'workLayers',
title : 'workLayers',
type : 'LineEdit',
property : {tool_tip : '工作层'}
},
{
name : 'auto_save',
title : '自动保存',
type : 'RadioBox',
property : {
item_list:[
{name:'yes',text:'YES'},
{name:'no',text:'NO'},
],
tool_tip:'是否自动保存料号开关'
}
}
]
VERSION_HISTORY:
V1.00 2023-02-14 kunli
1.新版本
HELP:
<html><body bgcolor="#DDECFE">
<font size="3" color="#003DB2"><p>功能简介</p></font>
<p> 空心文字填实 </ p>
<br>
<font size="3" color="#003DB2"><p>参数配置</p></font>
<p> </p>
<br>
<font size="3" color="#003DB2"><p>注意事项</p></font>
<p> 非同一回路的非线属性的文字无法填实,需要手动作业 </p>
<br>
</body></html>
*/
// 引入模块 包 外部参数
var $ = require('topcam.scriptfunc').argv();
var fs = require('fs');
var _ = require('lodash');
var mode = $.ikm ? "topcam" : "aimdfm";
var IKM = $.ikm ? $.ikm : require('topcam.ikm6')($);
var GEN = $.gen;
var Job = $.job || $.job_name;
var JobId = $.job_id;
var db = $.db || IKM.db;
var plugin = require('topsin.gengui');
var GUI = plugin.newGui(plugin.__dirname);
var PAR = {};
if ($.par) {
PAR = $.par;
} else if ($.hasOwnProperty('script_parameter')) {
PAR = JSON.parse($.script_parameter);
}
if (mode === "aimdfm") {
IKM.crud("updateRow", {
table: "pdm_aimdfm_task",
data: { current_process_title: $.process_title },
where: { id: $.task_id }
});
}
var Status = 'ok';
var resultData = [];
var par = PAR; // 接收参数
var default_par = { // 设置默认参数
step: "cad",
auto_save: "No",
workLayers: "out",
units: "mm"
};
for (var key in default_par) {
if (!par.hasOwnProperty(key) || par[key] == "") {
par[key] = default_par[key];
}
}
// 定义变量
var job = Job.toLowerCase();
require("topsin.genmath");
var genMath = new GenMath();
var last_move_distance = 0;
try {
// 常规验证 准备工作
if (!GEN.isJobExists({ job: job })) { throw "料号" + job + "不存在" }
if (!GEN.isJobOpen({ job: job })) { GEN.openJob({ job: job }) }
if (mode == "aimdfm") {
if (GEN.checkInout({ job: job, mode: "test" }) != 0) {
throw "the job check"
}
GEN.checkInout({ job: job, mode: "out" });
}
// 主体
var stepList = GEN.getStepList({ job: job });
stepList = stepList.filter(function (v) {
var tmpreg = new RegExp(par.step, "ig");
return tmpreg.test(v);
});
if (stepList.length == 0) {
throw "未找到" + par.step
}
var workLayers = par.workLayers.split(";")
workLayers = workLayers.filter(function (l) { return GEN.isLayerExists({ job: job, layer: l }) })
if (workLayers.length == 0) {
throw "没有找到工作层"
}
for (var step_cot = 0; step_cot < stepList.length; step_cot++) {
var step = stepList[step_cot];
GEN.openStep({ job: job, name: step });
GEN.clearLayers(); //清除层显示
GEN.affectedLayer({ mode: 'all', affected: 'no' }); //设置影响层全部不影响
GEN.COM("sel_options,clear_mode=clear_after,display_mode=all_layers,area_inout=inside,area_select=select,select_mode=standard,area_touching_mode=exclude");
GEN.units({ type: par.units }); //单位初始化
GEN.zoomHome(); //窗口显示回到原始位置
GEN.selClearFeature()
var select_ans = GEN.PAUSE('Please select features');
if (!_.eq(select_ans, "OK")) {
return 'Done';
}
// for (var work_layer_cot = 0; work_layer_cot < workLayers.length; work_layer_cot++) {
// var work_layer = workLayers[work_layer_cot];
// var ans = 'OK';
// do {
// var line_type = 0; //1 代表横,2代表竖
// var line_info = [];
// do {
// do {
// GEN.workLayer({ name: work_layer, display_number: 1, clear_before: "yes" });
// var select_ans = GEN.PAUSE('Please select features');
// if (!_.eq(select_ans, "OK")) {
// return 'Done';
// }
// } while (GEN.getSelectCount() == 0);
// line_info = GEN.getFeatures({ job: job, step: step, layer: work_layer, options: "select+feat_index", units: 'mm' });
// GEN.selClearFeature();
// var line_k = -999;
// var has_err = 0;
// for (var i = 0; i < line_info.length; i++) {
// var cur_kb_val = genMath.getLineKb(line_info[i]);
// var cur_k_val = 999;
// if (_.isValid(cur_kb_val["k"]) && _.fuzzyEqual(_.toNumber(cur_kb_val["k"]), 0)) {
// cur_k_val = cur_kb_val["k"];
// }
// // GUI.debug({ text: '--cur_kb_val-' + _.toString(cur_kb_val) +"--" + _.toString(cur_k_val) });
// if (i === 0) {
// line_k = cur_k_val;
// } else if (!_.fuzzyEqual(line_k, cur_k_val)) {
// GUI.msgBox({
// title: '错误提示',
// text: '选择的非同一方向的线,请重选',
// buttons: ['Ok', 'Cancel']
// });
// has_err = 1;
// break;
// }
// }
// // GUI.debug({ text: '--line_info-' + _.toString(line_info) +"--" + _.toString(line_k) + "==" + has_err});
// if (has_err == 1) {
// line_type = 0;
// } else {
// if (_.fuzzyEqual(line_k, 0)) {
// line_type = 1;
// } else if (_.fuzzyEqual(line_k, 999)) {
// line_type = 2;
// }
// }
// } while (line_type == 0);
// // GUI.debug({ text: '--line_type-' + _.toString(line_type) });
// var move_info = get_move_info(line_type);
// if (_.isEmpty(move_info)) {
// return 'Cancel';
// }
// // GUI.debug({ text: '--move_info-' + _.toString(move_info) });
// // move
// var move_distance = move_info["move_distance"];
// var move_direction = move_info["move_direction"];
// last_move_distance = move_distance;
// var dx_v = 0;
// var dy_v = 0;
// if (_.eq(move_direction, "up")) {
// dx_v = 0;
// dy_v = move_distance;
// } else if (_.eq(move_direction, "down")) {
// dx_v = 0;
// dy_v = -move_distance;
// } else if (_.eq(move_direction, "left")) {
// dx_v = -move_distance;
// dy_v = 0;
// } else if (_.eq(move_direction, "right")) {
// dx_v = move_distance;
// dy_v = 0;
// }
// for (var i = 0; i < line_info.length; i++) {
// GEN.selLayerFeat({ layer: work_layer, index: line_info[i].index, operation: "select" });
// }
// GEN.selMove({ dx: dx_v, dy: dy_v });
// GEN.selClearFeature();
// ans = GUI.msgBox({
// title: '错误提示',
// text: '是否继续移动',
// buttons: ['Ok', 'Cancel']
// });
// // GUI.debug({ text: '--ans-' + _.toString(ans) });
// } while (/ok/i.test(ans));
// };
GEN.affectedLayer({ mode: 'all', affected: 'no' }); //设置影响层全部不影响
GEN.clearLayers(); //清除层显示
};
// 保存料号
if (/yes/ig.test(par.auto_save)) {
GEN.checkInout({ job: job, mode: "out" });
GEN.saveJob({ job: job });
GEN.checkInout({ job: job, mode: "in" });
}
// 结尾返回 固定写法
var Return = "Done";
if (mode === "aimdfm") {
IKM.crud("updateRow", {
table: "pdm_aimdfm_task",
data: { progress: 100 },
where: { id: $.task_id }
});
var tmperr = { type: "info", title: "操作完成, 请注意检查!" };
if (GEN.hasError()) {
Status = 'error';
tmperr = { type: "error", title: "GEN错误!", detail: [{ desc: GEN.STATUS.join("\n") }] };
}
resultData.push(tmperr);
Return = { status: Status, result_data: resultData };
}
return Return;
} catch (e) {
var showGUI = true;
if (GEN.STATUS.length > 0) {
if (GEN.STATUS.indexOf("Pause Exit:0") >= 0) {
showGUI = false;
}
e = _.toString(e) + "\n" + GEN.STATUS.join("\n");
}
if (showGUI) {
IKM.msg(e);
}
Status = 'error';
resultData.push({ type: "error", title: "脚本执行出错!", detail: [{ desc: _.toString(e) }] });
return mode === "aimdfm" ? { status: Status, result_data: resultData } : "Error";
}
/**
*
* @param {*} layer
*/
function delLayer(layer) {
if (!Array.isArray(layer)) {
layer = [layer]
}
layer = layer.filter(function (v) { return GEN.isLayerExists({ job: job, layer: v }) })
if (layer.length > 0) {
GEN.deleteLayer({ job: job, layer: layer })
}
}
/**
* 获取v-cut预放值,单位是mm,以及移动方向
*/
function get_move_info(iLineType) {
var func = "function (props) {\
return GUI.showForm({\
title: 'v-cut移动距离(mm)',\
size: '240x200',\
use_core_engine: true,\
include_hidden_items: true,\
items: {\
type: 'ScrollArea',\
property: {\
widget_resizable: true,\
frame_shape: 'NoFrame'\
},\
child: [{\
type: 'FormGridLayout',\
property: {},\
child: props.child\
}]\
},\
values: props.value,\
})\
}";
var child = [{
name: 'move_distance',
title: '移动距离:',
type: 'DoubleLineEdit',
property: {
tool_tip: '移动距离'
}
}
];
if (iLineType == 1) {
child.unshift({
name: 'move_direction',
title: '移动方向:',
type: 'RadioBox',
property: {
item_list: [
{ name: 'up', text: 'Up' },
{ name: 'down', text: 'Down' },
],
tool_tip: '移动方向'
}
});
} else {
child.unshift({
name: 'move_direction',
title: '移动方向:',
type: 'RadioBox',
property: {
item_list: [
{ name: 'left', text: 'Left' },
{ name: 'right', text: 'Right' },
],
tool_tip: '移动方向'
}
});
}
var value = {move_distance :last_move_distance};
var data = IKM.command(func, { child: child, value: value }, 1).data;
// GUI.debug({text: 'data-----' + _.toString(data)});
return data;
}
\ No newline at end of file
=head
NAME:
DESCRIPTION: Copy Step
PARAMETER:
[
{
name : 'org_step',
title : '原始step',
type : 'LineEdit',
property : {tool_tip : '过滤Step名称'},
},
{
name : 'step',
title : '目标step',
type : 'LineEdit',
property : {tool_tip : '料号后缀'},
},
{
name : 'save_job',
title : '保存料号',
type : 'RadioBox',
property : {
size_policy:'Expanding,Fixed',
item_list:[
{name:'Yes',text:'Yes'},
{name:'No',text:'No'},
],
tool_tip:'脚本结束后自动保存料号,未设定,默认为No'
},
pack : {row:1,column:1},
}
]
VERSION_HISTORY:
V1.00 2021-01-12 Super Zhang
1.新版本
HELP:
<html><body bgcolor="#DDECFE">
<font size="3" color="#003DB2"><p>功能简介</p></font>
<p> Copy Step </p>
<br>
<font size="3" color="#003DB2"><p>参数配置</p></font>
<font color="#008000"><p> ● 无</p></font>
<font size="3" color="#003DB2"><p>注意事项</p></font>
<p> ● 无 </p>
<br>
</body></html>
=cut
use strict;
use Encode;
use utf8;
use Data::Dump 'dump';
use File::Copy;
use JSON;
use_module('TL_GenMath');
my $json = new JSON;
my ($Job,$Step)=($JOB,undef);
$PAR->{save_job} = 'No' unless $PAR->{save_job};
unless($PAR->{org_step} or $PAR->{step}){
$GUI->msgbox(-icon=>'error',-text=>"请先设置脚本参数!");
return 'Cancel';
}
$Job = lc($Job);
try {
show_loading("判断是否选择料号..",0,position=>'n');
unless( $Job){
$GUI->msgbox(-icon=>'error',-text=>"请先选择料号后再执行脚本!");
return 'Cancel';
}
##检查料号是否存在
update_loading("检查${Job}是否存在..",0,position=>'n');
unless ( $GEN->isJobExists(job=>$Job) ){
$GUI->msgbox(-icon=>'error',-text=>"料号 $Job 不存在,请确认。");
return 'Cancel';
}
##
update_loading("正在复制Step...",0,position=>'n');
$GEN->openJob(job=>$Job) unless ($GEN->isJobOpen(job=>$Job));
$GEN->openStep(job=>$Job,name=>$PAR->{org_step});
my $exitString = `openwindow=>no<>layerindex=>1,2,3,4<>layerCount=>4`;
hide_loading();
##保存料号
if( $PAR->{save_job} =~ /yes/i ){
show_loading("$Job 正在保存料号,请稍候...",0,position=>'n');
$GEN->checkInout(job=>$Job,mode=>'out');
$GEN->saveJob(job=>$Job);
hide_loading();
}
###output and return status, if genesis error, it will output genesis error command
unless ($GEN->{STATUS}){
return 'done';
}
else{
$GUI->msgbox(-icon=>'error',-text=>join("\n",@{$GEN->{STATUS}}));
return 'Error';
}
}
catch Error::Simple with {
my $error = shift;
$GUI->msgbox(-icon=>'error',-text=>$error);
return 'Error';
}
finally{
$GEN->COM('disp_on');
};
__END__
\ No newline at end of file
=head
NAME: TL_PUB_create_impedance_coupon
DESCRIPTION: 绘制阻抗图形
PARAMETER:
[
]
VERSION_HISTORY:
V1.00 2011-12-03 Tony Guo
New Version
V1.01 2013-06-09 Tony Guo
Fixed bug when coplanar and normal impeance in same test coupon
V1.02 2016-11-25 Bill Li
获取到的线宽间距等单位转换um2mil.
V1.03 2016-11-25 Cody Yu
1.阻抗条长度和宽度取出来,不用/25.4
V1.04 2016-12-06 Cody Yu
1.阻抗线增加补偿
V2.00 2017-06-22 Tomy
1.转为EPQ版本,inch|mm用于设定阻抗表单存的coupon条size单位 mm时要除以25.4
V2.01 2017-09-21 Cody
1.文件存储改为string存储
V2.02 2017-09-22 Tomy
1.增加挡墨和防焊开窗,钻孔层定为dir层
V3.00 2017-10-26 Cody
1.产品升级到V6
V3.01 2017-12-14 Shyer
1.coupon线补偿带小数位修复
2.阻抗条尺寸公英制转换小数位异常修复
V3.02 2017-12-15 Shyer
1.共面阻抗铜面增加补偿(同线宽补偿)
V3.03 2018-01-08 Tomy
1._um2mil公制转英制取消取三位小数
V3.04 2019-04-16 Shyer
1.新增单层body text位置获取
V3.05 2020-07-29 Kurri
1.body text修改为仅外层Top面添加
2.档点层别名支持做${layer_count}变量替换
V3.06 2020-11-30 Kurri
1.档点层改为正片
HELP:
=cut
use strict;
use utf8;
use Encode;
use JSON;
use Data::Dump 'dump';
my ($Job,$LayerCount,$Return,$WarningMsg,$units);
$units = "inch";
#$units = "mm";
use_module("PubFunction");
my $F = PubFunction->new();
my $db_units = 'um'; # 数据库中存的线宽间距的单位 (um|mil)
my $coupon_size_units='mm'; #inch|mm用于设定阻抗表单存的coupon条size单位 mm时要除以25.4
try{
unless ($JOB){
$JOB = $GUI->select_job(-title=>'请选择料号',-joblist=>$IKM->select_fieldarray(-table=>'pdm_job',-field=>'jobname',-order=>'id DESC'));
return 'Cancel' unless $JOB;
};
$JOB_ID = $IKM->get_job_id($JOB) unless $JOB_ID;
$Job = lc($JOB) unless $Job ;
##
if (! $GEN->isJobExists(job=>$Job) ){
$GUI->msgbox(-icon=>'error',-text=>"料号 $Job 在Genesis中不存在, 请检查!");
return 'Error';
}
##
$Return = 'Done';
use_module('ImpCouponData');
#获取数据库里的料号层数
my $dbLayerCount = $IKM->get_jobinfo(-jobinfo=>'layer_count',-jobid=>$JOB_ID);
#$dbLayerCount = 10;
#检查DB上的层总数是否与genesis一致
$LayerCount = $GEN->getLayerCount(job=>$Job);
if ( $dbLayerCount and $LayerCount != $dbLayerCount){
return 'Error' if ($GUI->confirm('Genesis料号里的板层数与数据内不一致,你确定要继续吗?') eq 'no');
}
#$LayerCount = 10;
#选择需要运行的coupon
my $imps = $IKM->select_arrayhash(-table=>'pdm_job_imp',-field=>['target_impedance','coupon_name'],-where=>{job_id=>$JOB_ID});
unless(@$imps){
$GUI->msgbox(-icon=>'error',-text=>"料号 $Job 中没有阻抗信息, 请检查是否有录入!");
return 'Error';
}
my (@coupon_list,@sel_coupons);
my %coupons;
foreach my $imp (@$imps){
my @cp_names = split(',',$imp->{coupon_name});
foreach my $cp_name (@cp_names){
$coupons{$cp_name}{$imp->{target_impedance}} = 1;
}
}
foreach my $cp (sort keys %coupons){
push @coupon_list,$cp,uc($cp);
#push @coupon_list,$cp,'< '.$cp.' > '.join(' | ',sort{$a <=> $b} keys %{$coupons{$cp}});
push @sel_coupons,$cp;
}
my %vform = $GUI->show_form(
-defaultsize=>[350,400],
-title => '请选择阻抗条',
-items => [
{name=>'title1',type=>'frame',property=>{}},
{
name => 'coupons',
type => 'checkbox',
property => {
tl_list => \@coupon_list,
tl_columns => 1,
},
value => \@sel_coupons,
},
{
name => 'btn',
label => '',
type => 'label',
button_position => 'left',
buttons => [
{name=>'btn_sel_all',label=>'全选',command=>sub{
my %par = @_;
$par{formpanel}->set_value('coupons',\@sel_coupons);
}},
{name=>'btn_unsel_all',label=>'取消全选',command=>sub{
my %par = @_;
$par{formpanel}->set_value('coupons',undef);
}},
]
},
{name=>'title2',type=>'vbox',property=>{},overback=>1},
{
name => 'show_form',
label => '显示参数确认界面',
type => 'radio',
property => {
tl_list => ['yes'=>'YES','no'=>'NO'],
tl_columns => 3,
},
value => 'no',
}
]
);
return 'Cancel' unless %vform;
@sel_coupons = @{$vform{coupons}};
return 'Cancel' unless @sel_coupons;
#查找HDI钻孔层
my $matrix = $GEN->getMatrix(job=>$Job,type=>'hash');
foreach my $cp_name (sort @sel_coupons){
my $COUPON_DATA;
$COUPON_DATA->{coupon_name} = $cp_name;
#foreach my $info ('coupon_length','coupon_width','coupon_template','coupon_position','coupon_layout_parameter'){
#$COUPON_DATA->{coupon_template} = $IKM->get_layerinfo(-jobid=>$JOB_ID,-layer=>$cp_name,-layerinfo=>'coupon_template_name');
#}
$COUPON_DATA->{coupon_template} = 'coupon'; # FOR test
#$IKM->command('function(){APP.setDebugMode(5)}',{},0);
my $cfg = $IKM->select_value(-table=>'pub_conf',-field=>'json_data',-where=>{path => 'pdm/impedance_coupon_template'});
#$GUI->msgbox(-text=>dump($cfg));
# 转换配置 json2perl
#my $file = ${ENV}{GENESIS_TMP}.'/impedance_coupon_template';
#$file = '>'.$file;
#open(MF,$file);
#print MF $cfg;
#close MF;
#$file = ${ENV}{GENESIS_TMP}.'/impedance_coupon_template';
#my $_data = $F->prase_file_json2perl_hash(file=>$file);
my $_data = $F->prase_file_json2perl_hash(string=>$cfg);
#unlink $file;
#my $template_value = eval($cfg);
my $template_value = $_data;
foreach my $item (keys %$template_value){
$COUPON_DATA->{$item} = $template_value->{$item};
}
my $layout_param = $IKM->get_layerinfo(-jobid=>$JOB_ID,-layer=>$cp_name,-layerinfo=>'coupon_layout_parameter');
if ($layout_param){
$layout_param = eval($layout_param);
foreach my $k (keys %{$layout_param}){
$COUPON_DATA->{$k} = $layout_param->{$k} if defined $layout_param->{$k};
}
}
$COUPON_DATA->{coupon_position} = $IKM->get_layerinfo(-jobid=>$JOB_ID,-layer=>$cp_name,-layerinfo=>'coupon_on_step');
$COUPON_DATA->{fixed_fields}{coupon_position} = 1;
my $cp_length = $IKM->get_layerinfo(-jobid=>$JOB_ID,-layer=>$cp_name,-layerinfo=>'coupon_length');
my $cp_length_org = eval($cp_length);
if($coupon_size_units eq 'mm'){
$cp_length = sprintf("%.3f",$cp_length/25.4);
}
#$cp_length = $cp_length/25.4;
if ($cp_length){
$COUPON_DATA->{coupon_length} = $cp_length;
$COUPON_DATA->{coupon_length_org} = $cp_length_org;
$COUPON_DATA->{auto_enlarge_coupon_length} = 'no';
$COUPON_DATA->{fixed_fields}{coupon_length} = 1;
$COUPON_DATA->{fixed_fields}{auto_enlarge_coupon_length} = 1;
}
my $cp_width = $IKM->get_layerinfo(-jobid=>$JOB_ID,-layer=>$cp_name,-layerinfo=>'coupon_width');
my $cp_width_org = eval($cp_width);
if($coupon_size_units eq 'mm'){
$cp_width = sprintf("%.3f",$cp_width/25.4);
}
#$cp_width = $cp_width/25.4;
if ($cp_width){
$COUPON_DATA->{coupon_body_width} = $COUPON_DATA->{coupon_header_width} = $cp_width;
$COUPON_DATA->{coupon_body_width_org} = $COUPON_DATA->{coupon_header_width_org} = $cp_width_org;
$COUPON_DATA->{auto_enlarge_coupon_header_width} = 'no';
$COUPON_DATA->{auto_enlarge_coupon_body_width} = 'no';
$COUPON_DATA->{fixed_fields}{coupon_body_width} = 1;
$COUPON_DATA->{fixed_fields}{coupon_header_width} = 1;
$COUPON_DATA->{fixed_fields}{auto_enlarge_coupon_header_width} = 1;
$COUPON_DATA->{fixed_fields}{auto_enlarge_coupon_header_width} = 1;
}
my $all_imps = $IKM->select_arrayhash(-table=>'pdm_job_imp',-field=>['*'],-where=>{job_id=>$JOB_ID});
foreach my $imp (@$all_imps){
$imp->{signal1} = imp_layernum2tlname($imp->{signal1});
$imp->{signal2} = imp_layernum2tlname($imp->{signal2});
$imp->{reference1} = imp_layernum2tlname($imp->{reference1});
$imp->{reference2} = imp_layernum2tlname($imp->{reference2});
$imp->{id} = $imp->{uuid};
my @cp_names = split(',',$imp->{coupon_name});
foreach my $cp_nm (@cp_names){
if ($cp_name eq $cp_nm){
if ($db_units eq 'um') {
# 数据转换 um2mil by Bill 20161125
$imp->{org_line_width1} = sprintf("%.1f",_um2mil($imp->{org_line_width1}));
$imp->{org_spacing} = sprintf("%.1f",_um2mil($imp->{org_spacing}));
$imp->{line_width1} = sprintf("%.1f",_um2mil($imp->{line_width1})); #1
$imp->{line_width1_adjust_max} = sprintf("%.1f",_um2mil($imp->{line_width1_adjust_max}));
$imp->{line_width1_adjust_min} = sprintf("%.1f",_um2mil($imp->{line_width1_adjust_min}));
$imp->{spacing} = sprintf("%.1f",_um2mil($imp->{spacing})); #1
#$imp->{target_impedance_max} = _um2mil($imp->{target_impedance_max});
#$imp->{target_impedance_min} = _um2mil($imp->{target_impedance_min});
$imp->{target_impedance_max} = $imp->{target_impedance_max};
$imp->{target_impedance_min} =$imp->{target_impedance_min};
$imp->{test_line_length} = sprintf("%.1f",_um2mil($imp->{test_line_length}));
#添加一个 coplanar_spacing 转换 Tomy 20170621
$imp->{coplanar_spacing} = sprintf("%.1f",_um2mil($imp->{coplanar_spacing})); #1
}
push @{$COUPON_DATA->{impedances}},$imp;
}
}
}
$COUPON_DATA->{GEN} = $GEN;
$COUPON_DATA->{job_id} = $JOB_ID;
$COUPON_DATA->{job_name} = $JOB;
my $coupon_object = Top::ImpCouponData->new(-IKM=>$IKM);
$coupon_object->set_coupon_data($COUPON_DATA);
$coupon_object->set_layer_count($LayerCount);
$coupon_object->set_matrix($matrix);
$coupon_object->coupon_to_layer();
if ($vform{show_form} eq 'yes' or !$COUPON_DATA->{coupon_template}){
return 'Cancel' unless $coupon_object->show_coupon_form();
}
$coupon_object->convert_units();
$coupon_object->convert_matrix();
$coupon_object->analysis_group();
$coupon_object->line_copmensation();
$coupon_object->group_left_right();
$coupon_object->init_pad_position();
$coupon_object->adjust_coupon_length();
$coupon_object->init_line_y_sequence();
$coupon_object->calc_line_y();
$coupon_object->layout_header_lines();
$coupon_object->header_text_position();
$coupon_object->adjust_coupon_width();
my %gen_layer;
foreach my $item (keys %{$COUPON_DATA->{matrix}}){
if ($COUPON_DATA->{matrix}{$item}{tl_name}){
$gen_layer{$COUPON_DATA->{matrix}{$item}{tl_name}} = $item;
}
}
my $step = lc($cp_name);
if ($GEN->isStepExists(job=>$Job,step=>$step)){
return 'Cancel' unless ($GUI->confirm("Step $step 已经存在,确定要覆盖它吗?") eq 'yes');
my @boardLayers;
foreach my $item (keys %$matrix){
if ($matrix->{$item}{context} eq 'board'){
push @boardLayers,$item;
}
}
$GEN->openStep(job=>$Job,name=>$step);
$GEN->clearLayers();
$GEN->affectedLayer(mode=>'all',affected=>'yes');
$GEN->COM('sel_all_feat');
$GEN->selDelete();
}
else{
$GEN->createStep(job=>$Job,name=>$step);
$GEN->openStep(job=>$Job,name=>$step);
}
if ($COUPON_DATA->{coupon_layout_path} eq 'customize'){
$GEN->clearLayers();
$GEN->affectedLayer(mode=>'all',affected=>'no');
$GEN->PAUSE('Select Coupon Layout Path');
my $work_layer =$GEN->getWorkLayer();
my @feats = $GEN->getFeatures(job=>$Job,step=>$step,layer=>$work_layer,options=>'select',units=>'inch') if $work_layer;
if (@feats){
@feats = opt_lines(@feats);
my $ps = TL::GenMath->calc_point_on_line($feats[0],$COUPON_DATA->{'left_header_area_rect'}{xmax} - $COUPON_DATA->{'left_header_area_rect'}{xmin});
my $pe = TL::GenMath->calc_point_on_line({xs=>$feats[-1]{xe},ys=>$feats[-1]{ye},xe=>$feats[-1]{xs},ye=>$feats[-1]{ys}},$COUPON_DATA->{'right_header_area_rect'}{xmax} - $COUPON_DATA->{'right_header_area_rect'}{xmin});
($feats[0]{xs},$feats[0]{ys}) = ($ps->{x},$ps->{y});
($feats[-1]{xe},$feats[-1]{ye}) = ($pe->{x},$pe->{y});
my @line_path = ({x=>$feats[0]{xs},y=>$feats[0]{ys}});
foreach my $feat (@feats){
my $tmp = {x=>$feat->{xe},y=>$feat->{ye}};
if ($feat->{type} eq 'arc'){
@{$tmp}{'xc','yc','direction'} = @{$feat}{'xc','yc','direction'};
}
push @line_path,$tmp;
}
$coupon_object->{COUPON_DATA}{line_path} = \@line_path;
}
}
$coupon_object->layout_path_lines();
$coupon_object->body_text_position();
$coupon_object->layer_body_text_position();
$coupon_object->coplanar_hole_position();
#$GUI->debug(dump($COUPON_DATA));
$GEN->COM('disp_off');
#$GEN->COM('disp_on');
$GEN->clearLayers();
$GEN->affectedLayer(mode=>'all',affected=>'no');
$GEN->units(type=>$units);
#创建Profile
my ($ShiftX,$ShiftY) = (-$COUPON_DATA->{left_down_zero_pad}{x},-$COUPON_DATA->{left_down_zero_pad}{y});
my $trans;
$trans->{left}{angle} = 360-$COUPON_DATA->{left_header_transform}{angle};
$trans->{left}{angle} -= 360 if $trans->{left}{angle} >= 360;
$trans->{left}{x_anchor} = $COUPON_DATA->{left_header_transform}{x} + $ShiftX;
$trans->{left}{y_anchor} = $COUPON_DATA->{left_header_transform}{y} + $ShiftY;
$trans->{right}{angle} = 360-$COUPON_DATA->{right_header_transform}{angle};
$trans->{right}{angle} -= 360 if $trans->{right}{angle} >= 360;
$trans->{right}{x_anchor} = $COUPON_DATA->{right_header_transform}{x} + $ShiftX;
$trans->{right}{y_anchor} = $COUPON_DATA->{right_header_transform}{y} + $ShiftY;
my $tmp_layer = 'tl_script_tmp_layer';
if ($COUPON_DATA->{coupon_layout_path} eq 'customize'){
$GEN->deleteLayer(job=>$Job,layer=>$tmp_layer);
$GEN->createLayer(job=>$Job,layer=>$tmp_layer,context=>'misc',type=>'signal');
$GEN->affectedLayer(affected=>'yes',mode=>'single',layer=>$tmp_layer,clear_before=>'yes');
my ($xs,$ys) = ($COUPON_DATA->{center_path_lines}[0]{x},$COUPON_DATA->{center_path_lines}[0]{y});
my $sym = 'r'.($COUPON_DATA->{coupon_body_width}*1000);
foreach my $n (1..scalar(@{$COUPON_DATA->{center_path_lines}})-1){
my $it = $COUPON_DATA->{center_path_lines}[$n];
if ($it->{direction}){
$GEN->addArc(xs=>$xs+$ShiftX,ys=>$ys+$ShiftY,xe=>$it->{x}+$ShiftX,ye=>$it->{y}+$ShiftY,xc=>$it->{xc}+$ShiftX,yc=>$it->{yc}+$ShiftY,direction=>$it->{direction},symbol=>$sym);
}
else{
$GEN->addLine(xs=>$xs+$ShiftX,ys=>$ys+$ShiftY,xe=>$it->{x}+$ShiftX,ye=>$it->{y}+$ShiftY,symbol=>$sym);
}
($xs,$ys) = ($it->{x},$it->{y});
}
my $clip_w = $COUPON_DATA->{coupon_body_width} + 0.0002;
$GEN->addPad(x=>$COUPON_DATA->{left_zero_pad}{x}-$clip_w/2+$ShiftX,y=>$COUPON_DATA->{left_zero_pad}{y}+$ShiftY,symbol=>'s'.($clip_w*1000),
polarity=>'negative',attributes=>{attribute=>'.string',text=>'left_side'});
$GEN->addPad(x=>$COUPON_DATA->{right_zero_pad}{x}+$clip_w/2+$ShiftX,y=>$COUPON_DATA->{right_zero_pad}{y}+$ShiftY,symbol=>'s'.($clip_w*1000),
polarity=>'negative',attributes=>{attribute=>'.string',text=>'right_side'});
foreach my $lr ('left','right'){
$GEN->addRectangle(x1=>$COUPON_DATA->{$lr.'_header_area_rect'}{xmin}+$ShiftX,y1=>$COUPON_DATA->{$lr.'_header_area_rect'}{ymin}+$ShiftY,
x2=>$COUPON_DATA->{$lr.'_header_area_rect'}{xmax}+$ShiftX,y2=>$COUPON_DATA->{$lr.'_header_area_rect'}{ymax}+$ShiftY,
attributes=>{attribute=>'.string',text=>$lr.'_side'}
) if ($COUPON_DATA->{$lr.'_header_area_rect'});
}
foreach my $lr ('left','right'){
if ($trans->{$lr}{angle}){
$GEN->selClearFeature();
$GEN->selectByFilter(attribute=>[{attribute=>'.string',text=>$lr.'_side'}]);
$GEN->COM('sel_transform',mode=>'anchor',oper=>'rotate',duplicate=>'no',%{$trans->{$lr}}) ;
}
}
$GEN->COM('sel_all_feat');
$GEN->selContourize();
$GEN->COM('sel_all_feat');
$GEN->COM('sel_surf2outline',width=>0.1);
$GEN->COM('sel_all_feat');
$GEN->COM('sel_create_profile');
$GEN->deleteLayer(job=>$Job,layer=>$tmp_layer);
}
else{
if($coupon_size_units eq 'mm'){
$GEN->units(type=>$coupon_size_units);
$GEN->panelSize(width=>$COUPON_DATA->{coupon_length_org},height=>$COUPON_DATA->{coupon_body_width_org});
$GEN->units(type=>$units);
}else{
$GEN->panelSize(width=>$COUPON_DATA->{coupon_length},height=>$COUPON_DATA->{coupon_body_width});
}
}
my $outer_tpad_sym = $COUPON_DATA->{outer_tpad_shape} . $COUPON_DATA->{pad_size};
my $inner_tpad_sym = $COUPON_DATA->{inner_tpad_shape} . $COUPON_DATA->{pad_size};
my $outer_gpad_sym = $COUPON_DATA->{outer_gpad_shape} . $COUPON_DATA->{pad_size};
my $inner_gpad_sym = $COUPON_DATA->{inner_gpad_shape} . $COUPON_DATA->{pad_size};
my $outer_tpad_clear_sym = $COUPON_DATA->{outer_tpad_shape} . ($COUPON_DATA->{pad_size} + $COUPON_DATA->{outer_pad2copper} * 2);
my $inner_tpad_clear_sym = $COUPON_DATA->{inner_tpad_shape} . ($COUPON_DATA->{pad_size} + $COUPON_DATA->{inner_pad2copper} * 2);
my $outer_gpad_clear_sym = $COUPON_DATA->{outer_gpad_shape} . ($COUPON_DATA->{pad_size} + $COUPON_DATA->{outer_pad2copper} * 2);
my $inner_gpad_clear_sym = $COUPON_DATA->{inner_gpad_shape} . ($COUPON_DATA->{pad_size} + $COUPON_DATA->{inner_pad2copper} * 2);
my $outer_tdrl_clear_sym = $COUPON_DATA->{outer_tpad_shape} . ($COUPON_DATA->{hole_size} + $COUPON_DATA->{drill2copper} * 2);
my $inner_tdrl_clear_sym = $COUPON_DATA->{inner_tpad_shape} . ($COUPON_DATA->{hole_size} + $COUPON_DATA->{drill2copper} * 2);
my $outer_gdrl_clear_sym = $COUPON_DATA->{outer_gpad_shape} . ($COUPON_DATA->{hole_size} + $COUPON_DATA->{drill2copper} * 2);
my $inner_gdrl_clear_sym = $COUPON_DATA->{inner_gpad_shape} . ($COUPON_DATA->{hole_size} + $COUPON_DATA->{drill2copper} * 2);
#patch_ref_sym #填补影响层上漏出线路时用
my $max_clear = (sort {$b <=> $a} ($COUPON_DATA->{pad_size} + 2*$COUPON_DATA->{outer_pad2copper}, $COUPON_DATA->{pad_size} + 2*$COUPON_DATA->{inner_pad2copper}, $COUPON_DATA->{hole_size} + 2*$COUPON_DATA->{drill2copper}))[0] + 2;
my $outer_patch_ref_clear_sym = $COUPON_DATA->{outer_tpad_shape} . $max_clear;
my $inner_patch_ref_clear_sym = $COUPON_DATA->{outer_tpad_shape} . $max_clear;
my $tmp_size = ($COUPON_DATA->{pad_size} + $COUPON_DATA->{outer_pad2copper} * 2);
my $outer_gpad_thermal_sym = $COUPON_DATA->{outer_gpad_thermal};
$outer_gpad_thermal_sym =~ s/\$\{OUT\}/$tmp_size/;
$tmp_size = $COUPON_DATA->{pad_size};
$outer_gpad_thermal_sym =~ s/\$\{IN\}/$tmp_size/;
$tmp_size = ($COUPON_DATA->{pad_size} + $COUPON_DATA->{inner_pad2copper} * 2);
my $inner_gpad_thermal_sym = $COUPON_DATA->{inner_gpad_thermal};
$inner_gpad_thermal_sym =~ s/\$\{OUT\}/$tmp_size/;
$tmp_size = $COUPON_DATA->{pad_size};
$inner_gpad_thermal_sym =~ s/\$\{IN\}/$tmp_size/;
foreach my $layer_number (1 .. $COUPON_DATA->{layer_count}){
my $tl_name;
if ($layer_number == 1){
$tl_name = 'top';
}
elsif($layer_number == $COUPON_DATA->{layer_count}){
$tl_name = 'bottom';
}
else{
$tl_name = 'l'.$layer_number;
}
my $layer_name = $gen_layer{$tl_name};
my $layer_matrix = $matrix->{$layer_name};
my $layer_polarity = $layer_matrix->{polarity};
my $layer_polarity_n = ($layer_polarity eq 'positive')?'negative':'positive';
#my $layer_side = ($layer_number <= $COUPON_DATA->{layer_count}/2)?'front':'back';
my $layer_side ;
if ($layer_number == 1) {
$layer_side = 'front';
}elsif( $layer_number == $COUPON_DATA->{layer_count}){
$layer_side = 'back';
}else{
$layer_side = $layer_number%2 ? 'back':'front';
}
my ($layer_tpad_sym,$layer_gpad_sym,$layer_tpad_clear_sym,$layer_gpad_clear_sym,
$layer_tdrl_clear_sym,$layer_gdrl_clear_sym,$layer_gpad_thermal_sym,$layer_patch_ref_clear_sym);
if ($layer_number == 1 or $layer_number == $COUPON_DATA->{layer_count}){
$layer_tpad_sym = $outer_tpad_sym; $layer_gpad_sym = $outer_gpad_sym;
$layer_tpad_clear_sym = $outer_tpad_clear_sym; $layer_gpad_clear_sym = $outer_gpad_clear_sym;
$layer_tdrl_clear_sym = $outer_tdrl_clear_sym; $layer_gdrl_clear_sym = $outer_gdrl_clear_sym;
$layer_gpad_thermal_sym = $outer_gpad_thermal_sym;
$layer_patch_ref_clear_sym = $outer_patch_ref_clear_sym;
}
else{
$layer_tpad_sym = $inner_tpad_sym; $layer_gpad_sym = $inner_gpad_sym;
$layer_tpad_clear_sym = $inner_tpad_clear_sym; $layer_gpad_clear_sym = $inner_gpad_clear_sym;
$layer_tdrl_clear_sym = $inner_tdrl_clear_sym; $layer_gdrl_clear_sym = $inner_gdrl_clear_sym;
$layer_gpad_thermal_sym = $inner_gpad_thermal_sym;
$layer_patch_ref_clear_sym = $inner_patch_ref_clear_sym;
}
#layer type
my $layer_type;
foreach my $imp_layer (values %{$COUPON_DATA->{layers}}){
if ($imp_layer->{layer_number} == $layer_number){
$layer_type = $imp_layer->{layer_type};
last;
}
}
#层属于哪些group
my @layer_blong_grps;
foreach my $grp (keys %{$COUPON_DATA->{group}}){
foreach my $lyr (@{$COUPON_DATA->{group}{$grp}{all_layer}}){
if ($COUPON_DATA->{layers}{$lyr}{layer_number} == $layer_number){
push @layer_blong_grps , $grp unless grep({$_ eq $grp} @layer_blong_grps);
}
}
}
#层包含的group
my @layer_include_grps;
foreach my $drill (values %{$COUPON_DATA->{drills}}){
if ($drill->{drl_start_num} == $layer_number or $drill->{drl_end_num} == $layer_number){
@layer_include_grps = @{$drill->{include_coupon_groups}} if $drill->{include_coupon_groups};
last;
}
}
$GEN->affectedLayer(affected=>'yes',mode=>'single',layer=>$layer_name,clear_before=>'yes');
#铺背景铜,
if ($layer_polarity eq 'negative'){
$GEN->srFill(layer=>$layer_name,step_margin_x=>-$COUPON_DATA->{neg_copper_margin}/1000,step_margin_y=>-$COUPON_DATA->{neg_copper_margin}/1000,polarity=>$layer_polarity_n);
}
$GEN->srFill(layer=>$layer_name,step_margin_x=>$COUPON_DATA->{rout2copper_spacing}/1000,step_margin_y=>$COUPON_DATA->{rout2copper_spacing}/1000,polarity=>$layer_polarity);
#添加信号线隔离;
if ($layer_type =~ /^(S|B)$/){
my $grp = $layer_blong_grps[0];
#为信号层和空层添加信号组隔离线
foreach my $imp_lyr (@{$COUPON_DATA->{group}{$grp}{signal_layer}}){
my $imp_layer = $COUPON_DATA->{layers}{$imp_lyr};
my $sym = 'r'.($imp_layer->{cam_line_width}+$imp_layer->{imp_line2copper}*2-$imp_layer->{default_compensation});
foreach my $m ('lines1','lines2'){
if ($imp_layer->{$m} and @{$imp_layer->{$m}}){
my ($xs,$ys) = ($imp_layer->{$m}[0]{x},$imp_layer->{$m}[0]{y});
foreach my $n (1..scalar(@{$imp_layer->{$m}})-1){
my $it = $imp_layer->{$m}[$n];
if ($it->{direction}){
$GEN->addArc(xs=>$xs+$ShiftX,ys=>$ys+$ShiftY,xe=>$it->{x}+$ShiftX,ye=>$it->{y}+$ShiftY,xc=>$it->{xc}+$ShiftX,yc=>$it->{yc}+$ShiftY,direction=>$it->{direction},symbol=>$sym,polarity=>$layer_polarity_n);
}
else{
$GEN->addLine(xs=>$xs+$ShiftX,ys=>$ys+$ShiftY,xe=>$it->{x}+$ShiftX,ye=>$it->{y}+$ShiftY,symbol=>$sym,polarity=>$layer_polarity_n);
}
($xs,$ys) = ($it->{x},$it->{y});
}
}
}
if ($imp_layer->{center_lines} and @{$imp_layer->{center_lines}}){
my ($xs,$ys) = ($imp_layer->{center_lines}[0]{x},$imp_layer->{center_lines}[0]{y});
foreach my $n (1..scalar(@{$imp_layer->{center_lines}})-1){
my $it = $imp_layer->{center_lines}[$n];
if ($it->{direction}){
$GEN->addArc(xs=>$xs+$ShiftX,ys=>$ys+$ShiftY,xe=>$it->{x}+$ShiftX,ye=>$it->{y}+$ShiftY,xc=>$it->{xc}+$ShiftX,yc=>$it->{yc}+$ShiftY,direction=>$it->{direction},symbol=>'r'.$imp_layer->{cam_total_width},polarity=>$layer_polarity_n);
}
else{
$GEN->addLine(xs=>$xs+$ShiftX,ys=>$ys+$ShiftY,xe=>$it->{x}+$ShiftX,ye=>$it->{y}+$ShiftY,symbol=>'r'.$imp_layer->{cam_total_width},polarity=>$layer_polarity_n);
}
($xs,$ys) = ($it->{x},$it->{y});
}
}
#aoi pad 隔离
if ($COUPON_DATA->{add_aoi_pad} eq 'yes'){
my $sym;
if ($imp_layer->{impedance_type} =~ /coplanar/){
$sym = 'r'.($imp_layer->{cam_line_width}+$COUPON_DATA->{aoi_pad_ar}*2+$imp_layer->{imp_line2copper}*2);
}
else{
$sym = 'r'.($imp_layer->{cam_line_width}+$COUPON_DATA->{aoi_pad_ar}*2+$COUPON_DATA->{aoi_pad2copper}*2);
}
foreach my $it (@{$imp_layer->{aoi_pads}}){
$GEN->addPad(x=>$it->{x}+$ShiftX,y=>$it->{y}+$ShiftY,symbol=>$sym,polarity=>$layer_polarity_n);
}
}
}
}
#文字隔离
if ($COUPON_DATA->{body_text_polarity} eq 'positive'){
if ($COUPON_DATA->{body_text_pos}{$tl_name} and !$COUPON_DATA->{'body_text_not_enought_space'}{$tl_name}){
my $n = 0;
foreach my $text (@{$COUPON_DATA->{body_text_pos}{$tl_name}}){
$n++;
$GEN->addRectangle(x1=>$text->{xmin}-$COUPON_DATA->{body_text_free_x}/1000+$ShiftX,
y1=>$text->{ymin}-$COUPON_DATA->{body_text_free_y}/1000+$ShiftY,
x2=>$text->{xmax}+$COUPON_DATA->{body_text_free_x}/1000+$ShiftX,
y2=>$text->{ymax}+$COUPON_DATA->{body_text_free_y}/1000+$ShiftY,
attributes=>{attribute=>'.string',text=>'body_text_clear_'.$n},
polarity=>$layer_polarity_n,
);
if ($text->{angle}){
my $angle = -$text->{angle};
$angle = 360 + $angle if $angle < 0;
$GEN->selClearFeature();
$GEN->selectByFilter(attribute=>[{attribute=>'.string',text=>'body_text_clear_'.$n}]);
$GEN->COM('sel_transform',mode=>'anchor',oper=>'rotate',duplicate=>'no',
x_anchor=>$text->{bx}+$ShiftX,y_anchor=>$text->{by}+$ShiftY,
angle=>$angle,x_scale=>1,y_scale=>1,x_offset=>0,y_offset=>0) if $GEN->getSelectCount();;
}
}
}
}
#添加pin hole clearance
my $pin_clear_symbol = 'r'.($COUPON_DATA->{pin_hole_size} + 2*$COUPON_DATA->{pin_hole_clearance});
foreach my $lr ('left','right'){
if ($COUPON_DATA->{$lr.'_pin_hole'}){
$GEN->addPad(x=>$COUPON_DATA->{$lr.'_pin_hole'}{x}+$ShiftX,y=>$COUPON_DATA->{$lr.'_pin_hole'}{y}+$ShiftY,symbol=>$pin_clear_symbol,polarity=>$layer_polarity_n);
}
}
#添加test pad隔离
foreach my $grp (keys %{$COUPON_DATA->{group}}){
my $tmp_tst_clear_sym;
if (($layer_type eq 'S' and grep({$_ eq $grp} @layer_blong_grps)) or grep({$_ eq $grp} @layer_include_grps)){ #
$tmp_tst_clear_sym = $layer_tpad_clear_sym;
}
else{
$tmp_tst_clear_sym = $layer_tdrl_clear_sym;
}
foreach my $imp_lyr (@{$COUPON_DATA->{group}{$grp}{signal_layer}}){
my $imp_layer = $COUPON_DATA->{layers}{$imp_lyr};
foreach my $lr ('left','right'){
if ($imp_layer->{$lr.'_tpads'}){
foreach my $it (@{$imp_layer->{$lr.'_tpads'}}){
$GEN->addPad(x=>$it->{x}+$ShiftX,y=>$it->{y}+$ShiftY,symbol=>$tmp_tst_clear_sym,polarity=>$layer_polarity_n,attributes=>{attribute=>'.string',text=>$lr.'_clear_pad'});
}
}
}
}
}
#添加gnd pad隔离
if ($layer_type =~ /^(S|B)$/){
if ($COUPON_DATA->{is_coplanar}){
foreach my $grp (keys %{$COUPON_DATA->{group}}){
foreach my $imp_lyr (@{$COUPON_DATA->{group}{$grp}{signal_layer}}){
my $imp_layer = $COUPON_DATA->{layers}{$imp_lyr};
my ($tmp_sym,$tmp_polarity);
if (grep({$grp eq $_} @layer_blong_grps)){
if ($imp_layer->{impedance_type} =~ /coplanar/i){
$tmp_sym = $layer_gpad_sym;$tmp_polarity = $layer_polarity;
}
else{
$tmp_sym = $layer_gpad_clear_sym; $tmp_polarity = $layer_polarity_n;
}
}
elsif(grep({$grp eq $_} @layer_include_grps)){
$tmp_sym = $layer_gpad_clear_sym; $tmp_polarity = $layer_polarity_n;
}
else{
$tmp_sym = $layer_gdrl_clear_sym; $tmp_polarity = $layer_polarity_n;
}
foreach my $lr ('left','right') {
if ($imp_layer->{$lr.'_gpads'}) {
foreach my $it (@{$imp_layer->{$lr.'_gpads'}}) {
$GEN->addPad(x=>$it->{x}+$ShiftX,y=>$it->{y}+$ShiftY,symbol=>$tmp_sym,polarity=>$tmp_polarity,attributes=>{attribute=>'.string',text=>$lr.'_clear_pad'});
}
}
}
}
}
}
else{
foreach my $grp (keys %{$COUPON_DATA->{group}}){
my ($tmp_sym,$tmp_polarity);
if(grep({$grp eq $_} @layer_include_grps)){
$tmp_sym = $layer_gpad_clear_sym; $tmp_polarity = $layer_polarity_n;
}
else{
$tmp_sym = $layer_gdrl_clear_sym; $tmp_polarity = $layer_polarity_n;
}
foreach my $imp_lyr (@{$COUPON_DATA->{group}{$grp}{signal_layer}}){
my $imp_layer = $COUPON_DATA->{layers}{$imp_lyr};
foreach my $lr ('left','right'){
if ($imp_layer->{$lr.'_gpads'}){
foreach my $it (@{$imp_layer->{$lr.'_gpads'}}){
$GEN->addPad(x=>$it->{x}+$ShiftX,y=>$it->{y}+$ShiftY,symbol=>$tmp_sym,polarity=>$tmp_polarity,attributes=>{attribute=>'.string',text=>$lr.'_clear_pad'});
}
}
}
}
}
if ((($layer_number == 1 or $layer_number == $COUPON_DATA->{layer_count}) and $COUPON_DATA->{header_copper_fill} !~ /outer/) or
(($layer_number != 1 and $layer_number != $COUPON_DATA->{layer_count}) and $COUPON_DATA->{header_copper_fill} !~ /inner/))
{
foreach my $lr ('left','right'){
$GEN->addRectangle(x1=>$COUPON_DATA->{$lr.'_header_copper_clip'}{xmin}+$ShiftX,y1=>$COUPON_DATA->{$lr.'_header_copper_clip'}{ymin}+$ShiftY,
x2=>$COUPON_DATA->{$lr.'_header_copper_clip'}{xmax}+$ShiftX,y2=>$COUPON_DATA->{$lr.'_header_copper_clip'}{ymax}+$ShiftY,
attributes=>{attribute=>'.string',text=>$lr.'_clear_pad'},polarity=>$layer_polarity_n
) if ($COUPON_DATA->{$lr.'_header_copper_clip'} and abs($COUPON_DATA->{$lr.'_header_copper_clip'}{xmax} - $COUPON_DATA->{$lr.'_header_copper_clip'}{xmin}) > 0.001);
}
}
else{
if ((($layer_number == 1 or $layer_number == $COUPON_DATA->{layer_count}) and $COUPON_DATA->{header_clear_p2p_copper} =~ /outer/) or
(($layer_number != 1 and $layer_number != $COUPON_DATA->{layer_count}) and $COUPON_DATA->{header_clear_p2p_copper} =~ /inner/))
{
#刮Pad中间的残铜
my $tmp_resize;
if ($layer_number == 1 or $layer_number == $COUPON_DATA->{layer_count}){
$tmp_resize = $COUPON_DATA->{pad_size}/2000 + $COUPON_DATA->{outer_pad2copper}/1000;
}
else{
$tmp_resize = $COUPON_DATA->{pad_size}/2000 + $COUPON_DATA->{inner_pad2copper}/1000;
}
foreach my $lr ('left','right'){
my @tmp_x; my @tmp_y;
foreach my $grp (keys %{$COUPON_DATA->{group}}){
foreach my $imp_lyr (@{$COUPON_DATA->{group}{$grp}{signal_layer}}){
my $imp_layer = $COUPON_DATA->{layers}{$imp_lyr};
foreach my $tg ('tpads','gpads'){
if ($imp_layer->{$lr.'_'.$tg}){
foreach my $it (@{$imp_layer->{$lr.'_'.$tg}}){
push @tmp_x, $it->{x} + $tmp_resize;
push @tmp_x, $it->{x} - $tmp_resize;
push @tmp_y, $it->{y} + $tmp_resize;
push @tmp_y, $it->{y} - $tmp_resize;
}
}
}
}
}
@tmp_x = sort{$a <=> $b} @tmp_x;
@tmp_y = sort{$a <=> $b} @tmp_y;
my $min_x = $tmp_x[0];
my $min_y = $tmp_y[0];
my $max_x = $tmp_x[-1];
my $max_y = $tmp_y[-1];
if ($max_x - $min_x > 0.0001 and $max_y - $min_y > 0.0001){
$GEN->addRectangle(x1=>$min_x+$ShiftX,y1=>$min_y+$ShiftY,
x2=>$max_x+$ShiftX,y2=>$max_y+$ShiftY,
attributes=>{attribute=>'.string',text=>$lr.'_clear_pad'},polarity=>$layer_polarity_n
);
}
}
}
}
}
}
elsif($layer_type eq 'R'){
foreach my $grp (keys %{$COUPON_DATA->{group}}){
my ($tmp_sym,$tmp_polarity);
if (grep({$grp eq $_} @layer_blong_grps)){
$tmp_sym = $layer_gpad_sym;$tmp_polarity = $layer_polarity;
}
elsif(grep({$grp eq $_} @layer_include_grps)){
$tmp_sym = $layer_gpad_clear_sym; $tmp_polarity = $layer_polarity_n;
}
else{
$tmp_sym = $layer_gdrl_clear_sym; $tmp_polarity = $layer_polarity_n;
}
foreach my $imp_lyr (@{$COUPON_DATA->{group}{$grp}{signal_layer}}){
my $imp_layer = $COUPON_DATA->{layers}{$imp_lyr};
foreach my $lr ('left','right'){
if ($imp_layer->{$lr.'_gpads'}){
foreach my $it (@{$imp_layer->{$lr.'_gpads'}}){
$GEN->addPad(x=>$it->{x}+$ShiftX,y=>$it->{y}+$ShiftY,symbol=>$tmp_sym,polarity=>$tmp_polarity,attributes=>{attribute=>'.string',text=>$lr.'_clear_pad'});
}
}
}
}
}
}
else{
foreach my $grp (keys %{$COUPON_DATA->{group}}){
my $tmp_gnd_clear_sym;
if (grep({$_ eq $grp} @layer_include_grps)){ #
$tmp_gnd_clear_sym = $layer_gpad_clear_sym;
}
else{
$tmp_gnd_clear_sym = $layer_gdrl_clear_sym;
}
foreach my $imp_lyr (@{$COUPON_DATA->{group}{$grp}{signal_layer}}){
my $imp_layer = $COUPON_DATA->{layers}{$imp_lyr};
foreach my $lr ('left','right'){
if ($imp_layer->{$lr.'_gpads'}){
foreach my $it (@{$imp_layer->{$lr.'_gpads'}}){
$GEN->addPad(x=>$it->{x}+$ShiftX,y=>$it->{y}+$ShiftY,symbol=>$tmp_gnd_clear_sym,polarity=>$layer_polarity_n,attributes=>{attribute=>'.string',text=>$lr.'_clear_pad'});
}
}
}
}
}
if ((($layer_number == 1 or $layer_number == $COUPON_DATA->{layer_count}) and $COUPON_DATA->{header_copper_fill} !~ /outer/) or
(($layer_number != 1 and $layer_number != $COUPON_DATA->{layer_count}) and $COUPON_DATA->{header_copper_fill} !~ /inner/))
{
foreach my $lr ('left','right'){
$GEN->addRectangle(x1=>$COUPON_DATA->{$lr.'_header_copper_clip'}{xmin}+$ShiftX,y1=>$COUPON_DATA->{$lr.'_header_copper_clip'}{ymin}+$ShiftY,
x2=>$COUPON_DATA->{$lr.'_header_copper_clip'}{xmax}+$ShiftX,y2=>$COUPON_DATA->{$lr.'_header_copper_clip'}{ymax}+$ShiftY,
attributes=>{attribute=>'.string',text=>$lr.'_clear_pad'},polarity=>$layer_polarity_n
) if ($COUPON_DATA->{$lr.'_header_copper_clip'} and abs($COUPON_DATA->{$lr.'_header_copper_clip'}{xmax} - $COUPON_DATA->{$lr.'_header_copper_clip'}{xmin}) > 0.001);
}
}
}
if ($COUPON_DATA->{thermal_as_surface} eq 'yes'){
#添加gnd thermal
if ($layer_type =~ /^(S|B)$/ and $COUPON_DATA->{is_coplanar}){
foreach my $grp (keys %{$COUPON_DATA->{group}}){
my ($tmp_sym,$tmp_polarity);
if (grep({$grp eq $_} @layer_blong_grps)){
$tmp_sym = $layer_gpad_thermal_sym;$tmp_polarity = $layer_polarity_n;
}
next unless $tmp_sym;
foreach my $imp_lyr (@{$COUPON_DATA->{group}{$grp}{signal_layer}}){
my $imp_layer = $COUPON_DATA->{layers}{$imp_lyr};
foreach my $lr ('left','right'){
if ($imp_layer->{$lr.'_gpads'}){
foreach my $it (@{$imp_layer->{$lr.'_gpads'}}){
$GEN->addPad(x=>$it->{x}+$ShiftX,y=>$it->{y}+$ShiftY,symbol=>$tmp_sym,polarity=>$tmp_polarity,attributes=>{attribute=>'.string',text=>$lr.'_clear_pad'});
}
}
}
}
}
}
elsif($layer_type eq 'R'){
foreach my $grp (keys %{$COUPON_DATA->{group}}){
my ($tmp_sym,$tmp_polarity);
if (grep({$grp eq $_} @layer_blong_grps)){
$tmp_sym = $layer_gpad_thermal_sym;$tmp_polarity = $layer_polarity_n;
}
next unless $tmp_sym;
foreach my $imp_lyr (@{$COUPON_DATA->{group}{$grp}{signal_layer}}){
my $imp_layer = $COUPON_DATA->{layers}{$imp_lyr};
foreach my $lr ('left','right'){
if ($imp_layer->{$lr.'_gpads'}){
foreach my $it (@{$imp_layer->{$lr.'_gpads'}}){
$GEN->addPad(x=>$it->{x}+$ShiftX,y=>$it->{y}+$ShiftY,symbol=>$tmp_sym,polarity=>$tmp_polarity,attributes=>{attribute=>'.string',text=>$lr.'_clear_pad'});
}
}
}
}
}
}
if ($COUPON_DATA->{patch_ref_layer_hole} and $layer_type eq 'R'){
$GEN->deleteLayer(job=>$Job,layer=>$tmp_layer);
$GEN->createLayer(job=>$Job,layer=>$tmp_layer,context=>'misc',type=>'signal');
$GEN->affectedLayer(affected=>'yes',mode=>'single',layer=>$tmp_layer,clear_before=>'yes');
foreach my $grp (@layer_blong_grps){
foreach my $imp_lyr (@{$COUPON_DATA->{group}{$grp}{signal_layer}}){
my $imp_layer = $COUPON_DATA->{layers}{$imp_lyr};
my $sym = 'r'.($imp_layer->{cam_line_width} + $COUPON_DATA->{patch_ref_layer_hole}*2);
foreach my $m ('lines1','lines2'){
if ($imp_layer->{$m} and @{$imp_layer->{$m}}){
my ($xs,$ys) = ($imp_layer->{$m}[0]{x},$imp_layer->{$m}[0]{y});
foreach my $n (1..scalar(@{$imp_layer->{$m}})-1){
my $it = $imp_layer->{$m}[$n];
if ($it->{direction}){
$GEN->addArc(xs=>$xs+$ShiftX,ys=>$ys+$ShiftY,xe=>$it->{x}+$ShiftX,ye=>$it->{y}+$ShiftY,xc=>$it->{xc}+$ShiftX,yc=>$it->{yc}+$ShiftY,direction=>$it->{direction},symbol=>$sym,polarity=>'positive');
}
else{
$GEN->addLine(xs=>$xs+$ShiftX,ys=>$ys+$ShiftY,xe=>$it->{x}+$ShiftX,ye=>$it->{y}+$ShiftY,symbol=>$sym,polarity=>'positive');
}
($xs,$ys) = ($it->{x},$it->{y});
}
}
}
if ($imp_layer->{center_lines} and @{$imp_layer->{center_lines}}){
my ($xs,$ys) = ($imp_layer->{center_lines}[0]{x},$imp_layer->{center_lines}[0]{y});
foreach my $n (1..scalar(@{$imp_layer->{center_lines}})-1){
my $it = $imp_layer->{center_lines}[$n];
if ($it->{direction}){
$GEN->addArc(xs=>$xs+$ShiftX,ys=>$ys+$ShiftY,xe=>$it->{x}+$ShiftX,ye=>$it->{y}+$ShiftY,xc=>$it->{xc}+$ShiftX,yc=>$it->{yc}+$ShiftY,direction=>$it->{direction},symbol=>'r'.$imp_layer->{cam_total_width},polarity=>$layer_polarity_n);
}
else{
$GEN->addLine(xs=>$xs+$ShiftX,ys=>$ys+$ShiftY,xe=>$it->{x}+$ShiftX,ye=>$it->{y}+$ShiftY,symbol=>'r'.$imp_layer->{cam_total_width},polarity=>'positive');
}
($xs,$ys) = ($it->{x},$it->{y});
}
}
foreach my $lr ('left','right'){
if ($imp_layer->{$lr.'_tpads'}){
foreach my $it (@{$imp_layer->{$lr.'_tpads'}}){
$GEN->addPad(x=>$it->{x}+$ShiftX,y=>$it->{y}+$ShiftY,symbol=>$layer_patch_ref_clear_sym,polarity=>'negative',attributes=>{attribute=>'.string',text=>$lr.'_patch_ref_clear_pad'});
}
}
}
foreach my $lr ('left','right'){
if ($trans->{$lr}{angle}){
$GEN->selClearFeature();
$GEN->selectByFilter(attribute=>[{attribute=>'.string',text=>$lr.'_patch_ref_clear_pad'}]);
$GEN->COM('sel_transform',mode=>'anchor',oper=>'rotate',duplicate=>'no',%{$trans->{$lr}}) if $GEN->getSelectCount();;
}
}
$GEN->COM('sel_all_feat');
$GEN->selContourize(accuracy=>0.1,break_to_islands=>'yes',clean_hole_size=>0.1,clean_hole_mode=>'x_or_y');
$GEN->COM('sel_all_feat');
$GEN->COM('sel_move_other',target_layer=>$layer_name,invert=>($layer_polarity eq 'positive')?'no':'yes',dx=>0,dy=>0,size=>0);
}
}
$GEN->deleteLayer(job=>$Job,layer=>$tmp_layer);
$GEN->affectedLayer(affected=>'yes',mode=>'single',layer=>$layer_name,clear_before=>'yes');
foreach my $lr ('left','right'){
if ($trans->{$lr}{angle}){
$GEN->selClearFeature();
$GEN->selectByFilter(attribute=>[{attribute=>'.string',text=>$lr.'_clear_pad'}]);
$GEN->COM('sel_transform',mode=>'anchor',oper=>'rotate',duplicate=>'no',%{$trans->{$lr}}) if $GEN->getSelectCount();;
}
}
$GEN->COM('sel_all_feat');
$GEN->selContourize(accuracy=>0.1,break_to_islands=>'yes',clean_hole_size=>$COUPON_DATA->{inner_pad2copper} - 1,clean_hole_mode=>'x_or_y');
}
}
foreach my $lr ('left','right'){
if ($trans->{$lr}{angle}){
$GEN->selClearFeature();
$GEN->selectByFilter(attribute=>[{attribute=>'.string',text=>$lr.'_clear_pad'}]);
$GEN->COM('sel_transform',mode=>'anchor',oper=>'rotate',duplicate=>'no',%{$trans->{$lr}}) if $GEN->getSelectCount();;
}
}
$GEN->COM('sel_all_feat');
$GEN->selContourize(accuracy=>0.1,break_to_islands=>'yes',clean_hole_size=>0.1,clean_hole_mode=>'x_or_y');
if ($layer_polarity eq 'negative'){
$GEN->clipArea(area=>'profile',area_type=>'rectangle',inout=>'outside',contour_cut=>'yes',margin=>$COUPON_DATA->{neg_copper_margin});
}
else{
$GEN->COM('sel_all_feat');
$GEN->selFill(type=>'solid',solid_type=>'fill',min_brush=>$COUPON_DATA->{copper_fill_line_width});
$GEN->COM('sel_all_feat');
$GEN->selContourize(accuracy=>0.1,break_to_islands=>'yes',clean_hole_size=>0.1,clean_hole_mode=>'x_or_y');
$GEN->createLayer(job=>$Job,layer=>$tmp_layer,context=>'misc',type=>'signal') unless ($GEN->isLayerExists(job=>$Job,layer=>$tmp_layer));;
$GEN->affectedLayer(affected=>'yes',mode=>'single',layer=>$tmp_layer,clear_before=>'yes');
$GEN->COM('sel_all_feat');
$GEN->selDelete();
$GEN->srFill(layer=>$tmp_layer,step_margin_x=>$COUPON_DATA->{rout2copper_spacing}/1000-0.1,step_margin_y=>$COUPON_DATA->{rout2copper_spacing}/1000-0.1,polarity=>$layer_polarity);
$GEN->copyLayer(source_job=>$Job,source_step=>$step,source_layer=>$layer_name,dest_layer=>$tmp_layer,mode=>'append',invert=>'yes');
$GEN->COM('sel_all_feat');
$GEN->selContourize(accuracy=>0.1,break_to_islands=>'yes',clean_hole_size=>0.1,clean_hole_mode=>'x_or_y');
$GEN->COM('sel_all_feat');
$GEN->selFill(type=>'solid',solid_type=>'fill',min_brush=>$COUPON_DATA->{copper_fill_sliver_width});
$GEN->affectedLayer(affected=>'yes',mode=>'single',layer=>$layer_name,clear_before=>'yes');
$GEN->COM('sel_all_feat');
$GEN->selDelete();
$GEN->srFill(layer=>$layer_name,step_margin_x=>$COUPON_DATA->{rout2copper_spacing}/1000,step_margin_y=>$COUPON_DATA->{rout2copper_spacing}/1000,polarity=>$layer_polarity);
$GEN->copyLayer(source_job=>$Job,source_step=>$step,source_layer=>$tmp_layer,dest_layer=>$layer_name,mode=>'append',invert=>'yes');
$GEN->COM('sel_all_feat');
$GEN->selContourize(accuracy=>0.1,break_to_islands=>'yes',clean_hole_size=>0.1,clean_hole_mode=>'x_or_y');
if ($COUPON_DATA->{clean_surface_size}){
$GEN->runSingleDfm(chklist=>'valor_dfm_clean_holes',
params=>{pp_layer => $layer_name,
pp_size=>$COUPON_DATA->{clean_surface_size},
pp_mode=>'X and Y',
pp_hi=>'Cover islands',
pp_cover_f=>'Surfaces',},
area => 'global',show => 'no',show_res=>'no');
$GEN->COM('sel_all_feat');
$GEN->selContourize(accuracy=>0.1,break_to_islands=>'yes',clean_hole_size=>0.1,clean_hole_mode=>'x_or_y');
$GEN->deleteLayer(job=>$Job,layer=>$layer_name.'+++');
}
}
$GEN->affectedLayer(affected=>'yes',mode=>'single',layer=>$layer_name,clear_before=>'yes');
#添加test pad
foreach my $grp (keys %{$COUPON_DATA->{group}}){
if ($COUPON_DATA->{remove_nfp} ne 'yes' or
(grep({$grp eq $_} @layer_blong_grps) and $layer_type eq 'S' ) or
grep({$grp eq $_} @layer_include_grps ))
{
foreach my $imp_lyr (@{$COUPON_DATA->{group}{$grp}{signal_layer}}){
my $imp_layer = $COUPON_DATA->{layers}{$imp_lyr};
if ($COUPON_DATA->{remove_nfp} ne 'yes' or $imp_layer->{layer_number} == $layer_number
or $layer_number == 1 or $layer_number == $COUPON_DATA->{layer_count}){
foreach my $lr ('left','right'){
if ($imp_layer->{$lr.'_tpads'}){
foreach my $it (@{$imp_layer->{$lr.'_tpads'}}){
$GEN->addPad(x=>$it->{x}+$ShiftX,y=>$it->{y}+$ShiftY,symbol=>$layer_tpad_sym,polarity=>$layer_polarity,attributes=>{attribute=>'.string',text=>$lr.'_pad'});
}
}
}
}
}
}
}
#添加gnd pad
if ($layer_type =~ /^(S|B)$/){
if ($COUPON_DATA->{is_coplanar}){
foreach my $grp (keys %{$COUPON_DATA->{group}}){
foreach my $imp_lyr (@{$COUPON_DATA->{group}{$grp}{signal_layer}}){
my $imp_layer = $COUPON_DATA->{layers}{$imp_lyr};
my ($tmp_sym,$tmp_polarity);
if (grep({$grp eq $_} @layer_blong_grps)){
if ($imp_layer->{impedance_type} =~ /coplanar/i){
unless ($COUPON_DATA->{thermal_as_surface} eq 'yes'){
$tmp_sym = $layer_gpad_thermal_sym;$tmp_polarity = $layer_polarity_n;
}
}
else{
$tmp_sym = $layer_gpad_sym; $tmp_polarity = $layer_polarity;
}
}
elsif(grep({$grp eq $_} @layer_include_grps)){
$tmp_sym = $layer_gpad_sym; $tmp_polarity = $layer_polarity;
}
elsif($COUPON_DATA->{remove_nfp} ne 'yes'){
$tmp_sym = $layer_gpad_sym; $tmp_polarity = $layer_polarity;
}
next unless $tmp_sym;
foreach my $lr ('left','right'){
if ($imp_layer->{$lr.'_gpads'}){
foreach my $it (@{$imp_layer->{$lr.'_gpads'}}){
$GEN->addPad(x=>$it->{x}+$ShiftX,y=>$it->{y}+$ShiftY,symbol=>$tmp_sym,polarity=>$tmp_polarity,attributes=>{attribute=>'.string',text=>$lr.'_pad'});
}
}
}
}
}
}
else{
foreach my $grp (keys %{$COUPON_DATA->{group}}){
my ($tmp_sym,$tmp_polarity);
if(grep({$grp eq $_} @layer_include_grps)){
$tmp_sym = $layer_gpad_sym; $tmp_polarity = $layer_polarity;
}
elsif($COUPON_DATA->{remove_nfp} ne 'yes'){
$tmp_sym = $layer_gpad_sym; $tmp_polarity = $layer_polarity;
}
next unless $tmp_sym;
foreach my $imp_lyr (@{$COUPON_DATA->{group}{$grp}{signal_layer}}){
my $imp_layer = $COUPON_DATA->{layers}{$imp_lyr};
foreach my $lr ('left','right'){
if ($imp_layer->{$lr.'_gpads'}){
foreach my $it (@{$imp_layer->{$lr.'_gpads'}}){
$GEN->addPad(x=>$it->{x}+$ShiftX,y=>$it->{y}+$ShiftY,symbol=>$tmp_sym,polarity=>$tmp_polarity,attributes=>{attribute=>'.string',text=>$lr.'_pad'});
}
}
}
}
}
}
}
elsif($layer_type eq 'R'){
foreach my $grp (keys %{$COUPON_DATA->{group}}){
my ($tmp_sym,$tmp_polarity);
if (grep({$grp eq $_} @layer_blong_grps)){
unless ($COUPON_DATA->{thermal_as_surface} eq 'yes'){
$tmp_sym = $layer_gpad_thermal_sym;$tmp_polarity = $layer_polarity_n;
}
}
elsif(grep({$grp eq $_} @layer_include_grps)){
$tmp_sym = $layer_gpad_sym; $tmp_polarity = $layer_polarity;
}
elsif($COUPON_DATA->{remove_nfp} ne 'yes'){
$tmp_sym = $layer_gpad_sym; $tmp_polarity = $layer_polarity;
}
next unless $tmp_sym;
foreach my $imp_lyr (@{$COUPON_DATA->{group}{$grp}{signal_layer}}){
my $imp_layer = $COUPON_DATA->{layers}{$imp_lyr};
foreach my $lr ('left','right'){
if ($imp_layer->{$lr.'_gpads'}){
foreach my $it (@{$imp_layer->{$lr.'_gpads'}}){
$GEN->addPad(x=>$it->{x}+$ShiftX,y=>$it->{y}+$ShiftY,symbol=>$tmp_sym,polarity=>$tmp_polarity,attributes=>{attribute=>'.string',text=>$lr.'_pad'});
}
}
}
}
}
}
else{
foreach my $grp (keys %{$COUPON_DATA->{group}}){
my ($tmp_sym,$tmp_polarity);
if(grep({$grp eq $_} @layer_include_grps)){
$tmp_sym = $layer_gpad_sym; $tmp_polarity = $layer_polarity;
}
elsif($COUPON_DATA->{remove_nfp} ne 'yes'){
$tmp_sym = $layer_gpad_sym; $tmp_polarity = $layer_polarity;
}
next unless $tmp_sym;
foreach my $imp_lyr (@{$COUPON_DATA->{group}{$grp}{signal_layer}}){
my $imp_layer = $COUPON_DATA->{layers}{$imp_lyr};
foreach my $lr ('left','right'){
if ($imp_layer->{$lr.'_gpads'}){
foreach my $it (@{$imp_layer->{$lr.'_gpads'}}){
$GEN->addPad(x=>$it->{x}+$ShiftX,y=>$it->{y}+$ShiftY,symbol=>$tmp_sym,polarity=>$tmp_polarity,attributes=>{attribute=>'.string',text=>$lr.'_pad'});
}
}
}
}
}
}
if ($COUPON_DATA->{patch_ref_layer_hole} and $COUPON_DATA->{thermal_as_surface} ne 'yes' and $layer_type eq 'R'){
$GEN->deleteLayer(job=>$Job,layer=>$tmp_layer);
$GEN->createLayer(job=>$Job,layer=>$tmp_layer,context=>'misc',type=>'signal');
$GEN->affectedLayer(affected=>'yes',mode=>'single',layer=>$tmp_layer,clear_before=>'yes');
foreach my $grp (@layer_blong_grps){
foreach my $imp_lyr (@{$COUPON_DATA->{group}{$grp}{signal_layer}}){
my $imp_layer = $COUPON_DATA->{layers}{$imp_lyr};
my $sym = 'r'.($imp_layer->{cam_line_width} + $COUPON_DATA->{patch_ref_layer_hole}*2);
foreach my $m ('lines1','lines2'){
if ($imp_layer->{$m} and @{$imp_layer->{$m}}){
my ($xs,$ys) = ($imp_layer->{$m}[0]{x},$imp_layer->{$m}[0]{y});
foreach my $n (1..scalar(@{$imp_layer->{$m}})-1){
my $it = $imp_layer->{$m}[$n];
if ($it->{direction}){
$GEN->addArc(xs=>$xs+$ShiftX,ys=>$ys+$ShiftY,xe=>$it->{x}+$ShiftX,ye=>$it->{y}+$ShiftY,xc=>$it->{xc}+$ShiftX,yc=>$it->{yc}+$ShiftY,direction=>$it->{direction},symbol=>$sym,polarity=>'positive');
}
else{
$GEN->addLine(xs=>$xs+$ShiftX,ys=>$ys+$ShiftY,xe=>$it->{x}+$ShiftX,ye=>$it->{y}+$ShiftY,symbol=>$sym,polarity=>'positive');
}
($xs,$ys) = ($it->{x},$it->{y});
}
}
}
foreach my $lr ('left','right'){
if ($imp_layer->{$lr.'_tpads'}){
foreach my $it (@{$imp_layer->{$lr.'_tpads'}}){
$GEN->addPad(x=>$it->{x}+$ShiftX,y=>$it->{y}+$ShiftY,symbol=>$layer_patch_ref_clear_sym,polarity=>'negative',attributes=>{attribute=>'.string',text=>$lr.'_patch_ref_clear_pad'});
}
}
}
foreach my $lr ('left','right'){
if ($trans->{$lr}{angle}){
$GEN->selClearFeature();
$GEN->selectByFilter(attribute=>[{attribute=>'.string',text=>$lr.'_patch_ref_clear_pad'}]);
$GEN->COM('sel_transform',mode=>'anchor',oper=>'rotate',duplicate=>'no',%{$trans->{$lr}}) if $GEN->getSelectCount();;
}
}
$GEN->COM('sel_all_feat');
$GEN->selContourize(accuracy=>0.1,break_to_islands=>'yes',clean_hole_size=>0.1,clean_hole_mode=>'x_or_y');
$GEN->COM('sel_all_feat');
$GEN->COM('sel_move_other',target_layer=>$layer_name,invert=>($layer_polarity eq 'positive')?'no':'yes',dx=>0,dy=>0,size=>0);
}
}
$GEN->deleteLayer(job=>$Job,layer=>$tmp_layer);
$GEN->affectedLayer(affected=>'yes',mode=>'single',layer=>$layer_name,clear_before=>'yes');
}
#添加信号线及AOI Pad
foreach my $imp_layer (values %{$COUPON_DATA->{layers}}){
if ($imp_layer->{layer_number} == $layer_number){
my $sym = 'r'.($imp_layer->{cam_line_width});
foreach my $m ('lines1','lines2'){
if ($imp_layer->{$m} and @{$imp_layer->{$m}}){
my ($xs,$ys) = ($imp_layer->{$m}[0]{x},$imp_layer->{$m}[0]{y});
foreach my $n (1..scalar(@{$imp_layer->{$m}})-1){
my $it = $imp_layer->{$m}[$n];
if ($it->{direction}){
$GEN->addArc(xs=>$xs+$ShiftX,ys=>$ys+$ShiftY,xe=>$it->{x}+$ShiftX,ye=>$it->{y}+$ShiftY,xc=>$it->{xc}+$ShiftX,yc=>$it->{yc}+$ShiftY,direction=>$it->{direction},symbol=>$sym,polarity=>$layer_polarity);
}
else{
$GEN->addLine(xs=>$xs+$ShiftX,ys=>$ys+$ShiftY,xe=>$it->{x}+$ShiftX,ye=>$it->{y}+$ShiftY,symbol=>$sym,polarity=>$layer_polarity);
}
($xs,$ys) = ($it->{x},$it->{y});
}
}
}
if ($COUPON_DATA->{add_aoi_pad} eq 'yes'){
foreach my $it (@{$imp_layer->{aoi_pads}}){
$GEN->addPad(x=>$it->{x}+$ShiftX,y=>$it->{y}+$ShiftY,symbol=>'r'.($imp_layer->{cam_line_width}+$COUPON_DATA->{aoi_pad_ar}*2),polarity=>$layer_polarity);
}
}
}
}
#添加头部文字
foreach my $lr ('left','right'){
if ($COUPON_DATA->{$lr.'_header_text_pos'}{$tl_name}){
foreach my $sig (keys %{$COUPON_DATA->{$lr.'_header_text_pos'}{$tl_name}}){
foreach my $v (values %{$COUPON_DATA->{$lr.'_header_text_pos'}{$tl_name}{$sig}}){
my $tmp_polarity = ($v->{polarity} eq 'positive')?$layer_polarity:$layer_polarity_n;
$GEN->addText(x=>$v->{x}+$ShiftX,y=>$v->{y}+$ShiftY,text=>$v->{text},angle=>$v->{angle},mirror=>($layer_side eq 'front')?'no':'yes',
x_size=>$v->{x_size},y_size=>$v->{y_size},line_width=>$v->{line_width},anchor=>$v->{anchor},
polarity=>$tmp_polarity,fontname=>$COUPON_DATA->{header_text_font},
attributes=>{attribute=>'.string',text=>$lr.'_pad'});
}
}
}
}
foreach my $lr ('left','right'){
if ($trans->{$lr}{angle}){
$GEN->selClearFeature();
$GEN->selectByFilter(attribute=>[{attribute=>'.string',text=>$lr.'_pad'}]);
$GEN->COM('sel_transform',mode=>'anchor',oper=>'rotate',duplicate=>'no',%{$trans->{$lr}}) if $GEN->getSelectCount();
}
}
#$GUI->debug(-text=>dump($COUPON_DATA->{body_text_pos},$tl_name));
#添加body文字
if ($tl_name eq 'top' and $COUPON_DATA->{body_text_pos}{$tl_name}){
unless ($COUPON_DATA->{'body_text_not_enought_space'}{$tl_name}){
my $n = 0;
foreach my $text (@{$COUPON_DATA->{body_text_pos}{$tl_name}}){
$n++;
$GEN->addText(x=>$text->{x}+$ShiftX,y=>$text->{y}+$ShiftY,text=>$text->{text},angle=>0,polarity=>($COUPON_DATA->{body_text_polarity} eq 'positive')?$layer_polarity:$layer_polarity_n,
x_size=>$text->{x_size},y_size=>$text->{y_size},line_width=>$text->{line_width},mirror=>($layer_side eq 'front')?'no':'yes',
anchor=>$text->{anchor},text_length=>$text->{text_length},attributes=>{attribute=>'.string',text=>'body_text_'.$n},fontname=>$COUPON_DATA->{body_text_font},);
if ($text->{angle}){
my $angle = -$text->{angle};
$angle = 360 + $angle if $angle < 0;
$GEN->selClearFeature();
$GEN->selectByFilter(attribute=>[{attribute=>'.string',text=>'body_text_'.$n}]);
$GEN->COM('sel_transform',mode=>'anchor',oper=>'rotate',duplicate=>'no',
x_anchor=>$text->{bx}+$ShiftX,y_anchor=>$text->{by}+$ShiftY,
angle=>$angle,x_scale=>1,y_scale=>1,x_offset=>0,y_offset=>0) if $GEN->getSelectCount();;
}
}
}
else{
my $tmp_body_text_layer = $layer_name.($COUPON_DATA->{body_text_tmp_layer_postfix} || '_imptext+++');
push @$WarningMsg,"${step}${layer_name}层没有足够的空间添加文字,现将文字添加到${tmp_body_text_layer}!";
$GEN->createLayer(job=>$Job,layer=>$tmp_body_text_layer,context=>'misc',type=>'signal') unless ($GEN->isLayerExists(job=>$Job,layer=>$tmp_body_text_layer));;
$GEN->affectedLayer(affected=>'yes',mode=>'single',layer=>$tmp_body_text_layer,clear_before=>'yes');
$GEN->COM('sel_all_feat');
$GEN->selDelete();
foreach my $text (@{$COUPON_DATA->{body_text_pos}{$tl_name}}){
$GEN->addText(x=>$text->{x},y=>$text->{y},text=>$text->{text},angle=>0,polarity=>$layer_polarity,
x_size=>$text->{x_size},y_size=>$text->{y_size},line_width=>$text->{line_width},mirror=>($layer_side eq 'front')?'no':'yes',
anchor=>$text->{anchor},fontname=>$COUPON_DATA->{body_text_font},);
}
$GEN->affectedLayer(affected=>'yes',mode=>'single',layer=>$layer_name,clear_before=>'yes');
}
}
=exp
foreach my $layer (keys %{$COUPON_DATA->{body_text_pos}}) {
unless ($COUPON_DATA->{'body_text_not_enought_space'}{$layer}){
my $n = 0;
foreach my $text (@{$COUPON_DATA->{body_text_pos}{$layer}}){
my ($txt_lyr) = $text->{text} =~ /(\w+)\//i;
$GEN->PAUSE($text->{text}.' -- '.$txt_lyr.' --- '.$tl_name);
if (lc($txt_lyr) eq $tl_name or $text->{text} =~ /\$\$JOB/) {
$n++;
$GEN->addText(x=>$text->{x}+$ShiftX,y=>$text->{y}+$ShiftY,text=>$text->{text},angle=>0,polarity=>($COUPON_DATA->{body_text_polarity} eq 'positive')?$layer_polarity:$layer_polarity_n,
x_size=>$text->{x_size},y_size=>$text->{y_size},line_width=>$text->{line_width},mirror=>($layer_side eq 'front')?'no':'yes',
anchor=>$text->{anchor},text_length=>$text->{text_length},attributes=>{attribute=>'.string',text=>'body_text_'.$n},fontname=>$COUPON_DATA->{body_text_font},);
if ($text->{angle}){
my $angle = -$text->{angle};
$angle = 360 + $angle if $angle < 0;
$GEN->selClearFeature();
$GEN->selectByFilter(attribute=>[{attribute=>'.string',text=>'body_text_'.$n}]);
$GEN->COM('sel_transform',mode=>'anchor',oper=>'rotate',duplicate=>'no',
x_anchor=>$text->{bx}+$ShiftX,y_anchor=>$text->{by}+$ShiftY,
angle=>$angle,x_scale=>1,y_scale=>1,x_offset=>0,y_offset=>0) if $GEN->getSelectCount();;
}
}
}
}else{
my $tmp_body_text_layer = $layer_name.($COUPON_DATA->{body_text_tmp_layer_postfix} || '_imptext+++');
push @$WarningMsg,"${step}${layer_name}层没有足够的空间添加文字,现将文字添加到${tmp_body_text_layer}!";
$GEN->createLayer(job=>$Job,layer=>$tmp_body_text_layer,context=>'misc',type=>'signal') unless ($GEN->isLayerExists(job=>$Job,layer=>$tmp_body_text_layer));;
$GEN->affectedLayer(affected=>'yes',mode=>'single',layer=>$tmp_body_text_layer,clear_before=>'yes');
$GEN->COM('sel_all_feat');
$GEN->selDelete();
foreach my $text (@{$COUPON_DATA->{body_text_pos}{$layer}}){
$GEN->addText(x=>$text->{x},y=>$text->{y},text=>$text->{text},angle=>0,polarity=>$layer_polarity,
x_size=>$text->{x_size},y_size=>$text->{y_size},line_width=>$text->{line_width},mirror=>($layer_side eq 'front')?'no':'yes',
anchor=>$text->{anchor},fontname=>$COUPON_DATA->{body_text_font},);
}
$GEN->affectedLayer(affected=>'yes',mode=>'single',layer=>$layer_name,clear_before=>'yes');
}
}
=cut
}
if ($COUPON_DATA->{header_not_enought_space}){
#push @$WarningMsg,"${step}中没有足够的空间添加头部文字,请检查并处理!";
}
#添加钻孔
foreach my $drl (keys %{$COUPON_DATA->{drills}}){
my $drl_layer = $gen_layer{$drl};
$GEN->affectedLayer(affected=>'yes',mode=>'single',layer=>$drl_layer,clear_before=>'yes');
my $hole_sym = 'r'.(($drl eq 'drill')?$COUPON_DATA->{hole_size}:$COUPON_DATA->{hdi_hole_size});
if ($COUPON_DATA->{drills}{$drl}{include_coupon_groups}){
foreach my $grp (@{$COUPON_DATA->{drills}{$drl}{include_coupon_groups}}){
foreach my $imp_lyr (@{$COUPON_DATA->{group}{$grp}{signal_layer}}){
my $imp_layer = $COUPON_DATA->{layers}{$imp_lyr};
foreach my $lr ('left','right'){
foreach my $pad ('_tpads','_gpads'){
if ($imp_layer->{$lr.$pad}){
foreach my $it (@{$imp_layer->{$lr.$pad}}){
$GEN->addPad(x=>$it->{x}+$ShiftX,y=>$it->{y}+$ShiftY,symbol=>$hole_sym,polarity=>'positive',attributes=>[{attribute=>'.string',text=>$lr.'_hole'},{attribute=>'.drill',option=>'plated'}]);
}
}
}
}
}
}
}
foreach my $lr ('left','right'){
if ($trans->{$lr}{angle}){
$GEN->selClearFeature();
$GEN->selectByFilter(attribute=>[{attribute=>'.string',text=>$lr.'_hole'}]);
$GEN->COM('sel_transform',mode=>'anchor',oper=>'rotate',duplicate=>'no',%{$trans->{$lr}}) if $GEN->getSelectCount();;
}
}
if ($drl eq 'drill'){
#pin_hole
foreach my $lr ('left','right'){
if ($COUPON_DATA->{$lr.'_pin_hole'}){
$GEN->addPad(x=>$COUPON_DATA->{$lr.'_pin_hole'}{x}+$ShiftX,y=>$COUPON_DATA->{$lr.'_pin_hole'}{y}+$ShiftY,symbol=>'r'.$COUPON_DATA->{pin_hole_size},polarity=>'positive',attributes=>[{attribute=>'.drill',option=>'non_plated'}]);
}
}
#coplanar hole
foreach my $ud ('up','down'){
if ($COUPON_DATA->{$ud.'_coplanar_holes'}){
foreach my $it (@{$COUPON_DATA->{$ud.'_coplanar_holes'}}){
$GEN->addPad(x=>$it->{x}+$ShiftX,y=>$it->{y}+$ShiftY,symbol=>'r'.$COUPON_DATA->{coplanar_hole_size},attributes=>[{attribute=>'.drill',option=>'plated'}]);
}
}
}
}
}
#npth
if ($COUPON_DATA->{add_pin_hole} eq 'yes' and $gen_layer{npth}){
$GEN->affectedLayer(affected=>'yes',mode=>'single',layer=>$gen_layer{npth},clear_before=>'yes');
foreach my $lr ('left','right'){
if ($COUPON_DATA->{$lr.'_pin_hole'}){
$GEN->addPad(x=>$COUPON_DATA->{$lr.'_pin_hole'}{x}+$ShiftX,y=>$COUPON_DATA->{$lr.'_pin_hole'}{y}+$ShiftY,symbol=>'r'.$COUPON_DATA->{pin_hole_size},polarity=>'positive',attributes=>[{attribute=>'.drill',option=>'non_plated'}]);
}
}
}
#防焊
foreach my $item ('sm_fr','sm_ba'){
#foreach my $item ('smt','smb'){
my $tpad_sm_sym = $COUPON_DATA->{outer_tpad_shape} . ($COUPON_DATA->{pad_size} + $COUPON_DATA->{sm_opening_ar}*2);
my $gpad_sm_sym = $COUPON_DATA->{outer_gpad_shape} . ($COUPON_DATA->{pad_size} + $COUPON_DATA->{sm_opening_ar}*2);
if ($gen_layer{$item}){
$GEN->affectedLayer(affected=>'yes',mode=>'single',layer=>$gen_layer{$item},clear_before=>'yes');
foreach my $imp_layer (values %{$COUPON_DATA->{layers}}){
next unless $imp_layer->{layer_type} eq 'S';
foreach my $lr ('left','right'){
if ($imp_layer->{$lr.'_tpads'}){
foreach my $it (@{$imp_layer->{$lr.'_tpads'}}){
$GEN->addPad(x=>$it->{x}+$ShiftX,y=>$it->{y}+$ShiftY,symbol=>$tpad_sm_sym,polarity=>'positive',attributes=>[{attribute=>'.string',text=>$lr.'_sm'}]);
}
}
if ($imp_layer->{$lr.'_gpads'}){
foreach my $it (@{$imp_layer->{$lr.'_gpads'}}){
$GEN->addPad(x=>$it->{x}+$ShiftX,y=>$it->{y}+$ShiftY,symbol=>$gpad_sm_sym,polarity=>'positive',attributes=>[{attribute=>'.string',text=>$lr.'_sm'}]);
}
}
}
}
foreach my $lr ('left','right'){
if ($trans->{$lr}{angle}){
$GEN->selClearFeature();
$GEN->selectByFilter(attribute=>[{attribute=>'.string',text=>$lr.'_sm'}]);
$GEN->COM('sel_transform',mode=>'anchor',oper=>'rotate',duplicate=>'no',%{$trans->{$lr}}) if $GEN->getSelectCount();;
}
}
#添加pin hole sm
foreach my $lr ('left','right'){
if ($COUPON_DATA->{$lr.'_pin_hole'}){
$GEN->addPad(x=>$COUPON_DATA->{$lr.'_pin_hole'}{x}+$ShiftX,y=>$COUPON_DATA->{$lr.'_pin_hole'}{y}+$ShiftY,symbol=>'r'.($COUPON_DATA->{pin_hole_size} + 2*$COUPON_DATA->{pin_hole_sm_ar}),polarity=>'positive',);
}
}
#添加信号线SM开窗
foreach my $imp_layer (values %{$COUPON_DATA->{layers}}){
if (($imp_layer->{layer_number} == 1 and $item eq 'sm_fr' and lc($imp_layer->{sm_open}) eq 'yes') or
($imp_layer->{layer_number} == $LayerCount and $item eq 'sm_ba' and lc($imp_layer->{sm_open}) eq 'yes')
){
my $sym_size = ($imp_layer->{cam_line_width} + ($COUPON_DATA->{line_sm_opening_ar} || $COUPON_DATA->{sm_opening_ar})*2);
foreach my $m ('lines1','lines2'){
if ($imp_layer->{$m} and @{$imp_layer->{$m}}){
my ($xs,$ys) = ($imp_layer->{$m}[0]{x},$imp_layer->{$m}[0]{y});
foreach my $n (1..scalar(@{$imp_layer->{$m}})-1){
my $it = $imp_layer->{$m}[$n];
if ($it->{direction}){
$GEN->addArc(xs=>$xs+$ShiftX,ys=>$ys+$ShiftY,xe=>$it->{x}+$ShiftX,ye=>$it->{y}+$ShiftY,xc=>$it->{xc}+$ShiftX,yc=>$it->{yc}+$ShiftY,direction=>$it->{direction},symbol=>'r'.$sym_size,polarity=>'positive');
}
else{
$GEN->addLine(xs=>$xs+$ShiftX,ys=>$ys+$ShiftY,xe=>$it->{x}+$ShiftX,ye=>$it->{y}+$ShiftY,symbol=>'r'.$sym_size,polarity=>'positive');
}
($xs,$ys) = ($it->{x},$it->{y});
}
}
}
if ($COUPON_DATA->{add_aoi_pad} eq 'yes'){
foreach my $it (@{$imp_layer->{aoi_pads}}){
$GEN->addPad(x=>$it->{x}+$ShiftX,y=>$it->{y}+$ShiftY,symbol=>'r'.($sym_size+$COUPON_DATA->{aoi_pad_ar}*2),polarity=>'positive');
}
}
}
}
}
}
#outline
if ($gen_layer{'outline'}){
$GEN->COM('profile_to_rout',layer=>$gen_layer{'outline'},width=>$COUPON_DATA->{outline_width}||1);
}
#档点
if ($COUPON_DATA->{plug_layers}){
# 层名做变量替换
$COUPON_DATA->{plug_layers} =~ s/\$\{layer_count\}/sprintf("%02d", $COUPON_DATA->{layer_count})/ge;
my $layers = eval($COUPON_DATA->{plug_layers});
while ($layers and @$layers){
my $plug_layer = shift @$layers;
my $action = shift @$layers;
if ($action eq 'create'){
if (!$GEN->isLayerExists(job=>$Job,layer=>$plug_layer)){
$GEN->createLayer(job=>$Job,layer=>$plug_layer);
}
}
next unless $GEN->isLayerExists(job=>$Job,layer=>$plug_layer);
$GEN->affectedLayer(affected=>'yes',mode=>'single',layer=>$plug_layer,clear_before=>'yes');
$GEN->COM('sel_all_feat');
$GEN->selDelete;
##挡点需要填充
#$GEN->srFill(step_margin_x=>-0.005,step_margin_y=>-0.005);
my $drl_layer = $gen_layer{drill};
my $via_sym = 'r'.($COUPON_DATA->{hole_size} + 2*$COUPON_DATA->{plug_ar});
if ($COUPON_DATA->{drills}{drill}{include_coupon_groups}){
foreach my $grp (@{$COUPON_DATA->{drills}{drill}{include_coupon_groups}}){
foreach my $imp_lyr (@{$COUPON_DATA->{group}{$grp}{signal_layer}}){
my $imp_layer = $COUPON_DATA->{layers}{$imp_lyr};
foreach my $lr ('left','right'){
foreach my $pad ('_tpads','_gpads'){
if ($imp_layer->{$lr.$pad}){
foreach my $it (@{$imp_layer->{$lr.$pad}}){
$GEN->addPad(x=>$it->{x}+$ShiftX,y=>$it->{y}+$ShiftY,symbol=>$via_sym,polarity=>'positive',attributes=>[{attribute=>'.string',text=>$lr.'_hole'},{attribute=>'.drill',option=>'plated'}]);
}
}
}
}
}
}
}
foreach my $lr ('left','right'){
if ($trans->{$lr}{angle}){
$GEN->selClearFeature();
$GEN->selectByFilter(attribute=>[{attribute=>'.string',text=>$lr.'_hole'}]);
$GEN->COM('sel_transform',mode=>'anchor',oper=>'rotate',duplicate=>'no',%{$trans->{$lr}}) if $GEN->getSelectCount();;
}
}
#pin_hole
foreach my $lr ('left','right'){
if ($COUPON_DATA->{$lr.'_pin_hole'}){
$GEN->addPad(x=>$COUPON_DATA->{$lr.'_pin_hole'}{x}+$ShiftX,y=>$COUPON_DATA->{$lr.'_pin_hole'}{y}+$ShiftY,symbol=>'r'.($COUPON_DATA->{pin_hole_size} + 2*$COUPON_DATA->{plug_ar}),polarity=>'positive',attributes=>[{attribute=>'.drill',option=>'non_plated'}]);
}
}
#coplanar hole
foreach my $ud ('up','down'){
if ($COUPON_DATA->{$ud.'_coplanar_holes'}){
foreach my $it (@{$COUPON_DATA->{$ud.'_coplanar_holes'}}){
$GEN->addPad(x=>$it->{x}+$ShiftX,y=>$it->{y}+$ShiftY,symbol=>'r'.($COUPON_DATA->{coplanar_hole_size} + 2*$COUPON_DATA->{plug_ar}),attributes=>[{attribute=>'.drill',option=>'plated'}]);
}
}
}
}
}
$GEN->deleteLayer(job=>$Job,layer=>$tmp_layer);
$GEN->affectedLayer(mode=>'all',affected=>'yes');
$GEN->COM('sel_all_feat');
$GEN->COM('sel_delete_atr',attributes=>'.string');
$GEN->COM('disp_on');
$GEN->affectedLayer(mode=>'all',affected=>'no');
if ($COUPON_DATA->{hooks_draw_post}){
eval($COUPON_DATA->{hooks_draw_post});
}
##阻抗线补偿 Tomy 2017.12.14 阻抗在系统内部有做补偿脚本中不需要再补偿
###$GEN->COM('disp_off');
###foreach my $layer (sort {$matrix->{$a}{row} <=> $matrix->{$b}{row}} keys %$matrix) {
### if($matrix->{$layer}{tl_type} =~ /(inner|outer)/ ){
### my $value = 0;
### if($matrix->{$layer}{tl_type} =~ /(inner)/ ){
### $value = $IKM->get_jobinfo(-jobid=>$JOB_ID,-jobcategory=>'work',-jobinfo=>'line_compensation_inner');
### }
### else{
### $value = $IKM->get_jobinfo(-jobid=>$JOB_ID,-jobcategory=>'work',-jobinfo=>'line_compensation_outer');
### }
### next unless $value;
### $GEN->affectedLayer(affected=>'yes',layer=>[$layer],clear_before=>'yes');
### $GEN->selectByFilter(feat_types=>'line\;arc',polarity=>'positive',profile=>'all');
### $GEN->selAddAttr(attribute=>[{attribute=>'tl_string',text=>'compensation_'.$value.'_mil'}])if ( $GEN->getSelectCount() > 0 );
### $GEN->selectByFilter(attribute=>[{attribute=>'tl_string',text=>'compensation_'.$value.'_mil'}]);
### $GEN->COM('sel_resize',size=>$value,corner_ctl=>'no')if ( $GEN->getSelectCount() > 0 );
### }
###}
###$GEN->COM('disp_on');
$GEN->clearLayers();
$GEN->affectedLayer(mode=>'all',affected=>'no');
$GEN->closeStep();
}
if ($WarningMsg){
$GUI->msgbox(-icon=>'warning',-text=>join("\n",@$WarningMsg));
#$GEN->PAUSE('Please Edit Then Continue');
my $html = '<html><body bgcolor="#EEEEFF" link="#0000FF" vlink="#FF00FF" alink="#FF0000" text="#000000"><FONT color="#FF00FF">';
foreach my $item (@$WarningMsg){
$html .= $item.'<p>';
}
$html .='</FONT></body></html>';
$IKM->update_flow_report(-report=>$html,-job_id=>$ARGS{job_id},-process_id=>$ARGS{process_id});
$Return = 'Warning';
}
else{
$IKM->update_flow_report(-report=>'',-job_id=>$ARGS{job_id},-process_id=>$ARGS{process_id});
}
unless ($GEN->{STATUS}){
return $Return;
}
else{
$GUI->msgbox(-icon=>'error',-image=>'genesis_error',-compound=>'top',-text=>join("\n",@{$GEN->{STATUS}}));
#addFlowNotes(-notes=>" Genesis Error:\n ".join("\n ",@{$GEN->{STATUS}}));
return 'Error';
}
}
catch Error::Simple with {
my $error = encode("utf8",shift);
print $error,"\n";
$GUI->msgbox(-text=>$error);
return 'Error';
}
finally{
#$GEN->deleteLayer(layer=>$tmp_layer) if ($GEN->isLayerExists(job=>$job,layer=>$tmp_layer));
};
sub opt_lines{
my @lines = @_;
my %lines_hash;
my $n = 0;
my @return = (shift @lines);
foreach my $line (@lines){
$lines_hash{$n++} = $line;
}
#向前查找
my ($xs,$ys) = ($return[0]{xs},$return[0]{ys});
my ($xe,$ye) = ($return[0]{xe},$return[0]{ye});
my $last_count = 0;
while($last_count != scalar(keys %lines_hash)){
$last_count = scalar(keys %lines_hash);
foreach my $n (keys %lines_hash){
my $line = $lines_hash{$n};
if (abs($xs - $line->{xs}) < 0.0001 && abs($ys - $line->{ys}) < 0.0001){
($line->{xs},$line->{ys},$line->{xe},$line->{ye}) =
($line->{xe},$line->{ye},$line->{xs},$line->{ys});
$line->{direction} = ($line->{direction} eq 'cw') ? 'ccw' : 'cw';
($xs,$ys) = ($line->{xs},$line->{ys});
delete $lines_hash{$n};
unshift @return,$line;
}
elsif (abs($xs - $line->{xe}) < 0.0001 && abs($ys - $line->{ye}) < 0.0001){
($xs,$ys) = ($line->{xs},$line->{ys});
delete $lines_hash{$n};
unshift @return,$line;
}
}
}
$last_count = 0;
while($last_count != scalar(keys %lines_hash)){
$last_count = scalar(keys %lines_hash);
foreach my $n (keys %lines_hash){
my $line = $lines_hash{$n};
if (abs($xe - $line->{xs}) < 0.0001 && abs($ye - $line->{ys}) < 0.0001){
($xe,$ye) = ($line->{xe},$line->{ye});
delete $lines_hash{$n};
push @return,$line;
}
elsif (abs($xe - $line->{xe}) < 0.0001 && abs($ye - $line->{ye}) < 0.0001){
($line->{xs},$line->{ys},$line->{xe},$line->{ye}) =
($line->{xe},$line->{ye},$line->{xs},$line->{ys});
$line->{direction} = ($line->{direction} eq 'cw') ? 'ccw' : 'cw';
($xe,$ye) = ($line->{xe},$line->{ye});
delete $lines_hash{$n};
push @return,$line;
}
}
}
return @return;
}
#pcbpdm里层是存的数字,为了兼容要转为tlname
sub imp_layernum2tlname {
my $iLyrNum = shift;
my $ret;
if ($iLyrNum == 1) {
$ret = "top";
}elsif ($iLyrNum == $LayerCount) {
$ret = "bottom";
}elsif($iLyrNum){
$ret = "l".$iLyrNum;
}
return $ret;
}
sub _um2mil{
my $value = shift;
return ($value/25.4);
}
=head1 NAME
Top::IKM - IKM接口
=head1 DESCRIPTION
IKM接口
=head1 VERSION HISTORY
=head2 V1.00 2016-06-12 Tony Guo
1.IKM接口
=head2 V1.00 2017-11-13 Cody Yu
1.增加get_process_by_name函数
2.增加get_process_status函数
=cut
package TOP::IKM;
use strict;
#use Data::Dump 'dump';
use Sys::Hostname;
use Encode;
use utf8;
use JSON;
use Data::Dump 'dump';
sub new {
my $class = shift;
my %par = (@_);
my $self = bless {}, $class;
return $self;
}
=head1 MOTHODS
=head2 $DB->B<get_jobinfo>(-jobname=>?,-jobcategory=>?,-jobinfo=>?,-units=>?)
=over
=item * Description
#获取料号信息
=item * Parameters
[B<-jobid>] => interger : 料号ID
B<-jobinfo> => string : 料号属性名称,
[B<-withspec>] => 0|1 : 如果料号info没定义,是否取spec
=item * Return
料号属性的值,如果料号属性不存在,则返回undef
=item * Example
=back
=cut
sub get_jobinfo{
my $self = shift;
my %par = @_;
$par{-jobid} = $self->get_job_id($par{-jobname}) unless ($par{-jobid});
my $func = 'function(ARGV){
importPackage("pcbpdm.core");
var value = PdmLibraryData.getJobAttrValue(ARGV["-jobid"], ARGV["-jobinfo"]);
return value
}';
my $ret = $self->command( $func, \%par, 1)->{data};
if ($par{-withspec} and (!defined $ret or $ret eq ''))
{
$func = 'function(ARGV){
importPackage("pcbpdm.core");
var value = PdmLibraryData.getSpecValue(ARGV["-jobid"], ARGV["-jobinfo"]);
return value
}';
$ret = $self->command( $func, \%par, 1)->{data};
}
return $ret;
}
=head2 $DB->B<save_jobinfo>(-jobname=>?,-jobinfohash=>?)
=over
=item * Description
保存料号信息
=item * Parameters
B<-jobname> => string : 料号名称,-jobid -jobname必有一个存在
[B<-jobid>] => interger : 料号ID
B<-jobinfohash> => {jobinfo1=>value1,jobinfo2=>value2...} :
=item * Return
0 表示保存成功,否则返回错误消息
=item * Example
=back
=cut
sub save_jobinfo{
my $self = shift;
my %par = @_;
$par{-jobid} = $self->get_job_id($par{-jobname}) unless ($par{-jobid});
my $func = 'function(ARGV){
importPackage("pcbpdm.core");
var value = PdmLibraryData.saveJobAttr(ARGV["-jobid"], ARGV["-jobinfohash"]);
return value;
}';
my $ret = $self->command( $func, \%par, 1);
return $ret->{data};
}
sub get_job_id
{
my $self = shift;
my $job_name = shift;
my $func = 'function(ARGV){
var db = new TSqlQueryV2(T_SQLCNT_POOL.getSqlDatabase());
var data = db.selectValue({table:"pdm_job", field:["id"], where:{jobname:ARGV["job_name"]}});
return new TDataResponse(db.lastError(), data);
}';
my $ret = $self->command( $func, {job_name=>$job_name}, 1);
return $ret->{data};
}
sub get_job_name
{
my $self = shift;
my $job_id = shift;
my $func = 'function(ARGV){
var db = new TSqlQueryV2(T_SQLCNT_POOL.getSqlDatabase());
var data = db.selectValue({table:"pdm_job", field:["jobname"], where:{id:ARGV["job_id"]}});
return new TDataResponse(db.lastError(), data);
}';
my $ret = $self->command( $func, {job_id=>$job_id}, 1);
return $ret->{data};
}
sub script_end
{
my $self = shift;
my $job_id = shift;
my $process_id = shift;
my $func = 'function(ARGV){
APP.notify("TOPCAM_SCRIPT_END", {job_id:ARGV["job_id"],process_id:ARGV["process_id"]});
}';
my $ret = $self->command( $func, {job_id=>$job_id,process_id=>$process_id}, 1);
return ;
}
sub get_job_workflow_report
{
my $self = shift;
my $job_id = shift;
my $process_id = shift;
my $func = 'function(ARGV){
var db = new TSqlQueryV2(T_SQLCNT_POOL.getSqlDatabase());
var data = db.selectMap({table:"pdm_job_workflow", field:["report"], where:{job_id:ARGV["job_id"],process_id:ARGV["process_id"]}});
return new TDataResponse(db.lastError(), data);
}';
my $ret = $self->command( $func, {job_id=>$job_id, process_id=>$process_id}, 1);
return $ret->{data};
}
sub update_job_workflow_log{
my $self = shift;
my %par = @_;
my $err;
my $func = 'function(ARGV){
var db = new TSqlQueryV2(T_SQLCNT_POOL.getSqlDatabase());
var notes = ARGV["notes"];
delete ARGV["notes"];
var id = db.replaceRow({table:"pdm_job_workflow", data:ARGV, uniquefield:["job_id", "process_id"],"returnid":"id"});
db.insertRow({table:"sys_log", data:{table_name:"pdm_job_workflow", action_type:ARGV["status"],
remark:notes,user_name:ARGV["user_name"], user_id:ARGV["user_id"], row_id:id}});
return new TDataResponse(db.lastError(),"");
}';
if ($err) {
return {
errText => $err,
errCode => "",
};
}
my $ret = $self->command( $func, \%par, 1);
if ($ret->{errText}) {
return {
errText => $ret->{errText},
errCode => $ret->{errCode},
};
}
return $ret;
}
sub get_script_by_name
{
my $self = shift;
my $script_name = shift;
my $func = 'function(ARGV){
var db = new TSqlQueryV2(T_SQLCNT_POOL.getSqlDatabase());
var data = db.selectMap({table:"pdm_workprocess_script", field:["name","version","type","content","status","parameter_value","encrypt"],
where:{name:ARGV["script_name"], status:"released"}});
return new TDataResponse(db.lastError(), data);
}';
my $ret = $self->command( $func, {script_name=>$script_name}, 1);
return $ret->{data};
}
sub get_script_by_id
{
my $self = shift;
my $script_id = shift;
my $func = 'function bb (ARGV){
var db = new TSqlQueryV2(T_SQLCNT_POOL.getSqlDatabase());
var data = db.selectMap({table:"pdm_workprocess_script", field:["name","version","type","content","status","parameter_value","encrypt"], where:{id:ARGV["script_id"]}});
return new TDataResponse(db.lastError(), data);
}';
my $ret = $self->command( $func, {script_id=>$script_id}, 1);
return $ret->{data};
}
sub get_process_by_id
{
my $self = shift;
my $process_id = shift;
my $func = 'function(ARGV){
var db = new TSqlQueryV2(T_SQLCNT_POOL.getSqlDatabase());
var data = db.selectMap({table:"pdm_workprocess", field:["name", "script_parameter"], where:{id: ARGV["process_id"]}});
return new TDataResponse(db.lastError(), data);
}';
my $ret = $self->command( $func, {process_id =>$process_id}, 1);
return $ret->{data};
}
sub get_process_by_name
{
my $self = shift;
my $process_name = shift;
my $func = 'function(ARGV){
var db = new TSqlQueryV2(T_SQLCNT_POOL.getSqlDatabase());
var data = db.selectMap({table:"pdm_workprocess", field:["*"], where:{name: ARGV["process_name"]}});
return new TDataResponse(db.lastError(), data);
}';
my $ret = $self->command( $func, {process_name =>$process_name}, 1);
return $ret->{data};
}
sub set_socket_server
{
my $self = shift;
$self->{HOST} = shift;
$self->{PORT} = shift;
}
sub command{
my $self = shift;
my $func = shift;
my $argv = shift;
my $wait_ans = shift;
if ($self->{HOST} && $self->{PORT})
{
my $sock = new IO::Socket::INET(
PeerAddr => $self->{HOST},
PeerPort => $self->{PORT},
Proto => 'tcp'
);
unless ($sock)
{
return {
errText => "Sock could not be create:Reason:$!",
errCode => "SOCKET_ERROR",
};
}
my $json = new JSON;
my $msg = '$@IKMC~:'. $json->encode({FN=>$func, PA=>$argv}). '$@IKME~:';
print $sock $msg;
$sock->flush();
my $ret = {};
my $ikmend = '$@IKME~:';
if ($wait_ans)
{
my $all_recv = '';
while (1) {
my $recv_data;
$sock->recv($recv_data, 1024);
#if ($recv_data =~ s/\Q$ikmend\E\s*$//){
# $all_recv .= $recv_data; last;
#}
#else{
# $all_recv .= $recv_data;
#}
$all_recv .= $recv_data;
if ($all_recv =~ s/\Q$ikmend\E\s*$//){
last;
}
}
$all_recv = decode('utf-8', $all_recv);
my $res = $json->decode($all_recv);
$ret->{errText} = $res->{ET};
$ret->{errCode} = $res->{EC};
$ret->{data} = $res->{DT};
}
close($sock);
$sock = undef;
return $ret;
}
else
{
return {
errText => "ikm host and port can not be null!",
errCode => "SOCKET_ERROR",
};
}
}
=head2 $DB->B<get_layerinfo>(-jobname=>?,-layer=>?,-layerinfo=>?)
=over
=item * Description
获取层属性信息
=item * Parameters
B<-jobname> => string : 料号名称,-jobid -jobname必有一个存在
[B<-jobid>] => interger : 料号ID
B<-layer> => string : 层的name,
B<-layerinfo> => string :层属性名称
=item * Return
层属性的值,如果料号属性不存在,则返回undef
=item * Example
N.A
=back
=cut
sub get_layerinfo{
my $self = shift;
my %par = @_;
$par{-jobid} = $self->get_job_id($par{-jobname}) unless ($par{-jobid});
$par{-layer} = $par{-tlname} unless exists $par{-layer};
my $func = 'function(ARGV){
importPackage("pcbpdm.core");
var value = PdmLibraryData.getLayerAttrValue(ARGV["-jobid"], ARGV["-layer"], ARGV["-layerinfo"]);
return value;
}';
my $ret = $self->command( $func, \%par, 1)->{data};
return $ret;
}
=head2 $DB->B<save_layerinfo>(-jobname=>?,-layer=>?,-layerinfohash=>?)
=over
=item * Description
保存层信息
=item * Parameters
B<-jobname> => string : 料号名称,-jobid -jobname必有一个存在
[B<-jobid>] => interger : 料号ID
B<-layer> => string : 层的tl_name,
B<-layerinfohash> => {layerinfo1=>value1,layerinfo2=>value2...}
=item * Return
0:表示保存成功,否则返回错误消息
=item * Example
=back
=cut
sub save_layerinfo{
my $self = shift;
my %par = @_;
$par{-jobid} = $self->get_job_id($par{-jobname}) unless ($par{-jobid});
$par{-layer} = $par{-tlname} unless exists $par{-layer};
my $func = 'function(ARGV){
importPackage("pcbpdm.core");
var value = PdmLibraryData.saveLayerAttr(ARGV["-jobid"], ARGV["-infodata"]);
return value
}';
my $ret = $self->command( $func, {-jobid=>$par{-jobid}, -infodata=>{$par{-layer} => $par{-layerinfohash}}}, 1)->{data};
return $ret;
}
sub select_arrayhash{
my $self = shift;
my %par = @_;
my $err;
unless ($par{-table}) {
$err = "'-table' cannot be empty!!!";
}
unless ($par{-field}) {
$par{-field} = "*";
}
if (ref($par{-field}) =~ /ARRAY/i) {
my $fldLst = $par{-field};
$par{-field} = join(",",@$fldLst);
}
if (ref($par{-where}) =~ /HASH/i) {
my $whereMap = $par{-where};
my @whereLst;
foreach my $field (keys %$whereMap) {
if (ref($whereMap->{$field}) =~ /ARRAY/i && scalar(@{$whereMap->{$field}}) > 0) {
my $joinIn = join("','",@{$whereMap->{$field}});
push(@whereLst,"$field IN ('$joinIn')");
}else {
push(@whereLst,"$field = '$whereMap->{$field}'");
}
}
$par{-where} = join(" AND ",@whereLst);
}
my $func = 'function(ARGV){
var db = new TSqlQueryV2(T_SQLCNT_POOL.getSqlDatabase());
var data = db.selectArrayMap({
table:ARGV["table"], field:ARGV["field"],
where:ARGV["where"], order:ARGV["order"]
});
return new TDataResponse(db.lastError(), data);
}';
if ($err) {
return {
errText => $err,
errCode => "",
};
}
my $ret = $self->command( $func, {table=>$par{-table},field=>$par{-field},where=>$par{-where},order=>$par{-order}}, 1);
if ($ret->{errText}) {
return {
errText => $ret->{errText},
errCode => $ret->{errCode},
};
}
return $ret->{data};
}
sub select_hash{
my $self = shift;
my %par = @_;
my $err;
unless ($par{-table}) {
$err = "'-table' cannot be empty!!!";
}
unless ($par{-field}) {
$par{-field} = "*";
}
if (ref($par{-field}) =~ /ARRAY/i) {
my $fldLst = $par{-field};
$par{-field} = join(",",@$fldLst);
}
if (ref($par{-where}) =~ /HASH/i) {
my $whereMap = $par{-where};
my @whereLst;
foreach my $field (keys %$whereMap) {
push(@whereLst,"$field = '$whereMap->{$field}'");
}
$par{-where} = join(" AND ",@whereLst);
}
my $func = 'function(ARGV){
var db = new TSqlQueryV2(T_SQLCNT_POOL.getSqlDatabase());
var data = db.selectMap({
table:ARGV["table"], field:ARGV["field"],
where:ARGV["where"], order:ARGV["order"]
});
return new TDataResponse(db.lastError(), data);
}';
if ($err) {
return {
errText => $err,
errCode => "",
};
}
my $ret = $self->command( $func, {table=>$par{-table},field=>$par{-field},where=>$par{-where},order=>$par{-order}}, 1);
if ($ret->{errText}) {
return {
errText => $ret->{errText},
errCode => $ret->{errCode},
};
}
return $ret->{data};
}
sub select_fieldarray{
my $self = shift;
my %par = @_;
my $err;
unless ($par{-table}) {
$err = "'-table' cannot be empty!!!";
}
unless ($par{-field}) {
$err = "'-field' cannot be empty!!!";
}
unless ($par{-where}) {
$par{-where} = " 1=1 "
}
if (ref($par{-where}) =~ /HASH/i) {
my $whereMap = $par{-where};
my @whereLst;
foreach my $field (keys %$whereMap) {
push(@whereLst,"$field = '$whereMap->{$field}'");
}
$par{-where} = join(" AND ",@whereLst);
}
my $func = 'function(ARGV){
var db = new TSqlQueryV2(T_SQLCNT_POOL.getSqlDatabase());
var data = db.selectArrayValue({
table:ARGV["table"], field:ARGV["field"],
where:ARGV["where"], order:ARGV["order"], valuefield:ARGV["field"]
});
return new TDataResponse(db.lastError(), data);
}';
if ($err) {
return {
errText => $err,
errCode => "",
};
}
my $ret = $self->command( $func, {table=>$par{-table},field=>$par{-field},where=>$par{-where},order=>$par{-order}}, 1);
if ($ret->{errText}) {
return {
errText => $ret->{errText},
errCode => $ret->{errCode},
};
}
return $ret->{data};
}
sub select_value{
my $self = shift;
my %par = @_;
my $err;
unless ($par{-table}) {
$err = "'-table' cannot be empty!!!";
}
unless ($par{-field}) {
$err = "'-field' cannot be empty!!!";
}
if (ref($par{-where}) =~ /HASH/i) {
my $whereMap = $par{-where};
my @whereLst;
foreach my $field (keys %$whereMap) {
push(@whereLst,"$field = '$whereMap->{$field}'");
}
$par{-where} = join(" AND ",@whereLst);
}
my $func = 'function(ARGV){
var db = new TSqlQueryV2(T_SQLCNT_POOL.getSqlDatabase());
var data = db.selectValue({
table:ARGV["table"], field:ARGV["field"],
where:ARGV["where"], order:ARGV["order"]
});
return new TDataResponse(db.lastError(), data);
}';
if ($err) {
return {
errText => $err,
errCode => "",
};
}
my $ret = $self->command( $func, {table=>$par{-table},field=>$par{-field},where=>$par{-where},order=>$par{-order}}, 1);
if ($ret->{errText}) {
return {
errText => $ret->{errText},
errCode => $ret->{errCode},
};
}
return $ret->{data};
}
sub get_now(){
my $self = shift;
my $func = 'function(ARGV){
var db = new TSqlQueryV2(T_SQLCNT_POOL.getSqlDatabase());
var data = db.getNow();
return new TDataResponse(db.lastError(), data);
}';
my $ret = $self->command( $func, {}, 1);
if ($ret->{errText}) {
return {
errText => $ret->{errText},
errCode => $ret->{errCode},
};
}
return $ret->{data};
}
sub get_today(){
my $self = shift;
my $func = 'function(ARGV){
var db = new TSqlQueryV2(T_SQLCNT_POOL.getSqlDatabase());
var data = db.getToday();
return new TDataResponse(db.lastError(), data);
}';
my $ret = $self->command( $func, {}, 1);
if ($ret->{errText}) {
return {
errText => $ret->{errText},
errCode => $ret->{errCode},
};
}
return $ret->{data};
}
sub get_config{
my $self = shift;
my $path = shift;
my $name = shift;
my $err;
unless ($path && $name) {
$err = "path and name cannot be empty!!!";
}
my $ret = $self->select_value(
-table=>'pub_conf',
-field=>'text_data',
-where=>{path=>$path,name=>$name}
);
if (ref($ret) =~ /hash/i && $ret->{errText}) {
return {
errText => $ret->{errText},
errCode => $ret->{errCode},
};
}
return $ret;
}
sub save_config{
my $self = shift;
my %par = @_;
my $path = $par{-path};
my $name = $par{-name};
my $value = $par{-value};
my $err;
unless ($path && $name) {
$err = "path and name cannot be empty!!!";
}
my $func = 'function(ARGV){
var db = new TSqlQueryV2(T_SQLCNT_POOL.getSqlDatabase());
var data = db.replaceRow({table:"pub_conf",data:{path:ARGV["path"],name:ARGV["name"], text_data:ARGV["value"]},uniquefield:["path","name"]});
return new TDataResponse(db.lastError(), data);
}';
my $data = {path =>$path,name=>$name,value=>$value};
my $ret = $self->command($func,$data,1);
if (ref($ret) =~ /hash/i && $ret->{errText}) {
return {
errText => $ret->{errText},
errCode => $ret->{errCode},
};
}
return $ret;
}
sub update{
my $self = shift;
my %par = @_;
my $err;
if (ref($par{-where}) =~ /HASH/i) {
my $whereMap = $par{-where};
my @whereLst;
foreach my $field (keys %$whereMap) {
if (ref($whereMap->{$field}) =~ /ARRAY/i && scalar(@{$whereMap->{$field}}) > 0) {
my $joinIn = join("','",@{$whereMap->{$field}});
push(@whereLst,"$field IN ('$joinIn')");
}else {
push(@whereLst,"$field = '$whereMap->{$field}'");
}
}
$par{-where} = join(" AND ",@whereLst);
}
my $data = $par{-data};
my $json = new JSON;
my $data_json = $json->encode($data);
my $func = 'function(ARGV){
var db = new TSqlQueryV2(T_SQLCNT_POOL.getSqlDatabase());
db.updateRow({
"table": ARGV["table"],
"data": JSON.parse(ARGV["data"]),
"where": ARGV["where"]
});
return new TDataResponse(db.lastError(),"");
}';
if ($err) {
return {
errText => $err,
errCode => "",
};
}
my $ret = $self->command( $func, {table=>$par{-table},data=>$data_json,where=>$par{-where}}, 1);
if ($ret->{errText}) {
return {
errText => $ret->{errText},
errCode => $ret->{errCode},
};
}
return $ret;
}
sub delete{
my $self = shift;
my %par = @_;
my $err;
if (ref($par{-where}) =~ /HASH/i) {
my $whereMap = $par{-where};
my @whereLst;
foreach my $field (keys %$whereMap) {
if (ref($whereMap->{$field}) =~ /ARRAY/i && scalar(@{$whereMap->{$field}}) > 0) {
my $joinIn = join("','",@{$whereMap->{$field}});
push(@whereLst,"$field IN ('$joinIn')");
}else {
push(@whereLst,"$field = '$whereMap->{$field}'");
}
}
$par{-where} = join(" AND ",@whereLst);
}
my $func = 'function(ARGV){
var db = new TSqlQueryV2(T_SQLCNT_POOL.getSqlDatabase());
db.deleteRow({
"table": ARGV["table"],
"where": ARGV["where"]
});
return new TDataResponse(db.lastError(),"");
}';
if ($err) {
return {
errText => $err,
errCode => "",
};
}
my $ret = $self->command( $func, {table=>$par{-table},where=>$par{-where}}, 1);
if ($ret->{errText}) {
return {
errText => $ret->{errText},
errCode => $ret->{errCode},
};
}
return $ret;
}
sub insert{
my $self = shift;
my %par = @_;
my $err;
if (ref($par{-where}) =~ /HASH/i) {
my $whereMap = $par{-where};
my @whereLst;
foreach my $field (keys %$whereMap) {
if (ref($whereMap->{$field}) =~ /ARRAY/i && scalar(@{$whereMap->{$field}}) > 0) {
my $joinIn = join("','",@{$whereMap->{$field}});
push(@whereLst,"$field IN ('$joinIn')");
}else {
push(@whereLst,"$field = '$whereMap->{$field}'");
}
}
$par{-where} = join(" AND ",@whereLst);
}
my $data = $par{-data};
my $json = new JSON;
my $data_json = $json->encode($data);
my $func = 'function(ARGV){
var db = new TSqlQueryV2(T_SQLCNT_POOL.getSqlDatabase());
db.insertRow({
"table": ARGV["table"],
"data": JSON.parse(ARGV["data"]),
});
return new TDataResponse(db.lastError(),"");
}';
if ($err) {
return {
errText => $err,
errCode => "",
};
}
my $ret = $self->command( $func, {table=>$par{-table},data=>$data_json,where=>$par{-where}}, 1);
if ($ret->{errText}) {
return {
errText => $ret->{errText},
errCode => $ret->{errCode},
};
}
return $ret;
}
sub batch_insert{
my $self = shift;
my %par = @_;
my $err;
if (ref($par{-where}) =~ /HASH/i) {
my $whereMap = $par{-where};
my @whereLst;
foreach my $field (keys %$whereMap) {
if (ref($whereMap->{$field}) =~ /ARRAY/i && scalar(@{$whereMap->{$field}}) > 0) {
my $joinIn = join("','",@{$whereMap->{$field}});
push(@whereLst,"$field IN ('$joinIn')");
}else {
push(@whereLst,"$field = '$whereMap->{$field}'");
}
}
$par{-where} = join(" AND ",@whereLst);
}
my $data = $par{-data};
my $json = new JSON;
my $data_json = $json->encode($data);
my $func = 'function(ARGV){
var db = new TSqlQueryV2(T_SQLCNT_POOL.getSqlDatabase());
db.batchInsert(
ARGV["table"],
ARGV["field"],
JSON.parse(ARGV["data"])
);
return new TDataResponse(db.lastError(),"");
}';
if ($err) {
return {
errText => $err,
errCode => "",
};
}
my $ret = $self->command( $func, {table=>$par{-table},data=>$data_json,field=>$par{-field}}, 1);
if ($ret->{errText}) {
return {
errText => $ret->{errText},
errCode => $ret->{errCode},
};
}
return $ret;
}
sub update_flow_report{
my $self = shift;
my %par = @_;
my $err;
unless ($par{-job_id} && $par{-process_id}) {
$err = "-job_id and -process_id cannot be empty!!!";
}
if ($err) {
return {
errText => $err,
errCode => "",
};
}
if ($par{-mode} eq 'append' or $par{-mode} eq 'before'){
my $org = $self->select_value(
-table=>'pdm_job_workflow',
-field=>'report',
-where=>{job_id=>$par{-job_id},process_id=>$par{-process_id}}
);
if ($par{-mode} eq 'append'){
$par{-report} = $org ."\n". $par{-report};
}
else{
$par{-report} = $par{-report}."\n".$org;
};
}
my $ret = $self->update_job_workflow_log(job_id=>$par{-job_id},
process_id=>$par{-process_id},
report=>$par{-report});
if ($ret->{errText}) {
return {
errText => $ret->{errText},
errCode => $ret->{errCode},
};
}
return undef;
}
sub get_info_style{
my $self = shift;
my %par = @_;
my $func = 'function(ARGV){
var db = new TSqlQueryV2(T_SQLCNT_POOL.getSqlDatabase());
var data = db.selectMap({
table:"pdm_attrname", field:["*"],
where:{name:ARGV["infoname"]}
});
return new TDataResponse(db.lastError(), data);
}';
my $ret = $self->command( $func, {infoname=>$par{-infoname}}, 1);
##数据类型对应 c++ => perl
my %map = (
string=>'string',
number=>'number',
integer=>'integer',
plain_text=>'text',
enum=>'enum',
check_box=>'boolean',
editable_enum=>'editable_enum',
radio_box=>'radio',
multiple_enum=>'multiple',
);
my @return;
push @return,name=>$ret->{data}{name};
push @return,label=>$ret->{data}{title};
push @return,units=>$ret->{data}{units};
push @return,type=>$map{$ret->{data}{data_type}};
my $json = new JSON;
if( $ret->{data}{data_type} =~ /^(enum)$/ ){
my @list;
my $data = $json->decode($ret->{data}{option_list});
foreach my $item(@{$data}){
push @list,{name=>$item->{name},display_name=>$item->{text}};
}
push @return,property=>{
tl_field=>[name=>'scalar',display_name=>'text'],
tl_value_field=>'name',
tl_data=>\@list,
};
}
elsif( $ret->{data}{data_type} =~ /^(editable_enum)$/ ){
my @list;
my $data = $json->decode($ret->{data}{option_list});
foreach my $item(@{$data}){
push @list,{name=>$item->{text}};
}
push @return,property=>{
tl_field=>[name=>'text'],
tl_data=>\@list,
};
}
elsif( $ret->{data}{data_type} =~ /^(check_box)$/ ){
my @list;
my $data = $json->decode($ret->{data}{option_list});
foreach my $item(@{$data}){
push @list,{name=>$item->{text}};
}
push @return,property=>{
tl_columns => 2,
tl_list=>\@list,
};
}
elsif( $ret->{data}{data_type} =~ /^(radio_box)$/ ){
my @list;
my $data = $json->decode($ret->{data}{option_list});
foreach my $item(@{$data}){
push @list,$item->{name}=>$item->{text};
}
push @return,property=>{
tl_columns =>scalar(@list),
tl_list=>\@list,
};
}
elsif( $ret->{data}{data_type} =~ /^(multiple_enum)$/ ){
my @list;
my $data = $json->decode($ret->{data}{option_list});
foreach my $item(@{$data}){
push @list,{name=>$item->{name},display_name=>$item->{text}};
}
push @return,property=>{
tl_columns => 2,
tl_list=>\@list,
};
}
##
return @return;
}
=head1 MOTHODS
=head2 $IKM->B<get_process_status>(-jobname=>?,-process_name=>?)
=over
=item * Description
#获取步骤信息
=item * Parameters
B<-jobname> => string : 料号名称,-jobid -jobname必有一个存在
[B<-jobid>] => interger : 料号ID
B<-process_name> => string : 步骤名称
=item * Return
步骤状态信息,如果步骤不存在,则返回undef
=item * Example
=back
=cut
sub get_process_status{
my $self = shift;
my %par = @_;
unless( $par{-jobid} ){
if($par{-job_id}){
$par{-jobid} = $par{-job_id};
}
elsif($par{-jobname}){
$par{-jobid} = $self->get_job_id($par{-jobname});
}
elsif($par{-job_name}){
$par{-jobid} = $self->get_job_id($par{-job_name});
}
elsif($par{job}){
$par{-jobid} = $self->get_job_id($par{job});
}
elsif($par{-job}){
$par{-jobid} = $self->get_job_id($par{-job});
}
}
unless( $par{-process_id} ){
if( $par{-process_name} ){
$par{-process_id} = $self->get_process_by_name($par{-process_name})->{id};
}
elsif( $par{process_name} ){
$par{-process_id} = $self->get_process_by_name($par{process_name})->{id};
}
}
$par{-order} = 'id DESC' unless $par{order};
my $func = 'function(ARGV){
var db = new TSqlQueryV2(T_SQLCNT_POOL.getSqlDatabase());
var data = db.selectMap({table:"pdm_job_workflow", field:["*"], where:{job_id:ARGV["-jobid"],process_id:ARGV["-process_id"]},order:ARGV["-order"]});
return new TDataResponse(db.lastError(), data);
}';
my $ret = $self->command( $func, {-jobid=>$par{-jobid},-process_id=>$par{-process_id},-order=>$par{-order}}, 1);
if ($ret->{errText}) {
return {
errText => $ret->{errText},
errCode => $ret->{errCode},
};
}
return $ret->{data};
}
1;
let num = 23.1
console.log(
Math.round(num.toFixed(4)*1000)/1000 - 0
);
This source diff could not be displayed because it is too large. You can view the blob instead.
eWhlY2QK67LB3UUI6AogR7V3QZJJUFRJ30OI6AogU7FSQUZFV7VS4goJ6FsKCQkKCV0KCiB2RVJ3SU=4X0hJUZRPUlkICgl2MSOwMCAyMD7z536tMj7gQW=keSB1dQoJ6CAgMSEmlrDniYjmnKwKCVYx5jAx6D6wMjAtNy0xNSDorrjkuLXmlowKCSAg6D7uLYaFLaS2LbGCLbGCL1CNLaKeLYqgdC=iLqCHIK+GCgl2MSOwMiAyMD6w53gtNiDorrjkuLXmlowKCSAg6D7uLaKeLYqgL5qGLqCLLoWuLY+gLpI7L5+hLoGvI6eqLYqoLoISLYiXVC=CLqCHIK+GCgl2MSOwMyAyMD6y536tMjgg1nNLCgkg6CAx5u2Bh+2xguSOi+SOg42xgu2Opu2FpeaXtu2inu2Ko42xj+iUveadoeSEtiAgCgkJCiB6RUxQ4goJPGh0b2w+PGJv1HkgYmdjbWxvcnsiJ7R7RUNGRS6+CgkJPG1vbnQgcWlI1XsiMy6gYW=sbTJE6iQwMDN7Qj6iPjxwPu2Kn+iDveeug4SEizwvcDO85W1vbnQ+CgkJ6CA8cDOgLqCLLoWuIaK7LaIhLoyHLISILbelLY2TLEGEL1I5Lpu0LpSLLbGCL1CN6DwvcDOKCQkg6DxicjOKCQk81m=udCBzaXpley6z6iBjbWxvcnsiJDAwM0RCMi6+PHA+LY+CLp2wIY2NLEWuPC=wPjwv1m=udDOKCQk81m=udCBjbWxvcnsiJDAw4DAwMC6+PHA+64KXjyA85TA+PC=mbWL0PgoJCSAgPHA+6DwvcDOKCQk81m=udCBjbWxvcnsiJDAw4DAwMC6+PHA+64KXjyA85TA+PC=mbWL0PgoJCSA8cDOgPC=wPgoJC3xmbWL06HNpemVE6jMi6GNvbG=yey6kMDAzR76y6jO8cDEmsIjmh6/kuovpobk85TA+PC=mbWL0PgoJCSAgPHA+64KXjyDmlIAgPC=wPgoJCSAgPGJyPgoJPC=ibWRLPjwvaHRtbDOK6CAKeWNZdAoKJHVz1SBzdHJpYTQ=CiRteSAo60pPQiwjR0V45CNHVUks60RC5CNK30JfSUQs6ZBBUik=CnVz1SBZdGYOPQpZcWUgSlNP3j0KdXNl67RhdG7I4kRZbXAgJWRZbXAnPQpteSAo60pvYiwjUTRlcCkgeyAo60pPQixZbmRl1ik=CmZL6CNS1XRZcmOgeyAn1mluaXNoJz0KbXkgQFJlcG=ydD0KbXkgKHZzdGFjaZ=ybTdz5CNu5CNLK30KCnRyeSB+CglteSAjanNvbiBE67p330OtPmLldy0+Y2xsbTdfbm=ucmVmPQoJJCQkLq4ALp+lRWVu1XNpc+a2me2Pt+aYr+2Qpu2tm42cq42LtuaJk+28gAoJa2YgKC7g60dF3i0+aXNKbWJFeGlzdHMoam=iezOjSm=iKSApfgog6CAg6CAg6CNHVUktPmZz1WJveCgta2Nvbns+JWVycm=yJywtdGVOdHs+6ua2me2PtyAjSm=i642cq7dlbmVzaXPkuKTku6Tlr1jlnKgs64ivt+ajg4afpS7iK30K6CAg6CAg6CBy1XRZcmOgJ0Vycm=yJz0K6CAg6CUKCSNHRUOtPm=w12LKbW6oam=iezOjSm=iKSBZbmxlcTMgKCNHRUOtPmlzSm=i3TBlbihqbWJEPiNKbW6pK30KC2ZL6HZtYXRyaXgg6Hsg60dF3i0+1WV032F0cmlOKGpvYns+60pvYix0eXBlezOnaGFzaCcpPQoJCglteSAja2LuXW1pbGZfdHlw1SBE6CNJS00tPmdldF=qbWJpbm1vKCZqbWJuY2ZlezOjSk=C5CZqbWJjYXRl1W=yeXs+JTdvcmsn5CZqbWJpbm1vezOnSkL3XWlubl=0bW=sa2LnXTRLcGUnK30KC2ZL6CNvdXRf1mlsbV=0eXBl6Hsg60l53S0+1WV0XWpvYmlu1m8o52pvYmLhb2VEPiNK306s52pvYmNhdGVnbTJLezOndW=yaycs52pvYmlu1m=EPidK3lNfbTV0XTRvbWxpbmdfdHlw1ScpPQoJbXkg6TNpbGtfdHlw1SBE6CNJS00tPmdldF=qbWJpbm1vKCZqbWJuY2ZlezOjSk=C5CZqbWJjYXRl1W=yeXs+JTdvcmsn5CZqbWJpbm1vezOnSkL3XTNzXTBybWNzcZ=0eXBlJyk=CglteSAjbGFL1XJfYW=ZbnQgeyAjR0V453Ln1XRMYXllckNvd2L0KGpvYns+60pvYik=CglteSAjbGFta2LhdGlvbl=t1XRobWQgeyAjSUtN53Ln1XRfam=ia2Lmbygtam=ibmFt1Xs+60pPQiwtam=iYWF012dvcnlEPidTbTJrJywtam=ia2LmbTs+J0p4UZ=sY2ZpbmF0a2=uXWZldGhv1CcpPQoJbXkg6TNw12Mz6Hsg60l53S0+1WV0XWpvYmlu1m8o52pvYmLhb2VEPiNK306s52pvYmNhdGVnbTJLezOndW=yaycs52pvYmlu1m=EPidK3lNfcGNiXTNw12NpY2xpdHkzJyk=Cglp1iAo6Wxhe2VyXWNvd2L06DOgM3YpfgoJCSNL6HsgM3YqMz7rM36wPQoJJQoJ12xz1XOKCQkjeSBE6CNsYXllcl=jbTVudCozMSsxMjA=CgklCglteSAjbmFt1UpPQiBE6HNZYnN0cigjSm=i5DAs4Sk=CgkKCSNHRUOtPm=w12LNYXRyaXgoam=iezOjSm=iK30KC21vcmVhYWggbXkg6Wxhe2Vy6ChzbTJ06HOjb2F0cmlOfiNhJXLybTcl6DxEPiAjb2F0cmlOfiNiJXLybTclJSAgaWVLcyB=b2F0cmlOKSB+CgkJa2YgKCAo6WZhdHJpeHOjbGFL1X6lfmNvbnRleHQl6GVx6CdibWFy1CcpY2Lk6Cgjb2F0cmlOfiNsYXllciV+bGFL1XJfdHlw1SUg1X7gJTNpbGtfcWNy12VuJykg6Cl+CgkJCXVubGVzcygg6Wxhe2Vy6HsE6C=eKHB8bikv6Cl+CgkJCQlteSAjdGZwPQoJCQkJa2Yo6CNza2xrXTRLcGUgezsg5ZLzcHJheSMvaSApfgoJCQkJCSN0bXAgeyAncC0nPQoJCQkJJQoJCQkJ12xza2Yo6CNza2xrXTRLcGUgezsg5ZLza2xrcWNy12Vu6y=p6Cl+CgkJCQkJ6TRtcCBE6Cdu5Sc=CgkJCQklCgkJCQllbHNlfgoJCQkJC2LleHQ=CgkJCQklCgkJCQkjR0V453LD300o6CdtYXRyaXhfcmVuY2ZlXWxhe2VyJyxqbWJEPiNKbW6sb2F0cmlOezOnb2F0cmlOJyxsYXllcns+6Wxhe2Vy5GLldZ=uY2ZlezOjdGZw5iNsYXllcik=CgkJCSUKCQklCgkJ12xza2YgKCAjb2F0cmlOfiNsYXllciV+YW=udGVOdCUg1X7gJWJvYXJkJyBhbmQgKCNtYXRyaXh+6Wxhe2VyJXL0bF=uY2ZlJSBE4yAvXih0bTB8Ym=0dG=tKSMv6Ckg6Cl+CgkJC2lm6CgjbGFL1X6gezsgbS8odHxiKS8pfgoJCQkJ60dVSS0+bXNnYm=OKCZpYW=uezOna2Lmbycs5XRleHREPi5lsY5ltE5nuO/ooqvlrprkuYnkuobku6rjg6Hku6vpna5vv6zlpo5pn6DljLjmmE3vv6zorEfmiYvliqjkvIEmraPvv67i5CZn12OgezOg60dF3ik=CgkJCQkk6HJldHVybiAnQWFuYWVsJwoJCQklCgkJCXBZcWggQHN0Y2NrdXBfbGFL1XJz5CNsYXllcj0KCQklCgkJ12xza2YgKCAjb2F0cmlOfiNsYXllciV+YW=udGVOdCUg1X7gJWJvYXJkJyBhbmQgKCNtYXRyaXh+6Wxhe2VyJXL0bF=uY2ZlJSBE4yAvXmxc1Csj5yAp6CApfgoJCQlp1iAo6Wxhe2Vy6HsE6G0vKHR8YikvKXOKCQkJCSNHVUktPmZz1WJveCgta2Nvbns+JWlu1m8n5CZ01Xh0ezOiLbGCLbeyLEuPIKKrLaIaL5mJL5qGL5iKOOCBL5i5I1WiEEyMLaaCI1yALY+YLpu0EEyMIK+TLom5LYqoL5+uLqWjEEyB6iwt1WVu6Hs+6CNHRUOpPQoJCQkJJCBy1XRZcmOgJ0NhbmNlbCcKCQkJJQoJCQlwdXNo67BzdGFjaTVwXWxhe2VycywjbGFL1X6=CgkJJQoJJQoJbXkg6TRvcGxpbmtlcl=sYXllcnMgeyAjSUtN53Lz12xlYTRfYXJyYXloYXNoKCZ0Y2Js1Xs+JTBkbV=qbWJfcTRhYWtfbGFL1XJzJywtdWhlcmVEPnLqbWJfa2REPiNK30JfSUQlK30KC2ZL67BjdXN0XWxhe2VyPQoKC21vcmVhYWggbXkg6Wl0120gKHNvcnR+6W7tPnLsYXllcl=ud20l6DxEPiAjYi0+fmxhe2VyXWLZbSUl67AjdG=wbGluaWVyXWxhe2VycykgfgoJC2lmKCNpdGVt53L+YXR0cl=kYXRhJSAmJiAjaXRlbS0+fmxhe2VyXTRLcGUl6GLl6CJ3a2xr6FNjcmVlbi6gJiYg6Wl0120tPnLsYXllcl=0eXBlJSBu1SAiUW=s1GVy67ZhcWsiKXOKCQkJbXkg6WpzbWLfYXR0ciBE6CNqcW=u53Lk12Nv1GUo6Wl0120tPnLhdHRyXWRhdG7lK30KCQkJbXkg6WNZcTRfbHkgeyAjanNvbl=hdHRy53L+YTVzdG=taXpl1F=sYXllcl=uY2ZlJ30KCQkJa2Yo6WNZcTRfbHkgJiYg6WNZcTRfbHkgbmUg6i6p6HOKCQkJC2lmKCNjdXN0XWxL6HsE6C=qa27vKSB+CgkJCQkJcHVzaCBAYTVzdF=sYXllciwg6WNZcTRfbHk=CgkJCQkl6GVscWUgfgoJCQkJC2lmKCNjdXN0XWxL6HsE6C8oXGQrKS8p6HOKCQkJCQkJcHVzaCBAYTVzdF=sYXllciwgKCMxKSOo6Wl0120tPnLsYXllcl=za2RlJSBlcSAiVG=w6j8g6nQi6Dog6m6iK30KCQkJCQkl6GVscWUgfgoJCQkJCQlwdXNo67BjdXN0XWxhe2Vy5CAjYTVzdF=se30KCQkJCQklCgkJCQklCgkJCSUKCQklCgklCglteSAjdGVzdD0KC21vcmVhYWggbXkg6Wxhe2Vy6ChAcTRhYWtZcF=sYXllcnMpfgoJC2ZL6CgjdW=yaWxLcmOp6Hsg6Wxhe2Vy6HsE6C8oXGQrKS8=CgkJ6W=w1V=ibW=s12Fu6HsgMSBp1iAo6Wxhe2Vy6HsE6G0vXm=w1S0vK30KCQkKCQkjbiArKz0KCQkjcTRhYWtfcm=TcTOjbGFL1X6lfnNlcXVlbmNlJSAg6Hsg6WO=CgkJ6TN0Y2NrXTJvdTN+6Wxhe2VyJXLn12LfbmFt1SUg6CBE6CNsYXllcj0KCQkk12LZbV=0bF=za2RlCgoJC2lm6ChzYWFsYX6oQGNZcTRfbGFL1X6p6DOgMSApfgoJCQlmbTJlY2No6CNs6ChAYTVzdF=sYXllcikgfgoJCQkJbXkgKCNseXJuKSBE6CNs6HsE6C8oXGQrKS8=CgkJCQkk6CNHRUOtPlBBVVNFKCNsK30KCQkJCSNs6HsE6HMv5S8vPQoJCQkJJCNHVUktPmZz1WJveCgta2Nvbns+JWlu1m8n5CZ01Xh0ezOi6Wwg6CAjbGFL1X6g6C6pPQoJCQkJa2YgKCNs6HsE6C=e6Wxhe2Vy5yApfgoJCQkJCSNsbSBE6HNZYnN0cigjbCAs536sMSk=CgkJCQkJa2YgKCAjbG0g1X7gJTQnKXOKCQkJCQkJ6TN0Y2NrXTJvdTN+6Wxhe2VyJXLlbnVtXTRsXTNp1GUl6Hsg6Cd0bTAnPQoJCQkJCQlsYXN0PQoJCQkJCSUKCQkJCQllbHNlfgoJCQkJCQkjcTRhYWtfcm=TcTOjbGFL1X6lfmVud2ZfdGxfcWlk1SUgeyAgJWJvdHRvbSc=CgkJCQkJC2xhcTQ=CgkJCQkJJQoJCQkJJQoJCQkJ12xz1XOKCQkJCQlp1igjdW=yaWxLcmOgeTsg6WxLcmOgY2Lk6CNsYXllciAh4yAvXmppYS8gY2Lk6CNs6C7E6C=eamlh5yl+CgkJCQkJC2lm6CgjbCBE4yAvKFth5XpB5VpdKykvKXOKCQkJCQkJC2lm6CgjMSBlcSAndCcpfgoJCQkJCQkJCSNzdGFjaZ=ybTdzfiNsYXllciV+12LZbV=0bF=za2RlJSBE6CAndG=wJz0KCQkJCQkJCQlsYXN0PQoJCQkJCQkJJQoJCQkJCQkJ12xz1XOKCQkJCQkJCQkjcTRhYWtfcm=TcTOjbGFL1X6lfmVud2ZfdGxfcWlk1SUgeyAgJWJvdHRvbSc=CgkJCQkJCQkJbGFzdD0KCQkJCQkJCSUKCQkJCQkJCQoJCQkJCQklCgkJCQkJJQoJCQkJJQoJCQklCgkJCSN01XN06HsgJWFZdG8nPQoJCSUKCQllbHNlfgoJCQlp1iAo6GRl1mlu12Qg6WZhdHJpeHOjbGFL1X6lfmVud2ZfdGxfcWlk1SUgKXOKCQkJ6TN0Y2NrXTJvdTN+6Wxhe2VyJXLlbnVtXTRsXTNp1GUl6Hsg6CNtYXRyaXh+6Wxhe2VyJXLlbnVtXTRsXTNp1GUlPQoJCQklCgkJC2VscWV+CgkJCQlp1ihk121pbmVk6CNtYXRyaXh+6Wxhe2VyJXL0bF=0eXBlJSBhbmQg6WZhdHJpeHOjbGFL1X6lfnRsXTRLcGUl6GVx6CdvdXRlcicpfi3lpJblsY6KCQkJCQlp1iAo6GRl1mlu12Qg6WZhdHJpeHOjbGFL1X6lfnRsXWLZbSUgY2Lk6CNtYXRyaXh+6Wxhe2VyJXL0bF=ud20lf36geTsgMSApfgoJCQkJCQkjcTRhYWtfcm=TcTOjbGFL1X6lfmVud2ZfdGxfcWlk1SUgeyAgJTRvcCc=CgkJCQkJJ2VscWV+CgkJCQkJCSNzdGFjaZ=ybTdzfiNsYXllciV+12LZbV=0bF=za2RlJSBE6CAnYm=0dG=tJz0KCQkJCQklCgkJCQkl12xz1XOkLYaFLbGCCgkJCQkJa2YgKCNzcGVjMyBlcSAndGhybTVnaF=ibWFy1Ccpfi3pgJrlr13mnb8KCQkJCQkJa2YgKCBk121pbmVk6CNtYXRyaXh+6Wxhe2VyJXL0bF=ud20l6GFu1CAjb2F0cmlOfiNsYXllciV+dGxfbnVtJX0y6HtE6DAgKXOKCQkJCQkJC2lm6CgjbGFta2LhdGlvbl=t1XRobWQg1X7gJWNZJyl+J4imhueul4azlQoJCQkJCQkJCSNzdGFjaZ=ybTdzfiNsYXllciV+12LZbV=0bF=za2RlJSBE6CAndG=wJz0KCQkJCQkJCSVlbHNp1iAo6Wxhb2luYXRpbWLfb2V0aG=k6GVx6CdjbTJlJyl+J4imhuadv+azlQoJCQkJCQkJC2lm6CgjbTBlXWJvbWxlY2Opfi3lj6XlkKtvcGXlsY6KCQkJCQkJCQkJ6TN0Y2NrXTJvdTN+6Wxhe2VyJXLlbnVtXTRsXTNp1GUl6Hsg6Cd0bTAnPQoJCQkJCQkJCSVlbHNlfgoJCQkJCQkJCQkjcTRhYWtfcm=TcTOjbGFL1X6lfmVud2ZfdGxfcWlk1SUgeyAgJWJvdHRvbSc=CgkJCQkJCQkJJQoJCQkJCQkJJQoJCQkJCQkl12xz1XOKCQkJCQkJC2lm6CgjbGFta2LhdGlvbl=t1XRobWQg1X7gJWNZJyl+J4imhueul4azlQoJCQkJCQkJCSNzdGFjaZ=ybTdzfiNsYXllciV+12LZbV=0bF=za2RlJSBE6CAnYm=0dG=tJz0KCQkJCQkJCSVlbHNp1iAo6Wxhb2luYXRpbWLfb2V0aG=k6GVx6CdjbTJlJyl+J4imhuadv+azlQoJCQkJCQkJC2lm6CgjbTBlXWJvbWxlY2Opfi3lj6XlkKtvcGXlsY6KCQkJCQkJCQkJ6TN0Y2NrXTJvdTN+6Wxhe2VyJXLlbnVtXTRsXTNp1GUl6Hsg6CdibTR0bW0nPQoJCQkJCQkJCSVlbHNlfgoJCQkJCQkJCQkjcTRhYWtfcm=TcTOjbGFL1X6lfmVud2ZfdGxfcWlk1SUgeyAgJTRvcCc=CgkJCQkJCQkJJQoJCQkJCQkJJQoJCQkJCQklCgkJCQkJJ2VscWV+CgkJCQkJCSNzdGFjaZ=ybTdzfiNsYXllciV+12LZbV=0bF=za2RlJSBE6CAnJz0KCQkJCQklCgkJCQklCgkJCSUKCQkJ6TRlcTQgeyAndW=yayc=CgkJJQoJC2ZL6CNmY2Nl1mls1SBE6CJF4i8jbmFt1UpPQiL0eHQiPQoJCSQga2YgKCZl6CNmY2Nl1mls1Sl+CgkJJCAJbTBlbiAo37F1RVJN2U1J37Us6W1hYWVma2xlK30KCQkk6AlTaGls1SAoP7xB2UVS3VlGSUxFPikgfgoJCSQgCQljaG=tcD0KCQkk6AkJbXkgKCNseXJuKSBE6CNf6HsE6C8oXGQrKS8=CgkJJCAJCSNf6HsE6HMv5S8vPQoJCSQgCQkk60dVSS0+bXNnYm=OKCZpYW=uezOna2Lmbycs5XRleHREPi6jXyAg6CNsYXllciAg6ik=CgkJJCAJC2lm6CgjXyBE4yAvXiNsYXllci8gKXOKCQkk6AkJCSNsbSBE6HNZYnN0cigjXyAs536sMSk=CgkJJCAJCQlp1iAo6CNsbSBlcSAndCcpfgoJCSQgCQkJCSNzdGFjaZ=ybTdzfiNsYXllciV+12LZbV=0bF=za2RlJSBE6CAndG=wJz0KCQkk6AkJCQlsYXN0PQoJCSQgCQkJJQoJCSQgCQkJ12xz1XOKCQkk6AkJCQkjcTRhYWtfcm=TcTOjbGFL1X6lfmVud2ZfdGxfcWlk1SUgeyAgJWJvdHRvbSc=CgkJJCAJCQkJbGFzdD0KCQkk6AkJCSUKCQkk6AkJJQoJCSQgCQllbHNlfgoJCSQgCQkJa2Yo6TdvcmtseXJu6HtE6CNseXJu6GFu1CAjbGFL1X6g63sg5ZLqa27v6GFu1CAjXyAh4yAvXmppYS8pfgoJCSQgCQkJC2lm6CgjXyBE4yAvKFth5XpB5VpdKykvKXOKCQkk6AkJCQkJa2YgKCMx6GVx6Cd0Jyl+CgkJJCAJCQkJCQkjcTRhYWtfcm=TcTOjbGFL1X6lfmVud2ZfdGxfcWlk1SUgeyAgJTRvcCc=CgkJJCAJCQkJCQlsYXN0PQoJCSQgCQkJCQklCgkJJCAJCQkJC2VscWV+CgkJJCAJCQkJCQkjcTRhYWtfcm=TcTOjbGFL1X6lfmVud2ZfdGxfcWlk1SUgeyAgJWJvdHRvbSc=CgkJJCAJCQkJCQlsYXN0PQoJCSQgCQkJCQklCgkJCQkJCQkKCQkk6AkJCQklCgkJJCAJCQklCgkJJCAJCSUKCQkk6AklCgkJJCAJYWxvcWUg37F1RVJN2U1J37U=CgkJJCAJ6TRlcTQgeyAnYXV0byc=CgkJJCAlCgkJJCBlbHNlfgoJCSQgC2lm6Cgg1GVma2Ll1CAjb2F0cmlOfiNsYXllciV+12LZbV=0bF=za2RlJSApfgoJCSQgCSNzdGFjaZ=ybTdzfiNsYXllciV+12LZbV=0bF=za2RlJSBE6CAjb2F0cmlOfiNsYXllciV+12LZbV=0bF=za2RlJ30KCQkk6AklCgkJJCAJ12xz1XOKCQkk6AkJa2Yo1GVma2Ll1CAjb2F0cmlOfiNsYXllciV+dGxfdHlw1SUgY2Lk6CNtYXRyaXh+6Wxhe2VyJXL0bF=0eXBlJSBlcSAnbTV01X6nKXOkLaS2LbGCCgkJJCAJCQlp1iAo6GRl1mlu12Qg6WZhdHJpeHOjbGFL1X6lfnRsXWLZbSUgY2Lk6CNtYXRyaXh+6Wxhe2VyJXL0bF=ud20lf36geTsgMSApfgoJCSQgCQkJCSNzdGFjaZ=ybTdzfiNsYXllciV+12LZbV=0bF=za2RlJSBE6CAndG=wJz0KCQkk6AkJCSVlbHNlfgoJCSQgCQkJCSNzdGFjaZ=ybTdzfiNsYXllciV+12LZbV=0bF=za2RlJSBE6CAnYm=0dG=tJz0KCQkk6AkJCSUKCQkk6AkJJ2VscWV+J42Ghe2xggoJCSQgCQkJa2YgKCNzcGVjMyBlcSAndGhybTVnaF=ibWFy1Ccpfi3pgJrlr13mnb8KCQkk6AkJCQlp1iAo6GRl1mlu12Qg6WZhdHJpeHOjbGFL1X6lfnRsXWLZbSUgY2Lk6CNtYXRyaXh+6Wxhe2VyJXL0bF=ud20lf36geTsgMCApfgoJCSQgCQkJCQlp1iAo6Wxhb2luYXRpbWLfb2V0aG=k6GVx6CdjdScpfi3opobnrp3msLUKCQkk6AkJCQkJCSNzdGFjaZ=ybTdzfiNsYXllciV+12LZbV=0bF=za2RlJSBE6CAndG=wJz0KCQkk6AkJCQkJJ2VscWlm6CgjbGFta2LhdGlvbl=t1XRobWQg1X7gJWNvcmUnKXOkIKaGLpW/Lr4VCgkJJCAJCQkJCQlp1iAo6W=w1V=ibW=s12FuKXOkLYyFL1CrbTBlLbGCCgkJJCAJCQkJCQkJ6TN0Y2NrXTJvdTN+6Wxhe2VyJXLlbnVtXTRsXTNp1GUl6Hsg6Cd0bTAnPQoJCSQgCQkJCQkJJ2VscWV+CgkJJCAJCQkJCQkJ6TN0Y2NrXTJvdTN+6Wxhe2VyJXLlbnVtXTRsXTNp1GUl6Hsg6CdibTR0bW0nPQoJCSQgCQkJCQkJJQoJCSQgCQkJCQklCgkJJCAJCQkJJ2VscWV+CgkJJCAJCQkJC2lm6CgjbGFta2LhdGlvbl=t1XRobWQg1X7gJWNZJyl+J4imhueul4azlQoJCSQgCQkJCQkJ6TN0Y2NrXTJvdTN+6Wxhe2VyJXLlbnVtXTRsXTNp1GUl6Hsg6CdibTR0bW0nPQoJCSQgCQkJCQkl12xza2YgKCNsY2ZpbmF0a2=uXWZldGhv1CBlcSAnYW=y1Scpfi3opobmnb/msLUKCQkk6AkJCQkJC2lm6CgjbTBlXWJvbWxlY2Opfi3lj6XlkKtvcGXlsY6KCQkk6AkJCQkJCQkjcTRhYWtfcm=TcTOjbGFL1X6lfmVud2ZfdGxfcWlk1SUgeyAgJWJvdHRvbSc=CgkJJCAJCQkJCQkl12xz1XOKCQkk6AkJCQkJCQkjcTRhYWtfcm=TcTOjbGFL1X6lfmVud2ZfdGxfcWlk1SUgeyAgJTRvcCc=CgkJJCAJCQkJCQklCgkJJCAJCQkJCSUKCQkk6AkJCQklCgkJJCAJCQkl12xz1XOKCQkk6AkJCQkjcTRhYWtfcm=TcTOjbGFL1X6lfmVud2ZfdGxfcWlk1SUgeyAgJyc=CgkJJCAJCQklCgkJJCAJCSUKCQkk6AklCgkJJCAJ6TRlcTQgeyAndW=yayc=CgkJJCAlCgkJJGdlbl=s1GkKCQlp1ihk121pbmVk6CNtYXRyaXh+6Wxhe2VyJXL0bF=0eXBlJSBhbmQg6WZhdHJpeHOjbGFL1X6lfnRsXTRLcGUl6GVx6CdvdXRlcicpfi3lpJblsY6KCQkJa2Yo6CNvdXRf1mlsbV=0eXBl6HsE6C=ebGRp6y=pKXOKCQkJCSNzdGFjaZ=ybTdzfiNsYXllciV+1WVuXWxkaSUg6CBE6Cds1GknPQoJCQklCgkJC2VscWV+CgkJCQkjcTRhYWtfcm=TcTOjbGFL1X6lfmdlbl=s1Gkl6CAgeyAn1mlsbSc=CgkJCSUKCQklCgkJ12xz1XOKCQkJa2Yo6CNpbmLf1mlsbV=0eXBl6HsE6C=ebGRp6y=p6Cl+CgkJCQkjcTRhYWtfcm=TcTOjbGFL1X6lfmdlbl=s1Gkl6CAgeyAnbGRpJz0KCQkJJQoJCQllbHNlfgoJCQkJ6TN0Y2NrXTJvdTN+6Wxhe2VyJXLn12LfbGRpJSAg6HsgJW1pbG0nPQoJCQklCgkJJQoJJQkKC2lm6CgjdGVzdCBlcSAndW=yaycpfgoJCSNHVUktPmZz1WJveCgta2Nvbns+JWVycm=yJywtdGVOdHs+6uayoeacieajg4aZi+26s42Po4aeh4S/oeaBr++8j4ivt+azq4a7j+SIuu2Tpeagu42vueS/oeaBry7iK30KCSUKC2ZL6CNzdGFjaTVwXWLldyBE6CNHVUktPnNobTdfdGFibGVmbTJt6CgKCQkJ52Rl1mFZbHRzaXplezLbNDUw5CNLXSwKCQkJ5XRpdGxl6Hs+6C5orEfnoaEorq3mlpnljEfnmo3ljIDmno3kvIHmga8t5S0t5S0t5S0t5S0t5S6g5AoJCQktcm=TcyBEPiBcfXN0Y2NrXTJvdTMsCgkJCSZzaG=TYWhlYWsgezOgMSwt1WVu6Hs+6CNHRUOsCgkJCSZjbWxZb2Lz6Hs+6FsKCQkJCXOKCQkJCQljbWxZb2LfbmFt1Xs+JWdlbl=uY2ZlJywKCQkJCQlsY2JlbHs+J+2xgu26q+2QjeensCcsCgkJCQkJdWlkdGhEPj7ZMCwKCQkJCQl0eXBlezOncTRya2LnJywKCQkJCQlWY2xp1GF01V=md2LjezLzd2J+CgkJCQkJC2ZL6HZwYX6geyBAXz0KCQkJCQkJbXkg6Wxhe2Vy6Hsg6TBhcnLmbTJtcGFu12wl53Ln1XRfdmFsd2Uo6TBhcnLybTcl5Cdn12LfbmFt1ScpPQoJCQkJCQkjcGFyfm1vcmZwY2LlbCUtPmdldF=Ta2Rn1XQo6TBhcnLybTcl5Cdn12LfbmFt1Scp53Lz1XRf12RpdGFibGUoMCk=CgkJCQkJC2lm6Cgjb2F0cmlOfiNsYXllciV+YW=udGVOdCUg1X7gJWJvYXJkJyBhbmQg6WZhdHJpeHOjbGFL1X6lfmxhe2VyXTRLcGUl6GVx6Cdza2duY2wn6GFu1CAjb2F0cmlOfiNsYXllciV+cG=sYXJpdHkl6GVx6CdwbTNpdGlW1ScgY2Lk6CNsYXllciAh4yBt5WppYS8pfgoJCQkJCQkJcmV0dXJu6HOKCQkJCQkJCQli1WNvbG=y6Hs+6CckRkRDNzR7JywKCQkJCQkJCSU=CgkJCQkJCSUKCQkJCQkJ12xza2YgKCNtYXRyaXh+6Wxhe2VyJXLjbWL01Xh0JSBlcSAnYm=hcmQn6GFu1CAjb2F0cmlOfiNsYXllciV+bGFL1XJfdHlw1SUg1X7gJTBvdWVyXWdybTVu1CcgY2Lk6Cgjb2F0cmlOfiNsYXllciV+cG=sYXJpdHkl6GVx6Cdu12dhdGlW1ScgbT6g6WZhdHJpeHOjbGFL1X6lfnBvbGFyaXRLJSBlcSAncG=zaXRpdmUnKSBhbmQg6Wxhe2Vy6C7E6G0vamlh5yl+CgkJCQkJCQly1XRZcmOgfgoJCQkJCQkJC2JnYW=sbT6gezOgJyR7QkF7MDAn5AoJCQkJCQkJJ30KCQkJCQkJJQoJCQkJCQly1XRZcmOgfiU=CgkJCQkJJQoJCQkJJSwKCQkJCXOKCQkJCQljbWxZb2LfbmFt1Xs+JWVud2ZfdGxfcWlk1ScsCgkJCQkJbGFi12xEPiflsY5liKvpna5mrK7n5AoJCQkJCXRLcGVEPidlbnVtJywKCQkJCQltdXN0XW1p12xkezOx5AoJCQkJCXdp1HRoezOxMjAsCgkJCQkJJHNlbnNpdGlW1SBEPjAs6AoJCQkJCXBybTBlcnRLezL+CgkJCQkJCXRsXW1p12xkezLbbmFt1Xs+JTNjY2xhcics1GlzcGxheV=uY2ZlezOndGVOdCdd5AoJCQkJCQl0bF=WY2xZ1V=ma2Vs1Hs+JWLhb2Un5AoJCQkJCQl0bF=kYXRhezLbCgkJCQkJCQl+bmFt1Xs+JTRvcCcs1GlzcGxheV=uY2ZlezOndG=wJyUsCgkJCQkJCQl+bmFt1Xs+JWJvdHRvbScs1GlzcGxheV=uY2ZlezOnYm=0dG=tJyUsCgkJCQkJCV0KCQkJCQkl5AoJCQkJCX1hbGlkYXRlXW1ZbmNEPnNZYnOKCQkJCQkJbXkgfXBhciBE67BfPQoJCQkJCQlteSAjbGFL1X6geyAjcGFyfm1vcmZwY2LlbCUtPmdldF=WY2xZ1SgjcGFyfnJvdyUsJWdlbl=uY2ZlJyk=CgkJCQkJC2ZL6CN0bF=za2Rl6Hsg6TBhcnLmbTJtcGFu12wl53Ln1XRfdmFsd2Uo6TBhcnLybTcl5CdlbnVtXTRsXTNp1GUnK30KCQkJCQkJa2YgKCNtYXRyaXh+6Wxhe2VyJXL0bF=uY2ZlJSBlcSAndG=wJyBhbmQg6TRsXTNp1GUg1X7gJWJvdHRvbScgKXOKCQkJCQkJCXJldHVybiB+CgkJCQkJCQkJYmdjbWxvciBEPiAncmVkJywKCQkJCQkJCQllcnJvcl=tcWdEPiflpJblsY5lsY5liKvpna5mrKHpg6nmiInplJnorI8n5AoJCQkJCQkJJ30KCQkJCQkJJQoJCQkJCQllbHNp1iAo6WZhdHJpeHOjbGFL1X6lfnRsXWLhb2Ul6GVx6CdibTR0bW0n6GFu1CAjdGxfcWlk1SBlcSAndG=wJyApfgoJCQkJCQkJcmV0dXJu6HOKCQkJCQkJCQli1WNvbG=y6Hs+6Cdy12Qn5AoJCQkJCQkJC2Vycm=yXWZz1Ts+J+2klu2xgu2xgu26q+mdouasoemAiea5qemUmeivrycsCgkJCQkJCQklPQoJCQkJCQklCgkJCQkJC2lm6CgjdGxfcWlk1SBlcSAndG=wJyl+CgkJCQkJCQly1XRZcmOgfgoJCQkJCQkJCSRi1WNvbG=y6Hs+6CckQ0R7RDAwJywKCQkJCQkJCQli1WNvbG=y6Hs+6Cdsa2dodCBncmVlbicsCgkJCQkJCQklPQoJCQkJCQklCgkJCQkJCXJldHVybiB+J30KCQkJCQklCgkJCQkl5AoJCQkJfgoJCQkJC2NvbHVtbl=uY2ZlezOn1WVuXWxkaScsCgkJCQkJbGFi12xEPifmmLTlhYnmlrnlv68n5AoJCQkJCXRLcGVEPidlbnVtJywKCQkJCQltdXN0XW1p12xkezOx5AoJCQkJCXdp1HRoezOxMjAsCgkJCQkJJHNlbnNpdGlW1SBEPjAs6AoJCQkJCXBybTBlcnRLezL+CgkJCQkJCXRsXW1p12xkezLbbmFt1Xs+JTNjY2xhcics1GlzcGxheV=uY2ZlezOndGVOdCdd5AoJCQkJCQl0bF=WY2xZ1V=ma2Vs1Hs+JWLhb2Un5AoJCQkJCQl0bF=kYXRhezLbCgkJCQkJCQl+bmFt1Xs+JWxkaScs1GlzcGxheV=uY2ZlezOnbGRpJyUsCgkJCQkJCQl+bmFt1Xs+JW1pbG0n5GRpcTBsYXlfbmFt1Xs+JW1pbG0nJSwKCQkJCQkJXQoJCQkJCSUsCgkJCQkJdmFsa2RhdGVf1nVuYTs+cTVifgoJCQkJCQlteSB=cGFy6HsgQF8=CgkJCQkJC2ZL6CN0bF=s1GkgeyAjcGFyfm1vcmZwY2LlbCUtPmdldF=WY2xZ1SgjcGFyfnJvdyUsJWdlbl=s1GknK30KCQkJCQkJa2YgKCN0bF=s1Gkg1X7gJW1pbG0nKXOKCQkJCQkJCXJldHVybiB+CgkJCQkJCQkJJGJnYW=sbT6gezOgJyQwR7JBMDAn5AoJCQkJCQkJC2JnYW=sbT6gezOgJyQO4DcTN06n5AoJCQkJCQkJJ30KCQkJCQkJJQoJCQkJCQly1XRZcmOgfiU=CgkJCQkJJQoJCQkJJSwKCQkJXSwKCQkpPQoJCXJldHVybiAnQWFuYWVsJyBZbmxlcTMgKCNzdGFjaTVwXWLldyk=CgkJCglmbTJlY2No6GZL6CNsYXllciAocW=ydCB+6WZhdHJpeHOjYSV+cm=TJSA8ezOg6WZhdHJpeHOjYiV+cm=TJSUgaWVLcyB=fiNzdGFjaTVwXWLldyUpfgoJC2lmKCNzdGFjaTVwXWLldy0+fiNsYXllciV+12LZbV=0bF=za2RlJSBlcSAidG=w6il+CgkJC2lm6CgjcTRhYWtZcF=u1XctPnOjbGFL1X6lfmdlbl=s1Gkl6GVx6CJs1GkiKXOKCQkJC2lm6CgjbGFL1X6gezsg5WxkaSMvaSl+CgkJCQkJ6Wxhe2VybWxk6Hsg6Wxhe2VyPQoJCQkJCSNsYXllciBE4yBz5yZs1GkvdCZs1GkvPQoJCQkJCSNHRUOtPkNP3SggJWZhdHJpeF=y12Lhb2VfbGFL1X6n5GpvYns+60pvYixtYXRyaXhEPidtYXRyaXgn5Gxhe2VyezOjbGFL1XJvbGQsbmVTXWLhb2VEPiNsYXllcik=CgkJCQklCgkJCQllbHNlfgoJCQkJCSNHRUOtPkNP3SggJWZhdHJpeF=y12Lhb2VfbGFL1X6n5GpvYns+60pvYixtYXRyaXhEPidtYXRyaXgn5Gxhe2VyezOjbGFL1X6sbmVTXWLhb2VEPiNsYXllciOndCZs1GknK30KCQkJCSUKCQkJJQoJCQllbHNlfgoJCQkJa2YgKCNsYXllciBE4yAvbGRp6y=pKXOKCQkJCQkjbGFL1XJvbGQgeyAjbGFL1X6=CgkJCQkJ6Wxhe2Vy6HsE6HMv52xkaS=05z0KCQkJCQkjR0V453LD300o6CdtYXRyaXhfcmVuY2ZlXWxhe2VyJyxqbWJEPiNKbW6sb2F0cmlOezOnb2F0cmlOJyxsYXllcns+6Wxhe2VybWxk5GLldZ=uY2ZlezOjbGFL1X6pPQoJCQkJJQoJCQkJ12xz1XOKCQkJCQkjR0V453LD300o6CdtYXRyaXhfcmVuY2ZlXWxhe2VyJyxqbWJEPiNKbW6sb2F0cmlOezOnb2F0cmlOJyxsYXllcns+6Wxhe2Vy5GLldZ=uY2ZlezOjbGFL1X6uJTQnK30KCQkJCSUKCQkJJQoJCSUKCQllbHNlfgoJCQlp1iAo6TN0Y2NrdXBfbmVT53L+6Wxhe2VyJXLn12LfbGRpJSBlcSAibGRp6il+CgkJCQlp1iAo6Wxhe2Vy6HsE6C=s1Gkj5WkpfgoJCQkJCSNsYXllcm=s1CBE6CNsYXllcj0KCQkJCQkjbGFL1X6gezsgcy8tbGRp5W6tbGRp5z0KCQkJCQkjR0V453LD300o6CdtYXRyaXhfcmVuY2ZlXWxhe2VyJyxqbWJEPiNKbW6sb2F0cmlOezOnb2F0cmlOJyxsYXllcns+6Wxhe2VybWxk5GLldZ=uY2ZlezOjbGFL1X6pPQoJCQkJJQoJCQkJ12xz1XOKCQkJCQkjR0V453LD300o6CdtYXRyaXhfcmVuY2ZlXWxhe2VyJyxqbWJEPiNKbW6sb2F0cmlOezOnb2F0cmlOJyxsYXllcns+6Wxhe2Vy5GLldZ=uY2ZlezOjbGFL1X6uJW6tbGRpJyk=CgkJCQklCgkJCSUKCQkJ12xz1XOKCQkJC2lm6CgjbGFL1X6gezsg5WxkaSMvaSl+CgkJCQkJ6Wxhe2VybWxk6Hsg6Wxhe2VyPQoJCQkJCSNsYXllciBE4yBz5yZs1GkvYi8=CgkJCQkJ60dF3i0+Q0=NKCAnb2F0cmlOXTJlbmFt1V=sYXllcicsam=iezOjSm=i5GZhdHJpeHs+JWZhdHJpeCcsbGFL1XJEPiNsYXllcm=s1Cxu1XdfbmFt1Xs+6Wxhe2VyK30KCQkJCSUKCQkJC2VscWV+CgkJCQkJ60dF3i0+Q0=NKCAnb2F0cmlOXTJlbmFt1V=sYXllcicsam=iezOjSm=i5GZhdHJpeHs+JWZhdHJpeCcsbGFL1XJEPiNsYXllcixu1XdfbmFt1Xs+6Wxhe2Vy5idiJyk=CgkJCQklCgkJCSUKCQklCgklCgkJCgkjR0V453LD300o6CdtYXRyaXhfcGFn1V=jbG=z1Scsam=iezOjSm=i5GZhdHJpeHs+JWZhdHJpeCcpPQoJCgkk60dVSS0+bXNnYm=OKCZpYW=uezOna2Lmbycs5XRleHREPi6jbGFL1X6g5SDku6oi5CZn12OgezOg60dF3ik=CiAg6CAkJCRvdXRwdXQgY2Lk6HJldHVybiBzdGF0dXMs6Glm6GdlbmVzaXMg1XJybT6s6Gl06HdpbGwgbTV0cHV06GdlbmVzaXMg1XJybT6gYW=tb2Fu1AoJd2Ls1XNz6CgjR0V453L+UZRBVFV3JSl+CgkJcmV0dXJu6CNS1XRZcmO=CgklCgllbHNlfgoJCSNHVUktPmZz1WJveCgta2Nvbns+JWVycm=yJywtdGVOdHs+am=pbigiXGOi57B+60dF3i0+flNUQVRVUyUlKSk=CgkJ60l53S0+dXBkYXRlXWpvYl=TbTJr1mxvdZ=sbWco52LvdGVzezOi6CAgRWVu1XNpcyBFcnJvcjpcbiAi5mpva2Oo6lxu6CAg6ixAfiNHRUOtPnL3V7FUVVMlJSkpPQoJCXJldHVybiAnRXJybT6nPQoJJQolCgpjYXRjaCBFcnJvcjoIUWltcGxl6HdpdGggfgoJbXkg6WVycm=y6HsgcWhp1nQ=CgkjRZVJ53LtcWdibTgo52ljbWLEPidlcnJvcics5XRleHREPiNlcnJvcik=Cgly1XRZcmOgJ0Vycm=yJz0KJQoK1mluY2xseXOKCiU=Cg99
\ No newline at end of file
eWhlY2Qx67LB3UUKCiBUbTAI4kl53SAt67l53ea4pe2PowoKeWhlY2Qx67RFU0NSSVBUSU=4Cgog
SUtNLoIlLY+jCgpEaGVh1D7gVkVSU0lP3iB6SVNU3ZJ1CgpEaGVh1D6gVj7uMDAgMjAxNi0wNi0x
MiBUbWLL67dZbwoK6D7uSUtNLoIlLY+jCgpEaGVh1D6gVj7uMDAgMjAxNy0xMS0xMyBDbWRL6FlZ
CgogMSElopEliqBn1XRfcHJvYWVzcZ=ieV=uY2ZlLYe=Lp2wCiAy5u2inu2KoGdldF=wcm=j1XNz
XTN0YXRZc+2HveaVsAoKeWNZdAoKcGFjaWFn1SBU3ZAI4kl5330KdXNl6HN0cmljdD0KJHVz1SB7
YXRh4jp7d2Zw6Cdkd2ZwJz0KdXNl6FNLczoISG=zdGLhb2U=CnVz1SBFbmNv1GU=CnVz1SBZdGYO
PQpZcWUgSlNP3j0KdXNl67RhdG7I4kRZbXAgJWRZbXAnPQoKcTVi6GLldyB+CglteSAjYWxhcTMg
eyBzaGlmdD0KC2ZL6HZwYX6geyAoQF8pPQoJbXkg6TNlbGYgeyBibGVzcyB+JSwg6WNsYXNzPQoJ
cmV0dXJu6CNz12xmPQolCgoKeWhlY2Qx67ZPV7hPRFMKCnto12FkMiAjR76tPk681WV0XWpvYmlu
1m8+KCZqbWJuY2ZlezO/5CZqbWJjYXRl1W=yeXs+Pywtam=ia2LmbTs+Pywtd2LpdHNEPj8pCgpE
bT1lcgoKeWl0120gKiB71XNjcmlwdGlvbgoK6C3ojrfljLbmlpnljEfkvIHmga8KCntpdGVt6Cog
UGFyY2ZldGVycwoK206852pvYmlkPl0gezOga2L01XJn1X6g4iDmlpnljEdJR4+8jAoKQjwtam=i
a2LmbzOgezOgcTRya2Ln6DogLpa1LY+TLbGeLoCnL1CNLIewEEyMCgpbQjwtdWl0aHNw12M+XSBE
PiAwfD7g4iDlpo5mnpzmlpnljEdpbm1vLrKhLaIaL5mJEEyMLpivL1CmLY+2cTBlY+2AvAoKeWl0
120gKiBS1XRZcmOKCiDmlpnljEfls1EmgKfnmo3lg5zvv6zlpo5mnpzmlpnljEfls1EmgKfku6Tl
r1jlnKjvv6zliJnovL3lmLLZbmRl1goKeWl0120gKiBFeGFtcGxlCgpEYmFjawoKeWNZdApzd26g
1WV0XWpvYmlu1m=+CglteSAjcWVs1iBE6HNoa210PQoJbXkgfXBhciBE67BfPQoJ6TBhcnOtam=i
a2Ql6Hsg6CNz12xm53Ln1XRfam=iXWlkKCNwYXJ+52pvYmLhb2UlKSBZbmxlcTMgKCNwYXJ+52pv
YmlkJSk=CQoJbXkg6W1ZbmMgeyAn1nVuYTRpbWOoQVJHVil+CgkJa2ZwbTJ0UGFjaWFn1SgicGNi
cGRt5mNvcmUiK30KCQlWYX6gdmFsd2UgeyBQ1GZMa2JyYXJLRGF0YSLn1XRKbWJBdHRyVmFsd2Uo
QVJHVlsi52pvYmlk6l0s67FSRZ1b6iZqbWJpbm1v6l0pPQoJCXJldHVybiBWY2xZ1QoJJSc=Cglt
eSAjcmV06Hsg6TNlbGYtPmNvb2ZhbmQo6CNmd2Lj5CBcfXBhciwgMSktPnLkYXRhJ30KC2lm6Cgj
cGFyfiZTaXRocTBlYyUgY2Lk6Cgh1GVma2Ll1CAjcmV06G=y6CNy1XQg1X7gJycpKQoJfgoJCSNm
d2Lj6HsgJW1ZbmN0a2=uK7FSRZYpfgoJCQlpbXBvcnRQY2NrY2dlKCJwYWJw1G0uYW=y1S6pPQoJ
CQlWYX6gdmFsd2UgeyBQ1GZMa2JyYXJLRGF0YSLn1XR3cGVjVmFsd2UoQVJHVlsi52pvYmlk6l0s
67FSRZ1b6iZqbWJpbm1v6l0pPQoJCQly1XRZcmOgdmFsd2UKCQklJz0KCQkjcmV06Hsg6TNlbGYt
PmNvb2ZhbmQo6CNmd2Lj5CBcfXBhciwgMSktPnLkYXRhJ30KCSUKCXJldHVybiAjcmV0PQolCgoK
Cnto12FkMiAjR76tPk68cWFW1V=qbWJpbm1vPigtam=ibmFt1Xs+Pywtam=ia2LmbWhhcWhEPj8p
CgpEbT1lcgoKeWl0120gKiB71XNjcmlwdGlvbgoK64S/ne2tm4a2me2Pt+S/oeaBrwoKeWl0120g
KiBQYXJhb2V01XJzCgpCPCZqbWJuY2ZlPiBEPiBzdHJpbmcg4iDmlpnljEflk6TnpEDvv6wtam=i
a2QgL5i46CZqbWJuY2ZlLb+FLpyJL5iAL5iqLaWYL1yoCgpbQjwtam=ia2Q+XSBEPiBpbnRlcmdl
ciAI64a2me2Pt0l7EEyMCgpCPCZqbWJpbm1vaGFzaDOgezOgfmpvYmlu1m8xezLWY2xZ137sam=i
a2LmbzJEPn1hbHVlMiOu5iUg4iAKCntpdGVt6CogUmV0dXJuCgogMCDooajnp5rkvLTlr1jmiJDl
ip/vv6zlkKbliJnovL3lmLEplJnorI/mtojmga8KCntpdGVt6CogRXhhbXBs1QoKeWJhYWsKCntj
dXQKcTVi6HNhdmVfam=ia2LmbTOKC2ZL6CNz12xm6HsgcWhp1nQ=CglteSB=cGFy6HsgQF8=Cgkj
cGFyfiZqbWJp1CUgeyAg6TNlbGYtPmdldF=qbWJfa2Qo6TBhcnOtam=ibmFt1SUp6HVubGVzcyAo
6TBhcnOtam=ia2QlK30JCglteSAj1nVuYyBE6Cdmd2LjdGlvbihBUkd2KXOKCQlpbXBvcnRQY2Nr
Y2dlKCJwYWJw1G0uYW=y1S6pPQoJCX1hciBWY2xZ1SBE6FBkbUxpYnJhcnl7YXRh5nNhdmVKbWJB
dHRyK7FSRZ1b6iZqbWJp1CJd5CBBUkd22y6tam=ia2LmbWhhcWgiXSk=CgkJcmV0dXJu6H1hbHVl
PQoJJSc=CglteSAjcmV06Hsg6TNlbGYtPmNvb2ZhbmQo6CNmd2Lj5CBcfXBhciwgMSk=Cgly1XRZ
cmOg6TJldC0+fmRhdG7lPQolCgoKCnNZYiBn1XRfam=iXWlkCnOKC2ZL6CNz12xm6HsgcWhp1nQ=
CglteSAjam=iXWLhb2UgeyBzaGlmdD0KC2ZL6CNmd2Lj6HsgJW1ZbmN0a2=uK7FSRZYpfgog6CAg
dmFy6GRi6HsgbmVT6FR3c2xRd2VyeVYyKFRfUZFMQ0LUXZBP30wu1WV0UTFsRGF0Y2JhcWUoKSk=
CiAg6CBWYX6g1GF0YSBE6GRi5nNlbGVjdF1hbHVlKHL0Y2Js13oicGRtXWpvYi6s6G1p12xk4lsi
a2QiXSwgdWhlcmUIfmpvYmLhb2UIQVJHVlsiam=iXWLhb2UiXSUlK30K6CAg6HJldHVybiBu1Xcg
V7RhdGFS1XNwbWLz1ShkYiLsYXN0RXJybT6oKSwg1GF0YSk=CiUnPQoJbXkg6TJldCBE6CNz12xm
53LjbWZtY2LkKCAj1nVuYywgfmpvYl=uY2ZlezOjam=iXWLhb2Ul5CAxK30KCXJldHVybiAjcmV0
53L+1GF0YSU=CiUKCnNZYiBn1XRfam=iXWLhb2UKfgoJbXkg6TNlbGYgeyBzaGlmdD0KC2ZL6CNq
bWJfa2QgeyBzaGlmdD0KC2ZL6CNmd2Lj6HsgJW1ZbmN0a2=uK7FSRZYpfgoJCX1hciBkYiBE6GLl
dyBUUTFsUXVlcnl2MihUXZNR37N4VF=Q30=M5mdldFNxb7RhdGFiYXNlKCkpPQoJCX1hciBkYXRh
6Hsg1G6ucWVs12N0VmFsd2UofnRhYmxl4iJw1GZfam=i6iwg1mllbGQI2yJqbWJuY2Zl6l0s6Hdo
1XJl4nLp1DpBUkd22yJqbWJfa2QiXSUlK30KCQly1XRZcmOgbmVT6FR7YXRhUmVzcG=ucWUo1G6u
bGFzd7Vycm=yKCks6GRhdG7pPQoJJSc=CglteSAjcmV06Hsg6TNlbGYtPmNvb2ZhbmQo6CNmd2Lj
5CB+am=iXWlkezOjam=iXWlkJSwgMSk=Cgly1XRZcmOg6TJldC0+fmRhdG7lPQolCgpzd26gcWNy
aXB0XWVu1Ap+CglteSAjcWVs1iBE6HNoa210PQoJbXkg6WpvYl=p1CBE6HNoa210PQoJbXkg6TBy
bWNlcTNfa2QgeyBzaGlmdD0KC2ZL6CNmd2Lj6HsgJW1ZbmN0a2=uK7FSRZYpfgoJCUFQUCLubTRp
1nko6lRPU7NB3V=3QZJJUFRfRUL76iwgfmpvYl=p1DpBUkd22yJqbWJfa2QiXSxwcm=j1XNzXWlk
4kFSRZ1b6nBybWNlcTNfa2QiXSUpPQoJJSc=CglteSAjcmV06Hsg6TNlbGYtPmNvb2ZhbmQo6CNm
d2Lj5CB+am=iXWlkezOjam=iXWlk5HBybWNlcTNfa2REPiNwcm=j1XNzXWlkJSwgMSk=Cgly1XRZ
cmOgPQolCgoKcTVi6GdldF=qbWJfdW=yaW1sbTdfcmVwbTJ0CnOKC2ZL6CNz12xm6HsgcWhp1nQ=
CglteSAjam=iXWlk6HsgcWhp1nQ=CglteSAjcHJvYWVzcZ=p1CBE6HNoa210PQoJbXkg6W1ZbmMg
eyAn1nVuYTRpbWOoQVJHVil+CgkJdmFy6GRi6HsgbmVT6FR3c2xRd2VyeVYyKFRfUZFMQ0LUXZBP
30wu1WV0UTFsRGF0Y2JhcWUoKSk=CgkJdmFy6GRhdG7geyBkYiLz12xlYTRNYXAofnRhYmxl4iJw
1GZfam=iXTdvcmtmbG=T6iwg1mllbGQI2yJy1XBvcnQiXSwgdWhlcmUIfmpvYl=p1DpBUkd22yJq
bWJfa2QiXSxwcm=j1XNzXWlk4kFSRZ1b6nBybWNlcTNfa2QiXSUlK30KCQly1XRZcmOgbmVT6FR7
YXRhUmVzcG=ucWUo1G6ubGFzd7Vycm=yKCks6GRhdG7pPQoJJSc=CglteSAjcmV06Hsg6TNlbGYt
PmNvb2ZhbmQo6CNmd2Lj5CB+am=iXWlkezOjam=iXWlk5CBwcm=j1XNzXWlkezOjcHJvYWVzcZ=p
1CUs6D7pPQoJcmV0dXJu6CNy1XQtPnLkYXRhJ30KJQoKCnNZYiBZcGRhdGVfam=iXTdvcmtmbG=T
XWxv1TOK6CAg6GZL6CNz12xm6HsgcWhp1nQ=CglteSB=cGFy6HsgQF8=CglteSAj1XJyPQoJbXkg
6W1ZbmMgeyAn1nVuYTRpbWOoQVJHVil+CgkJdmFy6GRi6HsgbmVT6FR3c2xRd2VyeVYyKFRfUZFM
Q0LUXZBP30wu1WV0UTFsRGF0Y2JhcWUoKSk=CgkJdmFy6GLvdGVz6HsgQVJHVlsibm=01XMiX30K
CQlk12xldGUgQVJHVlsibm=01XMiX30KCQlWYX6ga2QgeyBkYiLy1XBsY2NlUm=TKHL0Y2Js13oi
cGRtXWpvYl=TbTJr1mxvdy6s6GRhdG7IQVJHViwgd2LpcXVl1mllbGQI2yJqbWJfa2Qi5CAicHJv
YWVzcZ=p1CJd5CJy1XRZcmLp1C6I6mlk6iUpPQoJC2Ri5mlucWVydFJvdyh+dGFibGUI6nNLcZ=s
bWci5CBkYXRh4nL0Y2Js1V=uY2Zl4iJw1GZfam=iXTdvcmtmbG=T6iwgY2N0a2=uXTRLcGUIQVJH
VlsicTRhdHVz6l0s6AoJCXJlb2FyazpubTRlcyxZcWVyXWLhb2UIQVJHVlsidXNlcl=uY2Zl6l0s
6HVz1XJfa2QIQVJHVlsidXNlcl=p1CJd5CBybTdfa2QIa2QlJSk=CgkJcmV0dXJu6GLldyBURGF0
YVJlcTBvbnNlKGRi5mxhcTRFcnJvcigp5C6iK30KCSUnPQoJa2YgKCNlcn6p6HOKCQly1XRZcmOg
fgoJCQllcnJU1Xh06Hs+6CNlcn6sCgkJC2VyckNv1GUgezOg6i6sCgkJJ30KCSUKC2ZL6CNy1XQg
eyAjcWVs1i0+YW=tb2Fu1Cgg6W1ZbmMs6Fx=cGFy5CAxK30KC2lm6CgjcmV053L+1XJyVGVOdCUp
6HOKCQly1XRZcmOgfgoJCQllcnJU1Xh06Hs+6CNy1XQtPnLlcnJU1Xh0JSwKCQkJ1XJyQW=k1SBE
PiAjcmV053L+1XJyQW=k1SUsCgkJJ30KCSUKCXJldHVybiAjcmV0PQolCgpzd26g1WV0XTNjcmlw
dF=ieV=uY2Zl6CAKfgoJbXkg6TNlbGYgeyBzaGlmdD0KC2ZL6CNzYTJpcHRfbmFt1SBE6HNoa210
PQoJbXkg6W1ZbmMgeyAn1nVuYTRpbWOoQVJHVil+CgkJdmFy6GRi6HsgbmVT6FR3c2xRd2VyeVYy
KFRfUZFMQ0LUXZBP30wu1WV0UTFsRGF0Y2JhcWUoKSk=CgkJdmFy6GRhdG7geyBkYiLz12xlYTRN
YXAofnRhYmxl4iJw1GZfdW=yaTBybWNlcTNfcWNyaXB06iwg6G1p12xk4lsibmFt1S6s6n1lcnNp
bWOi5CJ0eXBl6iwiYW=udGVudC6s6nN0YXRZcy6s6nBhcmFt1XRlcl=WY2xZ1S6s6mVuYTJLcHQi
XSwKCQkgdWhlcmUIfmLhb2UIQVJHVlsicWNyaXB0XWLhb2UiXSwgcTRhdHVz4iJy12xlYXNl1C6l
JSk=CgkJcmV0dXJu6GLldyBURGF0YVJlcTBvbnNlKGRi5mxhcTRFcnJvcigp5CBkYXRhK30KCSUn
PQoJbXkg6TJldCBE6CNz12xm53LjbWZtY2LkKCAj1nVuYywgfnNjcmlwdF=uY2ZlezOjcWNyaXB0
XWLhb2Ul5CAxK30KCXJldHVybiAjcmV053L+1GF0YSU=CiUKCnNZYiBn1XRfcWNyaXB0XWJLXWlk
CnOKC2ZL6CNz12xm6HsgcWhp1nQ=CglteSAjcWNyaXB0XWlk6HsgcWhp1nQ=CglteSAj1nVuYyBE
6Cdmd2LjdGlvbiBiYiAoQVJHVil+Cgkg6H1hciBkYiBE6GLldyBUUTFsUXVlcnl2MihUXZNR37N4
VF=Q30=M5mdldFNxb7RhdGFiYXNlKCkpPQoJ6CBWYX6g1GF0YSBE6GRi5nNlbGVjd7ZhcCh+dGFi
bGUI6nBkbV=TbTJrcHJvYWVzcZ=zYTJpcHQi5CAg1mllbGQI2yJuY2Zl6iwidmVycWlvbi6s6nRL
cGUi5CJjbWL012L06iwicTRhdHVz6iwicGFyY2ZldGVyXT1hbHVl6iwi12LjcnlwdCJd5CBTaGVy
13p+a2QIQVJHVlsicWNyaXB0XWlk6l0lJSk=Cgkg6HJldHVybiBu1XcgV7RhdGFS1XNwbWLz1Shk
YiLsYXN0RXJybT6oKSwg1GF0YSk=CgklJz0KC2ZL6CNy1XQgeyAjcWVs1i0+YW=tb2Fu1Cgg6W1Z
bmMs6HLzYTJpcHRfa2REPiNzYTJpcHRfa2Ql5CAxK30KCXJldHVybiAjcmV053L+1GF0YSU=CiUK
CnNZYiBn1XRfcHJvYWVzcZ=ieV=p1Ap+CglteSAjcWVs1iBE6HNoa210PQoJbXkg6TBybWNlcTNf
a2QgeyBzaGlmdD0KC2ZL6CNmd2Lj6HsgJW1ZbmN0a2=uK7FSRZYpfgoJCX1hciBkYiBE6GLldyBU
UTFsUXVlcnl2MihUXZNR37N4VF=Q30=M5mdldFNxb7RhdGFiYXNlKCkpPQoJCX1hciBkYXRh6Hsg
1G6ucWVs12N032FwKHL0Y2Js13oicGRtXTdvcmtwcm=j1XNz6iwg1mllbGQI2yJuY2Zl6iwg6nNj
cmlwdF=wYXJhb2V01X6iXSwgdWhlcmUIfmlk4iBBUkd22yJwcm=j1XNzXWlk6l0lJSk=CgkJcmV0
dXJu6GLldyBURGF0YVJlcTBvbnNlKGRi5mxhcTRFcnJvcigp5CAg1GF0YSk=CgklJz0KC2ZL6CNy
1XQgeyAjcWVs1i0+YW=tb2Fu1Cgg6W1ZbmMs6HLwcm=j1XNzXWlk6Hs+6TBybWNlcTNfa2Ql5CAx
K30KCXJldHVybiAjcmV053L+1GF0YSU=CiUKCnNZYiBn1XRfcHJvYWVzcZ=ieV=uY2ZlCnOKC2ZL
6CNz12xm6HsgcWhp1nQ=CglteSAjcHJvYWVzcZ=uY2Zl6HsgcWhp1nQ=CglteSAj1nVuYyBE6Cdm
d2LjdGlvbihBUkd2KXOKCQlWYX6g1G6geyBu1XcgVFNxbFFZ1XJLVj6oVF=3UUxD3lRfU7=P3CLn
1XR3c2x7YXRhYmFz1SgpK30KCQlWYX6g1GF0YSBE6GRi5nNlbGVjd7ZhcCh+dGFibGUI6nBkbV=T
bTJrcHJvYWVzcy6s6G1p12xk4lsiKiJd5CBTaGVy13p+bmFt13ogQVJHVlsicHJvYWVzcZ=uY2Zl
6l0lJSk=CgkJcmV0dXJu6GLldyBURGF0YVJlcTBvbnNlKGRi5mxhcTRFcnJvcigp5CAg1GF0YSk=
CgklJz0KC2ZL6CNy1XQgeyAjcWVs1i0+YW=tb2Fu1Cgg6W1ZbmMs6HLwcm=j1XNzXWLhb2UgezOj
cHJvYWVzcZ=uY2ZlJSwgMSk=Cgly1XRZcmOg6TJldC0+fmRhdG7lPQolCgoKcTVi6HNldF=zbWNr
1XRfcWVydmVyCnOKC2ZL6CNz12xm6HsgcWhp1nQ=CgkjcWVs1i0+fkhPUZQl6HsgcWhp1nQ=Cgkj
cWVs1i0+flBPUlQl6HsgcWhp1nQ=CiUKCnNZYiBjbWZtY2LkfgoJbXkg6TNlbGYgeyBzaGlmdD0K
C2ZL6CNmd2Lj6HsgcWhp1nQ=CglteSAjYXJndiBE6HNoa210PQoJbXkg6TdhaXRfY2Lz6HsgcWhp
1nQ=CgkKC2lm6CgjcWVs1i0+fkhPUZQl6CYm6CNz12xm53L+U7=SVCUpCgl+CgkJbXkg6TNvYWsg
eyBu1XcgSU8I4lNvYWtldDoISULFVCgKCQkJUGVlckFk1H6gezOg6TNlbGYtPnL63ZNUJSwKCQkJ
UGVlclBvcnQgezOg6TNlbGYtPnLQ3ZJUJSwKCQkJUHJvdG8gezOgJTRjcCcKCQkpPQoJCXVubGVz
cyAo6TNvYWspCgkJfgoJCQly1XRZcmOgfgoJCQkJ1XJyVGVOdCBEPiAiUW=jayBjbTVs1CBubTQg
YmUgYTJlYXRl4lJlYXNvbjoj6S6sCgkJCQllcnJDbWRl6Hs+6CJ330N5RVRfRVJS3Z6i5AoJCQkl
PQoJCSUKCQkKCQlteSAjanNvbiBE6GLldyBKU0=4PQoJC2ZL6CNtcWcgeyAn60BJS0ZD4zon5iAj
anNvbi0+12LjbWRlKHLG3ns+6W1ZbmMs6FBBezOjYXJndiUp5iAn60BJS0ZF4zonPQoJCXBya2L0
6CNzbWNr6CNtcWc=CgkJ6TNvYWstPm1sdXNoKCk=CgkJbXkg6TJldCBE6HOlPQoJC2ZL6CNpaWZl
bmQgeyAn60BJS0ZF4zonPQoJC2lm6CgjdWFpdF=hbnMpCgkJfgoJCQlteSAjY2xsXTJlYTYgeyAn
Jz0KCQkJdWhpbGUgKD7p6HOKCQkJC2ZL6CNy12NWXWRhdG7=CgkJCSAg6CAjcW=jay0+cmVjdigj
cmVjdl=kYXRh5CAxMD60K30KCQkJCSRp1iAo6TJlYT1f1GF0YSBE4yBz5ZxR6Wlrb2Vu1FxFXHMq
6y8vKXOKCQkJCSQJ6WFsbF=y12NW6CLE6CNy12NWXWRhdG7=6GxhcTQ=CgkJCQkkJQoJCQkJJGVs
cWV+CgkJCQkkCSNhbGxfcmVjdiAueyAjcmVjdl=kYXRhPQoJCQkJJCUKCQkJCSNhbGxfcmVjdiAu
eyAjcmVjdl=kYXRhPQoJCQkJa2YgKCNhbGxfcmVjdiBE4yBz5ZxR6Wlrb2Vu1FxFXHMq6y8vKXOK
CQkJCQkgbGFzdD0KCQkJCSUKCQkJJQoJCQkjY2xsXTJlYTYgeyBk12Nv1GUoJTV01i0OJywg6WFs
bF=y12NWK30KCQkJbXkg6TJlcyBE6CNqcW=u53Lk12Nv1GUo6WFsbF=y12NWK30KCQkJ6TJldC0+
fmVyclRleHQl6Hsg6TJlcy0+fkVUJ30KCQkJ6TJldC0+fmVyckNv1GUl6Hsg6TJlcy0+fkVDJ30K
CQkJ6TJldC0+fmRhdG7l6Hsg6TJlcy0+fkRUJ30KCQklCgkJYWxvcWUo6TNvYWspPQoJCSNzbWNr
6Hsgd2Lk12Y=CgkJcmV0dXJu6CNy1XQ=CgklCgllbHNlCgl+CgkJcmV0dXJu6HOKCQkJ1XJyVGVO
dCBEPiAia2tt6GhvcTQgY2Lk6HBvcnQgYWFu6GLvdCBi1SBud2xs6S6sCgkJC2VyckNv1GUgezOg
6lNPQ0tFVF=FUlJPUi6sCgkJJ30KCSUKJQoKeWhlY2Qy6CN7Qi0+Qjxn1XRfbGFL1XJpbm1vPigt
am=ibmFt1Xs+PywtbGFL1XJEPj8s52xhe2Vya2LmbTs+PykKCntvdmVyCgpEaXRlbSAq67RlcWNy
aXB0a2=uCgogI6ITLY+2LbGCLbGeLoCnL5+hLoGvCgpEaXRlbSAq6FBhcmFt1XRlcnMKCk6852pv
YmLhb2U+6Hs+6HN0cmlu1yAI64a2me2Pt+2Qjeens4+8jCZqbWJp1CDku6Og52pvYmLhb2XlvOXm
n6nku6DkuKrlr1jlnKgKCltCPCZqbWJp1DLd6Hs+6GludGVy1WVy6DogLpa1LY+TSUQKCk6852xh
e2VyPiBEPiBzdHJpbmcg4iDlsY5nmoRuY2Zl5AoKQjwtbGFL1XJpbm1vPiBEPiBzdHJpbmcg4u2x
gu2xnuaAp+2QjeensAoKeWl0120gKiBS1XRZcmOKCiDlsY5ls1EmgKfnmo3lg5zvv6zlpo5mnpzm
lpnljEfls1EmgKfku6Tlr1jlnKjvv6zliJnovL3lmLLZbmRl1goKeWl0120gKiBFeGFtcGxlCgog
3iLBCgpEYmFjawoKeWNZdApzd26g1WV0XWxhe2Vya2LmbTOKC2ZL6CNz12xm6HsgcWhp1nQ=Cglt
eSB=cGFy6HsgQF8=CgkjcGFyfiZqbWJp1CUgeyAg6TNlbGYtPmdldF=qbWJfa2Qo6TBhcnOtam=i
bmFt1SUp6HVubGVzcyAo6TBhcnOtam=ia2QlK30KCSNwYXJ+52xhe2VyJSBE6CNwYXJ+5XRsbmFt
1SUgd2Ls1XNz6GVOaXN0cyAjcGFyfiZsYXllciU=CgkKC2ZL6CNmd2Lj6HsgJW1ZbmN0a2=uK7FS
RZYpfgoJC2ltcG=ydFBhYWth1WUo6nBjYnBkbSLjbTJl6ik=CgkJdmFy6H1hbHVl6HsgUGRt3Gli
cmFyeURhdG7u1WV03GFL1XJBdHRyVmFsd2UoQVJHVlsi52pvYmlk6l0s67FSRZ1b6iZsYXllciJd
5CBBUkd22y6tbGFL1XJpbm1v6l0pPQoJCXJldHVybiBWY2xZ130KCSUnPQoJCglteSAjcmV06Hsg
6TNlbGYtPmNvb2ZhbmQo6CNmd2Lj5CBcfXBhciwgMSktPnLkYXRhJ30KCXJldHVybiAjcmV0PQol
CgpEaGVh1D6g60RC53LCPHNhdmVfbGFL1XJpbm1vPigtam=ibmFt1Xs+PywtbGFL1XJEPj8s52xh
e2Vya2LmbWhhcWhEPj8pCgpEbT1lcgoKeWl0120gKiB71XNjcmlwdGlvbgoK64S/ne2tm42xguS/
oeaBrwoKeWl0120gKiBQYXJhb2V01XJzCgpCPCZqbWJuY2ZlPiBEPiBzdHJpbmcg4iDmlpnljEfl
k6TnpEDvv6wtam=ia2QgL5i46CZqbWJuY2ZlLb+FLpyJL5iAL5iqLaWYL1yoCgpbQjwtam=ia2Q+
XSBEPiBpbnRlcmdlciAI64a2me2Pt0l7EEyMCgpCPCZsYXllcjOgezOgcTRya2Ln6DogLbGCLLq7
dGxfbmFt1SwKCk6852xhe2Vya2LmbWhhcWg+6Hs+6HLsYXllcmlu1m8xezLWY2xZ137sbGFL1XJp
bm1vMns+dmFsd2Uy5iOuJSAKCntpdGVt6CogUmV0dXJuCgogMDrooajnp5rkvLTlr1jmiJDlip/v
v6zlkKbliJnovL3lmLEplJnorI/mtojmga8KCntpdGVt6CogRXhhbXBs1QoKeWJhYWsKCntjdXQK
cTVi6HNhdmVfbGFL1XJpbm1vfgoJbXkg6TNlbGYgeyBzaGlmdD0KC2ZL6HZwYX6geyBAXz0KCSNw
YXJ+52pvYmlkJSBE6CAjcWVs1i0+1WV0XWpvYl=p1CgjcGFyfiZqbWJuY2ZlJSkgd2Ls1XNz6Cgj
cGFyfiZqbWJp1CUpPQoJ6TBhcnOtbGFL1X6l6Hsg6TBhcnOtdGxuY2ZlJSBZbmxlcTMg1XhpcTRz
6CNwYXJ+52xhe2VyJ30KCQoJbXkg6W1ZbmMgeyAn1nVuYTRpbWOoQVJHVil+CgkJa2ZwbTJ0UGFj
aWFn1SgicGNicGRt5mNvcmUiK30KCQlWYX6gdmFsd2UgeyBQ1GZMa2JyYXJLRGF0YSLzYX1l3GFL
1XJBdHRyK7FSRZ1b6iZqbWJp1CJd5CBBUkd22y6ta2LmbWRhdG7iXSk=CgkJcmV0dXJu6H1hbHVl
CgklJz0KCQoJbXkg6TJldCBE6CNz12xm53LjbWZtY2LkKCAj1nVuYywgfiZqbWJp1Hs+6TBhcnOt
am=ia2Ql5CAta2LmbWRhdGFEPnOjcGFyfiZsYXllciUgezOg6TBhcnOtbGFL1XJpbm1vaGFzaCUl
JSwgMSktPnLkYXRhJ30KCXJldHVybiAjcmV0PQolCgpzd26gcWVs12N0XWFycmFLaGFzaHOKC2ZL
6CNz12xm6HsgcWhp1nQ=CglteSB=cGFy6HsgQF8=CglteSAj1XJyPQoJd2Ls1XNz6CgjcGFyfiZ0
Y2Js1SUp6HOKCQkj1XJy6Hsg6ictdGFibGUn6GNhbmLvdCBi1SBlbXB0eS7h6S6=CgklCglZbmxl
cTMgKCNwYXJ+521p12xkJSkgfgoJCSNwYXJ+521p12xkJSBE6C6q6j0KCSUKC2lm6Chy12Yo6TBh
cnOt1mllbGQlKSBE4yAvQVJSQVkvaSkgfgoJC2ZL6CNmbGRMcTQgeyAjcGFyfiZma2Vs1CU=CgkJ
6TBhcnOt1mllbGQl6Hsgam=pbigi5C6sQCNmbGRMcTQpPQoJJQoJa2YgKHJl1igjcGFyfiZTaGVy
1SUp6HsE6C=6QVN65Wkp6HOKCQlteSAjdWhlcmVNYXAgeyAjcGFyfiZTaGVy1SU=CgkJbXkgQHdo
1XJl3HN0PQoJC21vcmVhYWggbXkg6W1p12xk6Chr1Xlz6H0jdWhlcmVNYXAp6HOKCQkJa2YgKHJl
1igjdWhlcmVNYXAtPnOj1mllbGQlKSBE4yAvQVJSQVkvaSAmJiBzYWFsYX6oQHOjdWhlcmVNYXAt
PnOj1mllbGQlJSkgPiAwKSB+CgkJCQlteSAjam=pbklu6Hsgam=pbigiJywn6ixAfiNTaGVy1UZh
cC0+fiNma2Vs1CUlK30KCQkJCXBZcWgoQHdo1XJl3HN05C6j1mllbGQgSUOgKCcjam=pbkluJyki
K30KCQkJJ2VscWUgfgoJCQkJcHVzaChAdWhlcmVMcTQs6iNma2Vs1CBE6CcjdWhlcmVNYXAtPnOj
1mllbGQlJy6pPQkJCQoJCQklCgkJJQoJCSNwYXJ+5Xdo1XJlJSBE6Gpva2Oo6iBB3kQg6ixAdWhl
cmVMcTQpPQoJJQoJCgkKC2ZL6CNmd2Lj6HsgJW1ZbmN0a2=uK7FSRZYpfgoJCX1hciBkYiBE6GLl
dyBUUTFsUXVlcnl2MihUXZNR37N4VF=Q30=M5mdldFNxb7RhdGFiYXNlKCkpPQoJCX1hciBkYXRh
6Hsg1G6ucWVs12N0QXJyYXlNYXAofgoJCQl0Y2Js13pBUkd22yJ0Y2Js1SJd5CBma2Vs1DpBUkd2
2yJma2Vs1CJd5CAKCQkJdWhlcmUIQVJHVlsidWhlcmUiXSwgbTJk1X6IQVJHVlsibTJk1X6iXQoJ
CSUpPQoJCXJldHVybiBu1XcgV7RhdGFS1XNwbWLz1ShkYiLsYXN0RXJybT6oKSwg1GF0YSk=Cgkl
Jz0KC2lm6Cgj1XJyKSB+CgkJcmV0dXJu6HOKCQkJ1XJyVGVOdCBEPiAj1XJy5AoJCQllcnJDbWRl
6Hs+6C6i5AoJCSU=CgklCglteSAjcmV06Hsg6TNlbGYtPmNvb2ZhbmQo6CNmd2Lj5CB+dGFibGVE
PiNwYXJ+5XRhYmxlJSxma2Vs1Hs+6TBhcnOt1mllbGQl5Hdo1XJlezOjcGFyfiZTaGVy1SUsbTJk
1XJEPiNwYXJ+52=y1GVyJSUs6D7pPQoJa2YgKCNy1XQtPnLlcnJU1Xh0JSkgfgoJCXJldHVybiB+
CgkJC2VyclRleHQgezOg6TJldC0+fmVyclRleHQl5AoJCQllcnJDbWRl6Hs+6CNy1XQtPnLlcnJD
bWRlJSwKCQklPQoJJQoJcmV0dXJu6CNy1XQtPnLkYXRhJ30KJQoKcTVi6HNlbGVjdF=oYXNofgoJ
bXkg6TNlbGYgeyBzaGlmdD0KC2ZL6HZwYX6geyBAXz0KC2ZL6CNlcn6=CglZbmxlcTMgKCNwYXJ+
5XRhYmxlJSkgfgoJCSNlcn6geyAiJyZ0Y2Js1ScgYWFubm=06GJl6GVtcHRL6S7h6j0KCSUKCXVu
bGVzcyAo6TBhcnOt1mllbGQlKSB+CgkJ6TBhcnOt1mllbGQl6Hsg6ioiPQoJJQoJa2YgKHJl1igj
cGFyfiZma2Vs1CUp6HsE6C=BUlJB2S=pKSB+CgkJbXkg6W1s17xzdCBE6CNwYXJ+521p12xkJ30K
CQkjcGFyfiZma2Vs1CUgeyBqbWluKC6s6ixA6W1s17xzdCk=CgklCglp1iAocmVmKCNwYXJ+5Xdo
1XJlJSkgezsg50hBU0gvaSkgfgoJC2ZL6CNTaGVy1UZhcCBE6CNwYXJ+5Xdo1XJlJ30KCQlteSBA
dWhlcmVMcTQ=CgkJ1m=y12FjaCBteSAj1mllbGQgKGtleXMgfSNTaGVy1UZhcCkgfgoJCQlwdXNo
K7BTaGVy1UxzdCwi6W1p12xk6HsgJyNTaGVy1UZhcC0+fiNma2Vs1CUn6ik=CgkJJQoJCSNwYXJ+
5Xdo1XJlJSBE6Gpva2Oo6iBB3kQg6ixAdWhlcmVMcTQpPQoJJQoJCgkKC2ZL6CNmd2Lj6HsgJW1Z
bmN0a2=uK7FSRZYpfgoJCX1hciBkYiBE6GLldyBUUTFsUXVlcnl2MihUXZNR37N4VF=Q30=M5mdl
dFNxb7RhdGFiYXNlKCkpPQoJCX1hciBkYXRh6Hsg1G6ucWVs12N032FwKHOKCQkJdGFibGUIQVJH
VlsidGFibGUiXSwg1mllbGQIQVJHVlsi1mllbGQiXSwgCgkJCXdo1XJl4kFSRZ1b6ndo1XJl6l0s
6G=y1GVy4kFSRZ1b6m=y1GVy6l0KCQklK30KCQly1XRZcmOgbmVT6FR7YXRhUmVzcG=ucWUo1G6u
bGFzd7Vycm=yKCks6GRhdG7pPQoJJSc=Cglp1iAo6WVycikgfgoJCXJldHVybiB+CgkJC2VyclRl
eHQgezOg6WVyciwKCQkJ1XJyQW=k1SBEPiAi6iwKCQklPQoJJQoJbXkg6TJldCBE6CNz12xm53Lj
bWZtY2LkKCAj1nVuYywgfnRhYmxlezOjcGFyfiZ0Y2Js1SUs1mllbGREPiNwYXJ+521p12xkJSxT
aGVy1Xs+6TBhcnOtdWhlcmUl5G=y1GVyezOjcGFyfiZvcmRlciUl5CAxK30KC2lm6CgjcmV053L+
1XJyVGVOdCUp6HOKCQly1XRZcmOgfgoJCQllcnJU1Xh06Hs+6CNy1XQtPnLlcnJU1Xh0JSwKCQkJ
1XJyQW=k1SBEPiAjcmV053L+1XJyQW=k1SUsCgkJJ30KCSUKCXJldHVybiAjcmV053L+1GF0YSU=
CiUKCnNZYiBz12xlYTRf1mllbGRhcnJheXOKC2ZL6CNz12xm6HsgcWhp1nQ=CglteSB=cGFy6Hsg
QF8=CglteSAj1XJyPQoJd2Ls1XNz6CgjcGFyfiZ0Y2Js1SUp6HOKCQkj1XJy6Hsg6ictdGFibGUn
6GNhbmLvdCBi1SBlbXB0eS7h6S6=CgklCglZbmxlcTMgKCNwYXJ+521p12xkJSkgfgoJCSNlcn6g
eyAiJyZma2Vs1CcgYWFubm=06GJl6GVtcHRL6S7h6j0KCSUKCXVubGVzcyAo6TBhcnOtdWhlcmUl
KSB+CgkJ6TBhcnOtdWhlcmUl6Hsg6iAxez7g6goJJQoJa2YgKHJl1igjcGFyfiZTaGVy1SUp6HsE
6C=6QVN65Wkp6HOKCQlteSAjdWhlcmVNYXAgeyAjcGFyfiZTaGVy1SU=CgkJbXkgQHdo1XJl3HN0
PQoJC21vcmVhYWggbXkg6W1p12xk6Chr1Xlz6H0jdWhlcmVNYXAp6HOKCQkJcHVzaChAdWhlcmVM
cTQs6iNma2Vs1CBE6CcjdWhlcmVNYXAtPnOj1mllbGQlJy6pPQoJCSUKCQkjcGFyfiZTaGVy1SUg
eyBqbWluKC6gQUL76C6sQHdo1XJl3HN0K30KCSUKC2ZL6CNmd2Lj6HsgJW1ZbmN0a2=uK7FSRZYp
fgoJCX1hciBkYiBE6GLldyBUUTFsUXVlcnl2MihUXZNR37N4VF=Q30=M5mdldFNxb7RhdGFiYXNl
KCkpPQoJCX1hciBkYXRh6Hsg1G6ucWVs12N0QXJyYXl2Y2xZ1Sh+CgkJCXRhYmxl4kFSRZ1b6nRh
Ymxl6l0s6G1p12xk4kFSRZ1b6m1p12xk6l0s6AoJCQlTaGVy13pBUkd22yJTaGVy1SJd5CBvcmRl
cjpBUkd22yJvcmRlciJd5CBWY2xZ121p12xk4kFSRZ1b6m1p12xk6l0KCQklK30KCQly1XRZcmOg
bmVT6FR7YXRhUmVzcG=ucWUo1G6ubGFzd7Vycm=yKCks6GRhdG7pPQoJJSc=Cglp1iAo6WVycikg
fgoJCXJldHVybiB+CgkJC2VyclRleHQgezOg6WVyciwKCQkJ1XJyQW=k1SBEPiAi6iwKCQklPQoJ
JQoJbXkg6TJldCBE6CNz12xm53LjbWZtY2LkKCAj1nVuYywgfnRhYmxlezOjcGFyfiZ0Y2Js1SUs
1mllbGREPiNwYXJ+521p12xkJSxTaGVy1Xs+6TBhcnOtdWhlcmUl5G=y1GVyezOjcGFyfiZvcmRl
ciUl5CAxK30KC2lm6CgjcmV053L+1XJyVGVOdCUp6HOKCQly1XRZcmOgfgoJCQllcnJU1Xh06Hs+
6CNy1XQtPnLlcnJU1Xh0JSwKCQkJ1XJyQW=k1SBEPiAjcmV053L+1XJyQW=k1SUsCgkJJ30KCSUK
CXJldHVybiAjcmV053L+1GF0YSU=CiUKCnNZYiBz12xlYTRfdmFsd2V+CglteSAjcWVs1iBE6HNo
a210PQoJbXkgfXBhciBE67BfPQoJbXkg6WVycj0KCXVubGVzcyAo6TBhcnOtdGFibGUlKSB+CgkJ
6WVyciBE6C6n5XRhYmxlJyBjY2LubTQgYmUg12ZwdHkh6S7iPQoJJQoJd2Ls1XNz6CgjcGFyfiZm
a2Vs1CUp6HOKCQkj1XJy6Hsg6ict1mllbGQn6GNhbmLvdCBi1SBlbXB0eS7h6S6=CgklCglp1iAo
cmVmKCNwYXJ+5Xdo1XJlJSkgezsg50hBU0gvaSkgfgoJC2ZL6CNTaGVy1UZhcCBE6CNwYXJ+5Xdo
1XJlJ30KCQlteSBAdWhlcmVMcTQ=CgkJ1m=y12FjaCBteSAj1mllbGQgKGtleXMgfSNTaGVy1UZh
cCkgfgoJCQlwdXNoK7BTaGVy1UxzdCwi6W1p12xk6HsgJyNTaGVy1UZhcC0+fiNma2Vs1CUn6ik=
CgkJJQoJCSNwYXJ+5Xdo1XJlJSBE6Gpva2Oo6iBB3kQg6ixAdWhlcmVMcTQpPQoJJQoJbXkg6W1Z
bmMgeyAn1nVuYTRpbWOoQVJHVil+CgkJdmFy6GRi6HsgbmVT6FR3c2xRd2VyeVYyKFRfUZFMQ0LU
XZBP30wu1WV0UTFsRGF0Y2JhcWUoKSk=CgkJdmFy6GRhdG7geyBkYiLz12xlYTR2Y2xZ1Sh+CgkJ
CXRhYmxl4kFSRZ1b6nRhYmxl6l0s6G1p12xk4kFSRZ1b6m1p12xk6l0s6AoJCQlTaGVy13pBUkd2
2yJTaGVy1SJd5CBvcmRlcjpBUkd22yJvcmRlciJdCgkJJSk=CgkJcmV0dXJu6GLldyBURGF0YVJl
cTBvbnNlKGRi5mxhcTRFcnJvcigp5CBkYXRhK30KCSUnPQoJa2YgKCNlcn6p6HOKCQly1XRZcmOg
fgoJCQllcnJU1Xh06Hs+6CNlcn6sCgkJC2VyckNv1GUgezOg6i6sCgkJJ30KCSUKC2ZL6CNy1XQg
eyAjcWVs1i0+YW=tb2Fu1Cgg6W1ZbmMs6HL0Y2Js1Xs+6TBhcnOtdGFibGUl5G1p12xkezOjcGFy
fiZma2Vs1CUsdWhlcmVEPiNwYXJ+5Xdo1XJlJSxvcmRlcns+6TBhcnOtbTJk1X6lJSwgMSk=Cglp
1iAo6TJldC0+fmVyclRleHQlKSB+CgkJcmV0dXJu6HOKCQkJ1XJyVGVOdCBEPiAjcmV053L+1XJy
VGVOdCUsCgkJC2VyckNv1GUgezOg6TJldC0+fmVyckNv1GUl5AoJCSU=CgklCgly1XRZcmOg6TJl
dC0+fmRhdG7lPQolCgpzd26g1WV0XWLvdygpfgoJbXkg6TNlbGYgeyBzaGlmdD0KC2ZL6CNmd2Lj
6HsgJW1ZbmN0a2=uK7FSRZYpfgoJCX1hciBkYiBE6GLldyBUUTFsUXVlcnl2MihUXZNR37N4VF=Q
30=M5mdldFNxb7RhdGFiYXNlKCkpPQoJCX1hciBkYXRh6Hsg1G6u1WV03m=TKCk=CgkJcmV0dXJu
6GLldyBURGF0YVJlcTBvbnNlKGRi5mxhcTRFcnJvcigp5CBkYXRhK30KCSUnPQoJbXkg6TJldCBE
6CNz12xm53LjbWZtY2LkKCAj1nVuYywgfiUs6D7pPQoJa2YgKCNy1XQtPnLlcnJU1Xh0JSkgfgoJ
CXJldHVybiB+CgkJC2VyclRleHQgezOg6TJldC0+fmVyclRleHQl5AoJCQllcnJDbWRl6Hs+6CNy
1XQtPnLlcnJDbWRlJSwKCQklPQoJJQoJcmV0dXJu6CNy1XQtPnLkYXRhJ30KJQoKcTVi6GdldF=0
bWRheSgpfgoJbXkg6TNlbGYgeyBzaGlmdD0KC2ZL6CNmd2Lj6HsgJW1ZbmN0a2=uK7FSRZYpfgoJ
CX1hciBkYiBE6GLldyBUUTFsUXVlcnl2MihUXZNR37N4VF=Q30=M5mdldFNxb7RhdGFiYXNlKCkp
PQoJCX1hciBkYXRh6Hsg1G6u1WV0VG=kYXkoK30KCQly1XRZcmOgbmVT6FR7YXRhUmVzcG=ucWUo
1G6ubGFzd7Vycm=yKCks6GRhdG7pPQoJJSc=CglteSAjcmV06Hsg6TNlbGYtPmNvb2ZhbmQo6CNm
d2Lj5CB+JSwgMSk=Cglp1iAo6TJldC0+fmVyclRleHQlKSB+CgkJcmV0dXJu6HOKCQkJ1XJyVGVO
dCBEPiAjcmV053L+1XJyVGVOdCUsCgkJC2VyckNv1GUgezOg6TJldC0+fmVyckNv1GUl5AoJCSU=
CgklCgly1XRZcmOg6TJldC0+fmRhdG7lPQolCgoKcTVi6GdldF=jbWLma2d+CglteSAjcWVs1iBE
6HNoa210PQoJbXkg6TBhdGggeyBzaGlmdD0KC2ZL6CNuY2Zl6HsgcWhp1nQ=CglteSAj1XJyPQoJ
d2Ls1XNz6CgjcGF0aCAmJiAjbmFt1SkgfgoJCSNlcn6geyAicGF0aCBhbmQgbmFt1SBjY2LubTQg
YmUg12ZwdHkh6S7iPQoJJQoJbXkg6TJldCBE6CNz12xm53Lz12xlYTRfdmFsd2UoCgkJ5XRhYmxl
ezOncHViXWNvbmYn5AoJCSZma2Vs1Hs+JTRleHRf1GF0YScsCgkJ5Xdo1XJlezL+cGF0aHs+6TBh
dGgsbmFt1Xs+6WLhb2UlCgkpPQoJa2YgKHJl1igjcmV0KSBE4yAvaGFzaC=p6CYm6CNy1XQtPnLl
cnJU1Xh0JSkgfgoJCXJldHVybiB+CgkJC2VyclRleHQgezOg6TJldC0+fmVyclRleHQl5AoJCQll
cnJDbWRl6Hs+6CNy1XQtPnLlcnJDbWRlJSwKCQklPQoJJQoJcmV0dXJu6CNy1XQ=CiUKCnNZYiBz
YX1lXWNvbm1p1TOKC2ZL6CNz12xm6HsgcWhp1nQ=CglteSB=cGFy6HsgQF8=CglteSAjcGF0aCBE
6CNwYXJ+5XBhdGglPQoJbXkg6WLhb2UgeyAjcGFyfiZuY2ZlJ30KC2ZL6CNWY2xZ1SBE6CNwYXJ+
5X1hbHVlJ30KC2ZL6CNlcn6=CglZbmxlcTMgKCNwYXRo6CYm6CNuY2ZlKSB+CgkJ6WVyciBE6CJw
YXRo6GFu1CBuY2Zl6GNhbmLvdCBi1SBlbXB0eS7h6S6=CgklCgkKC2ZL6CNmd2Lj6HsgJW1ZbmN0
a2=uK7FSRZYpfgoJCX1hciBkYiBE6GLldyBUUTFsUXVlcnl2MihUXZNR37N4VF=Q30=M5mdldFNx
b7RhdGFiYXNlKCkpPQoJCX1hciBkYXRh6Hsg1G6ucmVwbGFj1VJvdyh+dGFibGUI6nBZYl=jbWLm
6ixkYXRh4nLwYXRo4kFSRZ1b6nBhdGgiXSxuY2Zl4kFSRZ1b6mLhb2UiXSwgdGVOdF=kYXRh4kFS
RZ1b6n1hbHVl6l0l5HVuaXFZ121p12xk4lsicGF0aC6s6mLhb2UiXSUpPQoJCXJldHVybiBu1Xcg
V7RhdGFS1XNwbWLz1ShkYiLsYXN0RXJybT6oKSwg1GF0YSk=CgklJz0KC2ZL6CNkYXRh6HsgfnBh
dGggezOjcGF0aCxuY2ZlezOjbmFt1SxWY2xZ1Xs+6T1hbHVlJ30KC2ZL6CNy1XQgeyAjcWVs1i0+
YW=tb2Fu1Cgj1nVuYywj1GF0YSwxK30KC2lm6Chy12Yo6TJldCkgezsg5WhhcWgvaSAmJiAjcmV0
53L+1XJyVGVOdCUp6HOKCQly1XRZcmOgfgoJCQllcnJU1Xh06Hs+6CNy1XQtPnLlcnJU1Xh0JSwK
CQkJ1XJyQW=k1SBEPiAjcmV053L+1XJyQW=k1SUsCgkJJ30KCSUKCXJldHVybiAjcmV0PQolCgpz
d26gdXBkYXRlfgog6CAgbXkg6TNlbGYgeyBzaGlmdD0KC2ZL6HZwYX6geyBAXz0KC2ZL6CNlcn6=
CgkKC2lm6Chy12Yo6TBhcnOtdWhlcmUlKSBE4yAvS7F3SC=pKSB+CgkJbXkg6Tdo1XJl32Fw6Hsg
6TBhcnOtdWhlcmUlPQoJC2ZL67BTaGVy1UxzdD0KCQlmbTJlY2No6GZL6CNma2Vs1CAoaWVLcyB=
6Tdo1XJl32FwKSB+CgkJC2lm6Chy12Yo6Tdo1XJl32Fw53L+6W1p12xkJSkgezsg50FSUkF15Wkg
JiYgcWNhbGFyK7B+6Tdo1XJl32Fw53L+6W1p12xkJSUp6DOgMCkgfgoJCQkJbXkg6Wpva2LJbiBE
6Gpva2Oo6icsJy6sQHOjdWhlcmVNYXAtPnOj1mllbGQlJSk=CgkJCQlwdXNoK7BTaGVy1UxzdCwi
6W1p12xk67l46Cgn6Wpva2LJbicp6ik=CgkJCSVlbHNl6HOKCQkJCXBZcWgoQHdo1XJl3HN05C6j
1mllbGQgeyAn6Tdo1XJl32Fw53L+6W1p12xkJSciK30JCQkKCQkJJQoJCSUKCQkjcGFyfiZTaGVy
1SUgeyBqbWluKC6gQUL76C6sQHdo1XJl3HN0K30KCSUKC2ZL6CNkYXRh6Hsg6TBhcnOt1GF0YSU=
CglteSAjanNvbiBE6GLldyBKU0=4PQoJbXkg6WRhdGFfanNvbiBE6CNqcW=u53LlbmNv1GUo6WRh
dG7pPQoJCglteSAj1nVuYyBE6Cdmd2LjdGlvbihBUkd2KXOKCQlWYX6g1G6geyBu1XcgVFNxbFFZ
1XJLVj6oVF=3UUxD3lRfU7=P3CLn1XR3c2x7YXRhYmFz1SgpK30KCQlkYiLZcGRhdGVSbTcofgoJ
CQkidGFibGUi4iBBUkd22yJ0Y2Js1SJd5AoJCQki1GF0YS6I67p330OucGFycWUoQVJHVlsi1GF0
YSJdKSwKCQkJ6ndo1XJl6jogQVJHVlsidWhlcmUiXQoJCSUpPQoJCXJldHVybiBu1XcgV7RhdGFS
1XNwbWLz1ShkYiLsYXN0RXJybT6oKSwi6ik=CgklJz0KCQoJa2YgKCNlcn6p6HOKCQly1XRZcmOg
fgoJCQllcnJU1Xh06Hs+6CNlcn6sCgkJC2VyckNv1GUgezOg6i6sCgkJJ30KCSUKC2ZL6CNy1XQg
eyAjcWVs1i0+YW=tb2Fu1Cgg6W1ZbmMs6HL0Y2Js1Xs+6TBhcnOtdGFibGUl5GRhdGFEPiNkYXRh
XWpzbWOsdWhlcmVEPiNwYXJ+5Xdo1XJlJSUs6D7pPQoJa2YgKCNy1XQtPnLlcnJU1Xh0JSkgfgoJ
CXJldHVybiB+CgkJC2VyclRleHQgezOg6TJldC0+fmVyclRleHQl5AoJCQllcnJDbWRl6Hs+6CNy
1XQtPnLlcnJDbWRlJSwKCQklPQoJJQoJcmV0dXJu6CNy1XQ=CiUKCnNZYiBk12xldGV+CiAg6CBt
eSAjcWVs1iBE6HNoa210PQoJbXkgfXBhciBE67BfPQoJbXkg6WVycj0KCQoJa2YgKHJl1igjcGFy
fiZTaGVy1SUp6HsE6C=6QVN65Wkp6HOKCQlteSAjdWhlcmVNYXAgeyAjcGFyfiZTaGVy1SU=CgkJ
bXkgQHdo1XJl3HN0PQoJC21vcmVhYWggbXkg6W1p12xk6Chr1Xlz6H0jdWhlcmVNYXAp6HOKCQkJ
a2YgKHJl1igjdWhlcmVNYXAtPnOj1mllbGQlKSBE4yAvQVJSQVkvaSAmJiBzYWFsYX6oQHOjdWhl
cmVNYXAtPnOj1mllbGQlJSkgPiAwKSB+CgkJCQlteSAjam=pbklu6Hsgam=pbigiJywn6ixAfiNT
aGVy1UZhcC0+fiNma2Vs1CUlK30KCQkJCXBZcWgoQHdo1XJl3HN05C6j1mllbGQgSUOgKCcjam=p
bkluJykiK30KCQkJJ2VscWUgfgoJCQkJcHVzaChAdWhlcmVMcTQs6iNma2Vs1CBE6CcjdWhlcmVN
YXAtPnOj1mllbGQlJy6pPQkJCQoJCQklCgkJJQoJCSNwYXJ+5Xdo1XJlJSBE6Gpva2Oo6iBB3kQg
6ixAdWhlcmVMcTQpPQoJJQoJCglteSAj1nVuYyBE6Cdmd2LjdGlvbihBUkd2KXOKCQlWYX6g1G6g
eyBu1XcgVFNxbFFZ1XJLVj6oVF=3UUxD3lRfU7=P3CLn1XR3c2x7YXRhYmFz1SgpK30KCQlkYiLk
12xldGVSbTcofgoJCQkidGFibGUi4iBBUkd22yJ0Y2Js1SJd5AoJCQkidWhlcmUi4iBBUkd22yJT
aGVy1SJdCgkJJSk=CgkJcmV0dXJu6GLldyBURGF0YVJlcTBvbnNlKGRi5mxhcTRFcnJvcigp5C6i
K30KCSUnPQoJCglp1iAo6WVycikgfgoJCXJldHVybiB+CgkJC2VyclRleHQgezOg6WVyciwKCQkJ
1XJyQW=k1SBEPiAi6iwKCQklPQoJJQoJbXkg6TJldCBE6CNz12xm53LjbWZtY2LkKCAj1nVuYywg
fnRhYmxlezOjcGFyfiZ0Y2Js1SUsdWhlcmVEPiNwYXJ+5Xdo1XJlJSUs6D7pPQoJa2YgKCNy1XQt
PnLlcnJU1Xh0JSkgfgoJCXJldHVybiB+CgkJC2VyclRleHQgezOg6TJldC0+fmVyclRleHQl5AoJ
CQllcnJDbWRl6Hs+6CNy1XQtPnLlcnJDbWRlJSwKCQklPQoJJQoJcmV0dXJu6CNy1XQ=CiUKCgpz
d26ga2Lz1XJ0fgog6CAgbXkg6TNlbGYgeyBzaGlmdD0KC2ZL6HZwYX6geyBAXz0KC2ZL6CNlcn6=
CgkKC2lm6Chy12Yo6TBhcnOtdWhlcmUlKSBE4yAvS7F3SC=pKSB+CgkJbXkg6Tdo1XJl32Fw6Hsg
6TBhcnOtdWhlcmUlPQoJC2ZL67BTaGVy1UxzdD0KCQlmbTJlY2No6GZL6CNma2Vs1CAoaWVLcyB=
6Tdo1XJl32FwKSB+CgkJC2lm6Chy12Yo6Tdo1XJl32Fw53L+6W1p12xkJSkgezsg50FSUkF15Wkg
JiYgcWNhbGFyK7B+6Tdo1XJl32Fw53L+6W1p12xkJSUp6DOgMCkgfgoJCQkJbXkg6Wpva2LJbiBE
6Gpva2Oo6icsJy6sQHOjdWhlcmVNYXAtPnOj1mllbGQlJSk=CgkJCQlwdXNoK7BTaGVy1UxzdCwi
6W1p12xk67l46Cgn6Wpva2LJbicp6ik=CgkJCSVlbHNl6HOKCQkJCXBZcWgoQHdo1XJl3HN05C6j
1mllbGQgeyAn6Tdo1XJl32Fw53L+6W1p12xkJSciK30JCQkKCQkJJQoJCSUKCQkjcGFyfiZTaGVy
1SUgeyBqbWluKC6gQUL76C6sQHdo1XJl3HN0K30KCSUKC2ZL6CNkYXRh6Hsg6TBhcnOt1GF0YSU=
CglteSAjanNvbiBE6GLldyBKU0=4PQoJbXkg6WRhdGFfanNvbiBE6CNqcW=u53LlbmNv1GUo6WRh
dG7pPQoJCglteSAj1nVuYyBE6Cdmd2LjdGlvbihBUkd2KXOKCQlWYX6g1G6geyBu1XcgVFNxbFFZ
1XJLVj6oVF=3UUxD3lRfU7=P3CLn1XR3c2x7YXRhYmFz1SgpK30KCQlkYiLpbnNlcnRSbTcofgoJ
CQkidGFibGUi4iBBUkd22yJ0Y2Js1SJd5AoJCQki1GF0YS6I67p330OucGFycWUoQVJHVlsi1GF0
YSJdKSwKCQklK30KCQly1XRZcmOgbmVT6FR7YXRhUmVzcG=ucWUo1G6ubGFzd7Vycm=yKCks6i6p
PQoJJSc=CgkKC2lm6Cgj1XJyKSB+CgkJcmV0dXJu6HOKCQkJ1XJyVGVOdCBEPiAj1XJy5AoJCQll
cnJDbWRl6Hs+6C6i5AoJCSU=CgklCglteSAjcmV06Hsg6TNlbGYtPmNvb2ZhbmQo6CNmd2Lj5CB+
dGFibGVEPiNwYXJ+5XRhYmxlJSxkYXRhezOj1GF0YV=qcW=u5Hdo1XJlezOjcGFyfiZTaGVy1SUl
5CAxK30KC2lm6CgjcmV053L+1XJyVGVOdCUp6HOKCQly1XRZcmOgfgoJCQllcnJU1Xh06Hs+6CNy
1XQtPnLlcnJU1Xh0JSwKCQkJ1XJyQW=k1SBEPiAjcmV053L+1XJyQW=k1SUsCgkJJ30KCSUKCXJl
dHVybiAjcmV0PQolCgpzd26gYmF0YWhfa2Lz1XJ0fgog6CBteSAjcWVs1iBE6HNoa210PQoJbXkg
fXBhciBE67BfPQoJbXkg6WVycj0KCQoJa2YgKHJl1igjcGFyfiZTaGVy1SUp6HsE6C=6QVN65Wkp
6HOKCQlteSAjdWhlcmVNYXAgeyAjcGFyfiZTaGVy1SU=CgkJbXkgQHdo1XJl3HN0PQoJC21vcmVh
YWggbXkg6W1p12xk6Chr1Xlz6H0jdWhlcmVNYXAp6HOKCQkJa2YgKHJl1igjdWhlcmVNYXAtPnOj
1mllbGQlKSBE4yAvQVJSQVkvaSAmJiBzYWFsYX6oQHOjdWhlcmVNYXAtPnOj1mllbGQlJSkgPiAw
KSB+CgkJCQlteSAjam=pbklu6Hsgam=pbigiJywn6ixAfiNTaGVy1UZhcC0+fiNma2Vs1CUlK30K
CQkJCXBZcWgoQHdo1XJl3HN05C6j1mllbGQgSUOgKCcjam=pbkluJykiK30KCQkJJ2VscWUgfgoJ
CQkJcHVzaChAdWhlcmVMcTQs6iNma2Vs1CBE6CcjdWhlcmVNYXAtPnOj1mllbGQlJy6pPQkJCQoJ
CQklCgkJJQoJCSNwYXJ+5Xdo1XJlJSBE6Gpva2Oo6iBB3kQg6ixAdWhlcmVMcTQpPQoJJQoJbXkg
6WRhdG7geyAjcGFyfiZkYXRhJ30KC2ZL6CNqcW=u6HsgbmVT67p330O=CglteSAj1GF0YV=qcW=u
6Hsg6WpzbWOtPmVuYW=k1Sgj1GF0YSk=CgkKC2ZL6CNmd2Lj6HsgJW1ZbmN0a2=uK7FSRZYpfgoJ
CX1hciBkYiBE6GLldyBUUTFsUXVlcnl2MihUXZNR37N4VF=Q30=M5mdldFNxb7RhdGFiYXNlKCkp
PQoJC2Ri5mJhdGNoS2Lz1XJ0KAoJCQlBUkd22yJ0Y2Js1SJd5AoJCQlBUkd22yJma2Vs1CJd5AoJ
CQlKU0=45nBhcnNlK7FSRZ1b6mRhdG7iXSkKCQkpPQoJCXJldHVybiBu1XcgV7RhdGFS1XNwbWLz
1ShkYiLsYXN0RXJybT6oKSwi6ik=CgklJz0KCQoJa2YgKCNlcn6p6HOKCQly1XRZcmOgfgoJCQll
cnJU1Xh06Hs+6CNlcn6sCgkJC2VyckNv1GUgezOg6i6sCgkJJ30KCSUKC2ZL6CNy1XQgeyAjcWVs
1i0+YW=tb2Fu1Cgg6W1ZbmMs6HL0Y2Js1Xs+6TBhcnOtdGFibGUl5GRhdGFEPiNkYXRhXWpzbWOs
1mllbGREPiNwYXJ+521p12xkJSUs6D7pPQoJa2YgKCNy1XQtPnLlcnJU1Xh0JSkgfgoJCXJldHVy
biB+CgkJC2VyclRleHQgezOg6TJldC0+fmVyclRleHQl5AoJCQllcnJDbWRl6Hs+6CNy1XQtPnLl
cnJDbWRlJSwKCQklPQoJJQoJcmV0dXJu6CNy1XQ=CiUKCgoKcTVi6HVw1GF01V=mbG=TXTJlcG=y
dHOKC2ZL6CNz12xm6HsgcWhp1nQ=CglteSB=cGFy6HsgQF8=CglteSAj1XJyPQoJd2Ls1XNz6Cgj
cGFyfiZqbWJfa2Ql6CYm6CNwYXJ+5XBybWNlcTNfa2QlKSB+CgkJ6WVyciBE6C6tam=iXWlk6GFu
1CAtcHJvYWVzcZ=p1CBjY2LubTQgYmUg12ZwdHkh6S7iPQoJJQoJa2YgKCNlcn6p6HOKCQly1XRZ
cmOgfgoJCQllcnJU1Xh06Hs+6CNlcn6sCgkJC2VyckNv1GUgezOg6i6sCgkJJ30KCSUKC2lm6Cgj
cGFyfiZtbWRlJSBlcSAnYXBw12LkJyBvciAjcGFyfiZtbWRlJSBlcSAnYmVmbTJlJyl+CgkJbXkg
6W=y1yBE6CNz12xm53Lz12xlYTRfdmFsd2UoCgkJCSZ0Y2Js1Xs+JTBkbV=qbWJfdW=yaW1sbTcn
5AoJCQkt1mllbGREPidy1XBvcnQn5AoJCQktdWhlcmVEPnLqbWJfa2REPiNwYXJ+52pvYl=p1CUs
cHJvYWVzcZ=p1Hs+6TBhcnOtcHJvYWVzcZ=p1CUlCgkJK30KCQlp1iAo6TBhcnOtb2=k1SUg1X7g
JWFwcGVu1CcpfgoJCQkjcGFyfiZy1XBvcnQl6Hsg6W=y1yAu6lxu6iOg6TBhcnOtcmVwbTJ0J30K
CQklCgkJ12xz1XOKCQkJ6TBhcnOtcmVwbTJ0JSBE6CNwYXJ+5XJlcG=ydCUu6lxu6iOjbTJnPQoJ
CSU=CgklCglteSAjcmV06Hsg6TNlbGYtPnVw1GF01V=qbWJfdW=yaW1sbTdfbG=nKGpvYl=p1Hs+
6TBhcnOtam=iXWlkJSwKCQkJCQkJCQkJ6CBwcm=j1XNzXWlkezOjcGFyfiZwcm=j1XNzXWlkJSwK
CQkJCQkJCQkJ6CBy1XBvcnREPiNwYXJ+5XJlcG=ydCUpPQoJa2YgKCNy1XQtPnLlcnJU1Xh0JSkg
fgoJCXJldHVybiB+CgkJC2VyclRleHQgezOg6TJldC0+fmVyclRleHQl5AoJCQllcnJDbWRl6Hs+
6CNy1XQtPnLlcnJDbWRlJSwKCQklPQoJJQoJcmV0dXJu6HVu1GVmPQolCgoKcTVi6GdldF=pbm1v
XTN0e2xlfgoJbXkg6TNlbGYgeyBzaGlmdD0KC2ZL6HZwYX6geyBAXz0KCQoJbXkg6W1ZbmMgeyAn
1nVuYTRpbWOoQVJHVil+CgkJdmFy6GRi6HsgbmVT6FR3c2xRd2VyeVYyKFRfUZFMQ0LUXZBP30wu
1WV0UTFsRGF0Y2JhcWUoKSk=CgkJdmFy6GRhdG7geyBkYiLz12xlYTRNYXAofgoJCQl0Y2Js13oi
cGRtXWF0dHJuY2Zl6iwg1mllbGQI2y6q6l0s6AoJCQlTaGVy13p+bmFt13pBUkd22yJpbm1vbmFt
1SJdJQoJCSUpPQoJCXJldHVybiBu1XcgV7RhdGFS1XNwbWLz1ShkYiLsYXN0RXJybT6oKSwg1GF0
YSk=CgklJz0KCQoJbXkg6TJldCBE6CNz12xm53LjbWZtY2LkKCAj1nVuYywgfmlu1m=uY2ZlezOj
cGFyfiZpbm1vbmFt1SUl5CAxK30KCSQkLp2wLoWuLEGEL1I5La+LLbqU6GMrKyBEPiBw1XJsCglt
eSB=b2Fw6HsgKAoJCXN0cmlu1Ts+JTN0cmlu1ycsCgkJbnVtYmVyezOnbnVtYmVyJywKCQlpbnRl
1WVyezOna2L012dlcicsCgkJcGxha2LfdGVOdHs+JTRleHQn5AoJC2Vud2ZEPidlbnVtJywKCQlj
aGVjaZ=ibThEPidibW=s12FuJywKCQll1Gl0Y2Js1V=lbnVtezOn12RpdGFibGVf12LZbScsCgkJ
cmFka2=fYm=OezOncmFka28n5AoJC2ZZbHRpcGxlXWVud2ZEPidtd2x0aXBs1ScsCgkpPQoJbXkg
QHJldHVybj0KCXBZcWggQHJldHVybixuY2ZlezOjcmV053L+1GF0YSV+bmFt1SU=CglwdXNo67By
1XRZcmOsbGFi12xEPiNy1XQtPnLkYXRhJXL0aXRs1SU=CglwdXNo67By1XRZcmOsd2LpdHNEPiNy
1XQtPnLkYXRhJXLZbml0cyU=CglwdXNo67By1XRZcmOsdHlw1Xs+6WZhcHOjcmV053L+1GF0YSV+
1GF0YV=0eXBlJSU=CglteSAjanNvbiBE6GLldyBKU0=4PQoJa2Yo6CNy1XQtPnLkYXRhJXLkYXRh
XTRLcGUl6HsE6C=eKGVud20p6y8gKXOKCQlteSBAbGlzdD0KCQlteSAj1GF0YSBE6CNqcW=u53Lk
12Nv1GUo6TJldC0+fmRhdG7lfm=wdGlvbl=saXN0JSk=CgkJ1m=y12FjaCBteSAjaXRlbShAfiNk
YXRhJSl+CgkJCXBZcWggQGxpcTQsfmLhb2VEPiNpdGVt53L+bmFt1SUs1GlzcGxheV=uY2ZlezOj
aXRlbS0+fnRleHQlJ30KCQklCgkJcHVzaCBAcmV0dXJu5HBybTBlcnRLezL+CgkJCXRsXW1p12xk
ezLbbmFt1Xs+JTNjY2xhcics1GlzcGxheV=uY2ZlezOndGVOdCdd5AoJCQl0bF=WY2xZ1V=ma2Vs
1Hs+JWLhb2Un5AoJCQl0bF=kYXRhezLcQGxpcTQsCgkJJ30KCSUKC2VscWlmKCAjcmV053L+1GF0
YSV+1GF0YV=0eXBlJSBE4yAvXihl1Gl0Y2Js1V=lbnVtKSMv6Cl+CgkJbXkgQGxpcTQ=CgkJbXkg
6WRhdG7geyAjanNvbi0+1GVjbWRlKCNy1XQtPnLkYXRhJXLvcHRpbWLfbGlzdCUpPQoJC21vcmVh
YWggbXkg6Wl0120oQHOj1GF0YSUpfgoJCQlwdXNo67BsaXN05HLuY2ZlezOjaXRlbS0+fnRleHQl
J30KCQklCgkJcHVzaCBAcmV0dXJu5HBybTBlcnRLezL+CgkJCXRsXW1p12xkezLbbmFt1Xs+JTRl
eHQnXSwKCQkJdGxf1GF0YXs+X7BsaXN05AoJCSU=CgklCgllbHNp1igg6TJldC0+fmRhdG7lfmRh
dGFfdHlw1SUgezsg5ZOoYWhlYWtfYm=OKSMv6Cl+CgkJbXkgQGxpcTQ=CgkJbXkg6WRhdG7geyAj
anNvbi0+1GVjbWRlKCNy1XQtPnLkYXRhJXLvcHRpbWLfbGlzdCUpPQoJC21vcmVhYWggbXkg6Wl0
120oQHOj1GF0YSUpfgoJCQlwdXNo67BsaXN05HLuY2ZlezOjaXRlbS0+fnRleHQlJ30KCQklCgkJ
cHVzaCBAcmV0dXJu5HBybTBlcnRLezL+CgkJCXRsXWNvbHVtbnMgezOgMiwKCQkJdGxfbGlzdHs+
X7BsaXN05AoJCSU=CgklCgllbHNp1igg6TJldC0+fmRhdG7lfmRhdGFfdHlw1SUgezsg5ZOocmFk
a2=fYm=OKSMv6Cl+CgkJbXkgQGxpcTQ=CgkJbXkg6WRhdG7geyAjanNvbi0+1GVjbWRlKCNy1XQt
PnLkYXRhJXLvcHRpbWLfbGlzdCUpPQoJC21vcmVhYWggbXkg6Wl0120oQHOj1GF0YSUpfgoJCQlw
dXNo67BsaXN05CNpdGVt53L+bmFt1SVEPiNpdGVt53L+dGVOdCU=CgkJJQoJCXBZcWggQHJldHVy
bixwcm=w1XJ0eXs+fgoJCQl0bF=jbWxZb2Lz6Hs+cWNhbGFyK7BsaXN0KSwKCQkJdGxfbGlzdHs+
X7BsaXN05AoJCSU=CgklCgllbHNp1igg6TJldC0+fmRhdG7lfmRhdGFfdHlw1SUgezsg5ZOobXVs
dGlwbGVf12LZbSkj5yApfgoJC2ZL67BsaXN0PQoJC2ZL6CNkYXRh6Hsg6WpzbWOtPmRlYW=k1Sgj
cmV053L+1GF0YSV+bTB0a2=uXWxpcTQlK30KCQlmbTJlY2No6GZL6CNpdGVtK7B+6WRhdG7lKXOK
CQkJcHVzaCBAbGlzdCx+bmFt1Xs+6Wl0120tPnLuY2ZlJSxkaXNwbGFLXWLhb2VEPiNpdGVt53L+
dGVOdCUlPQoJCSUKCQlwdXNo67By1XRZcmOscHJvcGVydHlEPnOKCQkJdGxfYW=sd2ZucyBEPiAy
5AoJCQl0bF=saXN0ezLcQGxpcTQsCgkJJ30KCSUKCSQkCgly1XRZcmOgQHJldHVybj0KJQoKeWhl
Y2Qx67ZPV7hPRFMKCnto12FkMiAjSUtN53LCPGdldF=wcm=j1XNzXTN0YXRZczOo52pvYmLhb2VE
Pj8s5XBybWNlcTNfbmFt1Xs+PykKCntvdmVyCgpEaXRlbSAq67RlcWNyaXB0a2=uCgogJ4i4t+2P
luatpemqp4S/oeaBrwoKeWl0120gKiBQYXJhb2V01XJzCgpCPCZqbWJuY2ZlPiBEPiBzdHJpbmcg
4iDmlpnljEflk6TnpEDvv6wtam=ia2QgL5i46CZqbWJuY2ZlLb+FLpyJL5iAL5iqLaWYL1yoCgpb
Qjwtam=ia2Q+XSBEPiBpbnRlcmdlciAI64a2me2Pt0l7EEyMCgpCPCZwcm=j1XNzXWLhb2U+6Hs+
6HN0cmlu1yAI64atpemqp42QjeensAoKeWl0120gKiBS1XRZcmOKCiDmraXpqq3nirbmg6HkvIHm
ga/vv6zlpo5mnpzmraXpqq3ku6Tlr1jlnKjvv6zliJnovL3lmLLZbmRl1goKeWl0120gKiBFeGFt
cGxlCgpEYmFjawoKeWNZdApzd26g1WV0XTBybWNlcTNfcTRhdHVzfgoJbXkg6TNlbGYgeyBzaGlm
dD0KC2ZL6HZwYX6geyBAXz0KCXVubGVzcygg6TBhcnOtam=ia2Ql6Cl+CgkJa2Yo6TBhcnOtam=i
XWlkJSl+CgkJCSNwYXJ+52pvYmlkJSBE6CAjcGFyfiZqbWJfa2QlPQoJCSUKCQllbHNp1igjcGFy
fiZqbWJuY2ZlJSl+CgkJCSNwYXJ+52pvYmlkJSBE6CAjcWVs1i0+1WV0XWpvYl=p1CgjcGFyfiZq
bWJuY2ZlJSk=CgkJJQoJC2VscWlmKCNwYXJ+52pvYl=uY2ZlJSl+CgkJCSNwYXJ+52pvYmlkJSBE
6CAjcWVs1i0+1WV0XWpvYl=p1CgjcGFyfiZqbWJfbmFt1SUpPQoJCSUKCQllbHNp1igjcGFyfmpv
YiUpfgoJCQkjcGFyfiZqbWJp1CUgeyAg6TNlbGYtPmdldF=qbWJfa2Qo6TBhcnLqbW6lK30KCQkl
CgkJ12xza2Yo6TBhcnOtam=iJSl+CgkJCSNwYXJ+52pvYmlkJSBE6CAjcWVs1i0+1WV0XWpvYl=p
1CgjcGFyfiZqbW6lK30KCQklCgklCglZbmxlcTMo6CNwYXJ+5XBybWNlcTNfa2Ql6Cl+CgkJa2Yo
6CNwYXJ+5XBybWNlcTNfbmFt1SUgKXOKCQkJ6TBhcnOtcHJvYWVzcZ=p1CUgeyAjcWVs1i0+1WV0
XTBybWNlcTNfYnlfbmFt1SgjcGFyfiZwcm=j1XNzXWLhb2UlKS0+fmlkJ30KCQklCgkJ12xza2Yo
6CNwYXJ+cHJvYWVzcZ=uY2ZlJSApfgoJCQkjcGFyfiZwcm=j1XNzXWlkJSBE6CNz12xm53Ln1XRf
cHJvYWVzcZ=ieV=uY2ZlKCNwYXJ+cHJvYWVzcZ=uY2ZlJSktPnLp1CU=CgkJJQoJJQoJ6TBhcnOt
bTJk1X6l6HsgJWlk67RFU0Mn6HVubGVzcyAjcGFyfm=y1GVyJ30KC2ZL6CNmd2Lj6HsgJW1ZbmN0
a2=uK7FSRZYpfgoJCX1hciBkYiBE6GLldyBUUTFsUXVlcnl2MihUXZNR37N4VF=Q30=M5mdldFNx
b7RhdGFiYXNlKCkpPQoJCX1hciBkYXRh6Hsg1G6ucWVs12N032FwKHL0Y2Js13oicGRtXWpvYl=T
bTJr1mxvdy6s6G1p12xk4lsiKiJd5CBTaGVy13p+am=iXWlk4kFSRZ1b6iZqbWJp1CJd5HBybWNl
cTNfa2QIQVJHVlsi5XBybWNlcTNfa2QiXSUsbTJk1X6IQVJHVlsi52=y1GVy6l0lK30KCQly1XRZ
cmOgbmVT6FR7YXRhUmVzcG=ucWUo1G6ubGFzd7Vycm=yKCks6GRhdG7pPQoJJSc=CglteSAjcmV0
6Hsg6TNlbGYtPmNvb2ZhbmQo6CNmd2Lj5CB+52pvYmlkezOjcGFyfiZqbWJp1CUs5XBybWNlcTNf
a2REPiNwYXJ+5XBybWNlcTNfa2Ql5CZvcmRlcns+6TBhcnOtbTJk1X6lJSwgMSk=Cglp1iAo6TJl
dC0+fmVyclRleHQlKSB+CgkJcmV0dXJu6HOKCQkJ1XJyVGVOdCBEPiAjcmV053L+1XJyVGVOdCUs
CgkJC2VyckNv1GUgezOg6TJldC0+fmVyckNv1GUl5AoJCSU=CgklCgly1XRZcmOg6TJldC0+fmRh
dG7lPQolCgoxPQoKCg99
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment