Introduction

Roburgger is a development environment for robotic arm programming. It will allow to use ER series of robots, to simulate programs and to debug them in the usual fashion (with watches, breakpoints, single step tracing, etc).

Implemented features

Download

Preliminary version, warning bugs ahead!
2-7-2003 21:37 Release9 2-6-2003 21:37 Release8 22-5-2003 21:37 Release7 18-5-2003 21:37 Release6 18-5-2003 2:08 Release5

Thanks to Diego for kindly testing it :)

You can get the MFC dlls from here.

Documentation

The documentation has been developed using Doxygen. (Currently removed for space reasons)

Progress

3rd June 2003

First tests for unreachable position trapping. I'll go to the Uni to test how the real thing handles unreachable positions (what happens to the program being run, to the variable, when the unreachable position is trapped: when the position is entered, when the move instruction is found, when the position is actually interpolated by the robot).

I'm also working on the definitive menu options and state switching for debug/build/run.

30th May 2003

Worked on memory deallocation on ACL compiler error. This got a bit involved because Bison's semantic structure (YYSTYPE) stores pointers, but on a compiler error you need be able to free them.

First thing I tried was using auto_ptr, but strict ownership transferral didn't work properly with the way Bison works (in some cases Bison copies the semantic value to the stack of symbols but it uses the temporary variable it just copied from).

So there's a need for refcounting and ownership releasing. After looking a bit at Boost library, I coded my own flavour of SharedPtr, a refcounted smartptr with ownership releasing. Because for design reasons I've limited SharedPtr usage to ACL program parsing, you can request the ownership of the pointer at any time so it's not destroyed when the refcount of the wrappers reach 0.

I haven't made it thread-safe, as I have no need for that.

I found my own set of problems when using this smart pointer:

The net result is that now YYSTYPE stores SharedPtr, which are properly freed on a compiler error (I'm using exceptions for that).

27th May 2003

A few updates:

Added GOTO operation support and changed a bit the workspace format (all input ports from conveyors, robots and sensors must now read from the same controller).

All routes are now read and created properly.

Implemented relative coordinates (although untested).

Encapsulated the solver inside CRobotInfo by adding SolveInvKin and SolveDirKin methods to that class.

Fixed some leaks: there's only missing 336 bytes in one allocation (only God knows where those bytes are allocated).

Added robot model proper loading (we can do it better, though, by using libraries for the models, thus avoiding to load the same model for different robots).

I was at the Uni to debug the program and I noticed that ER-V is indeed right-handed, so I changed the framework's coordinate frame again. Strangely enough, changing the pretransform matrix was enough to fix this (no need to change rotation axis sense). I plan to investigate this further in the future, as I was expecting to have to change some rotations in kinematics calculations from clockwise to counterclockwise or viceversa.

22nd May 2003

Sensors!!!

Sensor active
Sensor inactive
Yes, that green box at the end of the green vertical line is a sensor. When the vertical line collides against some object, the sensor is active and lit, it's inactive and dull otherwise. Sensors have coordinate frames, so you can orientate them in any way you want.

You can plug sensors into controller's input ports and read the sensor value from a program. The same goes for conveyor speed values, you can plug it to a controller and control the conveyor speed either using an interpolated position or a discrete output. You can also plug sensors directly into conveyors and robots.

Right now only ray sensors are implemented, but it should be easy to add other kind of sensors (cone, weight, area, segment). I took the ray AABB intersection code RealtimeRendering 3D object intersection page, which has a link to Pierre Terdiman's page.

Behind the scenes, there has been a major revamping in order to include input/output routing from controllers, sensors, robots and conveyors. This routing is supported via the IPortReadable interface, which allows a range of ports to be read. Ports are external outputs a controller, conveyor or sensor exports. The other entities in the system can consume those outputs in various ways.

There's some rationalisation and abstraction still missing as, although the framework allows it, arbitrary routing is not correctly generated from the .rbw file.


Sensor controlled table (656KB)
In this video you can see how the table speed is interpolated using a GROUP C position, so when the sensor is hit, the table speed decreases smoothly.

The controller is using the following program, where IN[1] is where the sensor is plugged to and axis 1 of GROUP C position is connected to the conveyor.

program sensr
define i
define spd
    set spd = 20
    open
    moveld oper
    for i = 0 to 20000
        setpv cnvy 1 spd
        move cnvy
        wait in[1] = 0
        wait in[1] <> 0
        setpv cnvy 1 0
        move cnvy
        
        moveld grab
        close
        moveld oper
        moveld drop
        open
        moveld oper
    endfor
end

18th May 2003

Wacky ODE tests.

Ever since I saw ODE I've been wanting to do something like this:


Domino glyph (483KB)

The domino model is taken from windows TTF Arial font glyph. To get the glyph outline, I took the code from TrueType Font Secrets by Michael Bertrand and Dave Grundgeiger. You can actually dominoise any string you want :).

The con is that as soon as you have a few domino pieces on top of the other, ODE takes about 3s to calculate each frame (a step of 0.02s, five steps per frame), although I guess my scene database code is also to blame for that. The animation finished beforetime because of a stack overflow (even if the linker was set to use 12MB of stack), ODE is really stack-hungry.

In the first frames you can see a strange whitefan-like artifact centered on the image. This seems to be some TNT2's OpenGL bug :? (I don't draw white polys anywhere) and looks like it happens when I use too many polys.

ACL program debugging was broken because of the latest fixes to allow local directories on workspaces (the debugger was checking a relative path against an absolute path, the comparison never matched and you couldn't step, breakpoint, etc). I took some time to fix this.

17th May 2003

I finally got to the bottom of an interesting problem.

When compiling for release, nicely stacked boxes in circbelt.rbw workspace would bounce as soon as you switched dynamics on. This didn't happen in a debug build. After chasing some red herrings about storing types and not pointer to types in some CArrays, I got to the conclusion that it had to be some FPU handling mismatch between release and debug builds (I saw that if I changed the top box position from 360 to 360.0001 the problem didn't arise).

When I mentioned the problem to mac, he reminded me the x87 extended internal precision (80bit) vs. IEEE 754 standard types (32 or 64bit) issues. It happens to be that the FPU internal calculations and FPU stack register sizes are 80bit. This means that operating on values spilled to memory do not give the same result as if the operands remained on the FPU stack (the latter keeps full 80bit precision).

He suggested using /Op (improve floating point consistency) compiler option and it worked!

While debugging the issue, I took some time to rework all the scene structures to use pointer to types as CArray element (as opposed to storing the type itself). This is needed because I use external references to array elements (CObjectInfo need to know about the CRobotInfo grabbing it and so on), and I cannot just use indices or the address of elements, as both can change when CArray elements are deleted or the CArray resized due to reallocations.

I also rationalised all the dynamics integration and the conveyor/object/robot handling by adding IStaticEntity and IDynamicEntity interfaces. No more conveyor hacked code!

16th May 2003

Conveyors!

Conveyors will only work if you have the dynamics module active (I plan to add a disable dynamics option). Although I've only done conveyor belt tests, I will add support for table conveyors as well (with angular motion instead of linear).

Right now a conveyor node is:

PROTO Conveyor [
   field    SFString       name ""           # Name of the conveyor
   field    SFMatrix       frame 1 0 0 0  0 1 0 0  0 0 1 0  0 0 0 1 # Coordinate frame
   field    SFVec3f        bbox 10 10 10 # Bounding box used for placing objects on top
   field    SFEnum         type BELT         # Conveyor type BELT / TABLE
   field    SFFloat        speed 1           # Speed in units/sec (or angular speed)
   field    SFBool         on FALSE          # ON/OFF 
   # Merge speed with on/off ?
   # Conveyor BELT direction is on XY plane towards +X
   # Conveyor TABLE direction is on XY plane and concentric
] { }

I've hacked together some pieces of code to do a quick and dirty conveyor test:


Conveyor belt test (575KB)

When I leave the simulation running for a while, I have some problems with objects bouncing for no apparent reason before reaching the end of the conveyor. I think I'm leaving some hidden body in the system which causes objects in the conveyor belt to jump.

And here we have the table-style conveyor in action:


Table conveyor test (557KB)

You can combine belts and semicircular tables (this is yet another rehacked version of conveyor) in such a way that a semicircular table forms the corner of two conveyor belts at both sides of the belts:


Circular belt built of tables and belts (424KB)

I tried building the circular belt out of two y-direction and two x-direction conveyors but the hard change of friction direction in the borders between x and y directions caused the boxes to jump wildy (half the box was pulled in x and the other half in y).

Semicircular conveyors avoid this problem because the friction direction in the boundary between conveyors is continuous. Nevertheless, I've noticed that the problem still happens once in a blue moon with the semicircular conveyor approach (and sometimes not even in the boundary between conveyors, which makes me hope this is just some bug I have somewhere.

The conveyor implementation is still a nasty hack (for example, I treat square conveyors as tables, half-square as half-tables and all the others as belts).

15th May 2003

At last, ODE integration!!!! ... or at least the first version.

I've had strange problems with ODE because of using a scale bigger than the one in the examples (it took me a while to understand why my objects were sinking meters and meters into the ground plane and then floating up). All the CMF, gravity and ERP measures were wrong for such a big scale. I've added a scale factor to the models I insert in the simulation, so they match the scale used in ODE examples (around 1.0, as opposed to 300~500 units). Looks like ODE is a quantum dynamics library ;)

Even with the scale factors in, I had to decrease the simulation step from 0.1 to 0.02 so boxes stacked up didn't shake and crumble. This means that I iterate several dynamics steps for each graphics step (currently 5 steps to 1). I hope I will be able to achieve stability modifying ERP or CFM parameters, as the system is quite sluggish with such a small simulation step. I can also try David Whittaker's stepfast version, which should behave better with resting objects.


Not quite the intended building (1.2MB)


Building and a final with a twist (1.42MB)

As you can see on those videos, I don't model the robot in the dynamics system, not even the grip (that explains why bars penetrate the robot):


Yet another possibility would be to model the robot completely in ODE (including articulations) and using the joint values to drive motors, but I don't think I care that much about proper dynamics simulation.

PS. I had to remove Doxygen's documentation because I'm running out of space in my ISP's tiny account :(.

14th May 2003

MSVC 6 blues.

I've been setting up the project to compile release builds (I've been building debug all this time).

Doh! time: I had some trouble debugging the release build because, although I set the Generate Debug Information combo in "Code Generation" Tab, I forgot Linker tab checkbox.

And now the blues: When compiling for release, I've been getting non-sense access violations when trying to create objects from classes registered in my factories. I've tracked it down to MSVC insisting on calling the constructor of Factory on every file I call Factory::Instance().

My factories are singletons, so the constructor should only be called once. After some debugging I found that the reason why MSVC calls them several times, is because it inlines the Instance() static function wherever it's called. For some reason it forgets that it doesn't need to call the constructor everytime.

I solved the problem disabling inlining optimisations in the files where I call Instance() (yeah, sad).

Another problem I found was that Project Settings dialog box options didn't match to what it was really stored in the .dsp file, so I spent some hours hair pulling thinking that the fix I had found didn't work.

And last but not least, a few days ago I had to disable "intellisense" support because FEACP.DLL kept crashing when parsing one of my files, after modifying something on it.

13th May 2003

Well, today it's Tuesday the 13th, this is the Spanish version of Friday the 13th (bad luck, don't engage on anything, yadda yadda yadda).

I've fixed the ER-V kinematics problems I had introduced yesterday, so I can resume now the multirobot tests. Unfortunately fixing those problems became an issue of trial and error in the fivedof kinematics. I really have to rationalise the kinematics at some point.

On the multirobot side, I've created a small sample program:


Shuffler robots (200KB)

When developing this shuffle test, I've noticed that a proper local coordinate frame shouldn't change the handedness of the coord system, otherwise you get "mirror" effects which cause rotation inversions at the joints: a given joint angle value will mean rotating right in one case and left in another (expected behaviour from using different handednesses).

What you really want is an even number of symmetry effects, so a given robot will always rotate the same way for a given joint angle value.

In the case of shuffle, both robots use the same program and positions without ever colliding (positions are specified wrt the robot local frame or in robot joint values), which is kind of neat:

dimp posa[3]

setpv posa[1]
2581
11982
-10346
-3249
0

setpv posa[2]
67
11543
-14526
-3248
0

setpv posa[3]
-2618
11922
-10395
-3248
0

program shufl
   define i
   moveld posa[1]
   for i = 0 to 20000
      open
      close
      movecd posa[3] posa[2]
      open
      movecd posa[1] posa[2]
   endfor
end

run shufl

12th May 2003

Implemented a new robot, the Scara-type Scora ER-14.

Just by implementing the kinematics for this robot (which is easy), you get a full controlled robot programmable in ACL (instantiating the ACL controller with 4 joints per Group A position).

This has served as pudding-proof of the flexibility of Roburgger's interface architecture, showing that adding new robot types is indeed easy. The idea is that it should be as easy to add a different controller/language (but I don't plan to do that at all!).

While implementing this robot I sort of broke the ER-5 support, because the ER-5 kinematics do not zero at the same zero position as the Denavit-Hartenberg model (ER-14 do, though).
Next thing I'll do is to change the kinematics to work using the zero DH position (this was already in the list of tasks to do).

You can see here a video of the new robot in action:


Scora ER-14 robot (161KB)

I've also added per-robot coordinate frame, so now you can have several robots in the same robot cell, each not only with a different center but a completely different local coordinate frame (orientation).
I've also adapted the point and click interface to work with this generalised per robot coordinate frame.

