Welcome to %s forums

BrainModular Users Forum

Login Register

Need information in "how is a user module handled in async situations"

Create your own modules in C++
Post Reply
hesspet
Member
Posts: 92
Location: Germany
Contact:

Unread post by hesspet » 09 Dec 2012, 01:40

Im working on a serial module for Usine

First draft for serial is ready and - let's say - it works in most situations:P. But I'm a little bit confused about a reaction/error. I'm not sure if this is a side effect or by design of usine. Maybe someone can help me out.

I'm testing with this patch:

Image

Simple - I create a counter and send the created number to the serial interface.

This works perfect until this moment the clock is to fast (invervall <50ms). Then I loose call to the callback() method.. I traced out the numbers, usine delivers the correct numbers but the callback has gaps.

Here's the tracelog:

Code: Select all

&#91;8638&#93; &#91;CallBack&#40;&#41;&#40;&#41;&#93; vor do_write dataout =  => .21151
&#91;8639&#93; &#91;do_write&#40;&#41;&#40;&#41;&#93; .21151
&#91;8640&#93; &#91;write_start&#40;&#41;&#40;&#41;&#93; len stream buffer => 6
&#91;8641&#93; &#91;write_start&#40;&#41;&#40;&#41;&#93; iTxDataLen => 6
&#91;8642&#93; &#91;CallBack&#40;&#41;&#40;&#41;&#93; nach do_write dataout =  => .21151
&#91;8643&#93; data out&#91;6&#93;46 50 49 49 53 50 
&#91;8644&#93; data out&#91;6&#93;46 50 49 49 53 51 
&#91;8645&#93; data out&#91;6&#93;46 50 49 49 53 52 
&#91;8646&#93; data out&#91;6&#93;46 50 49 49 53 53 
&#91;8647&#93; &#91;CallBack&#40;&#41;&#40;&#41;&#93; vor do_write dataout =  => .21155
&#91;8648&#93; &#91;do_write&#40;&#41;&#40;&#41;&#93; .21155
&#91;8649&#93; &#91;write_start&#40;&#41;&#40;&#41;&#93; len stream buffer => 6
&#91;8650&#93; &#91;write_start&#40;&#41;&#40;&#41;&#93; iTxDataLen => 6
&#91;8651&#93; &#91;CallBack&#40;&#41;&#40;&#41;&#93; nach do_write dataout =  => .21155
&#91;8652&#93; data out&#91;6&#93;46 50 49 49 53 54 
&#91;8653&#93; data out&#91;6&#93;46 50 49 49 53 55 
&#91;8654&#93; data out&#91;6&#93;46 50 49 49 53 56 
&#91;8655&#93; data out&#91;6&#93;46 50 49 49 53 57 
&#91;8656&#93; &#91;CallBack&#40;&#41;&#40;&#41;&#93; vor do_write dataout =  => 5
You can see Usine creates 4 numbers and at the 4th number my callback is "called". This is the reason why I loose numbers. It looks like that usine does not call the callback because ????

this line: [8647] [CallBack()()] vor do_write dataout = => .21155
callback begin

and this line: [8651] [CallBack()()] nach do_write dataout = => .21155
call back end.

So when the next number is traced by Usine, no code of the user module is running. This drives me crazy. When I slow down the counter everything works fine until the point < 50ms, then for unknown reason, my callback isn't called not so often.

Maybe I must do the handling in the process() method? The serial buffer is handled in a asynchronous process, so this can't be a problem of a blocking IO.

Any ideas :-)

This is how the callback is implemented:

Code: Select all

