Hiding Sequences


sshwash

Recommended Posts

Hey, still relatively new with DAFactory but experienced programmer, but had a quick question about sequences: Is there any way to group sequences such that I can collapse them in the Workspace viewer? Or better yet - is there a way to declare different methods within a Sequence, so that I can call Sequence.MySequence.MyMethod(). I know, I'm asking for too much. Basically I'm trying to avoid having a hundred sequences popup in my workspace viewer while I'm working my way around. So, for example, I would like the four sequences I have for e-mailing my alarms to collapse into one main "Email" grouping. If you had ideas for a workaround that doesn't involve putting it in one sequence, I'd love to hear them.

Link to comment
Share on other sites

Yes and no. There is no grouping of sequences, and in a normal sequence you can only have one function. However, you could create an object to hold groups of sequences. For that matter, as an experienced programmer, you might just prefer OOP altogether. The docs for OOP are in this forum post:

http://www.azeotech.com/board/index.php?&showtopic=3878

Be warned that the integrator debugger won't step through object functions, and any error messages indicating line numbers will show the line number from the beginning of the function, not the beginning of the sequence containing the class declaration, so you have to count from the beginning of the function. These two things are why we don't have OOP in the main docs, but there are so many good uses for it for those that don't mind these small issues.

Also remember that you'll have to instantiate your class with your functions before you can actually call them, but if you are an experienced programmer, you probably already know that :)

Link to comment
Share on other sites

Ho ho ho... that's even better than what I was hoping for. So happy I bothered to ask, thanks a lot for the link.

Based on your last bit and from reading through the linked page, I didn't see any way to create static functions. So, in the examples, rather than calling Frank.foo() or People[1].foo(), I'd like to be able to write a function that I can just call with Person.goo(). It'd be nice to have a set of static variables and functions, so I can read/write Emailer.foo and call Emailer.goo() to my hearts content. Or is the work around for this to write a sequence on startup that simply creates an "Emails" object of Type Emailer and use that to call my supposedly-static methods and variables?

I'm guessing you would have mentioned this in your bit about instantiation if static members was possible though, but my assumptions have been proven wrong before, so I thought I'd throw that thought out there.

Also - all classes are public and global, correct?

Link to comment
Share on other sites

Bundle this in with my previous questions: I'm having trouble making my instantiated object persist out of the scope of the sequence. So, if I create a "global Jim" and in my sequence set "Jim = new(Person)", I can get Jim to be created, but once the sequence ends Jim gets destroyed (poor Jim). We like Jim, he's a good guy and we want him to stick around once the sequence is over - how do I get him to persist?

Also, can I pass basic DAQFactory types by reference to my member functions? I know I can't extend them (boy, would that be sweet :) ), but it would be nice if I can have Person.notifyOfAlarm(newAlarm) which I can call from the "Fire Event" code of my FireAlarm simply by typing "Jim.notifyOfAlarm(local.Alarm.FireAlarm)", that way I can do crazy stuff like the ability to acknowledge any alarm in Jim's member function without having to specify a case for every alarm type (by writing a big ghetto switch statement that uses local.Alarm.strLatestAlarm).

Link to comment
Share on other sites

Woops, nevermind- I figured it out. I thought the OnDestroy was being called for the class I just created, but it was obviously being called for the class created in my previous running of the sequence.

With that working, I was able to experiment, and yes I can pass Alarms by reference.

Awesome. Back to more programming fun.

Link to comment
Share on other sites

Ah, actually - I can pass references of objects I've created, but I can't seem to figure out how to pass a DAQFactory object by reference into my functions (I get channel not recognized if I try to access AlarmName.Ack for example). Is this not possible? If it isn't, I can likely just create my ownObject type and fill it with the data from the base Alarm object, but that seems like an extraneous workaround.

Link to comment
Share on other sites

OK, there's a lot in there. Not quite sure where you ended up. Assigning an instantiated object to a global variable will cause it to persist until the global variable is assigned something else and all other references are removed. DAQFactory does automatic garbage collection of objects with reference counts. So, if you do global x = new(myobject) then do y = x, then do x = 3, x will no longer have a reference to that new myobject you instantiated, but y will, and the object remains. Then if you did y = 3, the object would be destroyed because there are no more references to it.