I found an interesting problem when implementing local coordinate frames: the VRML models and default robot coordinate frame are left-handed, so when the local coordinate frame is right-handed (due to this per-robot specifiable frame) you have to invert the clockwiseness of the polygons described in the VRML model.
I currently workout whether a coordinate frame is left or right handed by looking at the number of minus signs of the orientation submatrix (I've empirically verified that this methodd works).

8th May 2003

I've added point and click support to drive the robot: You click anywhere on the viewport and the grip of the robot is placed there (the maths are a bit more complex than that, but you get the idea). I've also added a Paste Position command to the program view, so you can paste the current robot position into the ACL program.

The whole enchilada is this: when you double click on a viewport, you obtain window X and Y coordinates to unproject, but the Z value is missing. That Z value is obtained from the intersection of the X,Y ray (starting from the viewpoint) with the plane parallel to the view plane and located at the current grip position.
That way, with two double clicks (on in XY viewport and another in the XZ one), you can accurately place the grip.
I think this is the way Cosimir works.

The biggest problem implementing it is that gluUnProjecting things didn't work properly because I thought NDC Z values ranged from 0 to 1, which is utterly wrong (Normalised Device Coordinates range from -1.0 to 1.0 for all three axis).
Errr ... actually I made two other minor mistakes like using milimiters instead of tenths of milimeters and forgetting to apply the glDepthRange parameters when calculating the OpenGL transformation by hand (nobody's perfect!).
Due to that, I began to think that I had misunderstood the way glOrtho worked (the MSDN stating the wrong matrix for glOrtho didn't help much either), so I spent a day testing several other glOrtho params and scribbling random figures on my notebook.


Hacking away in RobIDE

And now, today's video. It should ring a bell if you've studied Computer Science. It's not really done algorithmically (I'm too sleepy for that), but it looks cute nonetheless.


Well-known-tower builder (556KB)

6th May 2003

"Aha!" time (aka epiphany fest).

I've discovered that the unestability of the CODECs was due to the Angelpotion CODEC (even if I wasn't using it for anything). Removing that CODEC fixed the program crashes when deinitialising the DLL. The program crash caused 100% CPU usage from then on and forced a machine reboot.

I found the offending CODEC by looking at the process name causing the crash and looking at that name at the windows\temp directory (CODEC dlls are written there as .tmp files before usage).

I'm now going back to test the paletted AVI support, looking at WriteAVI SDK sample which uses a 256 color bmp to create the AVI. Looks like you can use MS RLE CODEC only with 256-color bitmaps, but I haven't been able to introduce palette switches in the stream yet (I have a very disco-age-groovy avifile of the arm, you won't find that in shops and it's available only upon request!).

I thought I had found a solution: TSCC Codec for lossless compression but the version available on their website doesn't allow compression, just decompressión, so I'm sticking to MPEG4.


Arm grab and drop video (465KB)

BTW, I've found that the MPEG4 CODEC doesn't work properly if the source bitmap's width & height are not multiple of 8 (it produces a sheared output).

5th May 2003

I've implemented open and close instructions and modified my arcs ACL program to grab and drop a piece on each full 360° turn.


First version of the grab & drop ACL program.

This has actually been the first time I've used the ACL debugger: the first version of the program was wrong, so I used RobIDE to fix it interactively :).


Fixed version of the grab & drop ACL program.

So what's next? Well, proper object support (not just boxes) and physics!. After adding physics (read: ODE), there will be few things left to do, but to add a Conveyor type of robot, polishing the UI and the rest of the code (mainly error checking at kin & compiler level).

I was also thinking on coding an iterative generic DH solver (Cyclic Coordinate Descent), so you can use any DH described robots, we'll see (note that there are joint configurations where the greedy CCD approach cannot solve the inverse kinematics - think of a prismatic joint with an angular joint on the prismatic's axis).

4th May 2003

Today's quote:

I then empirically found that I had to scale by -1 in x instead of in z, and also to scale the xa and xf values by -1. (Basically I just put in enough minus signs after the fact to make it work.) Al Barr refers to this technique as "making sure you have made an even number of sign errors."
"A trip down the graphics pipeline", Jim Blinn.
(as quoted by Carmack himself).

So true!. I've been spending (wasting actually) these days fixing some hardcoded calculations I had to the Denavit-Hartenberg vs. geometric model integration. You can tell the previous model wasn't quite right because the rotations weren't done wrt the z-axis (the blue one).

It turned out that I was using incorrect rotation matrices (one-too-many minus signs), which were making things work. As soon as I corrected them and removed some other hard coded calculations, coupled with changing to a left-handed coordinate system (the original robot uses that system), nothing worked and I was really puzzled.

At the end the problem was fixed adding an even number of minus signs here and there. The scientific reason is that left-handed coordinate systems don't hold the right-hand rule, which means rotations are clockwise wrt the axis (negating the angle does the job).

Using the the grip coordinate frame calculation, I've done a small object grabbing mockup: you can insert CObjectInfos in the scene and the robot can have one of them attached. Whenever the robot moves, it sets the attached object orientation and position to the grip one and voilá.


Object grabbing mockup. Note how rotation always happens around Z-axis (blue).

29th April 2003

So what have I been doing all these days, you may wonder:


VRML model with Denavit-Hartenberg coordinate frames.
It sort of looks like the Hellraiser twin of the robot I already had, but there's much more to it than a spiky robot. The robot framework finally uses the Denavit-Hartenberg cinematic definition stored in the wrl file. The "spikes" are the Denavit-Hartenberg coordinate frames at each joint.

Although I understand the necessity of formalising robot kinematics, I see Denavit-Hartenberg notation as some kind of "direct kinematics for dummies". I've also found that you cannot faithfully model ER-V with a DH model, because of the waist to shoulder link.

I've gone through a big deal to match the DH model to the geometric model:

For the aforementioned reasons, I need to perform several DH calculations in software for robot display (as opposed to just using OpenGL T&L commands), in order to convert from global coordinate frames to local DH frames and to be able to "undo" non-zero joint values in the stored geometric model.

Although those calculations are performed at display time, I plan to change them so they are performed only at model loadtime. I have only one hardcoded value to remove kinematics-wise, which is the relationship between Denavit-Hartenbert zero positions and inverse kinematics solver zero positions. I may need to change the solver to use DH zero positions.

All this coordinate juggling really wanted me to add matrix-transformation classes to my roburgger::vmath namespace, so I have. They match the structure I already had for vectors:

17th April 2003

I've created a patterns namespace and coded Singleton and Factory patterns.

Now I honor the controller node type field and the robot node solver field, creating the right kind of controller and solver by using controller and kinematics factories. No more acl.h and fivedof.h including in RobIDEDoc.h !.

The patterns are heavily based on Modern C++ Design ones:

Object creators are registered in the factory via static variable construction. For example, the fivedof kinematic solver registers itself in the kinematics factory by:
// Kinematics solver factory singleton initialisation
static Factory s_fact(kinematics::Factory::Instance(), "fivedof");

On a different front, I hand-compiled ODE and tested it with the test_boxstack sample program. Looks like it should be easy to plug into roburgger, we'll see.

I found a few glitches while compiling ODE, mainly some functions exported in the DLL's .def file which are not implemented in the code (this is due to the .def file being generated with a PERL script from an outdated file). Another problem is that the default makefile uses GNU's make (later on I found that you can get a win32 port of GNU's make from ODE's website), so I created my own VC6 workspace, which led me to including some legacy ODE files which don't have to be included unless you compile ODE in that legacy mode.

13th April 2003

The monster is alive and moving!.

Yes, the environment works again and with the VRML model.


Debugging a workspace with two ER-V robots.
The current rendering is a bit hardcoded to the ER-V model, in the sense that although you can load a different VRML model, the link sizes and joint angles are hard-coded to the ER-V. This gets worsened by the fact that each link of the VRML model is not described as based in the joint-origin, so some (hardcoded) juggling is necessary to center it that way. I'll change the VRML models to describe the graphical model of the joint in joint-space (instead of model-space).
I also need to code new solvers: fivedof coupled, sixdof, etc (and actually use the solver field of the robot node!).

I've finally fixed the memory leaks caused by the watch window (a watch was created on every update and never destroyed). This implied adding DeleteRow notifications to the CTreeListCtrl, so user data can be deleted when a row is deleted asynchronously to the app (when DELETE key is pressed over a row or when an empty name replaces a valid watch - and thus the watch gets removed).

