This project has moved. For the latest updates, please go here.

How to execute the command sequence

Nov 29, 2013 at 8:35 PM
I'm trying to find a way to execute the sequence of command. The example would be to turn the LMotor by 90 degrees CW, waiting for the operation completion, turning the motor back by 90 degrees CCW.

Another example would be to playing two tones one after another.

I would prefer to find a way to get the end-of-DirectCommand execution (or end-of-BatchCommand execution) into the event, but any possibility just to know that I can issue a next command and it won't be ignored is good enough for now.

So far I unsuccessfully tried many BatchCommand with awaiting for ReadyXX method combinations.

Any recommendation would be highly appreciated. THANKS!
Coordinator
Dec 6, 2013 at 8:00 PM
Working on an answer to this...will update when I have something concrete and working. Thanks!
Dec 31, 2013 at 11:02 AM
Hi all,

I was wondering about the same thing.
I have tried calling .Wait() on the Task returned by any of the async methods but for some reason I do not understand, the wait never returns.
It obviously waits indefinitely. (Unless I specify a timeout which is not what I would want of course)

So far the API unfortunately can not be used for creating sequences of commands.

So any solution would be greatly appreciated.

Regards
Jan 12, 2014 at 10:35 AM
So, today I have looked a little bit into this issue and here are my findings:
I have only worked with the UsbCommunication in my tests.
To make sure that this is not some kind of issue with the asynchronous nature of the architecture I have implemented "TurnMotorAtPower" and "StopMotor" in a purely synchronous way.
It still did not work to my surprise which led me to the UsbCommunication (Desktop) class itself.

The following line I used in analogy to the asynchronous implementation still seems to cause some kind of overlapping or incomplete transmission issue.
_stream.Write(_outputReport, 0, _outputReport.Length);

I added a _stream.Flush() call afterwards which increased the chances of a successful behavior. But it was still all very unreliable and unsatisfying.

But changing that all to the admittedly very ugly
            foreach (byte b in _outputReport)
            {
                _stream.WriteByte(b);
            }
I had a 99% success rate. The missing 1% I do currently attribute that to the known flakiness of the USB connection. (I might be wrong and there might still some other issue)

But now I am at a loss. I do not understand what is happening when using the FileStream.Write Method that is so different to the FileStream.WriteByte.
Might this be a buffer issue or some kind of USB problem when sending data in short succession that they get some kind of overlapping or data loss?
Coordinator
Jan 12, 2014 at 6:50 PM
Edited May 26, 2014 at 5:30 AM
The desktop USB code was added as a last minute feature based on my old Wiimote code since both are HID devices. I didn't get much of a chance to test, but I saw exactly what you're seeing. With the desktop USB connection, data gets "lost" in both directions, and the overlapping is a good description. I likely won't have time to get back to this anytime soon, which is why I called it out in the "known issues" section as you saw.

Now the other side of this, executing commands in sequence, is a bit more difficult. The only thing I can tell is when the command or batch of commands has been sent across the wire/air. I don't know when that command/batch of commands has finished executing, only that the commands were written. So, as the original question asked, how do we send a command to turn 90 degrees, wait until it has turned, and then send another command, is difficult to answer. The best I've come up with is to use the event or poll manually the position of the motor and when it hits this appropriate position, send another command.

The other answer here may be to implement the commands for brick logic....if/while/for/etc. constructs that exists in the desktop drag-and-drop environment. So you could essentially write the full program in code, then send that whole program to the brick, if that makes sense. But, given time constraints, this probably isn't in my schedule anytime soon. :(
May 5, 2014 at 10:59 PM
Edited May 5, 2014 at 11:00 PM
Hi all,

It can be solved by utilizing OutputReady (0xaa) command.

Just call it after a motor command.
Here is an example:
        static void Main(string[] args)
        {
            Brick brick = new Brick(new Lego.Ev3.Desktop.UsbCommunication(), true);
            brick.ConnectAsync().Wait();
            brick.DirectCommand.StepMotorAtPowerAsync(OutputPort.A, 50, 0, 360, 0, true).Wait();
            brick.DirectCommand.WaitOutputReadyAsync(OutputPort.A).Wait();
            brick.Disconnect();
        }