As for parameter passing, user objects are always passed by reference, while everything else is always passed by value. The exception is some of the LabJack device functions which actually take references, but the main sequence scripting engine doesn't support the user specifying values being passed by reference. So, if you want to pass a value by reference, you need to create an object that contains that value, which is kind of like extending a native type, but not quite.

As for built in DAQFactory objects, you can't pass them by reference. You can, however, use the execute function to achieve something close. So, if you had a function that you'd like to take a channel by reference so, perhaps, you could call the addvalue() function on it, then you could instead pass a string containing the name of the channel:

function foo(string channame)

and then build your command using strings and execute() to actually run the line:

execute(channame + ".addvalue(3)")

then if you did:

foo("mychannel")

the execute would do the same as mychannel.addvalue(3)

Link to comment
Share on other sites

Awesome, not ideal but I'm more than happy working with that. Sorry for the many posts, but after I ask a question I keep plugging away. Though, what's the final word on static methods? I assume they're a no-go.

And, unrelated question - if I call a class method from a FireAlarm event, can I assume it's running in it's own thread? I'm worried about having a few execute statements in the method the FireEvent is calling slow my system down. Should I be worried about missing channel updates, or can I assume it's in a lower priority thread (the priority of the sequence it's written in)?

Thanks for taking the time to answer these questions btw, and so quickly at that. I really appreciate the effort you guys are putting into these forums, it's really helping me out. :)

Link to comment
Share on other sites

No static methods.

Any calls to functions as functions (i.e. with () after the name) run in the thread of the calling function, in this case the OnFire event. OnFire runs in the thread of the channel, so delay will carry back to the channel acquisition.

To have a sequence run in its own thread, you have to do beginseq(). To fire off your own threads without a sequence (within a class), you'll need the StartLocalThread(threadname, functionname, priority) function along with its mate, the StopLocalThread(threadname) function. Since its a "local" thread, this function can only be called from inside a member function, and the thread is automatically stopped if the object instance is deleted

Link to comment
Share on other sites

Sorry, I can't find much documentation on StartLocalThread() - based on what you said, if I want all my code to run in a seperate thread, I should have my onFire event call one of my member function, which spawns another member function in another thread then returns?

So OnFire->MemberSetup->NewThread->MemberWorkhorse

that way the only time OnFire spends in the thread is a function call and thread generation, right?

Link to comment
Share on other sites

Sure, if what you need to do takes time, but I'd probably use thread pooling. Starting and stopping threads is kind of a cpu hog. Have the object start the thread when it is instantiated, then use a member variable (local) to tell it to do something. Otherwise, the thread just sits and loops. Unfortunately there is no real background wait, instead you'll have to create a loop that checks for whether there is something to do and if not, delays for half a second or so. You could code it so a single thread can perform multiple tasks.

Link to comment
Share on other sites

So OnFire calls MemberSetup which sets alertBit on

meanwhile, MemberWorkhorse is basically doing an infinite loop, only doing work when AlertBit is on (immediately setting it off once it recognizes it's on, of course).

Wouldn't I have to worry about access errors? I'm not sure how the behind-the-scenes threading work (I'm honestly surprised it all works so well, best on my past experiences with threading), and I'm worried about situations where the onFire() event wants call MemberSetup() to turn the alertBit on while the alertBit is being turned off by MemberWorkhorse (say two alarms called sequentially).

Link to comment
Share on other sites

There is a little of that. Yes we take care of a lot of threading issues, and you don't have to worry about a value being changed halfway when another thread tries to access it, but as you described it you do have to worry. However, the solution follows standard threading patterns: in the secondary thread, when it detects the bit on, it should immediately turn it off, then proceed through the code to process the existing alarm. So if another alarm comes in during secondary processing, the bit is set again for the next iteration of the worker thread.

That said, I personally wouldn't use a bit, but rather an array of commands for the worker thread to do. That way if two alarms occur before the worker thread gets a chance to process the first one, it is not lost. In the worker thread, you should copy that array to a private variable, and then clear the global array for more filling by the alarm thread. Then use the private for processing the alarms so far.

BTW: in the worker thread, you should make sure and include a delay() of some sort so it doesn't chew up all the processor time. DAQFactory doesn't have a WaitForSingleObject() type command, so the thread can't go to sleep and wake up on command, so you will have to loop, but the processor load should be minimal provided you have a reasonable delay() between checks.

Link to comment
Share on other sites

Cool, that makes plenty of sense. I guess there's always the risk, but I can mitigate it substantially.

Sorry to bother you guys again, but I'm stuck trying to figure out why I can't access member variables from member functions.

03/25/10 12:27:53.349

C1000 Channel or function not found: newalarm Line 13: Line 4

Link to comment
Share on other sites

Blagh, my last reply wasn't finished

class Alarmer
   //FSM variables
   local int greenState = 0
   local int redState = 1
   local int yellowState = 2
   local int alarmPanelFsmState = 0
   local int ackFSM = 0
   local int resFSM = 1
   local int firFSM = 2
   local AlarmsHistory
   local string alarmOutputFile = "AlarmTest.txt"

   function newAlarm( string fullAlarmPath, string alarmDetails, string alarmRecipients)
	  private string alarmName = Parse(fullAlarmPath, 2, ".")
	  ? "Got alarms " + alarmName
	  private string alarmDescription
	  execute(" alarmDescription = " + fullAlarmPath + ".strDescription")

	  //? alarmDescription + " test_positive"

	  //update how many times the alarm fired via the AlarmHistory

	  //sound the alarm
	  beginseq(local.Sequence.SoundAlarm)
	  if(alarmPanelFsmState == greenState)
		 ? "got the cookie"   
	  endif
	  //panelNotify of alarm
	  alarmPanelFsm(firFSM)

	  //emailNotify of alarm
	  private string strAlertSub = "Buccaneer Alert : " + FormatDateTime("%c", SysTime() )+ " " + alarmName
	  EmailsHandler.sendEmail(alarmRecipients, strAlertSub, alarmDetails)

	  //save the alarm to debug file
	  FilesHandler.writeFile( alarmName, alarmOutputFile, alarmDetails, 0)
	  //local.Alarm.AckAllAlarms()
	  //local.Alarm.Ack(alarmName)
   endfunction

   function OnCreate()
	  ? "Creating an AlarmHandler"
	  greenState = 0
	  alarmPanelFsmState = greenState
	  if(alarmPanelFsmState == greenState)
		 ? "got the cookie"
	  endif

   endfunction

   function OnDestroy()
	  ? "... an AlarmHandler has been destroyed!"
   endfunction

endclass

So, when I run the sequence, my terminal outputs "Creating an AlarmHandler" but not "got the cookie". And when my FireALarm event calls the new alarm function, I get the error in the previous reply.

I know I'm doing something extremely stupid, but I can't figure out what it is. SOrry ot trouble you guys with this.

Link to comment
Share on other sites

This is why we haven't heavily promoted the OOP part of DAQFactory: the error messages can be misleading. It says "newalarm line 13, line 4". Well if you count out 13 lines into newalarm (skipping blanks), you get to the function call alarmPanelFsm(firFSM). You then have to go to the 4th line of alarmPanelFsm to get the line with the error.

Link to comment
Share on other sites

Argh, I have to stop using fast reply, I hit tab then type something and it sends. Just ignore all the code at the end of that last message (its copied from above)

What I'm curious about is:

class Fooo
   local int me = 0
   local int he = 0

   function gooo()

   if(he==me)
	  ? "got the cookie"
   end if

   endfunction

endclass

That should work right? Do I have to specify a "this." for example? I didn't see that in the example code, so I assumed it wasn't necessary

Link to comment
Share on other sites

Just to make it clear: even when commenting out the " alarmPanelFsm(firFSM)" bit and everything after where I believe the error point is, I get the error. If I comment out the error point, I get no errors.

The problem is in the way I am trying to compare two instance variables, and I'm hoping it's a lexical mistake.

Link to comment
Share on other sites

Sorry, I didn't look at your code close enough and I counted wrong. You are obviously a C programmer because you tried to type your variables. In DAQFactory there are only two types: numbers and strings. To declare a local number, its just:

local x

while a string is:

local string x

you put "int" in there, which actually just makes a member variable named "int". The fact that you essentially did this 8 or so times is ignored.

Link to comment
Share on other sites

No worries. I do stuff like that all the time. Its the perils of working in 6 different languages at the same time. Its made worse by the fact that all 6 (DAQFactory, DAQConnect, C++, Java, JavaScript and php) have somewhat similar syntax, but not exactly the same.

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.