[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
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)
),
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!!!?
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