From b96aec7a759a678c29db1269b09f412902c6793b Mon Sep 17 00:00:00 2001 From: Michael Wiebusch Date: Wed, 21 Jan 2015 16:57:00 +0100 Subject: [PATCH] communication between processes via shm --- user_interface/coral_scanner.js | 73 ++++++++--- user_interface/coral_scanner.pm | 210 ++++++++++++++++++++++++++++---- user_interface/misc_subs.pm | 33 ++++- user_interface/pmt_ro.pm | 28 ++--- user_interface/shm_manager.pm | 137 +++++++++++++++++++++ user_interface/table_control.pm | 67 +++++----- 6 files changed, 454 insertions(+), 94 deletions(-) create mode 100644 user_interface/shm_manager.pm diff --git a/user_interface/coral_scanner.js b/user_interface/coral_scanner.js index 8602eb4..14da152 100644 --- a/user_interface/coral_scanner.js +++ b/user_interface/coral_scanner.js @@ -1,7 +1,7 @@ var timer; -var clientId = Math.random(); +// var clientId = Math.random(); @@ -21,36 +21,32 @@ $(document).ready(function(){ timer = $.timer(function() { + get_scan_status() }); unfolds($("#show_main_controls"),$("#main_controls_container")); unfolds($("#show_pmt_ro_settings"),$("#pmt_ro_settings_container")); unfolds($("#show_table_control_settings"),$("#table_control_settings_container")); + $("#button_start_scan").click(function(){ + start_scan(); + }); + $("#button_stop_scan").click(function(){ + stop_scan(); + }); + + + set_clear_timer(); + }); -function plot(){ - $.ajax({ - url: "index.pl", - cache: false, - async: true, - dataType: "text", - data: { - action : "plot", - tty : shared.tty, - clientId : clientId - }, - success: function(result) { - $("#plotContainer").html(result); - } - }); -} + function set_clear_timer(){ - timer.set({time:1000,autostart: true}); + timer.set({time:2000,autostart: true}); } function stop_timer(){ @@ -58,6 +54,47 @@ function stop_timer(){ } +function get_scan_status(){ + $.ajax({ + url: "coral_scanner.pl", + cache: false, + async: true, + dataType: "text", + data: { + sub : "scan_status", + report : "true" + }, + success: function(answer) { + $("#scan_status_container").html("
"+answer+"
"); + } + }); +} +function start_scan(){ + $.ajax({ + url: "coral_scanner.pl", + cache: false, + async: true, + dataType: "text", + data: { + sub : "start_scan" + }, + success: function(answer) { + } + }); +} +function stop_scan(){ + $.ajax({ + url: "coral_scanner.pl", + cache: false, + async: true, + dataType: "text", + data: { + sub : "stop_scan" + }, + success: function(answer) { + } + }); +} \ No newline at end of file diff --git a/user_interface/coral_scanner.pm b/user_interface/coral_scanner.pm index 55e48ba..4ea8d6a 100644 --- a/user_interface/coral_scanner.pm +++ b/user_interface/coral_scanner.pm @@ -12,6 +12,7 @@ use Data::Dumper; use SVG; use CGI ':standard'; +use JSON; # use settings_subs; use has_settings; @@ -23,6 +24,8 @@ use table_control; use misc_subs; +use shm_manager; + ## methods sub new { @@ -58,6 +61,12 @@ sub new { $self->{table_control} = table_control->new(); $self->load_settings(); + + $self->{status_shm} = shm_manager->new( shmName => __PACKAGE__.".status" ); + $self->{status_shm}->initShm(); + $self->{scan_shm} = shm_manager->new( shmName => __PACKAGE__.".scan" ); + $self->{scan_shm}->initShm(); + return $self; } @@ -92,12 +101,26 @@ sub main_html { print "

main controls