On a side note, watching the full controller in the watch window can make things go quite slow if updated on every movement. I guess the reasons behind that are having to parse and create the watch on every update (although this could be slow due to running it in debug mode).
The solutions (besides compiling a release target) are to find a way of not reparsing & recreating the watches on every view update (but this could be hairy in case you are watching a variable with different types in several tasks) or just not updating the window unless you are doing a step-by-step debug session (which is what VC++ does).

11th April 2003

Well, another whole week without updates!.

I haven't been very productive code-wise, as I have been giving some thought to the interactions between controllers, solvers and robots - how one and the other are created and what degree of flexibility to allow.
I think I will hide the solver from the user: each robot will have its own hard-coded solver (although internally solvers will be parameterised via link length, robot geometry and decoupled-ness).
So finally the user won't be able to add solvers to the workspace, just robots (I didn't see any sensible benefit in allowing adding solvers without a robot attached to it - something like having a robot controlled by two different controllers or several robots attached to the same interpolators of a controller is a nono under this scheme).


Workspace with the VRML model.
I added my VRML 1.0 loading code without much hassle and modified it to accept VRML97-style node prototyping (previously it was using VRML 1.0 user-defined instancing, which wasn't quite correct because it should instanciate a node and you should define all the fields on every instance, not just on the first found).

I also had to add prefixing to FLEX and BISON, as now I have two lexers/parsers in the project and some symbols were colliding.

The VRML model looks nice :), although it doesn't move yet and some VRML code needs reworking:

The workspace is now loaded from a VRML file with user-defined nodes. The format is still preliminary but it looks like this:

#VRML 1.0

Workspace {
   name "Test workspace"
   path "c:\works\pfc\src\RobIDE\"

   Controller {
      type "ACL"
      name "Ctrlr 1"
      programs [ "acl\movetest.acl" ]

      # Missing associations between interpolators & in/outs
      Robot {
         solver "fivedof"
         name  "Robot 1.1"
         model "wrl\er-v.wrl"
      }
   }

   Controller {
      type "ACL"
      name "Ctrlr 2"
      programs [ "acl\test.acl" ]

      # Robot {
      #    solver "fivedof"
      #    name  "Robot 1.2"
      #    model ""
      # }
   }
}
The robot model is loaded as specified by the model field of the Robot node. Maybe in the future the robot will be specified as the identifier from a library of robots, or just the VRML node identifier.

3rd April 2003

Just a quick update to paste a screenshot. Since the last changes to the interfaces, nothing is really working (had to disable running programs when I added kinematics::Solver interface, and the robot you see there is just a straw one) but the infrastructure is really shaping up.


Workspace with the new split 3D view.

28th March 2003

Ooops, almost a week since the last update.

I spent the weekend at Toledo (the Spanish one, famous for swordcrafting and being the crossroad of the three cultures - arabic , latin and jewish) and Madrid, with some friends having tons of fun :). If you like uphill cities, don't miss Toledo if you happen to be thereabout!.
When in Madrid, we also saw people's demonstrations against war in Puerta del Sol and the police charges against them (I didn't see any violence in the demonstrations - I think that one of the demonstrations was unscheduled, hence the police charges - the only thing I can say is that the police vans running over and spreading dump bins splashed more dirt than demonstrators did). People is getting really annoyed at the government here.
On a side note, I got nicely surprised at how many Spanish-speaking US americans tourists were in Madrid.

Back to work, I've implemented console input and output (PRINT, PRINTLN and READ commands) and added proper tabl control support. The tab control in the console/watch view is fully functional, so it actually switches controller when change tabs.

I've created a CControllerDoc that encapsulates each controller, so now the workspace contains an array of those documents. The CWatchConsoleFrame is now independent from the controller documents in the sense that you may have that window shown even if you don't have controllers loaded. Maybe in the future I'll change that window to be a dockable one.
Because the controller documents are actually owned by the workspace document, the CWatchConsoleFrame is never closed but hidden/shown (closing the frame would delete the documents embedded in it).

Clock is ticking away and I still have to do lots of things :(, I hope I'll be able to plug in the first version of the 3D renderer views this weekend.

19th March 2003

First implementation of console view and integration of watch view. I've also embedded both in a tab ctrl, with the intent of creating a tab per controller, although tabs are currently unoperational.


Workspace running with & console views (note PRINTLN instruction).

War

It's a sad time for mankind, the war has started :(. At 3:smthing am, when the attacks begun, my Vodafone cell-phone stopped working with a Tarjeta sin registrar error message, coincidence? (it's 4:44am and it still doesn't work).
Guess what: rebooting the phone fixed the problem, go figure.

18th March 2003

Began to code the final application, RobIDE.


Workspace view and two programs being run on two different controllers.
CProgramView and CWorkspaceView are implemented. You can currently run several controllers in parallel, each with its own set of programs, set breakpoints, and all the stuff you could already do with my previous prototype.

The CDocuments I have created are:

I'm thinking about the views I need beside the ones I already have.

There should be one CWatchView and one CConsoleView per controller, so I was thinking about embedding both in a split window. I would also add a tabctrl to cluster all the watchconsoleviews so you can select what controller to watch.

There a few glitches with this approach:

  1. I wanted to use a dockable window for the watches (CSizingControlBar by Cristi Posea), but it doesn't seem to work well with views (see faq section). The same problem goes for the workspace view (I wanted to make it dockable as well).
  2. There's no document associated to a controller, but a CView must have an associated controller. I have two options here, I either use the CRobIDEDoc or create documents for controllers.

Scintilla wrapper changes

I've fixed a few things with the scintilla wrappers.

ACL changes

I changed a few things including deleting some dead code. The net result is that I've removed all the memory leaks (ahem).

Sometime after this checkin I will probably rename the namespaces from languages to controllers, acl to scorbot and introduce the kinematics namespace.

11th March 2003

I've finalised the physicalisation of the robot kinematics. Now it should match the real thing in almost everything (I still have to test if joint positions store wrist-left and wrist-right encoder steps or just pitch and roll - I assume pitch and roll).

I've come across some precision problems with floats when using normalised plane vectors and big coordinates (as a result of using the real robot measures). The offending code was the movement following a circle (out of interest, the same code I fixed a few days ago). I was testing against the cross product being zero (yeah, I know exact float testing is evil but I need it for 180º tests and I get some moral relief from the fact that it's for zero-testing) and it wasn't zero (but close to it), so the code was taking the wrong path (more exactly, rotating against the wrong axis).
I could hack a fix using epsilons but I guess that using epsilons is just sweeping the corpse under the rug, so I just removed some unnecessary normalisations so the quantities calculated are similar.

The other big change (besides file structure revamping) is that I've moved the invkin / dirkin calculations to the PositionData classes: GetCoord, GetJoint, etc, methods now calculate the direct and inverse kinematics if needed. When the dir kin is modified, the invkin is marked as stale and viceversa, so before any modification the proper stale flag is checked and the given kin recalculated if necessary.
I have to remove the method which allows to access directly to the position's IntegerData (IIRC used only in watches), but other than that it works beautifully.

After some speed tests (sysmon pawah), looks like the thread-driven version will be needed: the processor is 90% idle when executing the program but it goes dog slow (right now virtual machine ticks are hooked on a 10msec WM_TIMER).

I've done a major revamp of file structure:

/roburgger
	roburgger.h
	roburgger.cpp
/languages
	languages.h
	languages.cpp
/acl
	acl.h
	acl.cpp	
	instructions.h
	instructions.cpp
	operands.h
	operands.cpp
