If you have a problem or need to report a bug please email : support@dsprobotics.com
There are 3 sections to this support area:
DOWNLOADS: access to product manuals, support files and drivers
HELP & INFORMATION: tutorials and example files for learning or finding pre-made modules for your projects
USER FORUMS: meet with other users and exchange ideas, you can also get help and assistance here
NEW REGISTRATIONS - please contact us if you wish to register on the forum
Users are reminded of the forum rules they sign up to which prohibits any activity that violates any laws including posting material covered by copyright
a .fsm canvas that won't produce a "nil" error at startup
8 posts
• Page 1 of 1
a .fsm canvas that won't produce a "nil" error at startup
Each time I am designing a new .fsm, I get thrown into the "nil" errors ocean at startup. I remain under the impression that, due to the lack of a particular genuine hard-coded "Ruby startup module", all Ruby code modules get randomly queued at startup, hence generating many "nil" errors, because most Ruby variables got not yet declared and initialized, before some other Ruby modules got randomly executed.
Fortunately, Ruby being surprisingly robust, most "nil" errors that you may fear to cause infinite loops or hangups, actually remain harmful. What a nice surprise. I thus remain under the impression that in case of a "nil" error, the Ruby/Flowstone sequencer is marking the problematic Ruby module as requiring a complete re-execute some time later, not from where it faced the "nil" error, but from its very beginning. By "some time later", I mean "after 10 millisecond, or after all other Ruby modules that are one the queue, duly got called".
I'll appreciate, some expert to say yes/now about my perception.
Fortunately, Ruby being surprisingly robust, most "nil" errors that you may fear to cause infinite loops or hangups, actually remain harmful. What a nice surprise. I thus remain under the impression that in case of a "nil" error, the Ruby/Flowstone sequencer is marking the problematic Ruby module as requiring a complete re-execute some time later, not from where it faced the "nil" error, but from its very beginning. By "some time later", I mean "after 10 millisecond, or after all other Ruby modules that are one the queue, duly got called".
I'll appreciate, some expert to say yes/now about my perception.
- steph_tsf
- Posts: 249
- Joined: Sun Aug 15, 2010 10:26 pm
Re: a .fsm canvas that won't produce a "nil" error at startu
I remain under the impression that when you are 100% faulty like when writing a local, endless loop in Ruby, that the Ruby/Flowstone sequencer is taking notice that it unsuccessfully re-executed many times the same faulty module. In such severe situation, I observed and admitted the radical Ruby/Flowstone decision, consisting in stopping the Ruby interpreter, and marking the .fsm as potentially harmful for the computer, and applying such sticker to the .fsm in case you commit the error of saving it. You better don't save it. In case you commit the error of saving it, you'll find yourself in deep trouble. Let me explain why. In theory, the solution consists in opening and examining the most recently edited Ruby code module, then locating your faulty code (like a local endless loop), then correcting it. Unfortunately, all other Ruby code modules of your .fsm will remain marked as potentially harmful for the computer, like potentially causing a BSOD (Blue Screen Of Death), fatal system error, system crash. Consequently, you need to manually open all other Ruby codes modules, one by one. Thus, in case all your labels, buttons, simulated LEDs, selection boxes, etc. all emanate from Ruby code modules, you quickly realize that you are committing to manually locate, open and re-activate hundreds of Ruby modules. Do not follow such path. There is a more effective one. Write on a piece of paper, 1) the name of the most recent good .fsm, 2) why you wanted to tweak it, and 3) what you did as tweaks, just before experiencing the infamous "Ruby interpreter stopped". Then, permanently destroy the .fsm that triggered the infamous "Ruby interpreted stopped". Cool down, take a meal, go to sleep, and upon waking up, I guarantee you'll visualize the zone that's containing the fatal tweak that you did.
I'll appreciate, some expert to say yes/now about my perception.
I'll appreciate, some expert to say yes/now about my perception.
Last edited by steph_tsf on Mon Feb 03, 2020 9:38 am, edited 1 time in total.
- steph_tsf
- Posts: 249
- Joined: Sun Aug 15, 2010 10:26 pm
Re: a .fsm canvas that won't produce a "nil" error at startu
Anyway, I remain under the impression that one cannot tell Ruby/Flowstone, to execute a particular Ruby code module at startup. And, I remain under the impression that the Ruby/Flowstone experts that we know here, can live with that. IMO, they all rely one some canvas, proven to be robust. Can some expert, sketch such robust canvas, minimizing the probability of "nil" errors at startup? What is actually required, is a simple canvas that's putting Ruby/Flowstone in its comfort zone at startup. The aim of such canvas is to help Ruby/Flowstone, adding as soon as possible into its execution queue, the particular Ruby module that you wrote, that's taking care of the global variables declaration and initialization. I guess that such canvas can take advantage of some timer, green timer or Ruby timer.
Any advice about such matter will be greatly appreciated.
Any advice about such matter will be greatly appreciated.
- steph_tsf
- Posts: 249
- Joined: Sun Aug 15, 2010 10:26 pm
Re: a .fsm canvas that won't produce a "nil" error at startu
I am guessing you are talking about the Init function? That runs everything at startup.
- adamszabo
- Posts: 667
- Joined: Sun Jul 11, 2010 7:21 am
Re: a .fsm canvas that won't produce a "nil" error at startu
The way that the time-out propagates to other RubyEdits is certainly a PITA sometimes - a consequence of them all using the same single-threaded Ruby interpreter (so if one RubyEdit hogs the interpreter, none of the others can do anything either). FS 3.0.8+ was meant to fix this by making the 'time-out' separate for each RubyEdit; but unfortunately, the way that it was done introduced a bad memory-leak (actually a bug in Ruby itself), so it's not recommended to use it.
Besides your 'backing up' strategy, there is another technique that I'd recommend - to use "unit testing". Rather than modifying a critical module within the main schematic, open another one where you'll only be modifying and testing the Ruby for that single module. Without any chance of harm to your main schematic, you can then feed the module likely (and unlikely!) input values to see whether it behaves itself, or add code to validate input values before processing them any further. Regardless of Ruby 'time-outs', this is always a useful practice, as you can catch bugs early in a situation where you know exactly where they're coming from, rather than having to hunt through a whole schematic looking for the source of the problem.
The initialization order isn't random, but it certainly isn't easy to control (and there can be times when you need to). Modules, primitives, and RubyEdits are initialized in the order that you added them to the schematic. Where the item is a module, it works through all of the items inside that module before moving on (and so on recursively for deeper module nesting - a "depth first" traversal). There are no triggers, redraws, or Ruby 'events' until every item has been initialised.
So, if you need a particular startup order, you can arrange it by adding items to the schematic in the order that you want, or by using module nesting. To add an item "before" existing ones, you have to cut the "after" items to the clipboard, put in the "before" item, then paste the "after" items back in again (and then repair any links that this broke!) Very awkward, but it does work reliably.
When a RubyEdit is initialised, the following steps are performed in this order:
1 - The input connector and output connector values from the saved schematic are restored.
2 - The RubyEdit's 'init' method is called if it is defined.
3 - The 'loadState' method is called to restore any values explicitly stored by 'saveState' if they are defined.
All of these steps are done before moving on to the next RubyEdit, and happen for all RubyEdits before the trigger system, drawing, and Ruby events sheduler are started (i.e. before 'After Load'). Note especially that 'init' is called before 'loadState', so data that you explicitly store using 'saveState' isn't available during the 'init' method. Every time you edit the code, 'init' gets called again, but the other steps aren't repeated.
Ruby View inputs and MIDI inputs are a special case, as it makes no sense to store them between saves (a new View object is needed for every redraw). These inputs are always 'nil' until the first redraw or the first MIDI event is received, neither of which can happen until 'After Load' (there's always a global redraw 'After Load').
Besides your 'backing up' strategy, there is another technique that I'd recommend - to use "unit testing". Rather than modifying a critical module within the main schematic, open another one where you'll only be modifying and testing the Ruby for that single module. Without any chance of harm to your main schematic, you can then feed the module likely (and unlikely!) input values to see whether it behaves itself, or add code to validate input values before processing them any further. Regardless of Ruby 'time-outs', this is always a useful practice, as you can catch bugs early in a situation where you know exactly where they're coming from, rather than having to hunt through a whole schematic looking for the source of the problem.
steph_tsf wrote:Anyway, I remain under the impression that one cannot tell Ruby/Flowstone, to execute a particular Ruby code module at startup
The initialization order isn't random, but it certainly isn't easy to control (and there can be times when you need to). Modules, primitives, and RubyEdits are initialized in the order that you added them to the schematic. Where the item is a module, it works through all of the items inside that module before moving on (and so on recursively for deeper module nesting - a "depth first" traversal). There are no triggers, redraws, or Ruby 'events' until every item has been initialised.
So, if you need a particular startup order, you can arrange it by adding items to the schematic in the order that you want, or by using module nesting. To add an item "before" existing ones, you have to cut the "after" items to the clipboard, put in the "before" item, then paste the "after" items back in again (and then repair any links that this broke!) Very awkward, but it does work reliably.
When a RubyEdit is initialised, the following steps are performed in this order:
1 - The input connector and output connector values from the saved schematic are restored.
2 - The RubyEdit's 'init' method is called if it is defined.
3 - The 'loadState' method is called to restore any values explicitly stored by 'saveState' if they are defined.
All of these steps are done before moving on to the next RubyEdit, and happen for all RubyEdits before the trigger system, drawing, and Ruby events sheduler are started (i.e. before 'After Load'). Note especially that 'init' is called before 'loadState', so data that you explicitly store using 'saveState' isn't available during the 'init' method. Every time you edit the code, 'init' gets called again, but the other steps aren't repeated.
Ruby View inputs and MIDI inputs are a special case, as it makes no sense to store them between saves (a new View object is needed for every redraw). These inputs are always 'nil' until the first redraw or the first MIDI event is received, neither of which can happen until 'After Load' (there's always a global redraw 'After Load').
All schematics/modules I post are free for all to use - but a credit is always polite!
Don't stagnate, mutate to create!
Don't stagnate, mutate to create!
-
trogluddite - Posts: 1730
- Joined: Fri Oct 22, 2010 12:46 am
- Location: Yorkshire, UK
Re: a .fsm canvas that won't produce a "nil" error at startu
Just a little PS to the above with a couple of other tips that came to mind once I booted up FS for the evening...
- When I start a new schematic, and I've got the 'top-level' module in place that I'll be exporting, I go inside the 'top-level' and put an empty module inside it called something like "Init First" before I do anything else. So long as I then never move that module, it gives me somewhere to put any Ruby code that the rest of the export might need to have pre-loaded, e.g. definitions for any custom Ruby Classes and Modules. The 'depth first' startup traversal means that I then only have to worry about the ordering of a handful of things inside that module. [Well, I don't really do that - I usually just load one of my "new project" templates that has this already set up!]
- It is possible to use the 'output' method to send a value to a RubyEdit output during initialisation (i.e. in the 'init' method). However, all this does is to queue a Ruby 'event'. The 'event' won't actually get sent out of the connector until 'After Load', when the trigger system starts up. So you can't send values between components during initialisation, but you can 'pre-load' a value to be sent 'After Load'.
- If you're sharing Ruby values between RubyEdits using 'Ruby Value' connectors and links, be careful what you send. Anything that goes via a RubyEdit connector is stored when the schematic is saved, and re-loaded at startup. However, this relies on a way of storing Ruby values which not all objects are able to use - and this includes several of the FS built-in classes such as Brushes and GraphicsPaths. You'll know that you have this problem if you see an error message that mentions "Marshal", "dump", "load", or similar.
- One of the reasons that you can end up with lots of 'nils' is that there's an error in a RubyEdit during initialisation or a 'saveState' method has failed. This can prevent the input connector values from being restored correctly or instance variables from being set, so they all end up being 'nil' (as for all FS components, startup values are read from the schematic file, not through links to other components). This is why a Ruby "time-out" can mess things up so badly. A RubyEdit can often be recovered from this state by giving each input connector a click to refresh its input value, then making a trivial edit to the code (add and delete a space, say) to force 'init' to be executed again.
- Whenever a RubyEdit successfully handles an event, it clears any errors from the RubyEdit watch panel. This can be very annoying for intermittent errors or errors at startup, as the error message can get cleared before you have a chance to read it - you just see the module border flash red briefly. You can get around this by "rescuing" the error and displaying the error message using 'watch'. For example, here's an 'event' method with error trapping and more detailed reporting than FS normally shows...
The exact same 'rescue block' will work at the end of any of a RubyEdit's methods, and you can clear the watch window by making any trivial edit to the code. I find it particularly handy for debugging mouse problems, as its not unusual for the various mouse methods to clear each other's errors (you can't go look for a mouse drag bug without letting go of the mouse'!)
- When I start a new schematic, and I've got the 'top-level' module in place that I'll be exporting, I go inside the 'top-level' and put an empty module inside it called something like "Init First" before I do anything else. So long as I then never move that module, it gives me somewhere to put any Ruby code that the rest of the export might need to have pre-loaded, e.g. definitions for any custom Ruby Classes and Modules. The 'depth first' startup traversal means that I then only have to worry about the ordering of a handful of things inside that module. [Well, I don't really do that - I usually just load one of my "new project" templates that has this already set up!]
- It is possible to use the 'output' method to send a value to a RubyEdit output during initialisation (i.e. in the 'init' method). However, all this does is to queue a Ruby 'event'. The 'event' won't actually get sent out of the connector until 'After Load', when the trigger system starts up. So you can't send values between components during initialisation, but you can 'pre-load' a value to be sent 'After Load'.
- If you're sharing Ruby values between RubyEdits using 'Ruby Value' connectors and links, be careful what you send. Anything that goes via a RubyEdit connector is stored when the schematic is saved, and re-loaded at startup. However, this relies on a way of storing Ruby values which not all objects are able to use - and this includes several of the FS built-in classes such as Brushes and GraphicsPaths. You'll know that you have this problem if you see an error message that mentions "Marshal", "dump", "load", or similar.
- One of the reasons that you can end up with lots of 'nils' is that there's an error in a RubyEdit during initialisation or a 'saveState' method has failed. This can prevent the input connector values from being restored correctly or instance variables from being set, so they all end up being 'nil' (as for all FS components, startup values are read from the schematic file, not through links to other components). This is why a Ruby "time-out" can mess things up so badly. A RubyEdit can often be recovered from this state by giving each input connector a click to refresh its input value, then making a trivial edit to the code (add and delete a space, say) to force 'init' to be executed again.
- Whenever a RubyEdit successfully handles an event, it clears any errors from the RubyEdit watch panel. This can be very annoying for intermittent errors or errors at startup, as the error message can get cleared before you have a chance to read it - you just see the module border flash red briefly. You can get around this by "rescuing" the error and displaying the error message using 'watch'. For example, here's an 'event' method with error trapping and more detailed reporting than FS normally shows...
- Code: Select all
def event(input_id, value, time_stamp)
# Your 'event' code here,
rescue StandardError => error
watch "ERROR", "#{error.class}: #{error.message}"
# Add the following line to find out if a nested method caused the error.
watch "TRACE", error.backtrace.join("\r\n")
# Repeat the error so that FS still sees it.
raise(error)
end
The exact same 'rescue block' will work at the end of any of a RubyEdit's methods, and you can clear the watch window by making any trivial edit to the code. I find it particularly handy for debugging mouse problems, as its not unusual for the various mouse methods to clear each other's errors (you can't go look for a mouse drag bug without letting go of the mouse'!)
All schematics/modules I post are free for all to use - but a credit is always polite!
Don't stagnate, mutate to create!
Don't stagnate, mutate to create!
-
trogluddite - Posts: 1730
- Joined: Fri Oct 22, 2010 12:46 am
- Location: Yorkshire, UK
Re: a .fsm canvas that won't produce a "nil" error at startu
PPS) And my apologies to anyone whom I've confused by repeatedly editing the above posts!
All schematics/modules I post are free for all to use - but a credit is always polite!
Don't stagnate, mutate to create!
Don't stagnate, mutate to create!
-
trogluddite - Posts: 1730
- Joined: Fri Oct 22, 2010 12:46 am
- Location: Yorkshire, UK
Re: a .fsm canvas that won't produce a "nil" error at startu
Dammit, I can't stop now (there must be some chores that I'm sub-consciously avoiding doing! )...
- You can protect code against 'nil' quite easily by using Ruby's logical-or operator ('||'). In Ruby, nil and false both count as being boolean false, and absolutely anything else counts as boolean true. Because of this, Ruby's logic operators don't always return true or false, they save a CPU cycle or two by returning one of their arguments instead. And a particularly useful example is this one:
It makes no difference to things like 'if' statements that it does this, because the returned object still counts as having the right boolean logical-true or logical-false value. The or-equals operator ('||=') takes this a step further; if a variable on the left is nil or false, it gets assigned the value on the right; if it has any other value, it keeps it.
These can be very handy for dealing with values which might be 'nil', and can be used to initialise variables for which this isn't possible in 'init' for whatever reason. However, don't use them for boolean variables, as 'false' is then a legal value, which looks the same as 'nil' as far as logical-or is concerned. In that case, it's best to test explicitly....
BTW, these also are quite handy inside the 'init' method sometimes. You might not always want every single code edit to reset all of the variable values because of the editing calling 'init'. If you use one of these "defaulting" techniques instead, they'll only get modified during the startup call to 'init', and will be left alone if 'init' gets called again.
- You can protect code against 'nil' quite easily by using Ruby's logical-or operator ('||'). In Ruby, nil and false both count as being boolean false, and absolutely anything else counts as boolean true. Because of this, Ruby's logic operators don't always return true or false, they save a CPU cycle or two by returning one of their arguments instead. And a particularly useful example is this one:
- Code: Select all
"value" || "default_value" #=> Returns "value"
nil || "default_value" #=> Returns "default_value"
false || "default_value" #=> Returns "default_value"
It makes no difference to things like 'if' statements that it does this, because the returned object still counts as having the right boolean logical-true or logical-false value. The or-equals operator ('||=') takes this a step further; if a variable on the left is nil or false, it gets assigned the value on the right; if it has any other value, it keeps it.
- Code: Select all
# Set @my_variable to "default_value" only if it is nil or false.
@my_variable ||= "default_value"
These can be very handy for dealing with values which might be 'nil', and can be used to initialise variables for which this isn't possible in 'init' for whatever reason. However, don't use them for boolean variables, as 'false' is then a legal value, which looks the same as 'nil' as far as logical-or is concerned. In that case, it's best to test explicitly....
- Code: Select all
@my_variable = "default_value" if @my_variable.nil?
# Or...
@my_variable = "default_value" unless defined?(@my_variable)
BTW, these also are quite handy inside the 'init' method sometimes. You might not always want every single code edit to reset all of the variable values because of the editing calling 'init'. If you use one of these "defaulting" techniques instead, they'll only get modified during the startup call to 'init', and will be left alone if 'init' gets called again.
All schematics/modules I post are free for all to use - but a credit is always polite!
Don't stagnate, mutate to create!
Don't stagnate, mutate to create!
-
trogluddite - Posts: 1730
- Joined: Fri Oct 22, 2010 12:46 am
- Location: Yorkshire, UK
8 posts
• Page 1 of 1
Who is online
Users browsing this forum: No registered users and 128 guests