#! perl -w ####################################################################### # Scanner.pl : Rudman scanner ####################################################################### use strict; use warnings; use Win32::SerialPort qw( :STAT 0.19 ); use Win32::GUI qw( MB_ICONQUESTION MB_ICONINFORMATION MB_YESNOCANCEL MB_ICONSTOP MB_OK IDYES IDCANCEL ); use Win32::TieRegistry( Delimiter => "/", ArrayValues => 0 ); my $Version = "2.14"; my $TimeBase = time; my $CurrentFile; my $EvilPort; my $CommandLineHelp = < 'RUNGUI'; use constant RUNINDIA => 'RUNINDIA'; use constant STOP => 'STOP'; use constant UNKNOWNRUN => 'n/a'; my $ForceRun = UNKNOWNRUN; for (@ARGV) { if (m(^[-/](.*?):(.*)$) || m(^[-/](.*?)$)) { my ($switch, $value) = ($1, $2); if ("\U$switch" eq RUNGUI) { $ForceRun = RUNGUI; } elsif ("\U$switch" eq RUNINDIA) { $ForceRun = RUNINDIA; } elsif ($switch =~ /^EDIT|STOP|NORUN$/i) { $ForceRun = STOP; } else { die qq($CommandLineHelp\nUnknown switch "$_"\n); } } elsif (!defined $CurrentFile) { $CurrentFile = $_; } elsif (!defined $EvilPort) { $EvilPort = $_; } else { die $CommandLineHelp; } } my $RegBase = "CUser/Software/Manzanita"; $Registry->{"$RegBase/LastRun"} = ''.localtime; $RegBase .= "/Scanner"; $ForceRun = $Registry->{"$RegBase/RunAtStart"} || UNKNOWNRUN if $ForceRun eq UNKNOWNRUN; ApplyDefault($CurrentFile, ''); ApplyDefault($EvilPort, 'COM13'); NormalizeCOM($EvilPort); my $CSVFile = "Scanner.csv"; my $LogFile = "Scanner.txt"; # Allow translatable messages our $LanguageFile = "$0.language.txt"; do 'Language.pl' || do '..\Language.pl' || die "Failed to load Language.pl: $@ $!"; # Port objects and history our $PortInput; our $SupplyPortObj; our $EvilPortObj; do 'Ports.pl' || do '..\Ports.pl' || die "Failed to load Ports.pl: $@ $!"; my $FindDlg; my $YScales = 5; my $graphmargin = 0.2; my $MaxLineWidth; my $MinLineWidth; my $SetlLineWidth; my $SethLineWidth; my $TempLineWidth; my $XtrnLineWidth; my $MaxLinePColor; my $MinLinePColor; my $SetlLinePColor; my $SethLinePColor; my $TempLinePColor; my $XtrnLinePColor; my $MaxLineBColor; my $MinLineBColor; my $SetlLineBColor; my $SethLineBColor; my $TempLineBColor; my $XtrnLineBColor; use constant INDIAWIDTH => 14; use constant STOPPED => 0; use constant RUNNINGGUI => 1; use constant RUNNINGINDIA => 2; use constant STOPPING => 3; my @Running = (STOPPED); my $EditFont = new Win32::GUI::Font ( -name => "Courier New", -size => 12, ); my $LabelFont = new Win32::GUI::Font ( -name => "Courier New", -size => 12, ); # Make Menu my $Menu = Win32::GUI::MakeMenu( "&File" => "File", " > &New" => { -name => "Exit", -onClick => \&Scanner_OnNewFile }, " > &Open..." => { -name => "Open", -onClick => \&Scanner_OnOpenFile }, " > &Save" => { -name => "Save", -onClick => \&Scanner_OnSaveFile }, " > Save &As ..." => { -name => "SaveAs", -onClick => \&Scanner_OnSaveAsFile }, " > -" => 0, " > Print &Setup ..." => { -name => "PrintSetup", -enabled => 0 }, " > &Print" => { -name => "Print", -enabled => 0 }, " > -" => 0, " > E&xit" => { -name => "Exit", -onClick => \&Scanner_OnQuitFile }, "&Edit" => "Edit", " > &Undo" => { -name => "Undo", -onClick => sub { my $self = shift; $self->Edit->Undo(); 0; } }, " > -" => 0, " > Cu&t" => { -name => "Cut", -onClick => sub { my $self = shift; $self->Edit->Cut(); 0; } }, " > &Copy" => { -name => "Copy", -onClick => sub { my $self = shift; $self->Edit->Copy(); 0; } }, " > &Paste" => { -name => "Paste", -onClick => sub { my $self = shift; $self->Edit->Paste(); 0; } }, " > &Delete" => { -name => "Delete", -onClick => sub { my $self = shift; $self->Edit->Clear(); 0; } }, " > -" => 0, " > &Select All" => { -name => "SelectAll", -onClick => sub { my $self = shift; $self->Edit->SelectAll(); 0; } }, "&View" => "_View", " > &Edit Font..." => { -name => "ChooseFont", -onClick => \&Scanner_OnChooseFont }, " > &Label Font..." => { -name => "ChooseFont", -onClick => \&Scanner_OnChooseLabelFont }, "F&ind" => "_Find", " > &Find..." => { -name => "Find", -onClick => \&Scanner_OnFind }, " > Find &Next" => { -name => "FindNext", -onClick => \&Scanner_FindText }, "&Control" => "_Control", " > &Run GUI" => { -name => "ControlRunGUI", -onClick => \&Scanner_RunGUI }, " > Run &India" => { -name => "ControlRunIndia", -onClick => \&Scanner_RunIndia }, " > Run at st&art..." => 0, " > > GUI" => { -name=> "ControlRunAtStartGUI", -checked => ($Registry->{"$RegBase/RunAtStart"} || STOP) eq RUNGUI }, " > > India" => { -name=> "ControlRunAtStartIndia", -checked => ($Registry->{"$RegBase/RunAtStart"} || STOP) eq RUNINDIA }, " > &Stop" => { -name => "ControlStop", -onClick => \&Scanner_Stop, -enabled => 0}, # " > Clear &History" => { -name => "ControlHistory", -onClick => \&Scanner_History, -enabled => 0}, " > &COM detect" => { -name => "ControlCOM", -onClick => \&Control_COMDetect }, " > COM and cell &detect" => { -name => "ControlCOMCell", -onClick => \&Control_COMCellDetect }, " > Save &logging..." => { -name => "ControlLogging", -checked => 0 }, " > Save CS&V..." => { -name => "ControlCSV", -checked => 0 }, " > Si&mulate" => { -name => "ControlSimulate", -checked => 0 }, " > Char&ger test" => { -name => "ChargerTest",-onClick => \&Scanner_OnChargerTest }, " > &Terminal" => { -name => "Term", -onClick => \&Scanner_OnTerm }, "&Help" => "?", " > &Help" => { -name => "Help", -onClick => \&Scanner_OnHelp }, " > -" => 0, " > &About" => { -name => "About", -onClick => \&Scanner_OnAbout }, ); # Create Main Window my $Window = new Win32::GUI::Window ( -name => "Window", -title => "Scanner", -pos => [$Registry->{"$RegBase/Left"} || 100, $Registry->{"$RegBase/Top"} || 100], -size => [$Registry->{"$RegBase/Width"} || 400, $Registry->{"$RegBase/Height"} || 400], -menu => $Menu, -onResize => \&Scanner_OnSize, -onInitMenu => \&Scanner_OnInitMenu, -onTerminate => \&Scanner_OnQuitFile, -eventmodel => "both", # Force both event model ) or die "new Window"; my $DC = $Window->GetDC; # Create Textfield for manual terminal $Window->AddTextfield( -name => "Term", -pos => [0, 0], -size => [100, 100], -multiline => 1, -hscroll => 1, -vscroll => 1, -autohscroll => 1, -autovscroll => 1, -keepselection => 1 , -font => $EditFont, -visible => 0, -injectcrdelay => 0, -processing => 0, -running => 0, ); # Create Textfield for Edit Text $Window->AddTextfield( -name => "Edit", -pos => [0, 0], -size => [100, 100], -multiline => 1, -hscroll => 1, -vscroll => 1, -autohscroll => 1, -autovscroll => 1, -keepselection => 1 , -font => $EditFont, -visible => 1, ); # Fill in the edit screen with default values if no file was specified if ($CurrentFile eq '') { $Window->Edit->Append(Translate("<$_>\r\n")) for (split(/[\r\n]/m,<Edit->Modify(0); # Set Modified state to false } # Create Textfield for Help $Window->AddTextfield( -name => "Help", -pos => [0, 0], -size => [100, 100], -multiline => 1, -autowrap => 1, -vscroll => 1, -autovscroll => 1, -keepselection => 1, -font => $EditFont, -visible => 0, ); $Window->Help->Append(Translate("<$_>\r\n")) for (split(/[\r\n]/m,<AddStatusBar(); # Run GUI button $Window->AddButton ( -name => "RunGUI", -text => "RunGUI", -pos => [0, 0], -size => [75 , 21], -onClick => \&Scanner_RunGUI, ); # Run India button $Window->AddButton ( -name => "RunIndia", -text => "RunIndia", -pos => [0, 0], -size => [75 , 21], -onClick => \&Scanner_RunIndia, -output => [], ); # Stop running $Window->AddButton ( -name => "Stop", -text => "Stop", -pos => [0, 0], -size => [75 , 21], -onClick => \&Scanner_Stop, ); $Window->Stop->Enable(0); # Done with help button $Window->AddButton ( -name => "DoneHelp", -text => "OK", -pos => [0, 0], -size => [75 , 21], -onClick => \&Scanner_DoneHelp, ); $Window->DoneHelp->Hide(); $Window->AddLabel( -name => "Label_Y_$_", -text => "0.000V", -pos => [0, 0], -multiline => 0, -hscroll => 0, -vscroll => 0, -autohscroll => 0, -autovscroll => 0, -font => $LabelFont, -visible => 0, ) for (1..$YScales); $Window->AddLabel( -name => "Label_MouseOver", -text => "mi.9.999 v.9.9999 ma.9.9999", -pos => [0, 0], -readonly => 1, -multiline => 0, -hscroll => 0, -vscroll => 0, -autohscroll => 0, -autovscroll => 0, -font => $LabelFont, -visible => 0, ); # Create Find DialogBox $FindDlg = new Win32::GUI::DialogBox( -name => "FindDlg", -title => "Find", -pos => [ 150, 150 ], -size => [ 360, 115 ], -parent => $Window, # Set $Window as parent for be modeless but not Top. ); # Text to find $FindDlg->AddTextfield ( -name => "FindDlg_Text", -pos => [10, 12], -size => [220, 21], -prompt => [ "Find : ", 30], -tabstop => 1, -onChange => sub { $FindDlg->FindDlg_Find->Enable(length($FindDlg->FindDlg_Text->Text) != 0); 0; }, ); # Case sensitive checkbox $FindDlg->AddCheckbox ( -name => "FindDlg_Case", -text => "Match case", -pos => [10, 60], -size => [100, 21], -tabstop => 1, ); # $FindDlg->AddGroupbox( -text => "Direction", -pos => [140, 40], -size => [120, 40], ); # Search Up RadioButton $FindDlg->AddRadioButton( -name => "FindDlg_FindUp", -text => "Up", -pos => [150, 55], -size => [50, 20], -group => 1, ); # Search Down RadioButton $FindDlg->AddRadioButton( -name => "FindDlg_FindDown", -text => "Down", -pos => [200, 55], -size => [50, 20], -tabstop => 1, -checked => 1, ); # Find button $FindDlg->AddButton ( -name => "FindDlg_Find", -text => "&Find", -pos => [270, 10], -size => [75 , 21], -onClick => sub { Scanner_FindText($Window); 0; }, -group => 1, -tabstop => 1, ); # Cancel Button $FindDlg->AddButton ( -name => "FindDlg_Cancel", -text => "C&ancel", -pos => [270, 40], -size => [75 , 21], -onClick => sub { $FindDlg->Hide(); 0; }, -tabstop => 1, ); Scanner_LoadFile($Window, $CurrentFile); # Set Focus to Edit and show Window $Window->Edit->SetFocus(); $Window->Show(); my $Pwhite = new Win32::GUI::Pen([ 255, 255, 255 ]); my $Pred = new Win32::GUI::Pen([ 255, 0, 0 ]); my $Pgreen = new Win32::GUI::Pen([ 0, 255, 0 ]); my $Pblue = new Win32::GUI::Pen([ 0, 0, 255 ]); my $Pblack = new Win32::GUI::Pen( -color => [ 0, 0, 0 ], -width => 1, ); my $Pcyan = new Win32::GUI::Pen([ 0, 255, 255 ]); my $Porange = new Win32::GUI::Pen([ 255, 128, 0 ]); my $Bwhite = new Win32::GUI::Brush([ 255, 255, 255 ]); my $Bred = new Win32::GUI::Brush([ 255, 0, 0 ]); my $Bgreen = new Win32::GUI::Brush([ 0, 255, 0 ]); my $Bblue = new Win32::GUI::Brush([ 0, 0, 255 ]); my $Bblack = new Win32::GUI::Brush([ 0, 0, 0 ]); my $Bcyan = new Win32::GUI::Brush([ 0, 255, 255 ]); my $Borange = new Win32::GUI::Brush([ 255, 128, 0 ]); my @busaddrs; my %emh; my @EMHLabels = (); my @EMHTypes = ( {Legend => 'C', Evil => 'c.', Value => '([+-]\d+\.\d+)', Format => '%.1f', Margin => 0.20}, {Legend => 'A', Evil => 'a.', Value => '([+-]\d+\.\d+)', Format => '%.1f', Margin => 0.20}, {Legend => 'W', Evil => 'w.', Value => '([+-]\d+\.\d+)', Format => '%.1f', Margin => 0.20}, {Legend => 'K', Evil => 'k.', Value => '([+-]\d+\.\d+)', Format => '%.1f', Margin => 0.20}, {Legend => 'V', Evil => 'v.', Value => '(\d+\.\d+)', Format => '%.3f', Margin => 0.20}, {Legend => 'G', Evil => 'g.', Value => 'G\s+(\d+\.?\d*)', Format => '%.1f', Margin => 0} ); my $MouseIndex; my @MouseOver; my $ShowMouseOver = 1; my $MouseY; my $MouseX; my %vbottom; my $vmin = 2.0; my $vmax = 4.5; my $cmin; my $cmax; my $setl = 9999; my $seth = 0; my $tempwarm = 9999; my $tempoff = 0; my $xtrnwarm = 50; my $xtrnoff = 120; my $vleft; my $vtop; my $vright; my $vbottom; my $vwidth; my $vheight; my $delta; my %uniquelog; if ($ForceRun eq RUNGUI) { Scanner_RunGUI(); } elsif ($ForceRun eq RUNINDIA) { Scanner_RunIndia(); } TestLoop(); sub AdjustTermText { my $pos = shift; my $text = shift; my $width = shift || INDIAWIDTH; my $term = $Window->Term->Text(); substr $term,$pos,$width,sprintf("%-*s", $width, $text); $Window->Term->Text($term); } sub DrawHbar { my ($DC, $index, $min, $current, $max, $margin) = @_; my $delta = int($vtop / @EMHLabels); my $top = $index * $delta; my $bottom = $top + $delta - 2; my $left; my $right; $DC->SelectObject($Pwhite); $DC->SelectObject($Bwhite); $DC->Rectangle($vleft, $top, $vright, $bottom); # Zero margin means a simple min-max bar if ($margin == 0) { $left = $vleft; if ($max == $min) { $right = $vright; } else { $right = $vleft + ($current - $min) / ($max - $min) * ($vright - $vleft); } $DC->SelectObject($Pblue); $DC->SelectObject($Bblue); $DC->Rectangle($left, $top, $right, $bottom); # Erase to the right $DC->SelectObject($Pwhite); $DC->SelectObject($Bwhite); $DC->Rectangle($right, $top, $vright, $bottom); return; } # Nonzero margin means 0.000 is in the center. $max = max(-$min, $max); $min = -$max; $min -= $margin; $max += $margin; if ($current < 0) { $right = $vleft + ($vright - $vleft) / 2; $left = $vleft + ($current - $min) / ($max - $min) * ($vright - $vleft); } else { $left = $vleft + ($vright - $vleft) / 2; $right = $vleft + ($current - $min) / ($max - $min) * ($vright - $vleft); } $DC->SelectObject($current < 0 ? $Pred : $Pgreen); $DC->SelectObject($current < 0 ? $Bred : $Bgreen); $DC->Rectangle($left, $top, $right, $bottom); } sub ProcessNotRunning { Win32::Sleep(10); if ($Window->Term->{running}) { if (!$Menu->{ControlSimulate}->Checked) { GetPortInput($EvilPortObj); } ++$Window->Term->{processing}; if (defined($PortInput) && length($PortInput) != 0) { $PortInput =~ s/[\r\n]+/\r\n/goim; $Window->Term->Append($PortInput); $Window->Term->{injectcrdelay} = ($PortInput =~ /[\r\n]$/oim) ? 0 : 5; } elsif ($Window->Term->{injectcrdelay} && --$Window->Term->{injectcrdelay} == 0) { $Window->Term->Append("\r\n"); } $Window->Term->Modify(0); --$Window->Term->{processing}; } return 1 if $Running[0] == STOPPED; if (!$Menu->{ControlSimulate}->Checked) { PortClose($EvilPortObj, OPENFAIL => {SUB => \&Evil_OpenCloseFail, PARMS => [$Window, Translate(":")]}); } $Window->Edit->Show(); $Window->Edit->SetFocus(); $Window->Term->Hide(); $Window->RunGUI->Enable(1); $Window->RunIndia->Enable(1); $Window->Stop->Enable(0); $Menu->{ControlRunGUI}->Enabled(1); $Menu->{ControlRunIndia}->Enabled(1); $Menu->{ControlStop}->Enabled(0); $Menu->{Help}->Enabled(1); $Menu->{Term}->Enabled(1); $Window->{"Label_$_"}->Hide() for map { ${$busaddrs[$_]}{BA} } (0..$#busaddrs); $Window->{"Label_Y_$_"}->Hide() for (1..$YScales); $Window->{"Label_EMH_$_"}->Hide() for (0..$#EMHLabels); undef $ShowMouseOver; $Window->{"Label_MouseOver"}->Hide(); $Running[0] = STOPPED; StatusBar_Update(); return 1; } sub ProcessIndia { my $outputline = 0; for my $line (split(/\r\n/m, $Window->Edit->Text())) { last unless $Running[0] == RUNNINGINDIA; next if $line =~ /^\s*;/; my $command; my $outputfield = 0; while (length($line)) { Win32::GUI::DoEvents(); last unless $Running[0] == RUNNINGINDIA; if ($line =~ s/^(.*?);//) { $command = $1; } else { $command = $line; $line = ''; } if ($command =~ /COM\d+/io) { NormalizeCOM($command); if ($EvilPort ne $command) { if (!$Menu->{ControlSimulate}->Checked) { my $SaveEvilPort = $EvilPort; $EvilPort = $command; my $NewEvilPortObj = PortOpen(PORT => $command, CRTERMINATE => 1, TRANSLATECR => 1, TIMEOUT => 200, OPENFAIL => {SUB => \&Evil_OpenCloseFail, PARMS => [$Window, Translate(":")]}); if (!defined($NewEvilPortObj)) { $Running[0] = STOPPING; $EvilPort = $SaveEvilPort; next; } PortClose($EvilPortObj, OPENFAIL => {SUB => \&Evil_OpenCloseFail, PARMS => [$Window, Translate(":")]}); $EvilPortObj = $NewEvilPortObj; } } next; } my ($silent) = ($command =~ s/^!//); Scanner_WriteEvil("\n", $command); if (!$silent) { ${${$Window->RunIndia->{output}}[$outputline]}[$outputfield++] = (length($PortInput) == 0 ? "--ERR--" : $PortInput); my $output; for (@{$Window->RunIndia->{output}}) { if (defined $_ && @{$_}) { $output .= join(' ', @{$_}) . "\r\n"; } else { $output .= "\r\n"; } } $Window->Term->Text($output); } } ++$outputline; } return 1; } sub ProcessEMH { for (0..$#EMHLabels) { my %row = %{$EMHLabels[$_]}; Scanner_WriteEvil("\n", "%s%s", Address($emh{BA}), $row{Evil}); $emh{$row{Legend}} = $1 if $PortInput =~ /$row{Value}/; $emh{"$row{Legend}MIN"} = min($emh{$row{Legend}}, $emh{"$row{Legend}MIN"}); $emh{"$row{Legend}MAX"} = max($emh{$row{Legend}}, $emh{"$row{Legend}MAX"}); DrawHbar($DC, $_, $emh{"$row{Legend}MIN"}, $emh{$row{Legend}}, $emh{"$row{Legend}MAX"}, $row{Margin}); } Labels_EMH_Update(); return 0; } sub ProcessRunningGUI { my $pass = $_[0]; my ($width, $height) = ($Window->Edit->Width(), $Window->Edit->Height()); my $ba; # These things do not need to be read every channel $ba = $busaddrs[$pass % @busaddrs]; $_ = $$ba{BA}; Scanner_WriteEvil("\n", "%s%s", Address($_), 'mi.'); ($$ba{VMIN}) = $1 if $PortInput =~ /(\d+\.\d+)/; uniquelogprint($_, 'mi.', $1); $vmin = min($vmin, $$ba{VMIN}); Scanner_WriteEvil("\n", "%s%s", Address($_), 'ma.'); ($$ba{VMAX}) = $1 if $PortInput =~ /(\d+\.\d+)/; uniquelogprint($_, 'ma.', $1); $vmax = max($vmax, $$ba{VMAX}); Scanner_WriteEvil("\n", "%s%s", Address($_), 't.'); ($$ba{TEMP}) = $1 if $PortInput =~ /(\d+)F/; uniquelogprint($_, 't.', $1); Scanner_WriteEvil("\n", "%s%s", Address($_), 'x.'); ($$ba{XTRN}) = $1 if $PortInput =~ /(\d+)F/; uniquelogprint($_, 'x.', $1); StatusBar_Update(); for (my $index = 0; $index < @busaddrs; ++$index) { $ba = $busaddrs[$index]; $_ = $$ba{BA}; if (!$$ba{PRELOADED}) { Evil_StatusBar("\n", "%s%s", Address($_), 'mi.'); ($$ba{VMIN}) = $PortInput =~ /(\d+\.\d+)/; uniquelogprint($_, 'mi.', $1); SaveMin($$ba{VMIN}); Evil_StatusBar("\n", "%s%s", Address($_), 'v.'); ($$ba{V}) = $PortInput =~ /(\d+\.\d+)/; uniquelogprint($_, 'v.', $1); Evil_StatusBar("\n", "%s%s", Address($_), 'ma.'); ($$ba{VMAX}) = $PortInput =~ /(\d+\.\d+)/; uniquelogprint($_, 'ma.', $1); SaveMax($$ba{VMAX}); Evil_StatusBar("\n", "%s%s", Address($_), 'r.'); ($$ba{SETL}) = $PortInput =~ /(\d+\.\d+)/; uniquelogprint($_, 'r.', $1); $setl = min($setl, $$ba{SETL}); SaveMin($setl); Evil_StatusBar("\n", "%s%s", Address($_), 'g.'); ($$ba{SETH}) = $PortInput =~ /(\d+\.\d+)/; uniquelogprint($_, 'g.', $1); $seth = max($seth, $$ba{SETH}); SaveMax($seth); Evil_StatusBar("\n", "%s%s", Address($_), 't.'); ($$ba{TEMP}) = $PortInput =~ /(\d+)F/; uniquelogprint($_, 't.', $1); Evil_StatusBar("\n", "%s%s", Address($_), 'x.'); ($$ba{XTRN}) = $PortInput =~ /(\d+)F/; uniquelogprint($_, 'x.', $1); Evil_StatusBar("\n", "%s%s", Address($_), 'tempoff.'); ($$ba{TEMPOFF}) = $PortInput =~ /(\d+)F/; uniquelogprint($_, 'tempoff.', $1); $tempoff = max($tempoff, $$ba{TEMPOFF}); Evil_StatusBar("\n", "%s%s", Address($_), 'tempwarm.'); ($$ba{TEMPWARM}) = $PortInput =~ /(\d+)F/; uniquelogprint($_, 'tempwarm.', $1); $tempwarm = min($tempoff, $$ba{TEMPWARM}); $$ba{TEMPWARM}-- if $$ba{TEMPOFF} == $$ba{TEMPWARM}; $vbottom{$busaddrs[$index]} = vmincolor($index); $$ba{PRELOADED} = 1; } Win32::GUI::DoEvents(); last if $Running[0] != RUNNINGGUI; $Window->{"Label_MouseOver"}->Show() if $ShowMouseOver; Scanner_WriteEvil("\n", "%s%s", Address($_), 'v.'); $$ba{V} = $1 if $PortInput =~ /(\d+\.\d+)/; uniquelogprint($_, 'v.', $1); SaveMinMax($index); my $color = vcolor($index); my $bcolor = defined($vbottom{$_}) ? $vbottom{$_} : $color; my $B = new Win32::GUI::Brush($color); my $Bbottom = new Win32::GUI::Brush($bcolor); my (@clear, @mid, @bottom, @min, @max, @setl, @seth, @temp1, @temp2, @xtrn); my ($left, $mid, $right, $bottom, $min, $max); $left = $vleft + $index * $delta; $right = $left + $delta - 2; $mid = ($left + $right) / 2; $min = $vtop + barheight($vheight, $$ba{VMIN}); $max = $vtop + barheight($vheight, $$ba{VMAX}); @clear = ($left, $vtop, $right, $vtop + barheight($vheight, $$ba{V})); @mid = ($left, $vtop + barheight($vheight, $$ba{V}), $right, $min); @bottom = ($left, $min, $right, $vbottom); @max = ([$left, $max], [$right, $max + $MaxLineWidth]); @min = ([$left, $min], [$right, $min + $MinLineWidth]); @setl = ([$left, $vtop + barheight($vheight, $$ba{SETL})], [$right, $vtop + barheight($vheight, $$ba{SETL}) + $SetlLineWidth]); @seth = ([$left, $vtop + barheight($vheight, $$ba{SETH})], [$right, $vtop + barheight($vheight, $$ba{SETH}) + $SethLineWidth]); $$ba{TOP} = $vtop; $$ba{BOTTOM} = $vbottom; $$ba{LEFT} = $left; $$ba{RIGHT} = $right; $DC->SelectObject($Pwhite); # Erase over the box top $DC->SelectObject($Bwhite); $DC->Rectangle(@clear); $DC->Rectangle($left, $vbottom, $right, $vbottom + max($TempLineWidth, $XtrnLineWidth)); $DC->SelectObject($Pblack); # Draw the box top $DC->SelectObject($B); $DC->Rectangle(@mid); $DC->SelectObject($Bbottom); # Draw the box bottom $DC->Rectangle(@bottom); $DC->SelectObject($MaxLinePColor); # Draw the max line $DC->SelectObject($MaxLineBColor); $DC->Rectangle(@{$max[0]}, @{$max[1]}); $DC->SelectObject($MinLinePColor); # Draw the min line $DC->SelectObject($MinLineBColor); $DC->Rectangle(@{$min[0]}, @{$min[1]}); $DC->SelectObject($SetlLinePColor); # Draw the low line $DC->SelectObject($SetlLineBColor); $DC->Rectangle(@{$setl[0]}, @{$setl[1]}); $DC->SelectObject($SethLinePColor); # Draw the high line $DC->SelectObject($SethLineBColor); $DC->Rectangle(@{$seth[0]}, @{$seth[1]}); if ($$ba{TEMP}) { my $tempsetl = ${$setl[0]}[1]; my $tempseth = ${$seth[0]}[1]; my $tempscaled = ($$ba{TEMP} - $tempwarm) * ($tempsetl - $tempseth) / ($tempoff - $tempwarm); $tempscaled = $tempsetl - $vbottom if $tempsetl - $tempscaled > $vbottom; $tempscaled = $tempseth - $vtop - $TempLineWidth if $tempsetl - $tempscaled < $vtop; @temp1 = ([$left, $tempsetl - $tempscaled], [$mid, $tempsetl - $tempscaled + $TempLineWidth]); @temp2 = ([$mid, $tempsetl - $tempscaled], [$right, $tempsetl - $tempscaled + $TempLineWidth]); $DC->SelectObject($TempLinePColor); # Draw the temp. line $DC->SelectObject($TempLineBColor); $DC->Rectangle(@{$temp1[0]}, @{$temp1[1]}); } if ($$ba{XTRN}) { my $xtrnsetl = ${$setl[0]}[1]; my $xtrnseth = ${$seth[0]}[1]; my $xtrnscaled = ($$ba{XTRN} - $xtrnwarm) * ($xtrnsetl - $xtrnseth) / ($xtrnoff - $xtrnwarm); $xtrnscaled = $xtrnsetl - $vbottom if $xtrnsetl - $xtrnscaled > $vbottom; $xtrnscaled = $xtrnseth - $vtop - $XtrnLineWidth if $xtrnsetl - $xtrnscaled < $vtop; @xtrn = ([$left, $xtrnsetl - $xtrnscaled], [$right, $xtrnsetl - $xtrnscaled + $XtrnLineWidth]); $DC->SelectObject($XtrnLinePColor); # Draw the xtrntemp line $DC->SelectObject($XtrnLineBColor); $DC->Rectangle(@{$xtrn[0]}, @{$xtrn[1]}); } $DC->SelectObject($TempLinePColor); # Draw the temp. line $DC->SelectObject($TempLineBColor); $DC->Rectangle(@{$temp2[0]}, @{$temp2[1]}) if @temp2; $Window->{"Label_MouseOver"}->Show() if $ShowMouseOver; } } sub TestLoop { my $index = 0; my $testminmax = 0; for (my $pass = 0; ; ++$pass) { Win32::GUI::DoEvents(); if ($Running[0] != RUNNINGGUI && $Running[0] != RUNNINGINDIA) { next if ProcessNotRunning(); } if ($Running[0] == RUNNINGINDIA) { next if ProcessIndia(); } Visible_Update(); if (%emh) { next if ProcessEMH(); } ProcessRunningGUI($pass); } } sub Visible_Update { my ($width, $height) = ($Window->Edit->Width(), $Window->Edit->Height()); $vleft = 100; $vtop = %emh ? 25 * @EMHLabels : 30; $vright = $width; $vbottom = $height - 30; $vwidth = $vright - $vleft; $vheight = $vbottom - $vtop; $delta = int($vwidth / @busaddrs); } sub vcolor { my $index = shift; my $ba = $busaddrs[$index]; my $volt = $$ba{V}; return $vbottom{$$ba{BA}} = [255, 0, 0 ] if $volt < ($$ba{SETL} || 2.500); return [0, 255, 0] if $volt < ($$ba{SETH} || 3.500); undef $vbottom{$$ba{BA}}; return [0, 0, 255]; } sub vmincolor { my $index = shift; my $ba = $busaddrs[$index]; my $volt = $$ba{VMIN}; return $vbottom{$$ba{BA}} = [255, 0, 0 ] if $volt < ($$ba{SETL} || 2.500); return [0, 255, 0] if $volt < ($$ba{SETH} || 3.500); undef $vbottom{$$ba{BA}}; return [0, 0, 255]; } sub barheight { my $total = shift; my $current = shift; my $min = min($setl, $vmin) - $graphmargin; my $max = max($seth, $vmax) + $graphmargin; int($total * (1 - ($current - $min) / ($max - $min))); } sub SaveMinMax { my $index = shift; my $ba = $busaddrs[$index]; if ($$ba{V} < $$ba{VMIN}) { $$ba{VMIN} = $$ba{V}; SaveMin($$ba{V}); StatusBar_Update(); } if ($$ba{V} > $$ba{VMAX}) { $$ba{VMAX} = $$ba{V}; SaveMax($$ba{V}); StatusBar_Update(); } } ####################################################################### # Window Event # Resize Window sub Scanner_OnSize { my ($self) = @_; $Registry->{"$RegBase/Left"} = $self->Left(); $Registry->{"$RegBase/Top"} = $self->Top(); $Registry->{"$RegBase/Width"} = $self->Width(); $Registry->{"$RegBase/Height"} = $self->Height(); my ($width, $height) = ($self->GetClientRect())[2..3]; $self->Edit->Resize($width+1, $height+1 - $StatusBar->Height() - $Window->RunGUI->Height() - 2) if exists $self->{Edit}; $self->Help->Resize($width+1, $height+1 - $StatusBar->Height() - $Window->RunGUI->Height() - 2) if exists $self->{Help}; $self->Term->Resize($width+1, $height+1 - $StatusBar->Height() - $Window->RunGUI->Height() - 2) if exists $self->{Term}; ($width, $height) = ($Window->ScaleWidth(), $Window->ScaleHeight()); $StatusBar->Move( 0, ($Window->ScaleHeight() - $StatusBar->Height()) ); $StatusBar->Resize( $Window->ScaleWidth(), $StatusBar->Height() ); StatusBar_Update(); Labels_Update(); sub buttonleft { $Window->ScaleWidth() - $_[0] * (2 + $Window->RunGUI->Width()); } my $buttontop = $height - $StatusBar->Height() - $Window->RunGUI->Height(); $Window->RunGUI->Move(buttonleft(1), $buttontop); $Window->RunIndia->Move(buttonleft(2), $buttontop); $Window->DoneHelp->Move(buttonleft(1), $buttontop); $Window->Stop->Move(buttonleft(3), $buttontop); $Window->InvalidateRect(1); } # Refresh menu item sub Scanner_OnInitMenu { my $self = shift; # File Menu # Edit Menu my $bSel = $self->Edit->HaveSel(); $Menu->{Undo}->Enabled($self->Edit->CanUndo()); $Menu->{Cut}->Enabled($bSel); $Menu->{Copy}->Enabled($bSel); $Menu->{Paste}->Enabled($self->Edit->CanPaste()); $Menu->{Delete}->Enabled($bSel); # Find Menu $Menu->{FindNext}->Enabled(length($FindDlg->FindDlg_Text->Text) != 0); 0; } # ####################################################################### sub Scanner_CheckSaveCurrentFile { my $self = shift; # Check if need to save if ($self->Edit->Modify()) { my $ret = $self->MessageBox ( "Save current file ?", "Scanner", MB_ICONQUESTION | MB_YESNOCANCEL); if ($ret == IDYES) { Scanner_OnSaveFile($self); } elsif ($ret == IDCANCEL) { return IDCANCEL; } } return IDYES; } # Find Text in Edit (search is defined by FindDlg) sub Scanner_FindText { my $self = shift; my $text = $self->Edit->Text; # Get Edit text my $find = $FindDlg->FindDlg_Text->Text; # Get text to find my ($start, $end) = $self->Edit->GetSel(); # Get current Edit Selection my $index; unless ( $FindDlg->FindDlg_Case->Checked() ) { # Check for case unsensitive search $text = lc $text; # Transform both to lowercase $find = lc $find; } if ($FindDlg->FindDlg_FindDown->Checked()) { # Search to botton ? $index = index($text, $find, $end); } else { # Or Search to Up ? $index = rindex($text, $find, $start-1); } if ($index >= 0) { $self->Edit->SetSel($index, $index + length $find); # Select text found $self->Edit->ScrollCaret(); # Make selected text visible } else { Win32::GUI::MessageBeep; # Sound if not found } 0; } ####################################################################### # Menu Event # # File menu # # New File sub Scanner_OnNewFile { my $self = shift; # Need to save current file ? return 0 if Scanner_CheckSaveCurrentFile($self) == IDCANCEL; # Set Default filename $CurrentFile = ""; StatusBar_Update(); $self->Text("Scanner"); # Change main window Title # Reset Edit $self->Edit->Text(""); # Set Edit text Empty $self->Edit->EmptyUndoBuffer(); # Empty Undo buffer $self->Edit->Modify(0); # Set Modified state to false $self->Edit->SetFocus(); # Focus to Edit 0; } # Open file sub Scanner_OnOpenFile { my $self = shift; # Need to save current file ? return 0 if Scanner_CheckSaveCurrentFile($self) == IDCANCEL; # Open a file my $file = Win32::GUI::GetOpenFileName( -owner => $Window, -title => "Open a text file", -filter => [ 'Mark 3 scanner (*.mk3s)' => '*.mk3s', 'Text file (*.txt)' => '*.txt', 'All files' => '*.*', ], ); Scanner_LoadFile($self, $file); 0; } sub Scanner_LoadFile { my $self = shift; my $file = shift; # Load file if ($file) { # Keep Current file. $CurrentFile = $file; StatusBar_Update(); $self->Text( "$CurrentFile - Scanner"); # Load File in Edit $self->Edit->LockWindowUpdate; # Lock Painting on Edit $self->Edit->Text(""); # Empty Edit open F, "<$file" or warn "Open file : $file"; while ( ) { chomp; $self->Edit->Append($_."\r\n"); # Need to add \r } close F; # Reset Edit $self->Edit->EmptyUndoBuffer(); $self->Edit->Modify(0); $self->Edit->Select(0,0); # Set Cursor on first char $self->Edit->ScrollCaret(); # Make cursor visible $self->Edit->LockWindowUpdate(1); # Unlock Painting on Edit $self->Edit->SetFocus(); } elsif (Win32::GUI::CommDlgExtendedError()) { $self->MessageBox ("ERROR : ".Win32::GUI::CommDlgExtendedError(), "GetOpenFileName Error"); } } # Save File sub Scanner_OnSaveFile { my $self = shift; # No filename, call SaveAs. return Scanner_OnSaveAsFile($self) unless (-f $CurrentFile ); # Retrieve Text my $text = $self->Edit->Text; $text =~ s/\r\n/\n/mg; # Need to remove \r # Save file open F, ">$CurrentFile" or die "Open file : $CurrentFile"; print F $text; close F; # Reset Edit state $self->Edit->Modify(0); $self->Edit->SetFocus(); 0; } # Save File sub Scanner_OnSaveAsFile { my $self = shift; # Open a file my $file = Win32::GUI::GetSaveFileName( -owner => $Window, -title => "Save As", -file => $CurrentFile, -filter => [ 'Mark 3 scanner (*.mk3s)' => '*.mk3s', 'Text file (*.txt)' => '*.txt', 'All files' => '*.*', ], -defaultextension => "mk3s", -createprompt => 1, ); # Save file if (defined $file) { # Keep Current file. $CurrentFile = $file; StatusBar_Update(); $self->Text( "$CurrentFile - Scanner"); # Retrieve Text my $text = $self->Edit->Text(); $text =~ s/\r\n/\n/mg; # Save file open F, ">$file" or die "Open file : $file"; print F $text; close F; # Reset Edit state $self->Edit->Modify(0); $self->Edit->SetFocus(); } elsif (Win32::GUI::CommDlgExtendedError()) { $self->MessageBox ("ERROR : ".Win32::GUI::CommDlgExtendedError(), "GetSaveFileName Error"); } 0; } # Quit Scanner sub Scanner_OnQuitFile { my $self = shift; # Need to save current file ? if (Scanner_CheckSaveCurrentFile($self) == IDCANCEL) { return 0; } else { exit 0; } } # # Edit menu # sub Scanner_OnChooseFont { my $self = shift; # Choose font my @font = Win32::GUI::ChooseFont( -owner => $Window, $EditFont->Info() ); if ($#font) { $EditFont = new Win32::GUI::Font (@font); $self->Edit->Change( -font => $EditFont); $self->Edit->Update(); $self->Help->Change( -font => $EditFont); $self->Help->Update(); $self->Term->Change( -font => $EditFont); $self->Term->Update(); $Window->InvalidateRect(1); } 0; } sub Scanner_OnChooseLabelFont { my $self = shift; # Choose font my @font = Win32::GUI::ChooseFont( -owner => $Window, $LabelFont->Info() ); if ($#font) { $LabelFont = new Win32::GUI::Font (@font); Labels_Update(); } 0; } # # Find menu # sub Scanner_OnFind { # Set focus and Enable Find button as appropriate $FindDlg->FindDlg_Text->SetFocus; $FindDlg->FindDlg_Find->Enable(length($FindDlg->FindDlg_Text->Text) != 0); $FindDlg->Show(); 0; } # # Help menu # # About box sub Scanner_OnAbout { my $self = shift; $self->MessageBox( "Perl Scanner, version $Version\r\n" . "Copyright BSD, 2010-2011", "About...", MB_ICONINFORMATION | MB_OK, ); 0; } # ####################################################################### sub StatusBar_Update { my $extra = ''; undef @MouseOver; if ($Running[0] == RUNNINGGUI && defined $MouseIndex) { my $ba = $busaddrs[$MouseIndex]; @MouseOver = "Cell $$ba{BA}"; my $min = min($setl, $vmin) - $graphmargin; my $max = max($seth, $vmax) + $graphmargin; my $range = $max - $min; $extra .= " Cell $$ba{BA} "; push @MouseOver, sprintf("mouse:%.3fV", $max - ($MouseY - $vtop) / ($vheight) * $range); push @MouseOver, "mi.$$ba{VMIN} v.$$ba{V} ma.$$ba{VMAX}"; push @MouseOver, "t.$$ba{TEMP}" if $$ba{TEMP}; push @MouseOver, "x.$$ba{XTRN}" if $$ba{XTRN}; $Window->{"Label_MouseOver"}->Text(join(" | ", @MouseOver)); $Window->{"Label_MouseOver"}->Width($Window->Stop->Left() - $vleft); $Window->{"Label_MouseOver"}->Left($vleft); $Window->{"Label_MouseOver"}->Top($Window->Edit->Height()); $extra = join(' | ', @MouseOver); } my $evil = $EvilPort; $evil =~ s/^[\\\.]+//; $StatusBar->Text( qq("$CurrentFile" $evil @_) ); } sub UpdateLogging { local $_; # Set up the log file (if requested) if ($Menu->{ControlLogging}->Checked) { my $newfile = Win32::GUI::GetSaveFileName( -owner => $Window, -title => "Save Log", -file => $LogFile, -filter => [ 'Text file (*.txt)' => '*.txt', 'All files' => '*.*', ], -defaultextension => "txt", -createprompt => 1, ); if (!defined $newfile) { $Menu->{ControlLogging}->Checked(0); return; } $LogFile = $newfile; if (!open(LOG, '>', $_ = $LogFile)) { $Window->MessageBox ( qq($! "$_"), "Scanner", MB_ICONSTOP | MB_OK); $Menu->{ControlLogging}->Checked(0); } } else { close LOG; } } sub UpdateCSV { # Set up the log file (if requested) %uniquelog = (); if ($Menu->{ControlCSV}->Checked) { my $newfile = Win32::GUI::GetSaveFileName( -owner => $Window, -title => "Save CSV", -file => $CSVFile, -filter => [ 'Text file (*.csv)' => '*.csv', 'All files' => '*.*', ], -defaultextension => "csv", -createprompt => 1, ); if (!defined $newfile) { $Menu->{ControlLogging}->Checked(0); return; } $CSVFile = $newfile; if (!open(CSV, '>', $_ = $CSVFile)) { $Window->MessageBox ( qq($! "$_"), "Scanner", MB_ICONSTOP | MB_OK); $Menu->{ControlCSV}->Checked(0); } } else { close CSV; } } sub ControlLogging_Click { $Menu->{ControlLogging}->Checked(!$Menu->{ControlLogging}->Checked); UpdateLogging(); $Window->InvalidateRect(1); } sub ControlCSV_Click { $Menu->{ControlCSV}->Checked(!$Menu->{ControlCSV}->Checked); UpdateCSV(); $Window->InvalidateRect(1); } sub ControlSimulate_Click { $Menu->{ControlSimulate}->Checked(!$Menu->{ControlSimulate}->Checked); $Window->InvalidateRect(1); } sub ControlRunAtStartGUI_Click { $Menu->{ControlRunAtStartGUI}->Checked(!$Menu->{ControlRunAtStartGUI}->Checked); if ($Menu->{ControlRunAtStartGUI}->Checked) { $Menu->{ControlRunAtStartIndia}->Checked(0); $Registry->{"$RegBase/RunAtStart"} = RUNGUI; } else { $Registry->{"$RegBase/RunAtStart"} = STOP; } $Window->InvalidateRect(1); } sub ControlRunAtStartIndia_Click { $Menu->{ControlRunAtStartIndia}->Checked(!$Menu->{ControlRunAtStartIndia}->Checked); if ($Menu->{ControlRunAtStartIndia}->Checked) { $Menu->{ControlRunAtStartGUI}->Checked(0); $Registry->{"$RegBase/RunAtStart"} = RUNINDIA; } else { $Registry->{"$RegBase/RunAtStart"} = STOP; } $Window->InvalidateRect(1); } sub min { my $retval; $retval = shift while (@_ && !defined($retval)); for (@_) { $retval = $_ if defined($_) && $_ < $retval; } $retval; } sub max { my $retval; $retval = shift while (@_ && !defined($retval)); for (@_) { $retval = $_ if defined($_) && $_ > $retval; } $retval; } sub Scanner_Stop { my $self = shift; $Running[0] = STOPPING; } sub ScanWindowText { my $requireaddrs = $_[0]; my $crterminate = @_ > 1 ? $_[1] : 1; my $translatecr = @_ > 2 ? $_[2] : 1; my $terminalmode = @_ > 3 ? $_[3] : 0; $MaxLineWidth = 2; $MinLineWidth = 2; $SetlLineWidth = 2; $SethLineWidth = 2; $TempLineWidth = 8; $XtrnLineWidth = 8; $MaxLinePColor = $Pblue; $MinLinePColor = $Pblue; $SetlLinePColor = $Pred; $SethLinePColor = $Pred; $TempLinePColor = $Porange; $XtrnLinePColor = $Pcyan; $MaxLineBColor = $Bred; $MinLineBColor = $Bred; $SetlLineBColor = $Bblue; $SethLineBColor = $Bblue; $TempLineBColor = $Borange; $XtrnLineBColor = $Bcyan; @busaddrs = (); %emh = (); my %busaddrs; # Retrieve Text my @text = split(/\r\n/m, $Window->Edit->Text()); for (my $line = 1; $line <= @text; ++$line) { $_ = $text[$line - 1]; $_ =~ s/\s*;.*//; next unless /\S/; if (/((Max|Min|Setl|Seth|Temp|Xtrn)LineColor)\s+(\d+)[,\s]+(\d+)[,\s]+(\d+)\s*$/io) { my $colors = [$3,$4,$5]; my $var = "\L$2"; eval "\$\u${var}LinePColor = new Win32::GUI::Pen ($colors)"; eval "\$\u${var}LineBColor = new Win32::GUI::Brush($colors)"; } elsif (/((Max|Min|Setl|Seth|Temp|Xtrn)LineWidth)\s+(\d+)\s*$/io) { my $var = "\L$2"; eval "\$\u${var}LineWidth = $3"; } elsif (/LineWidth\s+(\d+)\s*$/io) { $MaxLineWidth = $MinLineWidth = $SetlLineWidth = $SethLineWidth = $TempLineWidth = $XtrnLineWidth = $1; } elsif (my ($begin, $end) = /^(\d+)(?:-(\d+))?$/) { $end = $begin unless defined $end; if ($begin > 255 || $end > 255) { $Window->MessageBox ( qq(Illegal bus address "$_" in line $line), "Scanner", MB_ICONSTOP | MB_OK); return (); } for (my $busaddr = $begin; ; $busaddr += ($begin < $end) ? +1 : -1) { if ($busaddrs{$busaddr}) { $Window->MessageBox ( qq(Duplicate bus address "$busaddr" in lines $busaddrs{$busaddr} and $line), "Scanner", MB_ICONSTOP | MB_OK); return (); } $busaddrs{$busaddr} = $line; push @busaddrs, {BA => $busaddr, FIRST => $busaddr == $begin}; last if $busaddr == $end; } } elsif (my ($simulate) = /^Simulate\s+(.*?)\s*$/i) { if ($simulate !~ /^(On|Off)$/i) { $Window->MessageBox ( qq(Expected On or Off "$simulate" "$_" in line $line), "Scanner", MB_ICONSTOP | MB_OK); return (); } $Menu->{ControlSimulate}->Checked($simulate =~ /On/i); } elsif (my ($graphtype) = /^Graph\s+(\d+)\s*/i) { # This is ignored for historical compatibility } elsif (/^(?:EMH|SOC)\s+(\d+)\s*(.*)$/i) { my $busaddr = $1; my $legend = $2; $emh{BA} = $1; $emh{GMIN} = 0; $emh{GMAX} = 100; if ($legend) { my $legends = join('', map { ${$EMHTypes[$_]}{Legend} } (0..$#EMHTypes)); @EMHLabels = (); for ($legend =~ /\s/ ? split(/\s+/, $legend) : split(//, $legend)) { for (my $i = 0; ; ++$i) { if ($i > $#EMHTypes) { $Window->MessageBox ( qq(Expected one of "$legends" instead of "$_" in line $line), "Scanner", MB_ICONSTOP | MB_OK); return (); } if ("\U$_" eq "\U${$EMHTypes[$i]}{Legend}") { push @EMHLabels, $EMHTypes[$i]; last; } } } } else { @EMHLabels = @EMHTypes; } for (0..$#EMHLabels) { if (!exists($Window->{"Label_EMH_$_"})) { my $letter = ${$EMHLabels[$_]}{Legend}; $Window->AddLabel( -name => "Label_EMH_$_", -text => "$letter", -pos => [0, 0], -multiline => 0, -hscroll => 0, -vscroll => 0, -autohscroll => 0, -autovscroll => 0, -font => $LabelFont, -visible => 0, ); } } } elsif (/^COM\d+$/i) { NormalizeCOM($EvilPort = $_); StatusBar_Update(); } elsif (!$terminalmode) { $Window->MessageBox ( qq(Unknown command "$_" in line $line), "Scanner", MB_ICONSTOP | MB_OK); return (); } } if ($requireaddrs) { if (@busaddrs == 0) { $Window->MessageBox ( qq(Missing bus address range), "Scanner", MB_ICONSTOP | MB_OK); return (); } } if (!$Menu->{ControlSimulate}->Checked) { $EvilPortObj = PortOpen(PORT => $EvilPort, CRTERMINATE => $crterminate, TRANSLATECR => $translatecr, TIMEOUT => 200, OPENFAIL => {SUB => \&Evil_OpenCloseFail, PARMS => [$Window, Translate(":")]}); return () unless $EvilPortObj; } return $requireaddrs ? @busaddrs : 1; } sub SaveMin { my $newmin = min(@_); return if $vmin && $newmin >= $vmin; $vmin = $newmin; Visible_Update(); Labels_Y_Update(); } sub SaveMax { my $newmax = max(@_); return if $vmax && $newmax <= $vmax; $vmax = $newmax; Visible_Update(); Labels_Y_Update(); } sub Scanner_RunGUI { my $self = shift; my @busaddrs = ScanWindowText(1); return unless @busaddrs > 0; $vmin = $vmax = undef; $tempoff = 0; $tempwarm = 9999; for my $ba (@busaddrs) { $$ba{PRELOADED} = 0; if (!exists $Window->{"Label_$$ba{BA}"}) { $Window->AddLabel( -name => "Label_$$ba{BA}", -text => @busaddrs > 30 ? $$ba{BA} % 10 : "$$ba{BA}", -pos => [0, 0], -multiline => 0, -hscroll => 0, -vscroll => 0, -autohscroll => 0, -autovscroll => 0, -font => $LabelFont, -visible => 0, ); } } $Running[0] = RUNNINGGUI; Labels_Update(); $Window->Edit->Hide(); $Window->RunGUI->Enable(0); $Window->RunIndia->Enable(0); $Window->Stop->Enable(1); $Menu->{ControlRunGUI}->Enabled(0); $Menu->{ControlRunIndia}->Enabled(0); $Menu->{ControlStop}->Enabled(1); $Menu->{Help}->Enabled(0); $Menu->{Term}->Enabled(0); StatusBar_Update(); } sub Scanner_RunIndia { my $self = shift; $Window->Term->Text(''); my @output; my $foundcom = 0; for my $line (split(/\r\n/m, $Window->Edit->Text())) { last if $foundcom; next if $line =~ /^\s*;/; my $command; while (length($line)) { if ($line =~ s/^(.*?);//) { $command = $1; } else { $command = $line; $line = ''; } if (!$foundcom && $command =~ /COM\d+/io) { NormalizeCOM($EvilPort = $command); $foundcom = 1; last; } } push @output, []; } $Window->RunIndia->{output} = \@output; if (!$Menu->{ControlSimulate}->Checked) { $EvilPortObj = PortOpen(PORT => $EvilPort, CRTERMINATE => 1, TRANSLATECR => 1, TIMEOUT => 200, OPENFAIL => {SUB => \&Evil_OpenCloseFail, PARMS => [$Window, Translate(":")]}); return undef unless $EvilPortObj; } $Running[0] = RUNNINGINDIA; $Window->Edit->Hide(); $Window->Term->Show(); $Window->Term->SetFocus(); $Window->RunGUI->Enable(0); $Window->RunIndia->Enable(0); $Window->Stop->Enable(1); $Menu->{ControlRunGUI}->Enabled(0); $Menu->{ControlRunIndia}->Enabled(0); $Menu->{ControlStop}->Enabled(1); $Menu->{Help}->Enabled(0); $Menu->{Term}->Enabled(0); StatusBar_Update(); } sub Scanner_OnChargerTest { my $self = shift; my @busaddrs; if ($Window->Term->{running}) { $Window->Term->{processing} = 2; return; } else { @busaddrs = ScanWindowText(1); return unless @busaddrs > 0; $Window->Term->{processing} = 0; $Window->Term->{running} = 1; $Window->Edit->Hide(); $Window->Help->Hide(); $Window->RunGUI->Hide(); $Window->RunIndia->Hide(); $Window->Stop->Hide(); $Window->DoneHelp->Show(); $Window->Term->Text(''); $Window->Term->Show(); $Window->Term->SetFocus(); } $Window->Term->{processing} = 1; for my $ba (@busaddrs) { next unless $$ba{FIRST}; $_ = $$ba{BA}; Evil_StatusBar("\n", "%s%s", Address($_), 'd.'); $Window->Term->Append(sprintf("%s%s", Address($_), 'g.')); Evil_StatusBar("\n", "%s%s", Address($_), 'g.'); my ($seth) = $PortInput =~ /(\d+\.\d+)/; $Window->Term->Append(" $PortInput\r\n"); $Window->Term->Append(sprintf("%s%s", Address($_), 'v.')); Evil_StatusBar("\n", "%s%s", Address($_), 'v.'); my ($v) = $PortInput =~ /(\d+\.\d+)/; $Window->Term->Append(" $PortInput\r\n"); my $trigger = sprintf("%.4f", $v - 0.100); $trigger =~ s/\D//g; $trigger =~ s/(....).*/$1/; $Window->Term->Append(sprintf("%s%s%s", Address($_), 'seth.', $trigger)); Evil_StatusBar("\n", "%s%s%s", Address($_), 'seth.', $trigger); $Window->Term->Append(" $PortInput\r\n"); for (0..50) { Win32::Sleep(100); Win32::GUI::DoEvents(); last unless $Window->Term->{processing} == 1; } $Window->Term->Append(sprintf("%s%s%s", Address($_), 'seth.', $seth)); Evil_StatusBar("\n", "%s%s%s", Address($_), 'seth.', $seth); $Window->Term->Append(" $PortInput\r\n"); Evil_StatusBar("\n", "%s%s", Address($_), 'e.'); for (0..10) { Win32::Sleep(100); Win32::GUI::DoEvents(); last unless $Window->Term->{processing} == 1; } last if $Window->Term->{processing} == 2; } $Window->Term->Append("Done\r\n"); Scanner_DoneHelp($self); $Window->Term->{processing} = 0; Scanner_OnTerm($self); } sub Labels_Update { return unless $Running[0] == RUNNINGGUI; my ($width, $height) = ($Window->Edit->Width(), $Window->Edit->Height()); my ($left, $top); Visible_Update(); for my $index (0..$#busaddrs) { my $ba = $busaddrs[$index]; $delta = int($vwidth / @busaddrs); $left = $vleft + $index * $delta; $top = $height - 20; $left += ($delta - 30) >> 1 if $delta > 30; $Window->{"Label_$$ba{BA}"}->Hide(); $Window->{"Label_$$ba{BA}"}->Left($left); $Window->{"Label_$$ba{BA}"}->Width($delta - 2); $Window->{"Label_$$ba{BA}"}->Top($top); $Window->{"Label_$$ba{BA}"}->Change( -font => $LabelFont ); $Window->{"Label_$$ba{BA}"}->Show(); } Labels_Y_Update(); Labels_EMH_Update(); } sub Labels_Y_Update { return unless $Running[0] == RUNNINGGUI; my $min = min($setl, $vmin) - $graphmargin; my $max = max($seth, $vmax) + $graphmargin; my $range = $max - $min; for (1..$YScales) { $Window->{"Label_Y_$_"}->Hide(); $Window->{"Label_Y_$_"}->Left(0); $Window->{"Label_Y_$_"}->Text(sprintf("%.3fV", $max - ($_ - 1) * $range / ($YScales - 1))); $Window->{"Label_Y_$_"}->Top($vtop + ($_ - 1) * $vheight / ($YScales - 1)); $Window->{"Label_Y_$_"}->Change( -font => $LabelFont ); $Window->{"Label_Y_$_"}->Show(); } } sub Labels_EMH_Update { return unless %emh && $Running[0] == RUNNINGGUI; my $delta = int($vtop / @EMHLabels); for (0..$#EMHLabels) { my %row = %{$EMHLabels[$_]}; my $value = ''; if (exists($emh{$row{Legend}})) { $value = sprintf($row{Format}, $emh{$row{Legend}}); } $Window->{"Label_EMH_$_"}->Left(0); $Window->{"Label_EMH_$_"}->Width($vleft); $Window->{"Label_EMH_$_"}->Top($_ * $delta); $Window->{"Label_EMH_$_"}->Text("$row{Legend}$value"); $Window->{"Label_EMH_$_"}->Change( -font => $LabelFont ); $Window->{"Label_EMH_$_"}->Show(); } } sub Scanner_OnTerm { my $self = shift; if ($Window->Term->{running}) { Scanner_DoneHelp($self); } else { return unless ScanWindowText(0, 0, 0, 1); $Window->Term->{running} = 1; $Window->Edit->Hide(); $Window->Help->Hide(); $Window->RunGUI->Hide(); $Window->RunIndia->Hide(); $Window->Stop->Hide(); $Window->DoneHelp->Show(); $Window->Term->Show(); $Window->Term->SetFocus(); } } sub Scanner_OnHelp { my $self = shift; $Window->Edit->Hide(); $Window->RunGUI->Hide(); $Window->RunIndia->Hide(); $Window->Stop->Hide(); $Window->DoneHelp->Show(); $Window->Help->Show(); } sub Scanner_DoneHelp { my $self = shift; if ($Window->Term->{running}) { if (!$Menu->{ControlSimulate}->Checked) { PortClose($EvilPortObj, OPENFAIL => {SUB => \&Evil_OpenCloseFail, PARMS => [$Window, Translate(":")]}); } $Window->Term->{running} = 0; $Window->Term->{processing} = 0; } $Window->DoneHelp->Hide(); $Window->Help->Hide(); $Window->Term->Hide(); $Window->Edit->Show(); $Window->RunGUI->Show(); $Window->RunIndia->Show(); $Window->Stop->Show(); } sub NormalizeCOM { if ($_[0] =~ m(\\|(?:^N/A$))io) { $_[0] = uc($_[0]); } else { $_[0] =~ /(\d+)/; $_[0] = "\\\\.\\COM$1"; } } sub ApplyDefault { $_[0] = $_[1] if !defined($_[0]) || $_[0] eq '-'; } sub Death { warn(@_) if @_; # Release resources PortClose($EvilPortObj, Translate("")) if $EvilPortObj; # Pause before exiting in case this was started from a shortcut and would otherwise vanish. @ARGV = (); print(Translate("\n")); $_ = <>; exit @_; } sub logprint { return unless $Menu->{ControlLogging}->Checked; print LOG @_; } sub logprintf { return unless $Menu->{ControlLogging}->Checked; my $fmt = shift(@_); printf LOG $fmt, @_; } sub Evil_StatusBar { my $eos = shift; my $fmt = shift; StatusBar_Update(sprintf($fmt, @_)); Scanner_WriteEvil($eos, $fmt, @_); Win32::GUI::DoEvents(); } sub Evil_OpenCloseFail { my $error = shift; my $self = shift; $self->MessageBox ( qq(@_ $error), $EvilPort, MB_ICONSTOP | MB_OK); } sub Scanner_WriteEvil { if (!$Menu->{ControlSimulate}->Checked) { WriteEvil(@_); return; } my @scales = (0.00, 0.05, 0.15, 0.25, 0.50, 0.75, 0.85, 0.95, 1.00); push @scales, reverse(@scales); my $eos = shift; my $format = shift; my $evilcommand = sprintf($format, @_); Win32::Sleep(length($evilcommand)); if ($evilcommand =~ /(\d+)v\./) { my $busaddr = $1; my $scale = @scales[(time + $busaddr) % @scales]; my $delta = 0.500; for (my $index = 0; $index < @busaddrs; ++$index) { my $ba = $busaddrs[$index]; next unless $busaddr == $$ba{BA}; my $indexscale = (1 + $index) / @busaddrs; $delta = $indexscale - 0.5; } my $low = $setl - $delta || 1.5; my $high = $seth + $delta || 4.5; my $volt = $low + ($high - $low) * $scale; $PortInput = sprintf(" %sV %5.3f", $busaddr, $volt); } elsif ($evilcommand =~ /(\d+)mi\./) { my $busaddr = $1; my $volt = 2.5; for (my $index = 0; $index < @busaddrs; ++$index) { my $ba = $busaddrs[$index]; next unless $busaddr == $$ba{BA}; $volt = $$ba{VMIN} || $volt; last; } $PortInput = sprintf(" %sMI %5.3f", $busaddr, $volt); } elsif ($evilcommand =~ /(\d+)ma\./) { my $busaddr = $1; my $volt = 3.5; for (my $index = 0; $index < @busaddrs; ++$index) { my $ba = $busaddrs[$index]; next unless $busaddr == $$ba{BA}; $volt = $$ba{VMAX} || $volt; last; } $PortInput = sprintf(" %sMA %5.3f", $busaddr, $volt); } elsif ($evilcommand =~ /(\d+)h\./) { $PortInput = ''; } elsif ($evilcommand =~ /(\d+)r\./) { $PortInput = " $1R 2.000V"; } elsif ($evilcommand =~ /(\d+)g\./ && (!%emh || $1 != $emh{BA})) { $PortInput = " $1G 4.000V"; } elsif ($evilcommand =~ /(\d+)tempoff\./) { $PortInput = " $1TO 180F"; } elsif ($evilcommand =~ /(\d+)tempwarm\./) { $PortInput = " $1TW 150F"; } elsif ($evilcommand =~ /(\d+)t\./ && %emh && $1 == $emh{BA}) { my $busaddr = $1; my $seconds = time - $TimeBase; my $minutes = int($seconds / 60); $seconds %= 60; $minutes = min(9999, $minutes); $PortInput = sprintf(" %sT %04u:%02u", $busaddr, $minutes, $seconds); } elsif ($evilcommand =~ /(\d+)t\./ && (!%emh || $1 != $emh{BA})) { my $busaddr = $1; my $cold = !(($busaddr + 0) % 7); my $hot = !(($busaddr + 1) % 7); my $scale = @scales[(time + $busaddr) % @scales]; my $low = 60; my $high = $tempoff + 10; my $temp = $low + ($high - $low) * $scale; $PortInput = " ${busaddr}T " . ($cold ? "COLD" : $hot ? "HOT" : sprintf("%3.0fF", $temp)); } elsif ($evilcommand =~ /(\d+)x\./ && (!%emh || $1 != $emh{BA})) { my $busaddr = $1; my $cold = !(($busaddr + 2) % 7); my $hot = !(($busaddr + 3) % 7); my $scale = @scales[(time + $busaddr + 1) % @scales]; my $low = 60; my $high = $xtrnoff + 10; my $temp = $low + ($high - $low) * $scale; $PortInput = " ${busaddr}X " . ($cold ? "COLD" : $hot ? "HOT" : sprintf("%3.0fF", $temp)); } elsif ($evilcommand =~ /(\d+)s\./) { $PortInput = " $1S 00"; } elsif ($evilcommand =~ /(\d+)c\./) { my $busaddr = $1; my $scale = 2 * (@scales[(time + 0) % @scales] - 0.50); my $reply = $scale * 9999.9; $PortInput = sprintf(" %sC +%06.1fA", $busaddr, $reply); } elsif ($evilcommand =~ /(\d+)wh?\./) { my $busaddr = $1; my $scale = 2 * (@scales[(time + 1) % @scales] - 0.50); my $reply = $scale * 99999.9; $PortInput = sprintf(" %sC +%07.1fWH", $busaddr, $reply); } elsif ($evilcommand =~ /(\d+)ah?\./) { my $busaddr = $1; my $scale = 2 * (@scales[(time + 2) % @scales] - 0.50); my $reply = $scale * 999.99; $PortInput = sprintf(" %sC +%06.2fAH", $busaddr, $reply); } elsif ($evilcommand =~ /(\d+)k\./) { my $busaddr = $1; my $scale = 2 * (@scales[(time + 3) % @scales] - 0.50); my $reply = $scale * 999.9; $PortInput = sprintf(" %sC +%05.1fKW", $busaddr, $reply); } elsif ($evilcommand =~ /(\d+)ga?\./ && %emh && $1 == $emh{BA}) { my $busaddr = $1; my $scale = @scales[(time + 4) % @scales]; my $reply = $scale * 100.0; $PortInput = $reply == 100 ? " ${busaddr}G 100%" : sprintf(" %sG %04.1f%%", $busaddr, $reply); } else { Death(Translate(qq(: "$evilcommand"\n))); } Win32::Sleep(length($PortInput)); } # MouseMove event sub Window_MouseMove { return unless $Running[0] == RUNNINGGUI; my ($x, $y) = @_; for my $index (0..$#busaddrs) { my $ba = $busaddrs[$index]; if (exists $$ba{LEFT} && exists $$ba{RIGHT} && exists $$ba{TOP} && exists $$ba{BOTTOM}) { if ($$ba{LEFT} < $x && $x < $$ba{RIGHT} && $$ba{TOP} < $y && $y < $$ba{BOTTOM}) { $MouseY = $y; $MouseX = $x; $MouseIndex = $index; last; } } } StatusBar_Update(); } sub Window_MouseDown { return unless $Running[0] == RUNNINGGUI; } sub Window_MouseUp { return unless $Running[0] == RUNNINGGUI; $ShowMouseOver = !$ShowMouseOver; $Window->{"Label_MouseOver"}->Hide() if !$ShowMouseOver; } sub Control_COMDetect { my @original = split(/\r\n/m, $Window->Edit->Text()); my @new; for (@original) { push @new, $_ unless /^COM/i; } $Window->Edit->Text(join("\r\n", @new) . "\r\n"); for (my $com = 4; $com <= 255; ++$com) { local($EvilPortObj); my $portname; NormalizeCOM($portname = "COM$com"); StatusBar_Update("Testing $portname"); Win32::GUI::DoEvents(); $EvilPortObj = PortOpen(PORT => $portname, CRTERMINATE => 1, TIMEOUT => 50, OPENFAIL => {SUB => sub { }}); if ($EvilPortObj) { $EvilPort = "COM$com"; unshift @new, $EvilPort; $Window->Edit->Text(join("\r\n", @new) . "\r\n"); NormalizeCOM($EvilPort); PortClose($EvilPortObj, Translate("")); } } StatusBar_Update(); } sub Control_COMCellDetect { my @original = split(/\r\n/m, $Window->Edit->Text()); my @new; for (@original) { push @new, $_ unless /^(COM|\d)/i; } $Window->Edit->Text(join("\r\n", @new)); $Window->Edit->Append("\r\n"); for (my $com = 4; $com <= 255; ++$com) { local($EvilPortObj); my $portname; NormalizeCOM($portname = "COM$com"); StatusBar_Update("Testing $portname"); Win32::GUI::DoEvents(); $EvilPortObj = PortOpen(PORT => $portname, CRTERMINATE => 1, TIMEOUT => 50, OPENFAIL => {SUB => sub { }}); if ($EvilPortObj) { $Window->Edit->Append("COM$com\r\n"); StatusBar_Update("Testing $portname"); my $base = undef; my $last = undef; for my $busaddr (1..255) { Win32::GUI::DoEvents(); StatusBar_Update("Testing $portname ${busaddr}v."); Scanner_WriteEvil("\n", "%s%s", Address($busaddr), 'v.'); if ($PortInput =~ /\d\.\d/) { if (!defined($last)) { $Window->Edit->Append($busaddr); $base = $busaddr; } elsif ($busaddr != $last + 1) { $Window->Edit->Append("-$last\r\n"); undef $base; } $last = $busaddr; } else { if ($last) { $Window->Edit->Append("-$last") if $last && $busaddr != $base + 1; $Window->Edit->Append("\r\n"); } undef $base; undef $last; } } $Window->Edit->Append("-$last\r\n") if $last; PortClose($EvilPortObj, Translate("")); } } StatusBar_Update(); } sub TimeStamp { my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); return sprintf("%04u-%02u-%02u %02u:%02u:%02u", 1900 + $year, $mon + 1, $mday, $hour, $min, $sec); } sub uniquelogprint { return unless $Menu->{ControlCSV}->Checked; my ($busaddr, $command, $value) = @_; return if exists $uniquelog{"$busaddr$command"} && $uniquelog{"$busaddr$command"} eq $value; $uniquelog{"$busaddr$command"} = $value; print CSV (join(',', TimeStamp(), $value, Address($busaddr), $command), "\n"); } sub Address { if ($_[0] < 99) { return sprintf("%02u", $_[0]); } else { return sprintf("%03u", $_[0]); } } sub Term_Change { return if $Window->Term->{processing}; my @selpos = $Window->Term->GetSel(); my $lineindex = $Window->Term->LineFromChar($selpos[0]); if ($lineindex > 0 && $Window->Term->GetLine($lineindex) eq '' && (my $line = $Window->Term->GetLine($lineindex - 1)) ne '') { ++$Window->Term->{processing}; if ($Menu->{ControlSimulate}->Checked) { if ($line =~ /^(\d+)v[oltage\.]*$/) { $Window->Term->Append(sprintf("%s %5.3fV\r\n", $1, 3.000 + $1 / 1000)); } elsif ($line =~ /^(\d+)m(?:[inimum\.]+)$/) { $Window->Term->Append(sprintf("%s %5.3fMI\r\n", $1, 2.000 + $1 / 1000)); } elsif ($line =~ /^(\d+)m(?:[aximum\.]+)$/) { $Window->Term->Append(sprintf("%s %5.3fMA\r\n", $1, 4.000 + $1 / 1000)); } elsif ($line =~ /^(\d+|\*)h(?:[stclearmima\.]+)$/) { } elsif ($line =~ /^(\d+)t(?:[emperat\.]+)$/) { $Window->Term->Append(sprintf("%s %3.0fFT\r\n", $1, 70.0 + $1 / 50)); } elsif ($line =~ /^(\d+)x(?:[trntemp\.]+)$/) { $Window->Term->Append(sprintf("%s %3.0fFX\r\n", $1, 60.0 + $1 / 70)); } else { $Window->Term->Append(qq(Unsimulated command "$line"\r\n)); } } else { Scanner_WriteEvil("\n", $line); $PortInput = '(No reply)' if length($PortInput) == 0; $Window->Term->Append("$_\r\n") for split(/[\r\n]+/,$PortInput); } --$Window->Term->{processing}; } $Window->Term->Modify(0); }