void CallBack &#40;void* pModule, TMessage* Message&#41; 
&#123; 
	SerialOutModule* pSerialOutModule = &#40;&#40;SerialOutModule*&#41;pModule&#41;;

	try &#123;

		switch&#40;Message->wParam&#41; &#123;
		case PARAM_PORT&#58;	
                        ...
			break;

		case PARAM_BAUDRATE&#58;	
                        ....
			break;

		case PARAM_DATAOUT&#58;	

			// if the data changes let's read the data
			if &#40;Message->lParam == MSG_CHANGE&#41; 
			&#123;	
				std&#58;&#58;string strDataOut = pSerialOutModule->GetEvtPChar&#40; pSerialOutModule->m_pevtDataOut &#41;;
				pSerialOutModule->trace&#40;"CallBack&#40;&#41;", "vor do_write dataout = ", &#40;PCHAR&#41;strDataOut.c_str&#40;&#41;&#41;;
				if &#40;pSerialOutModule->m_p_SerialBasics != NULL &#41; &#123;
					try &#123;
						pSerialOutModule->m_p_SerialBasics->do_write&#40;strDataOut&#41;;
					&#125;
					catch&#40;exception& e&#41; 
					&#123;
						pSerialOutModule->trace&#40;"CallBack&#40;&#41;","exception=", &#40;PCHAR&#41;e.what&#40;&#41;&#41;;
					&#125;
					pSerialOutModule->trace&#40;"CallBack&#40;&#41;", "nach do_write dataout = ", &#40;PCHAR&#41;strDataOut.c_str&#40;&#41;&#41;;
				&#125;
			&#125;
                        break;
		default&#58;
			// do nothing
			break;
		&#125;
	&#125; catch&#40; exception& e&#41; &#123;
		if &#40;pSerialOutModule != NULL&#41;
			pSerialOutModule->trace&#40;"CallBack&#40;&#41;","exception=", &#40;PCHAR&#41;e.what&#40;&#41;&#41;;
	&#125;
&#125;
Peter

Added:---------------------
I've tested the callback with this line commented:

pSerialOutModule->m_p_SerialBasics->do_write(strDataOut);

same result. If do_write is not called, no serial handling is done. Looks like a Usine behaviour. Maybe it's by design?
+++ journeytounknownsoundscapes.blogspot.de +++
+++ DIY electronics +++ musical experiments +++ light experiments +++

hesspet
Member
Posts: 92
Location: Germany
Contact:

Unread post by hesspet » 09 Dec 2012, 12:06

Tomatos on my eyes :-)

I've found the reason.

This is the difference between:

pParamInfo->CallBackType = ctNormal;

and

pParamInfo->CallBackType = ctImmediate;


pParamInfo->CallBackType = ctNormal;
Usine calls you when it have enough time. So in high pressure situations you may get an delay (like my problem :-))

pParamInfo->CallBackType = ctImmediate;
Usine calls you every time your value changes. Instantly. This was what I want .....

Peter, now happy!
+++ journeytounknownsoundscapes.blogspot.de +++
+++ DIY electronics +++ musical experiments +++ light experiments +++

martignasse
Site Admin
Posts: 611
Location: Lyon, FRANCE
Contact:

Unread post by martignasse » 09 Dec 2012, 12:07

hi hesspet,

cool to see you already have some result :)

so, i'll try to understand how your module work :

- you have some async code (working thread ?) to write to the serial line.
- when data change on the inlet 'data out', the callback copy those data (with m_p_SerialBasics->do_write(strDataOut)) to some buffer handled by the async code who deal with the bauderate and the serial line.

and if you refresh data to often, some callback are just ignored ?


ok, first thing to check :

in the GetParamInfo(), for the ParamInfo struct of the 'data out' inlet, make sure to have a line like this :

Code: Select all

pParamInfo->CallBackType    = ctImmediate;
it will ensure that the callback will be called immediately and every time the 'data out' change
(for more info about that, search for 'ctImmediate' in the UsineDefinitions.h file)

let me know if that make it
Martin FLEURENT - Usine Developer - SDK maintainer

hesspet
Member
Posts: 92
Location: Germany
Contact:

Unread post by hesspet » 09 Dec 2012, 12:32

Hello martignasse!

Thanks for you reply.

Yes, was late in the night :-)

If forgot the idea of the TFastCallBackType. Using ctNormal was the mistake, I set up this value at the beginning of the project and didn't realize that this was the critical fact. Now it works perfect. I didn't understand the callback handling of Usine at all. After a little bit sleep it's now clear.

I wrote a blog about the TFastCallBackType. Maybe this helps the next developer not to run into the pitfall.

http://journeytounknownsoundscapes.blog ... ing_9.html

Keywords: ctNormal, ctImmediate, ctAsynchronous