"; print "
"; + print '
'; $self->{table_control}->scan_pattern_to_svg(html_tag => 1); print '
'; + print '
'; + print '
'; + + print br; + print "estimated scan duration: ".hms_string($self->scan_ETA()); print br; - print "some content!"; + + print "
"; + print "
";
+  $self->scan_status( report => 1 );
+  print "
"; + print "
"; + + print ""; + print ""; print "
"; @@ -124,43 +147,96 @@ sub scan_sample { my $tc = $self->{table_control}; - $self->{current_scan} = {}; - $self->{current_scan}->{meta} = {points => 0}; - $self->{current_scan}->{data} = []; + my $ro = $self->{pmt_ro}; + + + # $tc->home(); # $tc->scan( eval => 'print("test\n");' ); - $tc->scan( object => $self, method => 'scan_callback' ); + my $scan_pattern = $tc->scan_pattern(); - $self->save_scan_ascii(filename => "./scan.dat"); + my $ETA = $self->scan_ETA(); + $self->{status_shm}->updateShm({ + action => 'scanning', + abort => 0, + cols => $scan_pattern->{cols}, + rows => $scan_pattern->{rows}, + current_col => 0, + current_row => 0, + ETA => $ETA, + seconds_left => $ETA + }); - -} - -sub scan_callback { - my $self = shift; - my $point = shift; + $self->{current_scan} = {}; + $self->{current_scan}->{meta} = { + number_points => $scan_pattern->{number_points}, + cols => $scan_pattern->{cols}, + rows => $scan_pattern->{rows}, + time_per_pixel => $self->{settings}->{time_per_pixel}, + signal_thresh => $self->{pmt_ro}->{settings}->{signal_thresh}, + step_size => $self->{table_control}->{settings}->{sample_step_size} + + }; + $self->{current_scan}->{data} = []; - printf("Acquire PMT counts at point x,y = %3.3f,%3.3f i,j = %d,%d\n" ,$point->{x_rel},$point->{y_rel}, $point->{row},$point->{col}); - my $ro = $self->{pmt_ro}; + for my $point (@{$scan_pattern->{points}}) { + $tc->go_xy( x => $point->{x}, y => $point->{y}); + + printf("Acquire PMT counts at point x,y = %3.3f,%3.3f i,j = %d,%d\n" ,$point->{x_rel},$point->{y_rel}, $point->{row},$point->{col}); + + my $delay = $self->{settings}->{time_per_pixel}; + my $counts = $ro->count(delay => $delay, channel => "signal"); + my $col = $point->{col}; + my $row = $point->{row}; + + $self->{current_scan}->{data}->[$row]->[$col] = $counts; + print "counts: $counts\n"; + print "\n\n"; + + my $status = $self->{status_shm}->lockAndReadShm(); + + if ($status->{abort}) { + $status = { + %$status, + action => "aborted", + abort => 0, + current_col => $col, + current_row => $row, + seconds_left => 0 + }; + $self->{status_shm}->writeShm($status); + $self->{scan_shm}->writeShm($self->{current_scan}); + print "scan was aborted!\n"; +# last; # stop the acquisition loop! + exit; + } else { + my $seconds_left = floor($status->{ETA} * (1 - $row/$status->{rows})); + $status = { + %$status, + current_col => $col, + current_row => $row, + seconds_left => $seconds_left + }; + $self->{status_shm}->writeShm($status); + } + } - $self->{current_scan}->{meta}->{points}++; - my $delay = $self->{settings}->{time_per_pixel}; - my $counts = $ro->count(delay => $delay, channel => "signal"); - my $col = $point->{col}; - my $row = $point->{row}; + $self->{scan_shm}->writeShm($self->{current_scan}); + $self->{status_shm}->updateShm({ + action => 'idle', + seconds_left => 0 + }); + + $self->save_scan_ascii(filename => "./scan.dat"); - $self->{current_scan}->{data}->[$row]->[$col] = $counts; - print "counts: $counts\n"; -# push(@{$self->{current_scan}->{data}},{%$point,counts => $counts}); - print "\n\n"; - } + sub save_scan_ascii { my $self = shift; my %options = @_; @@ -194,7 +270,7 @@ sub scan_ETA { #estimated time to complete a scan my $pattern_length = 0; my $last_point; my $pattern = $tc->scan_pattern(); - for my $point (@$pattern){ + for my $point (@{$pattern->{points}}){ unless(defined($last_point)){ $pattern_length += max($point->{x},$point->{y}); } else { @@ -205,13 +281,95 @@ sub scan_ETA { #estimated time to complete a scan $last_point = $point; } - my $number_points = scalar(@$pattern); + my $number_points = $pattern->{number_points}; return $pattern_length/$speed + $number_points*$time_per_pixel; +} + +sub scan_status { + my $self = shift; + my %options = @_; + my $json = $options{json}; + my $report = $options{report}; + my $status = $self->{status_shm}->readShm(); + + if($json){ + print encode_json $status; + return " "; + } + if($report){ + print "Machine is : ".$status->{action}."\n"; + print "row ".$status->{current_row}."/".($status->{rows}-1)."\n"; + print "col ".$status->{current_col}."/".($status->{cols}-1)."\n"; + print "scan finished in ".hms_string($status->{seconds_left})."\n"; + print "total duration ".hms_string($status->{ETA})."\n"; + print "\n"; + return " "; + } else { + return $status; + } + +} + +sub last_scan { + my $self = shift; + return $self->{scan_shm}->readShm(); } +sub start_scan { + my $self= shift; + daemonize(); + $self->scan_sample(); +} +sub stop_scan { + my $self= shift; + $self->{status_shm}->updateShm({abort => 1}); + print "sent stop signal\n"; + return " "; +} + +sub scan_to_svg { + my $self = shift; + my %options = @_; + + my $tc = $self->{table_control}; + + my $scan = $self->{scan_shm}->readShm(); + + my $sample_rect_x1 = $tc->{settings}->{sample_rect_x1}; + my $sample_rect_x2 = $tc->{settings}->{sample_rect_x2}; + my $sample_rect_y1 = $tc->{settings}->{sample_rect_y1}; + my $sample_rect_y2 = $tc->{settings}->{sample_rect_y2}; + + my $sample_rect_size_x = $sample_rect_x2 - $sample_rect_x1; + my $sample_rect_size_y = $sample_rect_y2 - $sample_rect_y1; + + my $aperture_dia = $tc->{settings}->{sample_aperture_dia}; + + my $scale = 12; # pixel per mm + + # create an SVG object with a size of 40x40 pixels + + my $pic_width = ($sample_rect_size_x+5)*$scale; + my $pic_height = 250; + + my $svg = SVG->new( + -printerror => 1, + -raiseerror => 0, + -indent => ' ', + -docroot => 'svg', #default document root element (SVG specification assumes svg). Defaults to 'svg' if undefined + -inline => 1, + id => 'document_element', + width => $pic_width, + height => $pic_height, + ); + + + + +} diff --git a/user_interface/misc_subs.pm b/user_interface/misc_subs.pm index 18c951d..eda0f48 100644 --- a/user_interface/misc_subs.pm +++ b/user_interface/misc_subs.pm @@ -1,5 +1,5 @@ package misc_subs; - +use POSIX; BEGIN { require Exporter; @@ -8,7 +8,7 @@ BEGIN { # Inherit from Exporter to export functions and variables our @ISA = qw(Exporter); # Functions and variables which are exported by default - our @EXPORT = qw(printHeader min max echo require_run test); + our @EXPORT = qw(printHeader min max echo require_run test hms_string daemonize); # Functions and variables which can be optionally exported #our @EXPORT_OK = qw($Var1 %Hashit func3); } @@ -62,6 +62,35 @@ sub printHeader { } } +sub hms_string { + my $s = shift; + + my $hours = floor($s/3600); + my $mins = floor($s/60)-$hours*60; + my $secs = floor($s)-$hours*3600-$mins*60; + + my $string = ""; + $string .= $hours." h, " if $hours; + $string .= $mins." m, " if $mins; + $string .= $secs." s"; + return $string; +} + +sub daemonize { + # chdir '/' or die "Can't chdir to /: $!"; + + defined(my $pid = fork) or die "Can't fork: $!"; + if($pid){ +# printHeader('text/plain') if $isHttpReq; + print "this instance has terminated, the other one is a demon now\n"; + exit; + } + open STDIN, '/dev/null' or die "Can't read /dev/null: $!"; + open STDOUT, '>>/dev/null' or die "Can't write to /dev/null: $!"; + open STDERR, '>>/dev/null' or die "Can't write to /dev/null: $!"; + POSIX::setsid or die "Can't start a new session: $!"; + umask 0; +} 1; \ No newline at end of file diff --git a/user_interface/pmt_ro.pm b/user_interface/pmt_ro.pm index cd665dc..b173b7b 100644 --- a/user_interface/pmt_ro.pm +++ b/user_interface/pmt_ro.pm @@ -60,7 +60,7 @@ sub new { %options }; bless($self, $class); - + $self->load_settings(); return $self; } @@ -70,7 +70,7 @@ sub new { sub setup_regio { my $self = shift; - $self->require_run("load_settings"); +# $self->require_run("load_settings"); my $regio_options = { tty => $self->{settings}->{tty}, @@ -84,7 +84,7 @@ sub apply_device_settings { my $self = shift; my %options = @_; - $self->require_run("load_settings"); +# $self->require_run("load_settings"); $self->require_run("setup_regio"); $self->signal_thresh(value => $self->{settings}->{signal_thresh}); @@ -97,7 +97,7 @@ sub spectral_scan { my $self = shift; my %options = @_; - $self->require_run("load_settings"); +# $self->require_run("load_settings"); $self->require_run("setup_regio"); die "device zero offset calibration has to be performed first!\n @@ -139,7 +139,7 @@ sub spectral_scan_onesided { my $self = shift; my %options = @_; - $self->require_run("load_settings"); +# $self->require_run("load_settings"); $self->require_run("setup_regio"); die "device zero offset calibration has to be performed first!\n @@ -188,7 +188,7 @@ sub signal_thresh { my $self = shift; my %options = @_; - $self->require_run("load_settings"); +# $self->require_run("load_settings"); $self->require_run("setup_regio"); my $value = $options{value}; @@ -210,7 +210,7 @@ sub veto_thresh { my $self = shift; my %options = @_; - $self->require_run("load_settings"); +# $self->require_run("load_settings"); $self->require_run("setup_regio"); my $value = $options{value}; @@ -234,7 +234,7 @@ sub dead_time { my $self = shift; my %options = @_; - $self->require_run("load_settings"); +# $self->require_run("load_settings"); my $value = $options{value}; my $unit = $options{unit}||"cycles"; @@ -271,7 +271,7 @@ sub zero_calib { my $self = shift; my %options = @_; - $self->require_run("load_settings"); +# $self->require_run("load_settings"); $self->require_run("setup_regio"); my $iterations = $options{iterations} || 26; @@ -323,7 +323,7 @@ sub count { # count for a given time on a given channel my $self = shift; my %options = @_; - $self->require_run("load_settings"); +# $self->require_run("load_settings"); $self->require_run("setup_regio"); my $channel = $options{channel}; # can be "signal" or "veto" or "net" @@ -366,7 +366,7 @@ sub signal_range { # determine the range and the position the signal/noise in te my $self = shift; my %options = @_; - $self->require_run("load_settings"); +# $self->require_run("load_settings"); $self->require_run("setup_regio"); my $use_zero_calib = 1; @@ -450,7 +450,7 @@ sub find_baseline { my $self = shift; my %options = @_; - $self->require_run("load_settings"); +# $self->require_run("load_settings"); $self->require_run("setup_regio"); my $counter_addr = $options{counter_addr}; @@ -540,7 +540,7 @@ sub read_register { my $self = shift; my %options = @_; - $self->require_run("load_settings"); +# $self->require_run("load_settings"); $self->require_run("setup_regio"); my $addr = $options{addr}; @@ -564,7 +564,7 @@ sub write_register { my $self = shift; my %options = @_; - $self->require_run("load_settings"); +# $self->require_run("load_settings"); $self->require_run("setup_regio"); my $addr = $options{addr}; diff --git a/user_interface/shm_manager.pm b/user_interface/shm_manager.pm new file mode 100644 index 0000000..191f5d1 --- /dev/null +++ b/user_interface/shm_manager.pm @@ -0,0 +1,137 @@ +package shm_manager; + + +use strict; +use warnings; +use POSIX; + +use Storable qw(lock_store lock_retrieve fd_retrieve nstore_fd); +use Fcntl qw(:DEFAULT :flock); + +sub new { + my $class = shift; + my %options = @_; + my $self = { + %options + }; + + die "shm_manager must get an shmName" unless defined($self->{shmName}); + + $self->{shmFile} = "/dev/shm/".$self->{shmName}; +# $self->{dataDir} = $self->{shmFile}."_data"; +# $self->{dataFile} = $self->{dataDir}."/data"; + + $self->{shmFhLocked} = 0; + bless($self, $class); +} + + +sub initShm { + my $self = shift; + unless($self->existShm()){ + $self->createShm(); + } + +} + +sub existShm { + my $self = shift; + return 1 if( -e $self->{shmFile} ); + return 0; +} + +sub createShm { + my $self = shift; + die "shm ".$self->{shmFile}." already exists!\n" if( -e $self->{shmFile} ); + + lock_store({},$self->{shmFile}); +# die "data directory ".$self->{dataDir}." already exists!\n" if( -e $self->{dataDir} ); +# mkdir $self->{dataDir}; +}; + + +sub deleteShm { + my $self = shift; + unlink $self->{shmFile}; + system("rm -rf ".$self->{dataDir}); +} + +sub readShm { + my $self = shift; + + if ( -e $self->{shmFile} ){ + return lock_retrieve($self->{shmFile}); + } else { + die "shm does not exist!"; + } +} + +sub lockAndReadShm { + my $self = shift; + + if ( -e $self->{shmFile} ){ + die "Shm file handle already open and locked!\n" if $self->{shmFhLocked}; + sysopen(my $fh, $self->{shmFile}, O_RDWR|O_CREAT, 0666) + or die "can't open shm file: $!"; + flock($fh, LOCK_EX) or die "can't lock shm file: $!"; + $self->{shmFhLocked} = 1; + $self->{shmFh} = $fh; # store file handle in object + # attention! file handle is now still open! + return fd_retrieve(*$fh); + } else { + die "shm does not exist!"; + } +} + +## deprecated + +# sub writeShm { +# my $self = shift; +# my $shmHash = shift; +# if ( -e $self->{shmFile} ){ +# lock_store($shmHash,$self->{shmFile}); +# } else { +# die "shm does not exist!\n"; +# } +# } + +sub writeShm { # closes and unlocks shm file if already open + my $self = shift; + my $shmHash = shift; + + if ( -e $self->{shmFile} ){ + my $fh=$self->{shmFh}; + #check if file handle still open and locked + unless($self->{shmFhLocked}){ + print "found locked shm from previous lock-and-read\n"; + sysopen($fh, $self->{shmFile}, O_RDWR|O_CREAT, 0666) + or die "can't open shm file: $!"; + flock($fh, LOCK_EX) or die "can't lock shm file: $!"; + } + #in any case, store your hash in shm file + seek($fh,0,0); + nstore_fd($shmHash, *$fh) + or die "can't store hash\n"; + truncate($fh, tell($fh)); + close($fh); + $self->{shmFhLocked} = 0;# mark file handle as unlocked + return $shmHash; + } else { + die "shm does not exist!"; + } +} + + + +sub updateShm { + my $self = shift; + my $shmHash = shift; + + my $oldShmHash = $self->lockAndReadShm(); + my $compositeShmHash = {%$oldShmHash,%$shmHash}; + $self->writeShm($compositeShmHash); + return $compositeShmHash; +} + + +1; \ No newline at end of file diff --git a/user_interface/table_control.pm b/user_interface/table_control.pm index c66bf5d..b12b1dd 100644 --- a/user_interface/table_control.pm +++ b/user_interface/table_control.pm @@ -330,17 +330,14 @@ sub scan_pattern { my $sample_rect_size_y = $sample_rect_y2 - $sample_rect_y1; - my $steps_in_x = floor($sample_rect_size_x / $sample_step_size) +1; + my $steps_in_x = floor(min($sample_rect_size_x,$self->{settings}->{mm_to_scan}) / $sample_step_size) +1; + $steps_in_x = min($steps_in_x, $self->{settings}->{rows_to_scan} ); my $steps_in_y = floor($sample_rect_size_y / $sample_step_size) +1; my $coordinate_array = []; for( my $i = 0; $i < $steps_in_x; $i++ ) { - last if ($i >= $self->{settings}->{rows_to_scan}); - last if ($i*$sample_step_size > $self->{settings}->{mm_to_scan}); - - for( my $j = 0; $j < $steps_in_y; $j++ ) { if( $style eq "linebyline" ) { @@ -376,21 +373,20 @@ sub scan_pattern { } - return $coordinate_array; + return { points => $coordinate_array, cols => $steps_in_y, rows => $steps_in_x, number_points => scalar(@$coordinate_array) }; } sub scan_pattern_to_svg { my $self = shift; my %options = @_; - my $style = $options{style}; my $html_tag = $options{html_tag}; #$self->require_run("load_settings"); my $svg_file = $options{svg_file}; - my $scan_pattern = $self->scan_pattern(style => $style); + my $scan_pattern = $self->scan_pattern(); my $sample_rect_x1 = $self->{settings}->{sample_rect_x1}; @@ -471,7 +467,7 @@ sub scan_pattern_to_svg { my $lastpoint; my $counter=0; - for my $point (@$scan_pattern) { + for my $point (@{$scan_pattern->{points}}) { last if ( ($point->{x_rel})*$scale > $pic_width @@ -532,31 +528,34 @@ sub scan_pattern_to_svg { } -sub scan { - my $self = shift; - my %options = @_; - - my $eval = $options{eval}; - my $subref = $options{subref}; - - my $method = $options{method}; - my $object = $options{object}; - - #$self->require_run("load_settings"); - - for my $point (@{$self->scan_pattern()}) { - -# last if ($point->{row} >= $self->{settings}->{rows_to_scan}); - - $self->go_xy( x => $point->{x}, y => $point->{y}); - eval $eval if defined($eval); - $subref->($point) if defined($subref); - if(defined($object) && defined($method)){ - $object->$method($point); - } - } - -} +# sub scan { +# my $self = shift; +# my %options = @_; +# +# my $scan_pattern = $options{scan_pattern}; +# unless(defined($scan_pattern)) { +# $scan_pattern = $self->scan_pattern(); +# } +# +# my $eval = $options{eval}; +# my $subref = $options{subref}; +# +# my $method = $options{method}; +# my $object = $options{object}; +# +# +# for my $point (@{$scan_pattern->{points}}) { +# +# +# $self->go_xy( x => $point->{x}, y => $point->{y}); +# eval $eval if defined($eval); +# $subref->($point) if defined($subref); +# if(defined($object) && defined($method)){ +# $object->$method($point); +# } +# } +# +# } sub set_zero { -- 2.43.0