In the future I'll probably nest the directory structure. I also moved some of the functions in acl.h to implementation files (acl.cpp, instructions.cpp, etc).

C++ Rant

Even if I've moved code to .cpp files and I intend to keep doing so, I've decided that code should be written only in .h files (languages like java and, I believe, C# don't have this interface/implementation split).

The drawbacks I find in keeping .h/.cpp split are:

The allieged reasons to keep the interface/implementation split are:

So at the end of the day only the dynamic library and opaque library reasons seem to hold at all.

10th March 2003

On Friday I went to the Uni and did some tests with the real thing. Now I have the robot dimensions and I'm working on modifying the kinematics to match them.

There's a weird problem with the Z-buffer at the Uni's machines. I believe it has to do with ATI cards: The rendering looks like as if the z-buffer was disabled, so polygons further away are rendered on top of polygons closer to the viewer. That could be because of requesting a 32bit z-buffer and ATI card returning a 0bit z-buffer (this is just a suspicion grabbed from thin air). I've just noticed that I don't set the glDepthFunc , although it shouldn't be necessary (OpenGL warrants a default value of GL_LESS, which is what I want). I've added initialisation code just in case and I'll retest next time I go to the Uni.
The fact that it works OK on MS's software renderer (XP, Win98) and on TNT2 and GF2GTS makes me think that it's something dodgy in ATI drivers rather than in my app O:).

Oh, yes, and it doesn't work on WinNT 4.0 either. The MFC document menu doesn't show up (you only get the IDR_MAINFRAME menu), so you cannot do any debugging at all. I'm smelling outdated MFC libraries here...

I also found that ERV has decoupled kinematics even for joint coordinate system, so when you modify the shoulder joint value, the elbow motor encoder changes to keep the elbow-to-wrist link oriented. Note that even the elbow motor encoder changes, the elbow joint value itself doesn't change because joints for elbow (and wrist) are given in deltas from the previous joint (the delta shoulder-to-elbow remains constant when you modify the shoulder).

I've already modified my kinematics to mimic this behaviour and it's working fine :). In the way of doing that I fixed two features:

6th March 2003

I've been working on capturing the OpenGL rendering to AVIs. The part of getting the OpenGL rendering and writing bmps of it was a piece of cake (barring some misunderstandings of MS's BMP structs and forgetting to doubleword align lines), but using the VFW api has been a nightmare: AVI codecs are very unstable, using a codec other than MS's Video1 (DivX 4, MS MPEG codec, etc) caused the program to crash in what looked like the the CODEC's uninitialisation routine (at process exit time, beyond the end of my program).

The fact that the program crashes at deinitialisation time wouldn't be so much of a problem if it wasn't because sometimes it doesn't save the AVI file properly and it always forces me to reboot afterwards (the latter looks like an interaction of using the CODEC in a console app, as it's the Winoldap process the one that locks up and causes cpu chugging and unresponsiveness).