The communication between the background thread and the callback is fully covered by a asynchronous handlig. As I wrote, I've removed all my serial code and tested it again and the "problem" occurs again. So I was sure that it has nothing to do with the thread synchronisation. It was a classical beginners problem :-)
+++ journeytounknownsoundscapes.blogspot.de +++
+++ DIY electronics +++ musical experiments +++ light experiments +++

martignasse
Site Admin
Posts: 611
Location: Lyon, FRANCE
Contact:

Unread post by martignasse » 09 Dec 2012, 13:12

great, can't wait to test your module
my arduino is hot and ready :cool:

technically speaking, having three callback mode is one of the killer feature of usine, it make possible the high level of modularity with all the different kind of data we have to deal with inside a module.

ctNormal :
Usine inject events in the windows event loop at the 'refresh speed rate' chosen in the global setup.
But to not flood it, all similar/duplicated event are deleted
that's what you experienced i think, you should have a 'refresh speed rate' set to 50 ms ;)
it's the default choice.

ctImmediate :
just call immediately the concerned callback when the event occur, thus, no event loop involved.
it's the most powerful/accurate way to have params feedback.
But the price is : all the callback code is executed in the audio thread, so memory allocation or heavy process are the devil here

ctAsynchronous :
Usine event are injected in a complete separate thread event loop
it's not fast, it's asynchronous but ideal, for example, to an open file popup or to write to a file.
Martin FLEURENT - Usine Developer - SDK maintainer

hesspet
Member
Posts: 92
Location: Germany
Contact:

Unread post by hesspet » 09 Dec 2012, 13:46

Hi!

You wrote:
ctAsynchronous :
Usine event are injected in a complete separate thread event loop
it's not fast, it's asynchronous but ideal, for example, to an open file popup or to write to a file.
Maybe this could also be an alternative for sending slow data, e.g. to an arduino. Depends on what should be solved with an Arduino. If the Arduino is driven with 9600 baud e.g. to send a command to switch on a led or a motor, it's so slow that this mode is ok.

I'm thinking about making 2 different modules. One with the immediate mode and one with the asynchrounous mode. This "high performance" sending of serial information is not needed for every day usages of an Arduino. But for applications like a LED VU meter, or a beat to "whatever" transmission the latency must be reduced to "near zero".
great, can't wait to test your module
my arduino is hot and ready
The raw output module works. Should I post it here as a "alpha" version? :-) Testing serial is allways a little bit difficult, so feedback may help to make it bulletprove. I used the boost libary als underlying framework and I had a lot of problems to solve, but this may help to port the serial module to the upcomming new Usine (mac) version.

Peter
+++ journeytounknownsoundscapes.blogspot.de +++
+++ DIY electronics +++ musical experiments +++ light experiments +++

martignasse
Site Admin
Posts: 611
Location: Lyon, FRANCE
Contact:

Unread post by martignasse » 09 Dec 2012, 14:35

hesspet wrote:I'm thinking about making 2 different modules. One with the immediate mode and one with the asynchrounous mode. This "high performance" sending of serial information is not needed for every day usages of an Arduino. But for applications like a LED VU meter, or a beat to "whatever" transmission the latency must be reduced to "near zero".
well, i don't know if two mode are really needed, depends on lots of things
KISS approach here (keep it simple and smart )
first step, make it work with immediate mode
second step, test it a lot
third step, see if it's enough or if need some modifications

in any case, if two mode are needed, the query system is better than two module, it let you ask the user for some options before params are created.
The raw output module works. Should I post it here as a "alpha" version? :-) Testing serial is allways a little bit difficult, so feedback may help to make it bulletprove.
yep, it's a good way to have feedback and in this area, more is better.
personally, i'm quite busy and i'll can't do deep testing, but i'll try it for sure.
you could upload it in the add-ons section.
I used the boost libary als underlying framework and I had a lot of problems to solve, but this may help to port the serial module to the upcomming new Usine (mac) version.
seem's the good choice for Hollyhock and mac compatibility.
what kind of problems have you to solve with boost ?

keep up the good work
Martin FLEURENT - Usine Developer - SDK maintainer

headphoner
Member
Posts: 225
Contact:

Unread post by headphoner » 09 Dec 2012, 15:00

Hi,


I can't help you for the code but i can test the module, i've got 3 arduinos

hesspet
Member
Posts: 92
Location: Germany
Contact:

Unread post by hesspet » 09 Dec 2012, 15:10

seem's the good choice for Hollyhock and mac compatibility.
what kind of problems have you to solve with boost ?
The paradigm of the boost asio is sometimes a little bit confusing and the documentation is so fragmented. You'll find a lot of posts around, but often the info is misleading or "bad programming" :-).

And... I've stopped programming C++ in 1998 (without STL) and there are so many new things, I must learn to understand the ideas of boost. Now I see a little bit more how the async handling in boost works and how we can use it inside of a Usine module.

There is a lot of magic in the boost::asio lib!
BTW: boost::asio has nothing to do with the ASIO used in audio applications :-)

Code: Select all

	SerialBasics&#40;const char *dev_name, TUserModule* pTUserModule&#41; &#58; 
		m_io_service&#40;&#41;,                          // build instance of IO_SERVICE
		m_serial_port&#40;m_io_service, dev_name&#41;,   // initialize the COM Port
		m_work&#40;m_io_service&#41;                     // bind IO_SERIVCE to a worker thread pattern, so the run&#40;&#41; method is repeated endless 
	  &#123;
		  // must be set before any trace output! No tracing before this point!
		  m_p_user_module = pTUserModule; 

		  // start the endless running worker thread to process the async data.
		  boost&#58;&#58;thread t&#40;boost&#58;&#58;bind&#40;&boost&#58;&#58;asio&#58;&#58;io_service&#58;&#58;run, &m_io_service&#41;&#41;;

		  m_tWorker = &t;

		  trace&#40;"SerialBasics","constructed"&#41;;
	  &#125;;

	  void runner&#40;&#41; &#123;
		  trace&#40;"SerialBasics","before run"&#41;;
		  m_io_service.run&#40;&#41;;
		  trace&#40;"SerialBasics","after run"&#41;;
	  &#125;
This weaves everything together. Looks pretty simple but the things happening under the hood are strange. If this initialisation is done, the motor (thread) which does a endless repeating of the run (the method which is a blocking call to a sort of a socket processing) ist started. And then - if everything is pretty initialized the sending of the serial data is simple:

Code: Select all

/*
	Tries to send all async data available in the streambuffer.
	*/
	inline void write_start&#40;void&#41;
	&#123;

		// compute the data length to send in the stream
		int iTxDataLen = prepareSendBuffer&#40;&#41;;

		trace&#40;"write_start&#40;&#41;","iTxDataLen", iTxDataLen&#41;;

		// try to send data much as possible

		boost&#58;&#58;asio&#58;&#58;async_write&#40;
			m_serial_port,
			boost&#58;&#58;asio&#58;&#58;buffer&#40;m_TxBuffer, iTxDataLen &#41;,//m_buff,
			boost&#58;&#58;bind&#40;
			&SerialBasics&#58;&#58;write_complete, // called when all data was send.
			this,
			boost&#58;&#58;asio&#58;&#58;placeholders&#58;&#58;error
			&#41;
			&#41;;
	&#125;
The complete serial handling is covered in a portable lib. The horror of boost is, it's totally based on huge template structures which are (for me) impossible to debug. The debugging features in VC2010 express are limited :-) The buffer handling is a little bit tricky, cause it's up to the hosting application to ensure that the buffer is reliable over the complete time of the async processing, needs a little bit juggling with mutex patterns.

BTW: I decided to limit the maximum size of one serial message to 1024 bytes. So the buffer handling is simple. Dynamic buffers will bring a lot of overhead and need garbage collection and and and... KISS....

Peter
+++ journeytounknownsoundscapes.blogspot.de +++
+++ DIY electronics +++ musical experiments +++ light experiments +++

Clearscreen
Member
Posts: 482
Location: Australia
Contact:

Unread post by Clearscreen » 10 Dec 2012, 01:16

Can't say i follow this, but I wanted to say thanks for doing this hesspet, it'll breath new life into my arduino's!

Post Reply

Who is online

Users browsing this forum: No registered users and 112 guests