Follow data in file in realtime and import them if data in file was changed. Ruby script

Hello. Hope that someone can help me there.
I need to follow changing data in file(like json) and if data was changed i need to start ruby script inside SketchUp. I tryd to make infinite loop in a few different way, but sketchup crush on it. And i cant fiend method how to follow data changing in file in a real time and activate script for import them in Sketchup and do performance if data are changed. I need it for performance movement in real time. This is part of my thesis.
I have robot hand model inside sketchup and i have file with angles for robots joints. By other application on c# user writing value for joints and application stor them in file. Sketchup/Ruby must understand, that “log” file with angles values was changed and start script to import this data in sketchup for changeling robot’s positions. Implementation should be exactly as I wrote - Controlling robot from external program. Because latter it will change to client server application (User puts data in client - client send data to server witch is on the one side with Sketchup - Ruby script geted that in file are new data and import them in Sketchup). Hope that you have some idea. Thank you.
The best idea that came in my mind

require 'digest/sha1'

tmp = Digest::SHA1.hexdigest(File.read($log_file))
    loop do 
        if (tmp!= Digest::SHA1.hexdigest(File.read($log_file))) then
             joint_rotation()  #function that performance robot movement 
             tmp = Digest::SHA1.hexdigest(File.read($log_file))
        end
     end

$log_file - path to file wich i need to follow

Please read and correct your post …

Do not use global variables. SketchUp’s Ruby ObjectSpace is a shared space.

(Note that I reassigned your topic to the correct category.)

If the data file is JSON, then it is better to convert the JSON data directly to a Ruby hash inside your module.

Here is a template (updated to ver 1.0.1) …
GeorgeGrot_RobotHand_1-0-1.rbz (2.2 KB)

You’ll need to add the code to the "robot.rb" file …

# encoding: UTF-8
#
# "GeorgeGrot_RobotHand/robot.rb"
#
module GeorgeGrot
  module RobotHand

    def move_robot
      # use @data to move robot
    end

    # Add more methods to control robot here ...

  end
end


The extension registrar file that goes in the “Plugins” folder looks like …

GeorgeGrot_RobotHand.rb
# encoding: UTF-8
#
# "GeorgeGrot_RobotHand.rb"
#
module GeorgeGrot
  module RobotHand
    EXTENSION = SketchupExtension.new(
      'Robot Hand', 'GeorgeGrot_RobotHand/loader.rb'
    )
    EXTENSION.instance_eval {
      self.description='Animated robot hand model.'
      self.version='1.0.1'
      self.copyright='(c) 2020'
      self.creator='George Grot'
    }
    Sketchup.register_extension(EXTENSION, true)
  end
end


The loader file looks like …

GeorgeGrot_RobotHand/loader.rb
# encoding: UTF-8
#
# "GeorgeGrot_RobotHand/loader.rb"
#

  require 'json'

  require(File.join(__dir__,'main'))
  require(File.join(__dir__,'robot'))

module GeorgeGrot
  module RobotHand

    # AppObserver callbacks:
    def onQuit
      stop_listener()
      Sketchup.remove_observer(self)
    end


    if !@loaded

      # Create commands and menu items:
      submenu = UI.menu('Extensions').add_submenu(SUBMENU_NAME)

      submenu.add_item(MENU_START) {
        start_listener()
      }
      submenu.add_item(MENU_STOP) {
        stop_listener()
      }

      submenu.add_separator

      submenu.add_item(MENU_DATAFILE) {
        choose_datafile()
      }

      # Init @filepath from previous session:
      @filepath = Sketchup.read_default(OPTS_KEY,'datafile',"")

      # Attach this module to SketchUp as an AppObserver:
      Sketchup.add_observer(self)

      @loaded = true
    end

  end
end


The main file looks like …

GeorgeGrot_RobotHand/main.rb
# encoding: UTF-8
#
# "GeorgeGrot_RobotHand/main.rb"
#

module GeorgeGrot
  module RobotHand

    extend self

    # CONSTANTS
    WIN ||= Sketchup.platform == :platform_win

    OPTS_KEY ||= Module::nesting[0].name.gsub('::','_')

    DATAFILE_DIALOG_CAPTION ||= "Choose datafile ..."
    DATAFILE_TYPE ||= 'JSON'
    DATAFILE_EXTN ||= "*.#{DATAFILE_TYPE.downcase}"

    OPNAME ||= 'Move Robot'

    SUBMENU_NAME  ||= 'GeorgeGrot: RobotHand'
    MENU_START    ||= 'Start Listener'
    MENU_STOP     ||= 'Stop Listener'
    MENU_DATAFILE ||= 'Choose Datafile...'

    # Module variables:
    @data ||= {}
    @filepath ||= ""
    @last_mod_time = Time.now
    @interval = 5.0 # seconds


    def choose_datafile
      dir  = File.join(ENV['HOME'],'Documents')
      path = UI.openpanel(
        DATAFILE_DIALOG_CAPTION ,
        File.exist?(dir) ? dir : ENV['HOME'] ,
        WIN ? "#{DATAFILE_TYPE} Files|#{DATAFILE_EXTN}||" : DATAFILE_EXTN
      )
      if path
        @filepath = path.gsub(/(\\\\|\\)/,'/')
        Sketchup.write_default(OPTS_KEY,'datafile',@filepath)
      else
        false
      end
    end

    def file_changed?
      File.mtime(@filepath).to_i > @last_mod_time
    end

    def get_mod_time
      @last_mod_time = File.mtime(@filepath).to_i
    end

    def import_data
      json = File.read(@filepath)
      @data = JSON.parse(json)
    rescue => error
      UI.messagebox(error.message)
    end

    def process_data
      stop_listener()
      get_mod_time()
      import_data()
      model = Sketchup.active_model
      model.start_operation(OPNAME)
      ###
        #
        move_robot()
        #
      ###
      model.commit_operation
      start_listener()
    end

    def start_listener()
      result = choose_datafile() if @filepath.empty? || !File.exist?(@filepath)
      return if !result
      get_mod_time()
      @listener = UI.start_timer(@interval,true) {
        process_data() if file_changed?()
      }
    end

    def stop_listener
      UI.stop_timer(@listener) if @listener
      @listener  = nil
    end


  end
end


:bulb:

Oops … I see an error in process_data() method, in file “main.rb”.
It is missing a line …
model = Sketchup.active_model

EDIT (again) There is also a call to get_mod_time() missing, see below.

UPDATED examples above to ver 1.0.1

1 Like

thank you very much! I will response here after trying

Download the RBZ, then open the Extensions Manager from within SketchUp.
Use the red “Install Extension” button on bottom of the manager dialog window.
Browse to the RBZ archive and choose it. This will install it to your User “Plugins” folder.

The path to your extension subfolder will then be …
"%AppData%/SketchUp/SketchUp 2017/SketchUp/Plugins/GeorgeGrot_RobotHand"

@GeorgeGrot
Oops … I see an error in process_data() method, in file “main.rb”.
It is missing a line …
model = Sketchup.active_model

And another booboo …
Same file, same method.
Before the listener can be restarted, the @last_mod_time needs to be updated.
So insert a line into the method just after stopping the listener (2nd line of the method,) to call the get_mod_time() method. (You want to do it as soon as possible after noting that the time is newer, because it’s possible whilst moving the robot that the file is getting updated again.)

The updated method should look like …

    def process_data
      stop_listener()
      get_mod_time()
      import_data()
      model = Sketchup.active_model
      model.start_operation(OPNAME)
      ###
        #
        move_robot()
        #
      ###
      model.commit_operation
      start_listener()
    end

Thank you. Every things work perfect!