klayout as a position viewer on a microscope

Hi all,
I just starting using klayout (which is very powerful; thank you for that.).
I want to use klayout as a viewer for lithography designs and use the script engine to couple it with our microscopes (LM & SEM) to see where the actual view of the microscope in the design is. With a mouse click or double click or control key+mouse click I want to drive the microscope stage to that position in the design. The communication to the microscope should take place over a tcp-client socket.
I found the macro 'Browser example' as a good starting point.
My questions:
Did someone else something in this direction yet?
Can I have a kind of background script or a script which is called every second?
Can I react in a script to a mouse click or double click or control key+mouse click in a layout panel via script?
Thank you in advance.
Frank

Comments

  • ABSOLUTELY INGENIOUS IDEA !!!

    If you can't react to mouse clicks on layout_view, you can always write a GUI (QDialog) for it that has buttons for scope actions. As for a timer script, I just had to make one that checks for updates every 10min, so here is how I did that in my class MyUPDATER. The last lines are to avoid multiple timers running if the script gets executed multiple times (still fine tuning it).

    class MyUPDATER
       def initialize()
          @timerIsRunning = false
          @timer  = RBA::QTimer::new()
          @timer.setInterval(1000 * 60 * 10)
          @timer.setSingleShot(false)
          def @timer.event(e)
             puts "10 minutes have passed, checking on updates now"         
             $myUPDATER.checkForAvailableUpdates()
          end
       end 
       def checkForAvailableUpdates()
         # actions that check grain.xml on local and remote 
       end
       def stopTimer()
          @timer.stop
          @timerIsRunning = false
       end
       def startTimer()
          @timer.start
          @timerIsRunning = true
       end
    end
    if defined?($myUPDATER)
       $myUPDATER.stopTimer()
       $myUPDATER==nil
    end
    $myUPDATER = MyUPDATER.new() if !defined?($myUPDATER)
    $myUPDATER = MyUPDATER.new() if $ngc_updater==nil
    if !$myUPDATER.timerIsRunning              
       $myUPDATER.startTimer()        
    end
    

    If your scope has a motorized stage, you could let KLayout drive to wafer alignment points, ask the user to find the spots, then read it back for {x,y,rot} adjustment -- et voila: you have wafer alignment. Maybe even get dx(scope)/dx(KLayout) calibrated. If your scopes have auto-switchable objectives, you can bind that in as well. maybe switch to lower objective if layout_view.box is larger, etc.

    I have various electrical testers that have a scope and motorized stages. This idea is great for those: wafer alignment and driving to test sites + logging them for the tester.

  • edited December 2020

    Nice idea, I agree!

    But you're not the first one, I'm afraid :)

    One is here: https://www.klayout.de/forum/discussion/comment/2214#Comment_2214

    The first request dates back into the early days of KLayout (>10yrs back). I think there are even commercial systems including KLayout. But I was not involved in such a project. This application however was an initiator for the extended scripting capabilities, because the solution was to utilize TCP/IP to communicate with the stage controller hardware. When you use KLayout to control the stage, KLayout acts as the client, in the other direction, KLayout acts as a server which listens to stage movement notifications. This was the application behind the TCP/IP (HTTP) server example: https://www.klayout.de/examples/qtrubyserver.html

    KLayout can display images over your layout so basically you can overlay camera shots over layout. I doubt that the implementation is efficient, but at a low frame rate you may even be able to display a life image including rotation and perspective correction if required. True video overlay however will require an extensions of the composition layer.

    Matthias

  • Not quite what this thread is about, but I figure this could help the community with GPIB equipment. I made an NI488.2 GPIB wrapper driver class, I had just fought through it's implementation on MATLAB and JAVA. I tested it on a parameter analyzer and a probe station. Code is attached, and below is how it's called. It requires ni4882.dll which is part of the national instruments driver installation.
    I quickly tested a prober follower program (moving on the layout moves the prober / moving the prober moves the layout), but it's not presentable yet though it all worked ok. Unfortunately, unlike network handshakes, GPIB relies on continuous polling. The prober cannot send "I moved" via GPIB. Instead, KLayout has to ask for x/y positions every 100ms and determine if it moved.

    gpib = Gpib.new
    if gpib.classOK
                gpib.verbose = 0
                gpib.findDevices
                dev = gpib.getDevice("2001X")
                if dev.length==5
                    puts "found 2001X"
                    $prober_driver = Prober.new(gpib, dev)
                    $prober_driver.openAndInitialize
                end
    
                dev = gpib.getDevice("HP4145B")
                if dev.length==5
                    puts "#{dev[0]}:#{dev[1]}:#{dev[2]}:#{dev[3]}:#{dev[4]}"
                    gpib.sendTestCommands(dev, Gpib::T10s, 1, 0)
                end
    end
    
  • Really ... ist GPIB still around?

    I had to code for IEEE488 in the early 90s (and I think there was already ni4882.dll!). This was when USB entered the scene. I was so happy this nightmare was over for me by this time.

    If you need to poll, my suggestion is to use a QTimer - this will give you a reliable callback provided the main thread isn't blocked. If you want a rugged solution however, I'd suggest a separate process running as a service (or background process on Linux), which does the polling and on the other side opens a TCP/IP port for KLayout to attach asynchronously. If in 1991 I had something else other than MS-DOS + Windows 3.1 (yes, 16 bit!) I had done the same by this time ...

    Matthias

  • Thank you for all your comments here.
    I experimented with the QTimer using the example from tnoradam and TCPSocket from ruby.
    So I can now transfer data to and from Klayout in the background.
    For each device I want support I have/will have a small windows helper app which has a tcp-server reading the stage positions from the hardware or via windows messages from the GUI fields of the driver software.
    Using PluginFactory and Plugin I can react to the mouse klicks in the scripts. Klayout scripting is really powerful und good documented.
    Now I have to create a Qdialog for the coordinate transformations in the ruby script language.
    As soon as I have something working well, I will post the script here for other users as a starting point.
    I will also open an new discussion about importing several layouts interpreting an text-file, because all our structure writing machines (Raith EBL und DMO Laserwriter) work with a kind of tasklist (ascii file) where you define the structures and layers you write at once including coordinate offsets.
    Merry Christmas,
    Frank

  • Hi Matthias,
    you'd be surprised how many places still use GPIB for a lot of things :smile: I agree though, it is a pain to work with. Good idea with QTimer in sPoll. I will have to think about that. A lot of pieces of equipment need that while data is being collected, and some moving parts of handlers/stages/etc. are non-blocking as well - meaning I have to poll them to figure out when the motion ended.

    Thanks.

    Fröhliche Weihnachten!

    Thomas.

  • Hi Matthias,

    if I were to use QTimer in sPoll, then I would need to be able to hand down the parent instance. This turns out to be knifflig. Any suggestions on how to make an appropriate pointer to the parent instance when QTimer is created ?
    I would like to send GPIB commands until I get to an sPOLL point. There, I will have to move the remaining commands to be sent from a separate method, because if sPoll is a timer, it would allow the sending of the next command during the waiting time.

    Below is a snippet that I can't seem to get to run. @timer.event is in its own class at runtime, and besides making something global, I haven't figured out how to access the parent class instance testClass from inside @timer.event(e). I can customize the timer response via property, but I would like to execute action_of_timer when @timer runs down. I tried to fashion the instance testClass into QObject, but that always failed. I guess I just don't know the right ruby mechanics here (I do most of my work in JAVA).

    Any help would be appreciated ...

    Thanks.

    Thomas.

    PS: I plan to convert the list of GPIB commands into an array of strings that gets the 0th element sent out and then shifted to have the next one in position. However, the problem remains: when an sPOLL is encountered, and I wish to use QTimer, then I need that QTimer instance to be able to access some outside class variable to pause the send-and-shift - and I hate to make that global.

        module MyModule
            include RBA
            class TestClass
                def initialize
                    puts "initialized"
                    @timer_val = 0
                end
                def action_of_timer
                    puts "timer action"
                    self.destroy
                end
                def ini_and_start_timer(parent, mseconds, end_mseconds)
                    @timer = RBA::QTimer.new()
                    puts "timer initialized in class: " + self.class.name.split('::').last + ": #{self.kind_of? TestClass}"
                    @timer.parent = ???? 
                    puts @timer.parent.class.name
                    @timer.setInterval(mseconds)
                    @timer.setSingleShot(false)
                    @timer.setObjectName('genericTimer')
                    @timer.setProperty("SOURCE"     , self.class.name.split('::').last)
                    @timer.setProperty("interval"   ,     mseconds)
                    @timer.setProperty("timerVal"   ,          0.0)
                    @timer.setProperty("timerEndVal", end_mseconds)
                    def @timer.event(e)
                        err_str = e.to_s
                        if !e.to_s.include? 'QDynamicPropertyChangeEvent'
                            timer_val      = property(   "timerVal").to_f
                            timer_interval = property(   "interval").to_f
                            timer_end_val  = property("timerEndVal").to_f
                            puts "#{timer_val}/#{timer_end_val} timer fired. objectName = " + objectName.to_s + ": " + property("SOURCE") + " remaining time = #{remainingTime()}"
                            if timer_val>timer_end_val
                                stop 
                                action_of_timer(self)
                            else
                                setProperty("timerVal", (timer_val+timer_interval))
                            end
                        end
                    end 
                    @timer.start           
                end
            end # class
            testClass     = TestClass.new
            parentPointer = RBA::QObject.new()
    
            testClass.ini_and_start_timer(testClass, 1000, 3000)
        end # module
    
  • Never mind, I figured it out. You have to make it a class derived from QObject, and then you can do all that fancy stuff.

        module MyModule
            include RBA
            class TestClass < RBA::QObject
                def action_at_end_of_timer(timer)
                    puts "timer end action"
                    timer._destroy
                    puts "timer destroyed " + timer.to_s + ": #{timer._destroyed?}"
                    self._destroy
                    puts "testClass destroyed"
                end
                def ini_and_start_timer(parent, parent_var_name, mseconds, end_mseconds)
                    puts "timer to be initialized in class: " + self.class.name.split('::').last + ": #{self.kind_of? TestClass}:: " + (:parent).to_s + "->" + parent_var_name
                    puts "EXECUTING FROM var_name = " + parent_var_name + " [" + objectName.to_s + "] / " + property("SOURCE")
                    timer = RBA::QTimer.new
                    timer.parent=parent
                    timer.setInterval  (mseconds)
                    timer.setSingleShot(false)
                    timer.setObjectName('genericTimer')
                    timer.setProperty("SOURCE"     , self.class.name.split('::').last)
                    timer.setProperty("PARENT_NAME", parent_var_name)
                    timer.setProperty("interval"   ,        mseconds)
                    timer.setProperty("timerVal"   ,             0.0)
                    timer.setProperty("timerEndVal",    end_mseconds)
                    def timer.event(e)
                        err_str = e.to_s
                        if !e.to_s.include? 'QDynamicPropertyChangeEvent'
                            timer_val      = property(   "timerVal").to_f
                            timer_interval = property(   "interval").to_f
                            timer_end_val  = property("timerEndVal").to_f
                            puts "#{timer_val}/#{timer_end_val} timer fired. objectName = " + objectName.to_s + ": FROM " + property("SOURCE") + "::" + property("PARENT_NAME").to_s + ", parent = " + parent.to_s
                            if timer_val>timer_end_val
                                stop 
                                parent.action_at_end_of_timer(self)
                            else
                                setProperty("timerVal", (timer_val+timer_interval))
                            end
                        end
                    end 
                    timer.start           
                end
            end # class
            testClassInstance = TestClass::new
            testClassInstance.setObjectName('ObjectName = ' + binding.local_variables.last.to_s)
            testClassInstance  .setProperty("SOURCE"  , 'GPIB_COMMAND_STACK')
            testClassInstance.ini_and_start_timer(testClassInstance, binding.local_variables.last.to_s, 1000, 3000)
        end # module
    
  • Very good :)

    Basically you don't always need a parent object. It's just Qt's way of handling object lifetime: whenever a parent is deleted, the child objects go as well. That's required in C++, but not in Qt, where the members of an object are automatically deleted with the object. I assume you can simply pass "nil" (Python: "None") to the parent pointer.

    But your solution is valid of course too.

    Actually there was a bug which was fixed in KLayout 0.26.8: on Qt5, the timeout signal of QTimer was not visible to Ruby and Python. If you use the timeout signal, the code gets much cleaner I guess.

    In Ruby it's very simple to attach a method to the timer's timeout signal:

    class MyTimer < RBA::QObject
    
      def initialize
        @tick = 1
        @timer = RBA::QTimer::new(self)
        @timer.setInterval(500)
        @timer.start
        @timer.timeout { self.on_timeout }
      end
    
      def finish
        @timer._destroy
      end
    
      def on_timeout
         puts "Another tick: #" + @tick.to_s + " ..."
         @tick += 1
      end
    
    end
    
    my_timer = MyTimer::new
    

    For a Python version see here for example: https://www.klayout.de/forum/discussion/1642

    Best regards,

    Matthias

Sign In or Register to comment.