I recoded from scratch the AviWriter I wrote at 3Dlabs (I didn't have the sourcecode), so now I have a tool to convert a bunch of BMPs to an AVI or to compress an AVI. It's slightlier featureless, but it does the job.

I've found a couple of good pointers on AVI creation (besides MSDN):

In the robot side of things, I fixed joint by joint interpolation of cartesian positions, as it was never converting the cartesian positions to joints, but trying to convert them to joints (isn't copying & pasting code great?).
I also added movement through an array of positions (MOVES command), although current implementation will need some rework once I have the MPROFILE command in place (MPROFILE has to be applied to the first and last positions of the array, not to each position).

A video

And this is the product of these two days of AVI hacking. Not very impressive and too big for the image features (each frame uses less than 16 different colors!), I know. I hope I will be able to play a bit more with CODECs, specially Microsoft's RLE (the video is generated at 32bpp, maybe if I generate it as 4bpp with palette changes the size will decrease a lot).


Arm following an arc (574KB)

Websurfing blurb

I've read an article on employment in Spain on Russells's blog. I don't agree with lots of things he says (salaries in the rest of EU are higher because life-expenses are as well), but I like his style sheet, so I ripped it and changed a few things here and there O;)

4th March 2003

Added CircularMovement interpolation. With this interpolation (commands MOVEC and MOVECD) the TCP follows a circular path through three positions. It took me a while to get it right (more cosine & 180-range blues), but with the Quaternion support the implementation turned out to be quite elegant.
Fixed NormalMovement, now it should work when using cartesian coordinates as well as joints.

I've moved all the vector functionality to its own file (vmath.h in roburgger::vmath namespace). I've also added a simple Quaternion implementation to be used in the CircularMovement.

1st March 2003

At last I've fixed the invkin calculations (there were grip configurations causing strange interpolations). The main problem is that I was using acos to calculate angles, so the resulting angle ranges from 0 to 180º, which was giving me grief because I don't make assumptions on the working space of the robot, so I need full 360º angles.
I've changed everything to use atan2 (which BTW was what my 5-year-ago code did).

I say "the main problem" because I keep messing around with the coordinate frames and stupidly inverting the vector directions when dotting them (I also put one-too-many minus signs from time to time). Anyway, it's finally working, at least for the way I understand the decoupled nature of ERV.

I've also changed the CRobotCtrl to do the roll transformation on the grip, so now you can screw and unscrew things (screws).

28th February 2003

I've been trying to decipher 5DOF invkin calculations I did four years ago for my ARMS program, and I must admit I'm not able to understand lots of them (although they seem to magically work!). Because of that, I've reworked them in a more understandable manner and conforming to the way I think ERV works.

I've coded my first mathematical vector classes. The way I've done them is with a VecN template and with descendants of the template instanciation for 3 and 2 components (Vec3f and Vec2f). The template, instanciable providing vector element T (double, float, ...) and vector size (2D, 3D, ...) holds the common operations (VecN+VecN, VecN*T, etc).
The only striking thing is that in order to be able to use those operations when working with template instances, I had to add a Vec2f/Vec3f constructor from VecN, so it doesn't complain about trying to use VecN operators on Vec2f/Vec3f.

ACL changes

Added linear movements and inverse kinematic support for ERV+. The inverse kinematic support needs a bit of review as it looks like the pitch gets more and more disadjusted the more you move away from the XZ plane. (I don't know how the real thing works anyway, so I'll probably fix it once I've done tests with the real robot). Fixed PositionData string constructor (it was only getting the first coord value).

Added mathematical vecN classes. Added inverse kin calculations (pitch vals need reviewing when moving away from the XZ plane).

RobotCtrl & OGLCanvas

I've fixed the OGLCanvas, as it had a long-standing bug for which the Y calculations were wrong (negative sense was upwards). This got a bit convoluted because the Y sense was inverted in several matrices. I also fixed the single-buffer support (the singlebuffer flag was being updated too late so the pixel format was always double-buffered).

The RobotCtrl control is the OGLCanvas descendant in charge of drawing the robot (currently it just draws a few boxes). I've fixed the robot control drawing for OGLCanvas changes (proper alignment of Y axis) and fixed normal calculation for each face.

I've noticed that the CTreeCtrl has a few glitches with the selection line when going to the first editable cell (it doesn't select the next row if the next editable cell is in the next row). It also happens to assert from time to time when moving through cells using tab. Needs investigating.


Linear interpolation of 4 positions using inverse kin.

24th February 2003

At last I added display support to my test program. I've merged the watch window and the text program in a half-split subpane and added another 2-way splitter window to embed the graphics in one pane and the watch/scintilla combo in the other (copy/paste code from VIEWEX MFC example).
As you can see in the piccie below, the robot is just a few boxes stacked up together, as I want to finish the invkin and dirkin support before delving into either porting my VRML loader or just exploring the uncharted territories of XML parsers (for a more definitive robot data handling).

The graphic display reuses a hacked version of my OpenGL MFC control and is quite functional, it's updated properly on each subtick (BTW, I finally fixed the subtick vs. debugging problem I had yesterday) and you can see how the joint positions are smoothly interpolated. Heck, you can even modify the POSITION variable in the watch window and the robot joints get visually updated :).
If you watch POSITION variable (which reflects the current robot joint values), it only updates when the movement finishes and the control returns to the debugger. I had a version updating it continuously, but it was leaking too much memory so I removed that (I have a - hmmm - benign leak in the watch management and when breaking into the program back to the VC debugger I would get minutes and minutes of VC's memory checking dumps).


Preliminary graphical representation (z-fighting courtesy of TNT2 and bad adjusted far/near planes)

PS. I have to admit that after all this time doing non-OpenGL stuff, I had forgotten even how to draw polygons and it took me some hours to get them with the right colors - argh!

23rd February 2003

Scorchio!

I've added movement support to the virtualmachine. Right now only for joint-by-joint interpolation, but the framework is there for all the others as well.
The current status is that you can use MOVE and MOVED commands, and the current position is interpolated to the destination position in several ticks.
If the task is put to sleep (MOVED command), it's awaken when the movement finishes.
The movement is stored in the movement buffer, so in case this or other tasks append movements the system should take care of it (note that the buffer is currently unsized, but should be easy to add blocking when inserting in the movement buffer to simulate the real behaviour).

I've changed Tick so it takes several ticks to execute one instruction, this is so with a view to hook the movement feedback via graphics in the Tick function as well (so a tick is actually a subtick from an instruction standpoint).
This currently causes movement to interact with the step-by-step debugging in a weird way: when you step over a MOVED, the next subtick finds the task sleeping and it cannot update the instruction pointer to the right line of the program in the text window (as no instruction is currently being executed). The next time you press step over, because the current task is NULL, it completely forgets that you were debugging a specific task and it does a free-run instead. I haven't investigated a solution to this yet.

Next I'll add a very simple wireframe OpenGL rendering so I can check that movements work as expected. I'll also add inverse & direct kinematic hooks (I have the invkin calculations from the robot the uni previously had).

I've also fixed a stoopid bug when watching array elements (you would always see the contents of the first element). That bug made me think that the movement interpolation wasn't working, when it was the watch I used what wasn't working.

19th February 2003

I'm just writing to record that I found a CVS SCCS provider for Windows Igloo. Not that I'm using CVS at all, but I guess I will find some use for it in the future.

18th February 2003

I knew it, I knew moronic ACL syntax was going to chase me.
I've been all these days working on the syntax for watch expressions. The problem is that ACL namespaces for positions, tasks and integers, are independent. This means that a task can have the same name as a position or array of integers (or positions), so a way of resolving ambiguities was needed for a proper watch expression usage.

After sorting out the problem by working out a grammar to resolve ambiguities, finally watches are fully working (setting & getting).
The last thing I may need to add to watches is to make them fault tolerant (I guess they assert wildly right now if you provide incorrect expressions) and maybe simple arithmetic operations (+, -, /, *) and relational (==, >=, <=, >, <) so you can use the expressions for data breakpoints. But I want to progress with the main framework before getting on these nit-pickers.

ACL language

Implemented watch expression framework (set & get) and added SetWatch & GetWatch support for all the data (Task, PositionData, IntegerData, PositionVar, IntegerVar).
Changed PositionData to use IntegerData instead of directly using ints. This allows for simpler watch expression management.
At the end the accessor field turned out to be a good thing, as there are cases where it differs from the name field (fields of positions, when watching the whole virtual machine, etc). Designed and implemented a watch expression syntax:
EXPR  
   : !
   | TASKNAME
   | TASKNAME!VAREXPR
   | VAREXPR
VAREXPR 
   : INTEXPR
   | POSEXPR
   | ARRAYEXPR
INTEXPR 
   : ARRAYNAME[EXPR]
   | POSEXPR.INT
   | POSEXPR.FIELD
   | INT
POSEXPR 
   : POSARRAYNAME[EXPR]
   | POSARRAYNAME[EXPR].
   | POSNAME
   | POSNAME.
ARRAYEXPR 
   : POSARRAYNAME
   | INTARRAYNAME
   | POSARRAYNAME[]
   | INTARRAYNAME[]
FIELD 
   : 'X'
   | 'Y'
   | 'Z'
   | 'P'
   | 'R'
INT : any positive number or 0

In summary:

The selectors used without selection clause allow to resolve namespace ambiguities: Because all the XXXXXNAME tokens are actually the same token (a string with the symbol identifier), there's a huge ambiguity issue here when parsing the grammar.
I've coded my own grammar parser, in such a way that it allows to carry forward several "candidate" variables (upto one of each type) and when you find a token which narrows the variable type, I discard those types not allowed by that token.
I.e, if you find name it could be a task, position, array or integer; but if you then find [ you know it has to be an array (position array or integer array), so you can stop carrying forward all the other variable types. If no type-restrictor token is found, a priority lookup order is used: local integers are looked first, then local arrays, then global integers, then global arrays and then tasks.
Note that the grammar doesn't force you to use the ambiguity resolver tokens in the expression, in which case it will default to the aforementioned fixed resolving order.

CTreeListCtrl

Fixed tab handling when the current cell is not editable (it tabs to an editable cell).
Fixed button & line drawing (the original code was written by trial and error) and changed the indent to 8 pixels.

WatchView

Adapted to use the new expression syntax. Added support for setting watches values, as well as getting watches values.
There's a big todo here right now, as memory for watches is leaked (argh).

Today's screenie is the new watch expressions in the works. You can see how you can index an array with an arbitrary complex expression:


Several watch expressions

14th February 2003

VSS integration.

I've added VSS integration. This means that I will have a finer control on all the changes I make from now on. The VSS integration was a bit tricky because my VSS registry entries had vanished. Entering the following entries in the registry worked the magic (I had only the IDE VSS integration missing, i.e. the whole SourceCodeControlProvider branch):
[HKEY_LOCAL_MACHINE\Software\SourceCodeControlProvider]
"ProviderRegKey"="Software\\Microsoft\\SourceSafe"

[HKEY_LOCAL_MACHINE\Software\SourceCodeControlProvider\InstalledSCCProviders]
"Microsoft Visual SourceSafe"="Software\\Microsoft\\SourceSafe"

[HKEY_LOCAL_MACHINE\Software\Microsoft\DevStudio\6.0\Source Control]
"Disabled"=dword:00000000

WatchControl

In the control front, I renamed the CWatchCtrl to CTreeListCtrl, I may abstract it a bit and reuse it for property grids, etc. I also added deletion of rows via VK_DELETE and notification messages (NC_BEGININPLACEEDIT and NC_ENDINPLACEEDIT although only the latter is implemented).
With the addition of the textentry row, I've almost finished the control (barring painting optimisations & bugfixes)

Slight watchexpr rework

Regarding ACL, I tweaked a bit the VirtualMachine::GetWatch interface so it now tries to match the expression string to a given taskname, in which case it only returns the variables of that task (otherwise it returns a list of all the tasks and their variables).
By plugging that into the test program, I have now a fully working debugging session with user entered watches (previously it was hardcoded to show all the tasks and vars), variable value change update and highlight (in red).


Note the textentry row and the variable value change highlighting

13th February 2003

Doh, doh and more doh.

When trying to use "window standard scrollbars" to my CWatchCtrl (as opposed to independent scrollbar controls), I've noticed that STATIC controls do not forward click events to those scrollbars (no matter what static style you set to it). Using a BUTTON instead of a STATIC seems to work.

As usual, MSDN doesn't say anything about this (or it doesn't say it in the correct help topic).

Well the scrollbar interface is now working. I finally derived the CWatchCtrl from my own registered class (deriving it from "BUTTON" worked but caused a very big grey button to be drawn). There are still a few quirks with pageup/pagedn keyboard interface, but it more or less works.


Watch-control with integrated scrollbar

12th February 2003

Yeah!

Almost finished watch-control (note tree buttons, etc)
The scrollbar is still missing as are some gray-lines (and the lines between rows should be dotted, etc), but it's fully functional :)

Here's the latest version:


Real watches on an ACL program (note one tree per task, etc)
The code interfaces really need a severe polish-up, as right now everything is hacked together with staples. The CWatchControl itself is in quite a good shape, but for the few missing features (scrollbar, caching of positions and intelligent invalidation of rows, message notification to parent) and the column-width setting code (it currently creates negative-width columns and causes the code to crash ... doh).

11th February 2003

While trying to draw the tree buttons, I found the reason why DrawIcon didn't work properly when trying to use it to convert HICONs to pixmaps.
In the documentation of DrawIconEx, you can read:
To duplicate DrawIcon (hDC, X, Y, hIcon), call DrawIconEx as follows:
DrawIconEx (hDC, X, Y, hIcon, 0, 0, 0, NULL, DI_NORMAL | DI_COMPAT | DI_DEFAULTSIZE);
The DI_DEFAULTSIZE flag means that the icon will be stretched to SM_CXICON and SM_CYICON sizes (or to the cxWidth and cyHeight parameters if they are not zero).
I didn't check DrawIconEx before because MFC's CDC class doesn't have a wrapper for that :(.

10th February 2003


Preliminary watch-control (note inplace edit)
After much fighting against GDI stooopid controls, I've decided to build the watch window from the ground up. There's no sane way of using the TreeView or the ListView to emulate msvc's watch window.
I tried serveral things, but at the end of the day I learned that if I wanted inplace editing of two or more columns it was going to be very flickery and I would have to replace a big amount of the TreeView behaviour. The available per item custom draw hooks are not enough at all, as many things happen behind your back without you being able to help it (specially if you try to provide inplace editing of multicolumn tables, full row selection while keeping the tree "lines" - you cannot have lines and full row styles at the same time).

So I've built my own CStatic-derived control. Right now it allows inplace editing of cells, navegating with tab/return/updown, and row selecting with mouse. It's not very flicker free yet, and it lacks headings, a vertical scrollbar , column resizing and tree-like behaviour (hiding/showing files). I hope to code these shortcomings tomorrow.

On the language side, I've coded some functions to get WatchedExpr.

  1. The debugger framework fills the WatchedExpr "name" string and sends it to the language implementation.
  2. The language implementation fills in the value and one "accessor" field, needed in case the way of accessing the variable data is different from the name. For example, the watch window for array "arrayname" would be something like:
    NAME      | VALUE  | accessor (hidden from debugger output)
    arrayname | {...}  | arrayname
       [0]    | 225    | arrayname[0]
       [1]    | 256    | arrayname[1]
       [2]    | 1      | arrayname[2]
    
    the expression to watch is "arrayname", but the WatchedExpr returned is one composed by a base WatchedExpr with the accessor "arrayname" and several embedded WatchedExpr with names "[0]", "[1]", "[2]" and accessors "arrayname[0]", "arrayname[1]", etc. This decouples debugger-shown representation (the "name" of the expression) from the language-specific way of accessing one variable.
  3. As noted in the previous example, WatchedExpr are by nature recursive, as the debugger can query an array variable and the language will return a nested WatchedExpr with every position of the array (you could also have arrays of registers, registers of arrays, etc upto any nesting level).
  4. I will also introduce readonly/readwrite attributes to the WatchedExpr so the debugger knows which components of the expression can be written.
I'm still thinking on whether the accessors should be accumulative or not in case of nested expressions.
ACL will only allow simple WatchedExpr (id, id.id, id[n] and so on), but the interface is flexible enough so other languages can implement more complex ones if they feel like it.

While coding the watch control, I've watched (heh) a very moving movie (doh, this sentence is getting worse every moment), Hilary and Jackie, about the life of sisters Hilary and Jacqueline Du Pré (the latter a very well known celist player, who married famous pianist/conductor Daniel Baremboin). I won't spoil the film here, so I'll just say that the story is very interesting and touching, with a superb classical music score, so don't lose the chance of watching it if it happens to be aired at some TV channel nearby.

8th February 2003

Spent the day retrieving and reading information on Windows Scripting Host architecture and interfaces. Looks like it's quite easy to add scripting to your app by implementing IActiveScript and IActiveScriptSite. I found Mark Baker's webpage, he's the maintainer of the WSH faq , a superb reference for activescript.

BTW, I was told that there's a Lua WSH port and indeed there is

7th February 2003

Today's been great ... NOT!. The power supply of my PC (an oldish K7-500) began to do strange noises. I had seen it before flashing with some electric bolts when I would switch it on, but today's noises were too much. I switched it off and it never switched on back again :(.

My dad spotted that one of the round shaped pieces inside the supply was burnt. The piece is an NTC thermistor, a temperature-dependent resistor in charge of regulating the fan speed (google pawah). Looks like common resistor values for that piece are in the order of 10K ohms (as seen in some schematics on the net), but those pieces don't normally have a quantity label attached (and even less the one I have, as it was really burnt).

So I went out for the NTC hunt, but I only found 5K ohm thermistors and they were too small for my likings, so I bought a new power supply for 20 euros (I found 300Watt power supplies for 20, 30, 40, and 46 euros, so you'd better watch out when buying this kind of things).

Regarding the progress (no piccies today), I have the instruction debugging fully implemented and hooked to the Scintilla test app:

The method I've used is really working nicely, just by using Tick, GetCurrentFilePos, GetValidFilePos, the function void* GetLocalContext(int nLevel) and careful comparison of different Local contexts I can implement all the aforementioned functionality.
For my ACL interpreter, a Local Context is a task pointer, i.e., when nLevel is 0 this function returns a unique id for the local task being executed. Increasing values of nLevel return task ids in the calling stack frame.

I think this method is "language agnostic" as, for example, a recursive language (which ACL is not) could return the stack pointer if it can reenter subroutines, etc, so you could use the same debugger framework and just plug in the new interpreter.

Currently, the only problem is that I run the instruction interpreter hooked on an OnTimer, this means that each tick takes 10ms. I plan to move it to a different thread and run it full speed so if you are waiting for a breakpoint to hit you don't have to wait 10ms for every instruction it has to execute (unless you are in wall-clock accurate mode). This will introduce the usual multithread fun ;/.

So regarding the debugger, I have only missing symbol debugging (watches!), and maybe data breakpoints (once I have watches, that should be a child's play).

Oh, yes and I've been told that VC7 accepts void *rawMemory = operator new[] (10);, there it goes my call to fame :(.

6th February 2003

Today I've rationalised the HICON/HBITMAP to pixmap interface for the CScintillaEditCtrl (it's used for defining markers), you can convert them to pixmaps in various ways: And today's pic is:

The step-by-step debugger with HICON priority and breakpoints
I've added some framework to allow breakpoints. Now I have a reverse lookup of instructions: given a file line & file name: you provide a file line & name and it returns the closest file line & file name with a valid instruction on it.

One of the uses of this reverse lookup is to set breakpoints and check that the text line where the breakpoint is set contains an executable instruction, otherwise the breakpoint is moved to the closest line with a valid instruction. Plugging in proper breakpoint handling is trivial from there.

I've also noticed that Scintilla's markers have priorities, i.e., marker with higher ids are drawn over markers with lower ids. This means that the marker id signalling the current instruction must have the highest id (so it's displayed over breakpoints, bookmarks, etc).

5th February 2003


The step-by-step debugger using HICONS
I've created a DefineMarker overloaded function which accepts an HICON, so now I can use HICONS or IMAGELIST elements as markers :)

I went into a lot of trouble to get the pixel data from a memory HDC when using DrawIcon to render an HICON extracted from an IMAGELIST. For some reason, the icon would be drawn at x2 size (it worked ok if the icon was a plain HICON resource), so I've finally calculated the masking myself by accessing the mask and color bitmaps and not using DrawIcon.

I've also corrected some debug information mismatch when producing ELSE and ENDIF code (the file line would point at the wrong line) and rationalised which instructions are executed by the true branch of the IF and which are executed by the false one:

IF cond		-> Executed by both
	instr	-> Executed when cond is true
ELSE		-> Executed when cond is true
	instr	-> Executed when cond is false
ENDIF		-> Executed when cond is false
This will affect which lines you can put breakpoints on and when they trigger. It more or less follows the behaviour of VC++.

4th February 2003


Step-by-step execution of an ACL program
Cute, isn't it! :)

After spending the last few days blindly hacking my Scintilla MFC wrappers (CScintillaEditCtrl.h and CScintillaEditCtrl), I've finally been able to actually see some code being run line by line.

Today I've added debug information retrieval and request to the virtualmachine (get filename & fileline from the instruction to run). This allows to do a "step by step" execution and that's what the previous picture shows. I changed the Scintilla wrappers to use 1 as first line instead of 0 (I find it a bit strange that there's a "line 0" of a text).

I still have to add:

I should polish up the debug interfaces first, though. I've also had some ideas on the rendering features, I want to use depth shadowmaps:

I really need to begin to use source control <argh!>

31st January 2003

Not much progress in the last few days. I was ill on Monday because of having milk in bad shape (I was given it with my cream & chocolate at a coffeshop!!).
I spent Tuesday and Wednesday looking at using MS's ScriptControl, in order to use jscript code mainly to: As an added bonus, you should be able to use any WSH language (vbscript, perl or python from Activestate ... - Is there a lua WSH port?).
I also spent some time thinking about the invkin & dirkin interface to the language and the operations where that functionality is needed, mainly:
On Thursday I created a new project SciTest to test the great Scintilla editor, and I've created simple MFC View and Control wrappers for it. The idea is to use this test project to begin to plug in the debugging interface to the acl language (filename, lines for each instruction, variable watches, breakpoints...).

I found an interesting article by Paul Di Lascia on using CControlView to create views of controls. The bottom line is that CControlView (deriving the view directly from the control) is quite unless your control doesn't have extra local data (nor virt funcs), so I may create a thin CUserControlView to encapsulate the onsize & onfocus changes needed when going the route of making the control a child of the view.

Oh, yes, I almost forgot, these past days I also added parser support for SETP/MOVE instructions.

24th January 2003

After a last-minute bugfix, IF, ANDIF, ORIF, ELSE instructions are working. Out of interest, the false branch jumps to the post-ENDIF instruction (it's done that way to jump over a hypothetical ELSE), this is not really a problem, but I may want to change it to jump to the ENDIF instruction.

23rd January 2003

Well, it's finally working!!!: The bison code at last compiles, instructions are being executed by the virtual machine and I have task switching back in place, working for RUN and GOSUB.

I still have to:

The Interface base class is working ok, although I've found a strange problem with VC++ not being able to do *pIntReadWriteable = iMyInt;, although it's able to do *pIntWriteable = iMyInt, looks like some int constness problem in the IntReadable interface :? (out of interest, MEC++ recommends not to virtualize casting operands, shame on me).

I'm still thinking on using exceptions and rewrite some parts to exception-safe code (probably using auto_ptr).

20th January 2003

Created an abstract base class "Interface" for all the interfaces, all the interfaces now inherit this class virtually (via virtual inheritance).
This Interface class defines the destructor as pure virtual, thus forcing the implementors to declare the destructor as virtual, so any pointer to an interface can be deleted properly (this is used in lots of places).
Interfaces descendant of other interfaces may or may not inherit this class (as they are already indirectly inheriting it).
The virtual inheritance is necessary so a class can implement more than one interface without ambiguities.

Other implementation alternatives would be:

Both alternatives are too cumbersome to implement, as most of the time instances will be deleted via pointers to their interfaces (the ownership of a pointer is often transferred when the pointer is passed as an argument). Clear examples of this are operands of one instruction, where a pointer to an integer operand (which may come from a constant, an integer variable, an access in an array of integers, etc) is created and then its ownership transferred to the instruction it will be operand of. The instruction will delete the pointer when the instruction itself is deleted (there's no other record of this operand anywhere else).

With regard to the global progress, normal set operations are now implemented, still thinking on how implementing PVAL and PVALC (maybe creating a new IntFromPosOperand)

18th January 2003

I just found that ACL has only one command to delete positions and vectors of positions DELP, this means that they share the same namespace, so you cannot have a vector of positions with the same name as a position. Same happens to integer variables.

So the symbol table needs redesigning:

17th January 2003

Well, I will soon be able to compile again the .y and .l files (famous last words). Things seem to go Ok, although I haven't started the code generation routines yet (just the variable definition ones). The symbol management, split between VirtualMachine (global symbols) and Task (local ones) looks a bit cumbersome, but with the proper access functions the implementation is nicely hidden.

Still not very convinced on naming conventions. Right now I'm using smallcaps namespaces, capital initials for classes and methods, able for interfaces, m_ for members, initial capitals for compound variable names (aka camelcaps) and a bit of hungarian notation (initial p for pointer variables, etc).
I still think that the CACLWhatACuteClassIAm looked very nice.

On a side note, VC 6.0 / VC 7.0 don't seem to accept void *rawMemory = operator new[] (10); although gcc does. This is somewhat important because it's written on page 21 of More Effective C++ by Scott Meyers. If this was a true typo, I could send it to Mr. Meyers and I would appear as collaborator in the next reprint of the book (wow!).

13th January 2003

Problems found with storing the project in a different directory to the source:

12th January 2003

Although I would like to use STL & QT, because I don't think I will have time to finish my QT port to win32 (I have the command tools working, but I haven't delved into the GUI classes yet), I've decided to use MFC for everything, including containers.
I could use MFC & STL, but as I have to use MFC, I prefer to be consistent (specially because of string vs. CString interactions I might have).