Notifications
Clear all

[Closed] dotNet timer scope within structs

I want to create a struct instance to hold a .net instance of a timer, then have this self-contained struct act as a timing mechanism so I can monitor an external file for changes from another application.

However, once the timer is within a struct, it’s callback seems lose scope.

Is this me being thick / not knowing some detail, or a limitation of max?

I’ve pared it down to the bare minimum (fomr the struct I had) so you guys can pick it apart, test in max, perhaps catch what I’ve missed

Each script is WELL commented, and should let you see what my thinking is within max…

The global methods (in the help) that works just fine: (prints a value every 10 seconds)

-- variables
 	val 	= 0
 	
 -- callback function
 	function callback =
 	(
 		val += 1
 		print (val as string)
 	)
 	
 -- timer
 	tmr = dotNetObject  "System.Windows.Forms.Timer" 
 	tmr.interval = 10000
 	dotnet.addEventHandler tmr "tick" callback
 	tmr.start()
 
 -- stop the timer
 	tmr.stop()
 

The self contained struct methods that seems to lose scope and throw a nasty error on ech timer “tick”:

-- the main tmr struct
  	struct TimerStruct
  	(
  		-- struct properties
  			val = 0,
  			tmr,
  		
  		-- callback function, should call its own struct, right?
  			function callback =
  			(
  				val += 1
  				print (val as string)
  			),
  			
  		-- initialization function
  			function init =
  			(
  				-- create the timer
  					tmr = dotNetObject  "System.Windows.Forms.Timer" 
  				
  				-- set the iterval to every 10 seconds (so you can cancel!
  					tmr.interval = 10000
  				
  				-- set up the events, and start
  					dotnet.addEventHandler tmr "tick" callback
  					tmr.start()
  			),
  			
  		-- kick off the struct
  			initialized = init()
  	)
  
  -- create a new instance and start the timing
  	ts = TimerStruct 1
  
  -- call this to stop the tmr when it errors!
  	/*
  		ts.tmr.stop()
  	*/

So what’s going on with the struct method!? It’s such a pain as having everything within one strruct would obviously be far easier!

If anyone could spare a moment to investigate
Many thanks,
Dave

14 Replies
 PEN

The callBack function is not in the scope of the struct so I did this to make it work.

 
			 function callback =
			  (
				  ts.val += 1
				  print (ts.val as string)
			  ),

 PEN

Sorry, just did a restart and that didn’t work.

Thanks Paul,

It negates the purpose of using a struct though as encapsulation goes out the window.
I tried handing the scope in as part of the initializer argument for it to self-reference, but that didn’t work either.

Is there any way to figure out where the scope is?

I tried setting the tag property of the timer to the struct as well, then tracing the “tag” property in the callback (in other languages this might have been “this” or “Me”) but no luck either.

Where is that scope!!!?

 PEN

Good question as I just tried all sorts of things and couldn’t get it to work. I even tried starting the timer out side of the struct but calling the callBack in it and that didn’t do anything at all.

Yeah, it’s a real shame as the functionality I’m aiming for is something like:

TimerStruct
(
	-- variables
	-- callback code
	-- checking code
	-- initializer
)

csvTimer = TimerStruct "file-to-monitor.csv" callback:reImportFile interval:5

That way you just create a self-contained struct instance for each file you want to monitor, as opposed to messing around with multiple sets of local variables

Dash it all!

This sort of goes there… can you improve? [font=Verdana]Following the [/font]tag method you mentioned earlier…


          
          struct TimerStruct (
          
          	val = 0,
          	tmr = dotnetobject "System.Windows.Forms.Timer",
          	interval = 8000,
          	
          	[color=Wheat]--'static' method[/color]
          	fn TimerStructCallback sender eventargs = (
          		--recover the reference to 'this'
          		TSInstance = *(sender.tag.value)
          		TSInstance.InstanceCallback() 
          	), 
          	--'instance' method
          	fn InstanceCallback = (
          	
          		val += 1
          		print val as string
          		if val > 99 then tmr.stop()
          	 
          	),
          	
          	fn start TSInstanceRef = (
          		--pass the ref to 'this' into the tag of the timer 
          		tmr.tag = dotNetMXSValue TSInstanceRef
          		tmr.interval = interval
          		--set the callback to the static method
          		dotnet.addEventHandler tmr "tick" TimerStructCallback 
          		tmr.start()
          	)
          
          )
          
          
          ts = TimerStruct val:90 interval:1000
          
          -- seems a bit silly to pass ts twice, once as a reference
          
          ts.start &ts
          
          --uncomment to stop in emergency
          --ts.tmr.stop()
 plus it might be playing havoc with memory, for all I know, yet ...

Hey hey,

That looks pretty good! I was thinking about references in bed last night, but hadn’t had time to get on it. I had forgotten about dereferening as well, so I probably would have given up there!

The 2d pass is a bit untidy, and I tried building a factory method for it…

ts = TimerFactory.create()

… but the references were getting lost, and to be honest one it gets into anything more complicated than a simple byref variable pass, I get a bit lost.

Thanks loads, I’ll probbaly use it like that.
Keep the code coming of you have any more insights!

Alright Robin,

My thanks to you, as I’ve managed to create a fairly elegant file monitor with your help!

I’ve basically created a bunch of accessor methods, rather than sticking everything in the constuctor, as things like files and callbacks need to be checked for existance. All the arguments also go in the init function for consistency:

-- callback
	function trace file: date: =
	(
		format "The file '%' was last modfied on '%'
" file date
	)

-- create a new FileMonitor, without any arguments...
	fm = FileMonitor()

-- basic usage
	fm.init &fm "FileMonitor example.ms" callback

-- more options
	fm.init &fm "FileMonitor example.ms" callback seconds:1 pause:true

This class is responsible for monitoring (polling) a file for changes, and when it’s been updated, will fire a callback.

Why would you need such a thing? Well another project I’m working on shows selected data from a script file in a rollout, but also allows you to edit the file. However, once edited, the interface needs to know about it, so I’ll probably set a check for every 2 seconds or so.

Or perhaps you need to kick off a render when another file on your network changes, or who knows, but it works and I’m chuffed.

There’s a hell of a lot of error checkig in there too! On the many occasions that things didn’t go quite right in testing, .net is relentless at making those callbacks, and it can easily overload and crash max!

Coolio

 PEN

Nice work.

Page 1 / 2