PDA

View Full Version : dotNet timer scope within structs


davestewart
12-03-2008, 11:34 PM
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 :D

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

PEN
12-04-2008, 12:15 AM
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
12-04-2008, 12:20 AM
Sorry, just did a restart and that didn't work.

davestewart
12-04-2008, 12:27 AM
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!!!?

:D

PEN
12-04-2008, 12:38 AM
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.

davestewart
12-04-2008, 12:45 AM
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!

drdubosc
12-04-2008, 06:29 PM
This sort of goes there... can you improve? Following the tag method you mentioned earlier....


struct TimerStruct (

val = 0,
tmr = dotnetobject "System.Windows.Forms.Timer",
interval = 8000,

--'static' method
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 ...

davestewart
12-04-2008, 08:23 PM
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!

:D

davestewart
12-04-2008, 11:12 PM
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 '%'\n" 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 :D

PEN
12-04-2008, 11:39 PM
Nice work.

magicm
12-05-2008, 12:56 AM
You might also want to check out the System.IO.FileSystemWatcher class, which allows you to monitor file/folder changes. I've attached a simple example.

Cheers,
Martijn

davestewart
12-05-2008, 01:11 AM
Hey, that rocks! Or at least it rocked for about 2 files, then it stopped working and now it won't start again :surprised

LoneRobot
12-05-2008, 10:29 AM
You might also want to check out the System.IO.FileSystemWatcher class, which allows you to monitor file/folder changes. I've attached a simple example.


Thanks for posting this martijn, I have been meaning to take a look at this class for a while! cheers!

p.s. seems to work fine for me dave, whatcha doin?

drdubosc
12-05-2008, 07:16 PM
... I've managed to create a fairly elegant file monitor ..

That's tidied it up very nicely ... glad the passing technique helped :) .. and then magicm comes along and cuts the Gordian Knot! But for general purposes, it might still be useful to be able to ping struct instance methods.

One thing that struck me .. way too many timers hanging around. They're not cheap. If you're monitoring lots of things, they shouldn't have to have one timer each. It would be nice to bring both sides of the observer pattern into MXS. One timer pinging one 'raise event' function, which then calls its personal array of instance methods. It would be a doddle in some languages, but a fair amount of faffing around hasn't got me very far, in my bad MXS. Things like looking for existing functions in the array, to check against duplication when adding callbacks to it, are beyond me atm.

davestewart
12-05-2008, 07:46 PM
You're totally right. I'd just started adding an addEventListener() method and was registering different callback varaibles, when I relised that I was suddenly suffering from scope creep! So I stopped. There's only so many hours in the day you know!

CGTalk Moderation
12-05-2008, 07:46 PM
This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.