It's not implemented in SDK, so you need to download and modify the sources.
public void WaitOutputReady(OutputPort ports)
        {
            AddOpcode(Opcode.OutputReady);
            AddParameter(0x00);         // layer
            AddParameter((byte)ports);  // ports
        }
Coordinator
May 26, 2014 at 5:54 AM
I feel dumb for not knowing that's what the OutputReady command did. Added, will be in the next release. Thanks! :)
Sep 22, 2014 at 7:04 PM
Ahilevich,

Thanks for the suggestion. Have you actually tried it? Wait in your solution waits indefinetely, construction with await doesn't work.

Maybe I am missing something obvious.

What actually did work for me was to:

Add in DirectCommand.cs:
public  Task<bool> GetOutputTestAsync(OutputPort port)
{
   return GetOutputTestAsyncInternal(port);
}

internal async Task<bool> GetOutputTestAsyncInternal(OutputPort port)
{            
   Command c = new Command(CommandType.DirectReply, 1, 0);            
   c.OutputTest(port, 0);            
            
   await _brick.SendCommandAsyncInternal(c);
   return BitConverter.ToBoolean(c.Response.Data, 0);
}
Add in Commands.cs
     public void OutputTest(OutputPort port, int index)
        {
            if (index > 1024)
                throw new ArgumentException("Index cannot be greater than 1024", "index");

            AddOpcode(Opcode.OutputTest);
            AddParameter(0x00);             // layer
            AddParameter((byte)port);       // port            
            AddGlobalIndex((byte)index);    // index for return data
        }
Add in Enum.cs in Opcode enum:
OutputTest = 0xa9,
Output test returns 1 if engine is busy and 0 otherwise.

Now this function can be called to test if the engine is busy or idle. One has to simply wait until it returns false (preferably also in asynchronous way)

If called repeatedly it happends to fail with NullPointerException ocassionaly (I didn't investigate why, maybe the command call simply fails sometimes and the error is not checked?)


The preferable way to implement it might be somewhat different.

Ideally one would add Busy property to Port (there are similiar InputTest opcodes for sensors also) and read it in PollSensorsAsync in Brick.cs .
Now one could always check if Input/Output port is busy or get notified by event if state changed from busy to idle for example.

Kind regards!
Sep 27, 2014 at 12:46 PM
bmegli,
You should use OutputReady command, not OutputTest. OutputReady command does not return anything, it just waits until an output becomes ready and then returns.
Please check my fork https://git01.codeplex.com/forks/ahilevich/ev3usb. It contains necessary command. I also reworked USB communication to use hidlib. Now it is much more stable.
Mar 11, 2015 at 10:24 PM
peekb wrote:
I feel dumb for not knowing that's what the OutputReady command did. Added, will be in the next release. Thanks! :)
I'm not trying to sound impatient here, because believe me I definitely appreciate everything. I was just wondering how long it was going to be before the next release, because if I understand correctly from this and ahilevich's posts, it will allow us to send commands that wait until the previous one has finished executing. I am just asking because since I got the API using NuGet, I want to know if I should try to figure out how to modify the source code, or just wait for the next release. But overall, I love this API!
Coordinator
Mar 12, 2015 at 12:41 AM
Unfortunately I'm in a different role at Microsoft now and this isn't part of my normal work, hence why development has stagnated. I have no formal plans to update this at present, but if free time allows I will. I just haven't had much of that lately. :)
Mar 12, 2015 at 3:32 PM
No problem, I can't argue with work comes first. I just wanted to see if anybody could give slightly more detail on how to modify the source code (which files to modify, what to modify in them, which assemblies do I need to reference and/or copy, etc.). I am not trying to be lazy here, I simply don't want to screw up the API we already have. Thanks.