Parent/Child relationship question
jcwren
Posts: 44
Here's my scenario: The top level cog, Scrooge, gets launched and in turn launches 3 additional cogs, Huey, Dewey and Louie. Although all three are launched, only one is active at any time. However, there's a common resource they all want to use when they're active, called Quack. When Huey is active, he may need to Quack. When Dewey is active, he made need to Quack, and the same for Louie.
In C, for example, I'd create Quack first, then as I launched Huey, Dewey, and Louie, I'd pass a pointer to Quack, and when they need to use it, they'd simply (*quack) (parameter1, parameter2, etc).
As near as I can tell in Spin, there's no way to do something similar? I can have Huey, Dewey and Lewey each launch Quack when they become active, and stop it when they go inactive, but this seems less efficient.
So is there a way to pass an object created at the top level to a child process for it to use? If not, other than launching and aborting Quack as needed, any suggestions?
--jc
In C, for example, I'd create Quack first, then as I launched Huey, Dewey, and Louie, I'd pass a pointer to Quack, and when they need to use it, they'd simply (*quack) (parameter1, parameter2, etc).
As near as I can tell in Spin, there's no way to do something similar? I can have Huey, Dewey and Lewey each launch Quack when they become active, and stop it when they go inactive, but this seems less efficient.
So is there a way to pass an object created at the top level to a child process for it to use? If not, other than launching and aborting Quack as needed, any suggestions?
--jc
Comments
You have full access to any HUB-RAM. So, you could use a buffer as communication buffer between Huey, Louie, Dewey and Quack. Quack is watching the buffer to see if he has to do something. Huey, Louie and Dewey simply put the parameters to this buffer.
You can't create objects and pass it to child processes. But you can use the same buffers and send messages. Either you hardcode the buffer address in all modules or you pass it as parameter to all childs.
I guess I don't understand why you're organizing things this way. Since Huey, Dewey, and Louie never execute at the same time, why do they have to be in separate cogs?
If there's a good reason for the separate cogs (like when Huey, Dewey, and Louie pause for the others to act, they're way in the middle of something, so you're using the cogs to hold the state of the processes), then you could start up Quack in its own cog and have it wait for parameters to be placed in shared memory. Typically you would have one long with a flag in it. If false, Quack is idle waiting for data. You can store the other parameters in adjacent locations whenever it's idle. The last item you'd set is to make the flag a -1. Quack would see that and process the data. When it's done, it would set the flag back to zero (false).
If Huey, Dewey, and Louie are Spin cogs in the same object as Scrooge, along with Quack, they can simply call Quack. Or, if Quack is in a separate object, that object can be declared in an OBJ section, and each nephew can call objref.Quack.
If Huey, Dewey, and Louie are separate Spin objects, each can declare the object containing Quack (not Scrooge, though) and call objref.Quack. The code and DAT variables in Quack's object are not replicated for each of the nephews, only the VAR variables.
If the nephews are PASM cogs and/or if Quack is a PASM cog, then as others have indicated, Quack will need to monitor a hub variable to know when to quack.
-Phil
Cripes! That's got to be confusing to a newcomer. I wish there were a better way to explain it.
For the record, here's the real application. I have 3 amateur radios in my car, a Kenwood TK-931HD 900Mhz radio, an Alinco DR-235T 220Mhz radio, and an Icom IC-208H 2m/70cm. This also means there are three microphones, and I find that to be awkward and space consuming (small car). Also, the Kenwood microphone is a commercial job. Very heavy, very stout. I refer to it as a "weaponized microphone". You could easily beat someone to death with it. It also doesn't have a DTMF pad, which is handy for controlling the repeater.
My solution to this is to design a microphone consolidator. The microphone for the Icom radio is called an HM-133. It has a bunch of handy buttons for controlling the radio. It does this via a digital interface. Unlike older radios, when DTMF is sent, the microphone just sends a "DTMF key 'x' pressed" message to the radio, and the radio actually synthesizes the DTMF. Unfortunately, the Alinco and the Kenwood don't. They generate the DTMF in the microphone and ship down as audio, muting the mic element in the process, so that voice isn't mixed with the DTMF.
Each radio has it's own cog that manages it. These are all started when the system first comes up. A cog is also started that listens and decodes the data stream from the HM-133. These keys are passed up to cog 0 (the application). As keys come in from the HM-133, they're first checked to see if it's a meta-key (F2, one of two function keys on the mic) that's used to determine which radio the mic is "connected" to. When F2 is seen, the next 1, 2, or 3 key selects which radio the microphone is connected to. If the key is not needed, it's passed on to the currently selected radio.
In the case of the IC-208H, it's merely repeated out, since the mic is native to the radio.
If the DR-235T is selected (F2, then 2), then received keys are passed to the DR-235T cog who remaps what keys it can. Some keys on the mic aren't applicable, since the radio doesn't support equivalent functions. If it's a DTMF key, the DTMF generator should start generating the appropriate tone. The exact same is true for the TK-931 cog, except if uses F2, '3' to be selected.
Even when the mic is not "connected" to a particular radio, that cog is still doing some monitoring and statistics collections. It just means that since the mic isn't connected to it, it'll never need to generated a DTMF key at that point. Only the currently selected radio needs DTMF generated, and only then if it's not the IC-208H.
For the cleanest interface, the idea was to have the DTMF method be self-contained. The DR-235T cog doesn't want to worry about knowing that a '1' key is a 697Hz tone and a 1209Hz tone. It just wants to pass a '1' to the DTMF method and have it done.
Now here's where you can correct me if I'm wrong. If I have the top level cog (the application cog) create the DTMF object, the other cogs cannot call methods in the parent object (right?). This in effect would amount to a callback, which SPIN doesn't support. I can pass a pointer to a DTMF control buffer as each radio's cog is created, but then it requires each radio cog to know the structure of the buffer (however simple or complex it may be), and while workable, it's not clean. Or lastly (and this is what looks like is the cleanest interface), as each radio's cog is selected (the microphone "connected" to it), it can start the DTMF cog, and treat as a proper object, using the DTMF objects methods to remap '1' to 697Hz/1209Hz, put things in buffers, etc. It will, of course, release the DTMF cog when it's deselected.
Nothing in the last area is particularly time critical. We're dealing with human reaction times here, and as I select a radio by pressing F2 then '2', there's going to be at least another second before I'd be pressing a key to send DTMF. I.e., more than enough time to stop the DTMF cog assigned to the TK-931, and restart it assigned to the DR-235T.
Just theorizing here: I suppose it's possible for the application cog to start the DTMF cog, and pass a control buffer pointer to each radio cog. Each radio object (cog) can then create a DTMF object, but never call the method that actually starts it into its own cog. The methods in the DTMF object could accept a buffer pointer, and manipulate it as necessary, isolating the radio object from knowing the internals of the DTMF object. The only thing I'd need to keep aware of is not using any of the elements in the VAR section of the DTMF object.
I am using cogs and objects slightly interchangeably here. The main purpose of that was to indicate what was off running it's own cog as opposed to just being an object in the top level application.
your understanding of cogs and objects is wrong.
ONE object can use 1-8 cogs. And 1-8 cogs can share 1-64 methods (don't know if 64 is the exact limit but just want to say 1-n methods)
Now I would like to describe the things as code-segments. Because you can NOT say app-cog as the part of the code that you call "application" can use one cog or three cogs or eight cogs
You have the following code-segments
code-segment 1: toplevel app-segment
code-segment 2: TK-931-segment
code-segment 3: DR-235-segment
code-segment 4: IC-208H-segment
Now if I understand right
the app-segment is connected to the HM-133-microphone
the app-segment receives digital data from the HM-133-microphone and has to process the digital data
If I understand right this is a onedirection-flow of data
HM-133--->Propeller
>DR-235
OR
HM-133--->Propeller
>TK-931
OR
HM-133--->Propeller
>IC-208H
Am I right if I assume that NO data or any signals go into the opposite direction?
If this is the case ONE cog is enough! And using just one cog has the advantage that everything is processed sequential WITHOUT using busy-flags.
If you start different cogs each waiting for commands to be processed you have to use busy-flags to make sure that things are not processed cross-over.
Things become different if you are a microphone and radio-amateur artist that changes a channel on radio-A, quickly changes to radio-B saying a short message to person B, quickly changes to radio-C
changing the channel while listining to the answer of person B on radio-B. Saying a sort message to person C on radio-C and then calling person A on radio-A
If it is Ok for you that you have to wait until a DTMF-sequence has been transmitted before the next mic-command will be processed you only need a command-buffer in the app-segment
that stores commands (=stores keystrokes on the mic)
If you want to keep the code clean you create three objects.
the TK_931-object
the DR_235-object
and the IC_208H-object
then you "connect" these three objects in the app-segment
with this code you would have to wait until the keystroke-processing has finsihed before the app-segment can listen again to the Mic
If you want to go on with a second sequence or with stroke-sequences that have different numbers of keystrokes
you would start only ONE more cog that listens to the Mic all the time storing the keystrokes to a buffer and the app-segment reads out the buffer
where the method "Receive_HM_133_commands" continously listens to the Mic and stores the strokes into a FIFO-buffer
the method "Read_MicStrokeBuffer" reads the keystroke-buffer and the case structure directs the strokes to the right radio
this is a raw concept that needs to be corrected for things like you don't want to type F2 for every command but the basic structure how many cogs you need and which cog is doing what stays the same
once again:
one *-SPIN-file can start 1 to 7 additional cogs.
If the commands "cognew" are inside the SAME *.spin-file all methods and all cogs have access to all global variables defined in the VAR-section
This means every method from the same *.spin-file has access to all other methods regardless which cog calls the method
every cog that is launched from the same *.spin-file has access to all variables in the same *.spin-file
If you want to have access to variables across OBJECTS (= another *.spin-file) you need to define methods get_value and set_value INSIDE the object so you can do a
myVar := objref.get_value and objref.set_value(MyVar)
best regards
Stefan
Your latter diagram is, in effect, what I have. Except that each radio object (TK931, DR235, IC208) is run in it's own cog. There are methods provided for the parent object to pass in messages. These are run in individual cogs because there's some background processing for each radio that's constantly done. Checking if there's a new message from the parent is just part of that.
However, this wasn't the question I was asking. First, the currently selected radio remains selected until the next 'F2' 'n' is seen. That's just a clarification, and not really relevant.
The issue is that as a radio is selected, it wants the DTMF generator assigned to it. If the app-segment (as you called it) creates the DTMF object (let's call it 'dtmf'), there's no way for the DR235, IC208 or TK931 object to call 'dtmf.MakeTone', because child objects don't have access to the parents methods. The best you could hope to do in that case is to provide a method called 'PleaseSendDTMF' in each of the radio objects, and have the parent object call the method in the currently selected radio constantly to see if there's anything to send to the DTMF object.
Alternatively, each radio object can have a 'dtmf : "DTMF"' object. As each radio object is first selected (when the 'F2' '1' arrives, let's say we're selecting the TK931), the app-segment can call DR235.Deselect and IC208.Deselect, then TK931.Select. The 'Deselect' methods call 'dtmf.Stop', and if it was running, it does a cogstop(). If not, nothing happens. The 'Select' method calls 'dtmf.Start', and starts the DTMF generator in a new cog. Later, if any actual DTMF keys come from the app-segment, the radio object calls 'dtmf.StartTone (key)' and the tone plays until 'dtmf.StopTone' is called.
The disadvantages to the first technique is that it requires the parent to shuffle data and adds latency between putting the mic key into the radio objects buffer and noticing there's a message for a DTMF request (the app-segment could (theoretically) be doing something else, like processing a command from a serial port. The downside to the second method is that each time the radio is selected, there's the shutdown and start-up time of managing the DTMF cog when the radio object 'Select' method is called.
Somewhere I saw a post that indicated what the start-up time for a cognew() is. I can't seem to find that again. Any recollection what the approximately time can be?
--jc
VAR images are copied per instance of each object.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Nyamekye,
Nevertheless I want to suggest again to do it sequentially. If it really works sequentially depends on how fast the backroundprocessing has to be done.
For example if you have low-high-low pulses that are VERY short of course you have to dedicate an extra cog to detect these pulses properly.
How fast does it has to be in the worst case and how long does a complete loop take?
If you keep ALL methods in ONE spin-file EVERY method regardless of which cog is calling the method has access to any other method and any variable.
Maybe you believe me if a post a democode here
if you run this code you should see an output in PST.exe similar to this
If the baclroundprocessing has to be done continously you have to implement a flag like "dtmf_in_use" to avoid crossing access to the dtmf-method
If You want to have only ONE DTMF-generator that changes his connection you have to use a busy-flag regardless of which version you use to realise that.
If you start a DTMF-cog each time you need it you have to take care that it isn't still in use by a former command. It is the same if you let the dtmf-cog run all the time.
Whenever two DTMF-activities came across the signals get messed up
If you define a DTMF-object in its own *.spin-file you could add a DTMF_Busy-method that gives back true or false
First thing that the send_dtmf-method does is to set the flag DTMF_busy to true and last thing it does is to set the flag DTMF_busy to false
the method that wants to call send_dtmf has to wait
If you can't wait all the time code a repeat-loop
Maybe the logic could be done more elegant. If so I invite everybody to post the more elegant code
best regards
Stefan
Post Edited (StefanL38) : 1/31/2010 4:40:09 PM GMT
http://forums.parallax.com/showthread.php?p=777920
Scroll down to "simpler example".