tag:blogger.com,1999:blog-43435805532771183102024-03-08T10:48:38.032+01:00Macke's BlogRandom tidbits related to software development...Mackehttp://www.blogger.com/profile/06392128938508053935noreply@blogger.comBlogger47125tag:blogger.com,1999:blog-4343580553277118310.post-7883398624634207022019-08-06T17:01:00.002+02:002019-08-06T17:01:44.957+02:00Home Automation - Sun blinders, plans vs reality<h2>
Outdoor lighting prevention, part 1</h2>
As you may have deduced from my previous post, living in Sweden in autumn/spring, you will notice that the sun doesn't go up very high unless it's high summer. It shines, more or less horisontally, right through your windows and onto your TV, computer monitor or face. That quickly gets annoying, plus the houses are built for winter, not summer, so when it's +30 degrees Celcius outside and sunny, it gets awfully hot inside.<br />
<br />
Thus, some external sun blinders are nice, as they keep heat and solar megalumens outside (where it belongs during summer). They still allow you to see your more-or-less-green lawn and the state of the great outside. Hence, I prefer them quite a bit before curtains in various forms. Also, our house had them when we bought it.<br />
<br />
The idea for this project started after we built a new patio-roof starting at the house wall . We had to replace the 8-foot manual crank pole to the 2nd floor blinder as the roof physically blocked it. So, I bought and installed an electric motor instead. Quite nice, but it added 3000 SEK (~300€) to the roof project cost. Ugh, However, the motor is actually quite neat, AC-mains driven (one wire for up, another for down, plus neutral and earth) and adjustable end-stops to avoid damaging anything, plus it fits inside the tube.<br />
<br />
I declined the wireless option (didn't even ask for price) as I didn't feel it was worth it then (I didn't want to buy the motor in the first place!). My experience was that such systems are usually proprietary, closed, expensive ones that are hard to integrate with much anything else.<br />
<br />
Then, as we spent the rest of that summer moving the blinder up and down more or less every day, down in the morning sun (which comes up at about 5.a.m, before any wholesome beings are awake),<br />
up in wind and rain, or after 2 p.m. when the sun wasn't shining in anymore and you actually wanted to enjoy the view, my inner nerd/optimizer finally had it. We had to automate this!<br />
<br />
This was a year or so before I got the Raspberry/Tellstick/Nexa combo, so this was actually my first home automation project.<br />
<br />
Anyway, I bought a bunch of Arduinos but nothing really came from that. Instead, I got a Nexa motor receiver and a wall transmitter. The transmitter is used to lower the blinds in the morning (when one wakes up) and the Raspberry/Tellstick then sends a signal (essentially "lamp off") to retract the blinders at 14:00 every day. This has worked well enough, although I would like for it to automatically lower in the early morning (04-05:00) during summer. I'm not there yet.<br />
<br />
However, I am now building a controller for a second sun cover (5x4m on top of our transparent patio roof), and that is almost done. Stand by for more info. :) Mackehttp://www.blogger.com/profile/06392128938508053935noreply@blogger.com0tag:blogger.com,1999:blog-4343580553277118310.post-82818374380961044772019-08-06T16:49:00.002+02:002019-08-06T16:52:46.781+02:00First adventures in Adobe Fusion 360I made a thing!<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-wroaaxn6G3g/XUmQiEWP79I/AAAAAAAAClM/RMDoyAATGm8Cu8dRAJvQi50uef3gmjhIgCLcBGAs/s1600/Wheel_CAD_2.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="987" data-original-width="1005" height="196" src="https://1.bp.blogspot.com/-wroaaxn6G3g/XUmQiEWP79I/AAAAAAAAClM/RMDoyAATGm8Cu8dRAJvQi50uef3gmjhIgCLcBGAs/s200/Wheel_CAD_2.PNG" width="200" /></a><a href="https://1.bp.blogspot.com/-KjbSqisSW8o/XUmQiN4SqnI/AAAAAAAAClQ/i1dWeLhhGw8Xxa2SqYAIdVsLSzlLCwCAwCLcBGAs/s1600/Wheel_CAD.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="951" data-original-width="1015" height="186" src="https://1.bp.blogspot.com/-KjbSqisSW8o/XUmQiN4SqnI/AAAAAAAAClQ/i1dWeLhhGw8Xxa2SqYAIdVsLSzlLCwCAwCLcBGAs/s200/Wheel_CAD.PNG" width="200" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
This is a split timing belt pulley that will drive the sun-cover on my terrace. It is is currently manually operated and quite often fails to retract when the sun goes down, it starts to rain or the tenants leave for vacation.</div>
<br />
The base is a 32 tooth T5 belt pulley from MacMaster-Carr. (I had a T5 belt and some smaller pulleys already.)<br />
<br />
The sides are extended on to accommodate for clamping/fastening.<br />
The belt guides where made a bit thicker and a fillet was added make the sides a bit more robust.<br />
Additionally, the gap between the two halves have been reduced by 0.5 mm to actually provide some clamping force.<br />
<br />
Four M5 bolts/nuts pairs clamp on the 13mm axle.<br />
<br />
Two M6 set screws to grip into the axle itself. (Those holes will be threaded manually. If the plastic isn't strong enough I can always drill it up a bit larger and insert helicoils, or redesign with nut cutouts on the inside similar to the outside).<br />
<br />
The plan is to 3D-print it, which will look something like this:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-jqvhNknVKes/XUmQiNPNOJI/AAAAAAAAClU/qG5M31lkCnU2XLz7c9SSCSoxToF0DyXqwCLcBGAs/s1600/wheel_render.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="600" data-original-width="800" height="240" src="https://1.bp.blogspot.com/-jqvhNknVKes/XUmQiNPNOJI/AAAAAAAAClU/qG5M31lkCnU2XLz7c9SSCSoxToF0DyXqwCLcBGAs/s320/wheel_render.png" width="320" /></a></div>
<br />
I'm quite happy with this little piece, and that I managed to do it without any previous Fusion 360 experience in a few hours. :-D<br />
<br />
The step file is available for download: <a href="https://drive.google.com/open?id=1OJXW0V5NUZisEDNyUDbtnnTzYdQ9hcet">13x5T-32-Split v21.step</a><br />
<br />
<br />Mackehttp://www.blogger.com/profile/06392128938508053935noreply@blogger.com2tag:blogger.com,1999:blog-4343580553277118310.post-91922198851009219522016-02-04T21:42:00.000+01:002016-02-14T22:08:52.291+01:00Home Automation - Raspberry flavour<h2>
Wireless indoor lighting, sort-of</h2>
If you've been to Sweden on different times of the year, you quickly notice that the amount of sunlight varies between 5 hours to 20 hours. Hence, in the winter, you want the lights on quite a bit,<br />
<br />
To avoid adjusting the light timers to account for the dusk/dawn moving about 15 minutes per week in spring and autumn, I've wireless:d a lot of the lighting with the help of a <a href="https://www.raspberrypi.org/products/raspberry-pi-2-model-b/">Raspberry Pi 2 Moveld B</a>, a <a href="https://www.adafruit.com/products/1601">2.8" touchscreen</a>, a <a href="http://www.telldus.se/products/tellstick_duo">Tellstick Duo</a> and a ok-ish Java program called <a href="http://nexahome.se/">NexaHome</a>. I've mainly used Nexa and Proove switches and relays. The latter are cheaper and works just as well. The Proove switches' relays give a good "thunk" when switching on or off and that actually feels safer than something quiet and tiny, especially when you're dealing with mains voltage. <br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://www.kjell.com/resources/sv-se/fraga_kjell/hur-funkar-det/smarta-hem/2-1_2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://www.kjell.com/resources/sv-se/fraga_kjell/hur-funkar-det/smarta-hem/2-1_2.jpg" height="204" width="320" /></a></div>
There are plenty of guides on the Internet on how to setup each of these, all better than what I can write. Suffice to say, installing Raspbian and downloading & installing packages (Java, Qt, etc), compiling what you need (the Telldus daemon/GUI and touchscreen driver) and setting everything up wasn't too hard.<br />
<br />
The setup works pretty well and the wireless 433 MHz signal reaches 15 m to our storehouse where we have some christmas lights hooked up. NexaHome has a web server so I spent a few minutes crafting a special webpage to fit the RaspBerry's touchscreen. Thus, one can control the lights by not having a smartphone. I like that, but I also like being able to shut off the lights from bed if I went to sleep a bit earlier.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-HQlSHW_kkGA/Vqpd-WiS4GI/AAAAAAAABZI/T8lqAT3R0Vc/s1600/raspberrytellstick.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://4.bp.blogspot.com/-HQlSHW_kkGA/Vqpd-WiS4GI/AAAAAAAABZI/T8lqAT3R0Vc/s320/raspberrytellstick.jpg" width="319" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
The main issue I had with the setup once I got it up and running was that I couldn't get the touch screen and the HDMI output to work at the same time. I think it's just a limitation of how the driver was written at the time. (Maybe it has changed now). It'd been nice to use it as a media player or some such, but with Netflix, Spotify and Viaplay via Chromecast, we're pretty well set and I haven't missed it. Nowadays I don't have time to consume as much media, so the few series/movies I watch I can pay for. (Not like back in college where I had watched ~140 Naruto episodes in a month or two.)</div>
<br />
The NexaHome software isn't the best nor greatest, but it has worked for me over a year or two, so I've actually donated a bit. The commercial alternative was <a href="http://www.switchking.se/">SwitchKing</a>, but I felt the price at the time (250 SEK, ~25€) was a bit too much since what I had at no-cost worked. I noticed that the price has recently dropped as the authors have put the project into support-mode, so it may be worth taking a look again. However, having worked with real industrial PLC:s the recent year, I'd much prefer something of that sort now.<br />
<br />
Anyway, the Pi will probably serve it's duty as a home (lighting) automation center for a few years to come. The 433 MHz RF system is nice and good value-for-money, even though it misses a light or two sometimes (even on repeat, but rarely more than a few times per month),<br />
<br />
We'll see what happens when or if new requirements are needed.<br />
<br />
<br />
Next post will be about my (scrapped) first plan for the house's sun blinders ... and maybe hint of what duty that Arduino will actually end up doing...<br />
<br />
TTFN.Mackehttp://www.blogger.com/profile/06392128938508053935noreply@blogger.com0tag:blogger.com,1999:blog-4343580553277118310.post-9218436474652273732016-01-28T19:17:00.003+01:002016-01-29T07:55:20.102+01:00I'm back!So, after a three year hiatus on postings I'm here again! Yaay!<br />
<br />
This post will be the first of a short-ish recap of past highlights and future plans, of which I am quite excited. I will also try and keep the posts shorter but more frequent.<br />
<h3>
Professional life changes</h3>
Halfway through that time I changed employer, and managed to learn quite a few new tricks at the new place. I'm still doing software development, but as a consultant, which has exposed me to a lot of new people, places, projects, PLC:s and PowerPoints. (And companies, languages, APIs and hardware, but those don't start with a p. ;)<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://www.bitaddict.se/wp-content/themes/bitaddict/img/logo.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://www.bitaddict.se/wp-content/themes/bitaddict/img/logo.png" /></a></div>
<br />
My current job is as a (heavily developing) systems architect at <a href="http://www.bitaddict.se/">Bit Addict</a>. The company is lead by an old friend of mine from the university days, and I signed on as peon #1. We've grown from 2 to 5 people in 1,5 years and are targeting around 8-10 in a few years. We're doing software work for manufacturing & automation companies, ranging from ERP-integrations all the way to safety hardware installation. Our customer are pretty high tech, high power lasers (4-6 kW), titanium 3D printers or surgery simulations, so the applications are quite exciting.<br />
<br />
Nevertheless, the best part is that we're out there, doing good work that our customers appreciate and building a company and relations by our own. A lot is being learnt, but as long as one avoids making the same mistake twice, it's all part of the process.<br />
<h3>
Reminiscing...</h3>
I suggested the name of the company as I used the same name for a small PC demo a friend and I did together for DreamHack 1997 (this was back in high-school). This, kids, was when all we had to play with was a framebuffer and a weak CPU. No libraries, APIs or GPUs or whatnots. It was all our own code, except for the music-player, the VGA-mode setter, and a bit of interpolation code we got from his big brother.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-UFfiAFwhvcQ/VqsL3L1dzUI/AAAAAAAABZY/Kqg3457V9oE/s1600/neuraldebris.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="200" src="http://3.bp.blogspot.com/-UFfiAFwhvcQ/VqsL3L1dzUI/AAAAAAAABZY/Kqg3457V9oE/s320/neuraldebris.jpg" width="320" /></a></div>
<br />
You can still <a href="http://www.pouet.net/prod.php?which=18007">download</a> and run it in a <a href="http://www.dosbox.com/">DOS emulator</a> if you want to. It's not that good, honestly, but as we came in 3rd place out of 7-ish entries, I suppose a tiny bit of pride is in order. This was back when there was about 400 attendees with their own computers, as opposed to 9500 last year (2015).<br />
<br />
It's a different world today, since the competition is more about creativity than technology, but looking at 4K or 64K demos never fails to impress.<br />
<br />
I actually got a huge flashback to the demo scene days by playing <a href="http://www.zachtronics.com/tis-100/">TIS-100</a> over the holiday (christmas gift from a friend, thanks Nix!). The fun part if the game is sitting and optimizing a program until it is as small, fast or efficient as it can be. The downer is that I can't fool my brain into thinking it's a game all the time, thus it feels a bit close to work at times. Still, good fun and recommended to anyone who've done a bit of assembler hacking in their youth.<br />
<br />
<div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://i.ytimg.com/vi/ZkUHGvy2pNU/hqdefault.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://i.ytimg.com/vi/ZkUHGvy2pNU/hqdefault.jpg" width="320" /></a></div>
<br /></div>
<div>
My next post will be about ... Home Automation! .. Starring a raspberry pi... Stay tuned.</div>
<div>
<br /></div>
<div>
TTFN.</div>
Mackehttp://www.blogger.com/profile/06392128938508053935noreply@blogger.com0tag:blogger.com,1999:blog-4343580553277118310.post-19462935791560055812013-01-06T22:23:00.001+01:002013-01-07T09:26:18.628+01:00Singletons in C++, a tour through lessons learned(Sigh. I just noted that Blogger's WYSIWYG editor doesn't escape brackets properly, so all the template parameters are gone from the code examples... sorry about that.)<br />
<br />
Having coded a lot of C++ (10 years professionally), plus a brief (1,5 year) string in Python, I've gained some perspective on how to cope with these beasts. I hope some of you will find it interesting, but I expect (or rather, hope) experienced readers with mostly just nod and agree.<br />
<div>
<br /></div>
<div>
The notable C++ apps I've been involved with usually has between two and fifteen "manager" objects that we use here and there. This involves various tasks, accessing the file system (usually with some caching and custom path lookups), managing logging, loading shared (graphics) resources such as sounds, textures, shaders (and during development, instantly reload these if the change on-disk), being etc etc etc.</div>
<div>
<br /></div>
<div>
So, you have all these things that:</div>
<div>
<ul>
<li>need to be accessed by a large number of components in your app</li>
<li>should usually exist as one instance</li>
<li>should exist when we access it</li>
<li>will probably depend on each other a bit (loading a texture needs the file system, logging needs file system, file system might want to log, etc.)</li>
</ul>
</div>
<div>
(Experienced readers will loudly think "separation of concerns", and rightly so. I'll get there eventually.)</div>
<div>
<br /></div>
<div>
As touted by the GoF as a proper Design Pattern (which, IMHO according to them, is often just a term for how to code something in pure-OOP that should be in the language from the start, such as multiple-dispatch, visitor, etc etc..) they should be initialized lazily.</div>
<div>
<br /></div>
<div>
<span style="font-size: large;">Singleton implementation 101</span></div>
<div>
<br /></div>
<div>
The easiest way in C++ to do this is a function local static variable. This is not thread-safe unless your compiler supports it (usually with a flag).</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">class Singleton {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> static Singleton& the() {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> static Singleton s_the;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> return s_the;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> void foo() { ... }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">private:</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> ... </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">};</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: inherit;">(FYI, I prefer the m_ prefix for member variables, s_ for static, and ms_ for member-static...)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: inherit;">To get something thread-safe without compiler support, you have to check this yourself, but you also need a mutex or two, which must be created safely too. (Locking the mutex on every access can be avoided by doing double-dispatch. This requires careful attention to the memory semantics on your platfrom (default behaviour x86 seem ok though), but note that <a href="http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html">Java's double-dispatch is broken</a> due to it's slack memory model & aggressive optimizating compilers. It might be fixed in more recent version though.)</span></div>
<div>
<br /></div>
<div>
So, with locking, you have something like this:</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;">class Singleton {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> static Singleton& the() {</span><span style="font-family: 'Courier New', Courier, monospace;"> </span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> scoped_lock lock(&ms_mutex);</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> if (</span><span style="font-family: 'Courier New', Courier, monospace;">ms_the</span><span style="font-family: Courier New, Courier, monospace;">== 0) {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> </span><span style="font-family: 'Courier New', Courier, monospace;">ms_the </span><span style="font-family: Courier New, Courier, monospace;">= new Singleton();</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> atexit(deleteMe);</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> }</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> return *</span><span style="font-family: 'Courier New', Courier, monospace;">ms_the</span><span style="font-family: 'Courier New', Courier, monospace;">;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> void foo() { ... }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">private:</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> static std::Singleton* </span><span style="font-family: 'Courier New', Courier, monospace;">ms_the</span><span style="font-family: Courier New, Courier, monospace;"> // set to null before main(), declared in cpp-file</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> static Mutex ms_mutex; // constructed before main()</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> static void deleteMe() { delete </span><span style="font-family: 'Courier New', Courier, monospace;">ms_the</span><span style="font-family: Courier New, Courier, monospace;">; }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> ...</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;">};</span></div>
</div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">// you could/should probably use boost/std scoped_ptr/unique_ptr to avoid calling the c-library's atexit(), if you have access to those.</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
A lot of problems here are caused by having to implement lazy initialization. And most often, you don't need that, because your managers-as-singletons are created deterministically anyway, and if things get hairy, you want explicit control. I'll get to that in a moment.</div>
<div>
<br /></div>
<div>
My default singleton implementation for small C++ projects avoids lazy initialization and the problems with order-dependency during construction/destruction between managers by simply not constructing itself. That is usually left to the main() function. (Or the unit test fixture, or whatnot...) Usually your app has some well defined, single-threaded, execution-paths where this takes place, if main() doesn't suit you.</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;">class Singleton {</span></div>
<div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;">public:</span></div>
</div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> Singleton& the() {</span><span style="font-family: 'Courier New', Courier, monospace;"> </span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> assert(ms_the);</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> return *</span><span style="font-family: 'Courier New', Courier, monospace;">ms_the</span><span style="font-family: 'Courier New', Courier, monospace;">;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> Singleton() { </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> assert(ms_the == 0);</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> ms_the = this;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> ~Singleton() {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> assert(</span><span style="font-family: 'Courier New', Courier, monospace;">ms_the</span><span style="font-family: Courier New, Courier, monospace;"> == this);</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> ms_the = 0;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> void foo() { ... }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">private:</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> static Singleton* </span><span style="font-family: 'Courier New', Courier, monospace;">ms_the</span><span style="font-family: Courier New, Courier, monospace;"> // declared in cpp file, so is set to null before main()</span></div>
</div>
<div>
<span style="font-family: Courier New, Courier, monospace;">};</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">int main(...) {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> Singleton mysingleton;</span></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> Singleton2 mysingleton2; // uses first singleton</span></div>
</div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> mainloop();</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> return 0;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">}</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: inherit;">This is rather neat, I think. The singleton class has pretty minimal logic, just guarding against programming errors. It's constructed at a well defined point, and it's cleaned up on exceptions, or before main() returns (so we don't end up with half-unloaded DLLs during exit. This can be a problem..)</span></div>
<div>
<span style="font-family: inherit;"><br /></span></div>
<div>
<span style="font-family: inherit;">This way, we've solved how to make singletons always exist before we access them. We create the objects up-front, in a specific order. They will be destroyed in opposite order.</span></div>
<div>
<span style="font-family: inherit;"><br /></span></div>
<div>
Also, if we have several managers that depend on each other, any ordering issues can be resolved manually. This is especially important when shutting down (if you want to clean up properly...). Modern OS:es don't require that as much, since they will clean up your mess regardless if you crash and burn or exit the building the right way. However, it feels better to do it properly. You might have hardware that need shutting down or notification to network services to say that you're going down and you know about it (more or less). </div>
<div>
<br /></div>
<div>
Having an explicit construction order like this means that at the very least, you don't have to mess with something like the <a href="http://loki-lib.sourceforge.net/html/a00521.html">Loki SingletonHolder's</a> LifetimePolicy imlemented by the LifeTimeTracker and the GetLongevity funciton. Ugh. I've done that once, hunting around and setting longevity values in files all over the framework. Never again,! </div>
<div>
<br /></div>
<div>
It should be noted that the drawback of managing your singletons manually is that you need to alter a lot of main methods or unit test fixtures if you add a new singleton to a library that many of your apps uses. However, that's usually only done once (or twice), so it's not that bad. The asserts should trip in your smoke tests if something is wrong (smoke tests is incidentally the minimal amount of testing anyone should have, IMO. The rest is a never-ending debate...)</div>
<div>
<br /></div>
<div>
So, all's seems fine here... until you want:</div>
<div>
<br /></div>
<div>
<span style="font-size: large;">Two instances of a singleton!</span></div>
<div>
<br /></div>
<div>
(This is not a doubleton, which is more like what happens when you get the singleton creation logic wrong, or load the different versions of the same DLL, or are running Java, or... well..)</div>
<div>
<br /></div>
<div>
As an example, say if you have a TextureManager. This loads the images you apply on everything in the game, from environment to characters to the GUI. It has a function for reloading all textures (if the graphics context is lost, as can happens on Windows), clearing everything (done before loading a new level) etc. etc. Say, you might want one for overlay GUI (static throughout the run) and another for the in-game assets (that differ between each level). Separating this inside the TextureManager might feel backward to you, so what do we do? </div>
<div>
<br /></div>
<div>
If we had managed the singleton's lifetime with a static local function variable, just adding a second such function would work pretty well. Except that we don't want to do that, since the lazy initialization has issues with threading and lifetime ordering.</div>
<div>
<br /></div>
<div>
We could, and should, extract the management of the singleton's global access out from the class itself. ("Separate these concerns, already!" I hear... :) This is what Loki's SingletonHolder tries to do, except it's a toolbox so big you're likely to get something that <a href="https://www.google.se/search?q=c%2B%2B%20shoot%20in%20foot">shoots you in the foot, C++ style</a>, or at least chose the wrong tool). </div>
<div>
<br /></div>
<div>
Here's something smaller for brevity, where we use an integer template parameter simply to tag the class so that the compiler will generate different types (i.e. different classes) and thus separate static member variables.</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">template<class int="int" t="t" tag="tag"></class></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">class SingletonHolder {</span></div>
<div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;">public:</span></div>
</div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> T& the() { assert(ms_the); return *ms_the; }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> SingletonHolder() { assert(!ms_the); ms_the = new T(); };</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> ~SingletonHolder() { assert(ms_the); delete ms_the; ms_the = 0; }</span></div>
<div>
<br /></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;">private:</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> static T* ms_the;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">};</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
I'd put these typedefs somewhere, probably in the same header as the TextureMgr class for a small project.</div>
<div>
<span style="font-family: inherit;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">typedef </span><span style="font-family: 'Courier New', Courier, monospace;">SingletonHolder<texturemgr 0="0"> GuiTexMgr;</texturemgr></span></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;">typedef </span><span style="font-family: 'Courier New', Courier, monospace;">SingletonHolder<texturemgr 1="1"> LevelTexMgr;</texturemgr></span></div>
</div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"><br /></span></div>
<div>
Tehn, our main() would then look something like this:</div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">int main(...) {</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> GuiTexMgr</span><span style="font-family: Courier New, Courier, monospace;"> gui_texmgr;</span></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> LevelTexMgr level_texmgr;</span></div>
</div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> mainloop();</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> return 0;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">};</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: inherit;">... and we need to declare these, as with all the other statics from previous examples (I left that part out earlier).</span></div>
<div>
<span style="font-family: inherit;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">TextureMgr* GuiTexMgr::ms_the = 0;</span></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;">TextureMgr* LevelTexMgr::ms_the = 0;</span></div>
</div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
This is not too bad actually. The TextureMgr class doesn't need to bother about it's own lifetime or visibility. Our SingletonHolder does that work for us, isn't overly complex, and the order is well defined by main as we saw earlier.</div>
<div>
<br /></div>
<div>
Still, there's room for improvement, especially if we want to...</div>
<div>
<br /></div>
<div>
<span style="font-size: large;">Swap implementations!</span></div>
<div>
<br /></div>
<div>
As might be required depending on the current situation. (mocking out stuff during testing is a prime example, running a local vs. remote master server is another, as was done in Quake/Doom...). Not that singletons are really necessary for that, it could be hidden beneath interfaces and so on.</div>
<div>
<br /></div>
<div>
Anyway, it's rather simple to alter our SingletonHolder to manage that. We simply give it the instance that it should store:</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;">template<class int="int" t="t" tag="tag"></class></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">class SingletonHolder {</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;">public:</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> T& the() { assert(ms_the); return *ms_the; }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> SingletonHolder(T* t) { assert(!ms_the); ms_the = </span><span style="font-family: 'Courier New', Courier, monospace;">t</span><span style="font-family: Courier New, Courier, monospace;">; };</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> ~SingletonHolder() { assert(ms_the); delete ms_the; ms_the = 0; }</span></div>
<div>
<br /></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;">private:</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> static T* ms_the;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">};</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
And the main() is just this:</div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">int main(...) {</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> GuiTexMgr</span><span style="font-family: Courier New, Courier, monospace;"> gui_texmgr(new TextureMgr);</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> LevelTexMgr level_texmgr(new TextureMgr);</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> mainloop();</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> return 0;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">};</span></div>
</div>
<div>
<br /></div>
<div>
This also allows us to send constructor parameters to our instances (cache size or max memory usage, as could be relevant in the case of texture management in the example here).</div>
<div>
<br /></div>
<div>
Note that we're essentially going backwards from fully-automatic to manual handling of things. I argue that this is a good thing as your application grows. I also think we're quite low on overhead & complexity still, and the goings-on are easy to follow, understand and debug.</div>
<div>
<br /></div>
<div>
You can also write a small test that uses a mock implementation in the fixture (if you remove the GuiTexMgr from your test applications global fixture).</div>
<div>
<br /></div>
<div>
<span style="font-size: large;">SingletonRegistry</span></div>
<div>
<br /></div>
<div>
If (a.k.a. once) we get enough of these objects (and there could be more than a few, if you have a GUI app and want to track undo management, the applications "current" document's status, global time of the process you're modelling, settings saving to file/registry, etc) it can get both expensive and bothersome to having to create/destroy all these every time you want to test something. Also, there will be a lot of SingletonHolder instances here and there.</div>
<div>
<br /></div>
<div>
So, rather than having a lot of SingletonHandlers, we store everything in one place. For simplicity here, we assume all singleton-objects have a common base class (use <a href="http://www.boost.org/doc/libs/1_52_0/doc/html/any.html">boost::any </a>to work around that) and that we store and lookup the objects by strings (integers are fine too if it suits you, then you could use a std::vector instead of map for extra speed.). </div>
<div>
<br /></div>
<div>
Since we want to store everything in one place, we need to singletonize the holder I've used one of the earlier methods for that here. Recurse ad lib (ad nauseum?).</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">class SingletonRegistry {</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;">public:</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> static </span><span style="font-family: 'Courier New', Courier, monospace;">SingletonRegistry* the() { return ms_the; }</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> </span><span style="font-family: 'Courier New', Courier, monospace;">SingletonRegistry() { ms_the = this; }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> void store(Manager* mgr, const std::string& id) {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> assert(m_map.find(id) != m_map.end());</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> m_map[id] = mgr;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> m_seq.push_back(id);</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> };</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> template<class t="t"></class></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> T& lookup(const std::string& id) {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> assert(m_map.find(id) != m_map.end());</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> Manager* mgr = m_map[id];</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> assert(dynamic_cast<t>(mgr));</t></span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> return *static_cast<t>(*t);</t></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> } </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> ~SingletonRegistry() {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> // destroy in insertion order</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> for(size_t i=0; i<m_seq .size=".size" font="font" i="i" nbsp="nbsp"></m_seq></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> Manager* mgr = </span><span style="font-family: 'Courier New', Courier, monospace;">lookup(m_seq[i]);</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> m_map.erase(m_seq[i]);</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> delete mgr;</span><span style="font-family: 'Courier New', Courier, monospace;"> </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> ms_the = 0;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">private:</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> std::map<std::string manager="manager"> m_map;</std::string></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> std::vector<std::string> </std::string></span><span style="font-family: 'Courier New', Courier, monospace;">m_seq;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">};</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<div>
(Here, I've used RTTI via dynamic_cast to assert that the type is right. If you want to use some other type-lookup, feel free to do that. I think enforcing type-safety inside the lookup is a good thing.)</div>
</div>
<div>
<br /></div>
<div>
Our main function then looks like:</div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">int main(...) {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> </span><span style="font-family: 'Courier New', Courier, monospace;">SingletonRegistry reg;</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> reg.store(new TextureMgr, "textures/gui");</span></div>
<div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> reg.store(new TextureMgr, "textures/level");</span></div>
</div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> mainloop();</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> return 0;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">}</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
Now accessing the object is different, because we need to get by string rather than just call a static function on a class:</div>
<div>
<br /></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;">TextureMgr* mgr = </span><span style="font-family: 'Courier New', Courier, monospace;">SingletonRegistry::the().lookup<texturemgr>("textures/gui");</texturemgr></span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;">mgr->load(...);</span></div>
<div>
<br /></div>
<div>
<span style="font-family: inherit;">If we like strings (note the hierarchy I introduced), we can avoid doing this string-based lookup each time, we can trade time for space by re-casting our SingletonHolder into a reference holder:</span></div>
<div>
<span style="font-family: inherit;"><br /></span></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;">template<class t="t"></class></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">class SingletonHolder {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">public:</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> SingletonHolder(const std::string& id) : m_the( SingletonRegistry::the().lookup<t>(id)) { }</t></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> ~SingletonHolder() { ms_the = 0; }</span></div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> T& the() { </span><span style="font-family: Courier New, Courier, monospace;">return *</span><span style="font-family: 'Courier New', Courier, monospace;">m_the</span><span style="font-family: Courier New, Courier, monospace;">; }</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> // or, even neater:</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> T* operator->() </span><span style="font-family: 'Courier New', Courier, monospace;">{ </span><span style="font-family: 'Courier New', Courier, monospace;">return *</span><span style="font-family: 'Courier New', Courier, monospace;">m_the</span><span style="font-family: 'Courier New', Courier, monospace;">; }</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;">private:</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> T* m_the;</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">};</span></div>
</div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
So, a class using this could look like this:</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">class ProgressBar {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">public:</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> </span><span style="font-family: 'Courier New', Courier, monospace;">ProgressBar() : m_texmgr("texture/gui") {</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> m_texmgr->loadTexture("progress.tex");</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> }</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> void draw() {</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> m_texmgr->bindTexture("progress.tex");</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> ...</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> } </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">private:</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> SingletonHolder<texturemgr> m_texmgr;</texturemgr></span></div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">};</span></div>
<div>
<br /></div>
<div>
Then, we can add instances of this class as member variables to our objects, or as on-stack function variables. Of course, you don't want this in a class that you have millions of instances of, but do each instance there really need global access this way? I think not...but you could fall back to some of the options listed above if necessary, or combine and mix as you'd like.</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;">class ParticleSystem {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">public:</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> </span><span style="font-family: 'Courier New', Courier, monospace;">ProgressBar() : m_texmgr("texture/dynamic") {</span><span style="font-family: 'Courier New', Courier, monospace;"> }</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> void draw() {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> foreach(Particle& p : m_particles) {</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> p.draw(m_texmgr);</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> }</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"> } </span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">private:</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> SingletonHolder<texturemgr> m_texmgr;</texturemgr></span></div>
</div>
<div>
<span style="font-family: Courier New, Courier, monospace;"> std::vector<particle> m_particles;</particle></span><br />
<span style="font-family: Courier New, Courier, monospace;"><particle>};</particle></span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: inherit;">(For nitpicks: Yes, I know you should don't do a draw call per particle... you'd store the data (pos, vel, colour, whatnot) in a buffer on to the graphics card and render it with one call. That buffer could be a texture from which the vertex shader reads... so, this code isn't too wrong.. ;-p.)</span></div>
<div>
<br /></div>
<div>
<span style="font-size: large;">Final words..</span></div>
<div>
<br /></div>
<div>
So, we've basically gone from the singleton-is-the-class to <a href="http://en.wikipedia.org/wiki/Dependency_injection">dependency injection</a>. A side note is that the classes are called InjectionRegistry/Injected on our own library. Also, that wikipedia page has Java examples with object lookup based on type, whose issues we covered earlier. </div>
<div>
<br /></div>
<div>
One could extend the implementation outlined above to a dependency injection "framework" by reading stuff from a config file and instantiating different classes based on that, but then you need some way to lookup types and set properties and so on, which is not the sort of batteries includedo in C++. (OTOH, if you're developing with <a href="http://qt-project.org/">Qt</a> or a similar framework, you have much of the required tools at your disposal.)</div>
<div>
<br /></div>
<div>
The registry shown here could alsobe used for more than just long-lifed objects, of course, but then you'd need to communicate changes in the registry to the holders and maybe also the instances which contain them. </div>
<div>
<br /></div>
<div>
However, this post is already long enough, so I'll draw the line here.</div>
<div>
<br /></div>
<div>
I hope you enjoyed it. :)</div>
Mackehttp://www.blogger.com/profile/06392128938508053935noreply@blogger.com0tag:blogger.com,1999:blog-4343580553277118310.post-61337273538873939302012-05-31T11:17:00.001+02:002012-05-31T12:59:43.216+02:00Python: Killing subprocesses on WindowsWe're on Python 2.6, and using the subprocess module to execute some applications (like we do on our build system), sometimes things hang (i.e. an open dialog box or something).<br />
<br />
So far, I've just killed the process after timeout, which is fine if there are no childprocesses in there.<br />
<br />
Now, we've shifted to the <a href="http://readthedocs.org/docs/nose/en/latest/">Nose</a> module for running our unit-tests (and <a href="https://github.com/rlisagor/freshen">Freshen</a> for the <a href="http://specificationbyexample.com/">specification-by-example </a>acceptance tests), and that always uses one or more subprocesses to execute tests in... ergo: problems.<br />
<br />
The simplest way is just to run taskkill, which is available at least from Windows XP...<br />
<span style="font-family: 'Courier New', Courier, monospace;">
</span><br />
<pre><span style="font-family: 'Courier New', Courier, monospace;">p = subprocess.POpen(....)
# wait for exit or timeout
if timeout:
subprocess.call(['taskkill', '/F', '/T', '/PID', str(p.pid)])
</span></pre>
<span style="font-family: 'Courier New', Courier, monospace;">
</span>
<br />
<div>
<span style="font-family: inherit;">If you don't want to run another program, there's the long way around, by using <a href="http://pypi.python.org/pypi/comtypes">comtypes</a> and <a href="http://docs.python.org/library/ctypes.html">ctypes </a>to access the <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/aa394582(v=vs.85).aspx">WMI</a> and Win32 API functions. I wrote this before I found out about the above, but it was easy to port C++ samples to this, and I already had comtypes in our system. Maybe it's useful for someone...</span></div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;">def killsubprocesses(parent_pid):</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> '''kill parent and all subprocess using COM/WMI and the win32api'''</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> log = logging.getLogger('killprocesses')</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> try:</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> import comtypes.client</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> except ImportError:</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> log.debug("comtypes not present, not killing subprocesses")</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> return</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> logging.getLogger('comtypes').setLevel(logging.INFO)</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> log.debug('Querying process tree...')</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"><br /></span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> # get pid and subprocess pids for all alive processes</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> WMI = comtypes.client.CoGetObject('winmgmts:')</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> processes = WMI.InstancesOf('Win32_Process')</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> subprocess_pids = {} # parent pid -> list of child pids</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> for process in processes:</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> pid = process.Properties_('ProcessID').Value</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> parent = process.Properties_('ParentProcessId').Value</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> log.trace("process %i's parent is: %s" % (pid, parent))</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> subprocess_pids.setdefault(parent, []).append(pid)</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> subprocess_pids.setdefault(pid, [])</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> </span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> # find which we need to kill</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> log.debug('Determining subprocesses for pid %i...' % parent_pid)</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"><br /></span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> processes_to_kill = []</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> parent_processes = [parent_pid]</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> while parent_processes:</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> current_pid = parent_processes.pop()</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> subps = subprocess_pids[current_pid]</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> log.debug("process %i children are: %s" % (current_pid, subps))</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> parent_processes.extend(subps)</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> processes_to_kill.extend(subps)</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"><br /></span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> # kill the subprocess tree</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> if processes_to_kill:</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> log.info('Process pid %i spawned %i subprocesses, terminating them...' % </span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> (parent_pid, len(processes_to_kill)))</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> else:</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> log.debug('Process pid %i had no subprocesses.' % parent_pid)</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"><br /></span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> import ctypes</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> kernel32 = ctypes.windll.kernel32</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> for pid in processes_to_kill:</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> hProcess = kernel32.OpenProcess(PROCESS_TERMINATE, FALSE, pid);</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> if not hProcess:</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> _log.warning('Unable to open process pid %i for termination' % pid)</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> else:</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> _log.debug('Terminating pid %i' % pid) </span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> kernel32.TerminateProcess(hProcess, 3)</span></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"> kernel32.CloseHandle(hProcess)</span></div>
</div>
<div>
<span style="font-family: 'Courier New', Courier, monospace; font-size: x-small;"><br /></span></div>
<div>
<div>
<span style="font-family: inherit;">This code could of course be improved by not walking all processes, OTOH, I have between 80 and 200 running on my PC at all times, so it's not a lot of data. Especially, since I only call this function if I've already waited too long, it doesn't matter if it takes a few more seconds.</span></div>
</div>
<div>
<br /></div>
<div>
Also, I long for Haskell where this tree-walking mapping stuff could be way more concise. Maybe python's itertools package has something I could use to avoid coding the dirty stuff by hand.<br />
<br />
A few more solutions is available at <a href="http://stackoverflow.com/q/1230669/72312">this StackOverflow question</a>, depending on what kind third-party of libraries you have available.</div>
<br />
Moral of the story:<br />
<br />
It doesn't matter if you have to write a lot of code in order to find out that there's a one liner which solves your problem. Use the short version and be happy you don't have to maintain the 30+ lines version. :)Mackehttp://www.blogger.com/profile/06392128938508053935noreply@blogger.com0tag:blogger.com,1999:blog-4343580553277118310.post-8629717836314247632012-04-17T20:38:00.002+02:002012-04-17T20:59:43.512+02:00E-books are awesome! (And the Kindle app is ok-ish)I've recently read quite a few books on my Android tablet (Asus Eee Pad Transformer) via the Kindle app, and the experience has been quite exquisite so far.<br />
<ul>
<li>Quiet reading at night (in the dark) is no problem at all. Excellent for an insomniac as yours truly when the rest of the family is sleeping soundly in the same room.</li>
<li>Start/stop time is virtually nil, since the app remembers the last page read.<br />(This is also synced between devices, so reading a few pages off the phone on the train/tram/bus/loo/whatnot works well.)</li>
<li>The Oxford dictionary is included, so most odd words can be explained quickly.</li>
<li>No bookshelves required!</li>
</ul>
<div>
The drawbacks are not insignificant:</div>
<div>
<ul>
<li>not all books are good to read in night mode, as they don't cope with the inverted color scheme.</li>
<li>maps/drawings are seldom included or look good. The format & applications are best suited to text-only books.</li>
<li>night-mode makes everything feel doomed and evil. Do bear this in mind when reading Lovecraft, <a href="http://www.amazon.com/The-Road-ebook/dp/B000OI0G1Q">The Road</a> or other horror-themed literature.</li>
<li>table of contents and chapters are ususally poorly implemented and could benefit greatly from a little bit more adaptation to the electronic format.</li>
</ul>
</div>
<div>
Apart from these issues, it's pretty excellent for fiction and beats trying to hold a regular old paper book hands down, especially as I read mostly during night.</div>
<div>
<br /></div>
<div>
What's more worrying in the long term is perhaps the DRM bit (I can't really lend or sell an e-book currently) as well as Amazon's monopoly/monopsony.<br />
<br />
This is better explained in this <a href="http://www.antipope.org/charlie/blog-static/2012/04/understanding-amazons-strategy.html">excellent blog post.</a> </div>Mackehttp://www.blogger.com/profile/06392128938508053935noreply@blogger.com1tag:blogger.com,1999:blog-4343580553277118310.post-69916488532241683722012-03-26T22:49:00.001+02:002012-03-28T10:58:21.714+02:00Status updateMy main occupation these days is as executive of damage control, for a 1y old toddler. Doing so leaves precious little time and brain power for any creative software work.<br />
<br />
Whatever free time I get I tend to use for gaming/surfing, which is what my spare brain cycles allows for. It's plain hard to regularly find some dedicated time for creative work, when your brain is trying to cope with the mood swings and antics of someone who has to program her neural network by trial and error. :)<br />
<br />
So, the going-ons in my software world (outside work) are rather slim:<br />
<ul>
<li><a href="http://www.buildbot.net/">BuildBot</a> is ticking along fine without me, although I'm trying to do a bit of pull request checking, since <a href="https://plus.google.com/105883044168332773236/posts">Dustin Mitchell</a>, "chief maintainer", needs some offloading on that part.</li>
<li><a href="http://www.blogger.com/">ThrustRacer<span id="goog_1948079618"></span></a> remains a concept and dream. I've had some doubts as to whether twitchy arcade games can be as good on mobile devices as they were in the golden age of the Tac-2, thus wondering if I should spend my time thinking on thinking about something else instead.</li>
<li><a href="http://www.tf2.com/">TF2 </a>is fun, but kinda hard unless you find a server with comparable players. Ranking, seeding and matching is sorely needed. I had to by a new high-DPI mouse to be at least vaguely competitive</li>
<li>Skyrim is .. scary .. and buying the discount PC-version on Steam over the overprices PS3 version was not as good an idea as it first sounded. I need a new graphics card, which costs way more! (OTOH, might help with TF2). The scariness (i.e. I'm really afraid to see what will come at me next...) is a problem though, since I play at night/evenings (ref. above mentioned toddler) and playing in the dark, quiet and trying to quickly go asleep afterwards doesn't really mix well. (Still haven't finished BioShock, nor seen Alien 1 to completion...)</li>
<li>Our (recently acquired) house sorely needs more power and Ethernet outlets, a small herd of Ardunio boards controlling all the light fixtures and a motorized garage door.</li>
</ul>
<br />
Ah, the woes of geekdom and fatherhood combined...<br />
<br />Mackehttp://www.blogger.com/profile/06392128938508053935noreply@blogger.com0tag:blogger.com,1999:blog-4343580553277118310.post-15433621177874734372011-09-30T11:11:00.005+02:002013-11-20T11:11:32.643+01:00Trac and Kanban - ReduxHello there!<br />
<br />
Since my last post before summer, the number of total visits has gone from 2000 to over 7000! One of the reasons for this was the <a href="http://mackeblog.blogspot.com/2011/01/trac-and-kanban.html">Trac and Kanban</a> post, where I breifly describe how to use a Trac wiki page as a basic kanban board. (The main reason is<a href="http://stackoverflow.com/q/1791474/72312"> this Stack Overflow question</a> which asks for a bit more, such as drag-n-drop and the like.)<br />
<br />
We'we worked with this for half a year now, and it still feels like a good thing. So good, actually, that I simply had to improve it a bit.<br />
<br />
<span class="Apple-style-span" style="font-size: large;">Trac and Kanban, v2.0</span><br />
<br />
Mainly I now use the "format=table" option to get a table, which means we get color for the priority, we can show who is owning the item and what resolution it had (duplicate/invalid/worksforme/wontfix, etc) where suitable.<br />
<br />
It looks like this (with no one working on anything at the moment, but you get the idea):<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-H7gT4_DCWEI/ToWD3eodeMI/AAAAAAAAAGo/DobE0ZwJi94/s1600/orzone_trac_kanban_dashboard.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="http://2.bp.blogspot.com/-H7gT4_DCWEI/ToWD3eodeMI/AAAAAAAAAGo/DobE0ZwJi94/s640/orzone_trac_kanban_dashboard.png" width="640" /></a></div>
<br />
This could be even better if Trac supported sorting on several ticket fields, but that is not possible with 0.12. There is a <a href="http://trac.edgewall.org/ticket/10178">ticket and patch</a> available on the Trac home site, but I haven't tried it ... yet.<br />
<span class="Apple-style-span" style="font-size: large;"><br />
</span><br />
<span class="Apple-style-span" style="font-size: large;">Code review</span><br />
<br />
For the confused, note that I still use two custom fields for post-commit code review "reviewed_by" and "has_review_issues".<br />
<br />
I will probably change the boolean review_issues to a checkbox that better handles the states and workflow for code review:<br />
<br />
<ul>
<li>not_reviewed</li>
<li>reviewed_with_issues</li>
<li>review_issues_fixed</li>
<li>reviewed_and_approved</li>
</ul>
<br />
A custom TicketChangeListener could then manage the has_issues to issues_fixed transition, so that it's automatically visible that the code has changed.<br />
<br />
<span class="Apple-style-span" style="font-size: large;">Automatic subpage/dashboard lists </span><br />
<br />
The second new thing I've discovered and deployed is the <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">TitleIndex </span>macro, to simply list show all sub pages for a single wiki page. Since we have all the dashboards under Dashboard/ProductNameX.Y pages, it's easy to list them all using<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"> [[TitleIndex(Dashboard/ProductName,hideprefix)]] </span>on a product's "home" wiki page.<br />
<br />
Old/completed milestones could then be moved to DashboardOld container so that they're still there, but does not clutter the "current and upcoming milestones/sprints" list.<br />
<br />
<span class="Apple-style-span" style="font-size: large;">Limit right column to last two weeks</span><br />
<span class="Apple-style-span" style="font-size: large;"><br /></span>
Adding <span style="font-family: Courier New, Courier, monospace;">modified=2weeksago.. </span><span style="font-family: inherit;">to the queries for closed tickets makes the dashboard work over long time, since older (closed) tickets are not modified and thus removed from the table after some time.</span><div>
<br /></div>
<div>
<div>
This really helps with using the system in a "pure" Kanban setting, without sprints or milestones. It's also useful to keep a current status view during longer milestones with 100+ tickets.</div>
<br />
<span class="Apple-style-span" style="font-size: large;">Wiki code</span><br />
<br />
The wiki page above was generated from the following code:<br />
<code style="font-size: small;"><br />
This is the ticket dashboard for **[milestone:"Orsync 1.0"]**<br />
([/query?milestone=Orsync+1.0&status=new&status=reopened&status=testable&status=accepted&order=priority&col=id&col=summary&col=status&col=type&col=priority&col=milestone&col=component open tickets])<br />
-- [wiki:Orcamp/FeatureList current feature list]<br />
<br />
Create new<br />
[/newticket?milestone=Orsync+1.0&component=Apps/Orsync&type=defect defect],<br />
[/newticket?milestone=Orsync+1.0&component=Apps/Orsync&type=enhancement enhancement],<br />
[/newticket?milestone=Orsync+1.0&component=Apps/Orsync&type=suggestion suggestion],<br />
[/newticket?milestone=Orsync+1.0&component=Apps/Orsync&type=change change],<br />
[/newticket?milestone=Orsync+1.0&component=Apps/Orsync&type=task task],<br />
[/newticket?milestone=Orsync+1.0&component=Apps/Orsync&type=demo demo] or<br />
[/newticket?milestone=Orsync+1.0&component=Apps/Orsync generic] ticket.<br />
<br />
{{{<br />
#!div style="float:left; margin-right:1em; width:30%"<br />
<br />
= Open Tickets = <br />
<br />
Total: [[TicketQuery(status=new|reopened,type!=suggestion,milestone=Orsync 1.0,format=count)]] <br />
([[TicketQuery(status=new|reopened,type=suggestion,milestone=Orsync 1.0,format=count)]])<br />
<br />
== New & Reopened ==<br />
<br />
[[TicketQuery(format=table,col=summary|type,status=reopened|new,type!=suggestion,milestone=Orsync 1.0,group=priority,order=type)]]<br />
<br />
== Suggestions == <br />
<br />
[[TicketQuery(group=priority,status=new|reopened,type=suggestion,milestone=Orsync 1.0)]]<br />
<br />
}}}<br />
{{{<br />
#!div style="float:left; margin-right:1em; width:30%" <br />
<br />
= Work In Progress =<br />
<br />
Total: <br />
[[TicketQuery(status=accepted,order=priority,group=owner,milestone=Orsync 1.0,format=count)]]<br />
([[TicketQuery(status=accepted|testable,milestone=Orsync 1.0,format=count)]])<br />
<br />
== Started ==<br />
<br />
[[TicketQuery(status=accepted,group=owner,order=priority,format=table,col=summary|type|owner,milestone=Orsync 1.0)]]<br />
<br />
== Unreviewed tickets == <br />
<br />
[[TicketQuery(review_issues!=1,reviewed=,status=testable,group=owner,order=priority,format=table,col=summary|type|owner,milestone=Orsync 1.0)]]<br />
<br />
== Reviewed tickets with issues ==<br />
<br />
[[TicketQuery(review_issues=1,status=testable,group=owner,order=priority,format=table,col=summary|type|owner,milestone=Orsync 1.0)]]<br />
<br />
== Ready for Test ==<br />
<br />
[[TicketQuery(review_issues=1,status=testable,group=developertest,order=priority,format=table,col=summary|type,milestone=Orsync 1.0)]]<br />
<br />
<br />
<br />
}}}<br />
{{{<br />
#!div style="float:left; margin-right:1em; width:30%" <br />
<br />
<br />
= Closed Tickets = <br />
<br />
Total: [[TicketQuery(status=closed,resolution=fixed,milestone=Orsync 1.0,format=count)]] </code><br />
<code style="font-size: small;">([[TicketQuery(status=closed,resolution!=fixed,milestone=Orsync 1.0,format=count)]])<br />
<br />
== Fixed tickets (last two weeks) ==<br />
<br />
[[TicketQuery(status=closed,format=table,col=summary|type,resolution=fixed,order=priority,desc=1,milestone=Orsync 1.0,modified=2weeksago..)]]<br />
<br />
== Denied tickets </code><span style="font-family: monospace; font-size: x-small;">(last two weeks) </span><span style="font-size: x-small;">==</span><br />
<code style="font-size: small;">
<br />
[[TicketQuery(status=closed,resolution=wontfix|worksforme,format=table,col=summary|type|resolution,order=priority,desc=1,milestone=Orsync 1.0</code><span style="font-family: monospace; font-size: x-small;">,modified=2weeksago..</span><span style="font-size: x-small;">)]]</span><br />
<code style="font-size: small;">
<br />
== Invalid/duplicate/other </code><span style="font-family: monospace; font-size: x-small;">(last two weeks)</span><span style="font-size: x-small;"> ==</span><br />
<code style="font-size: small;">
<br />
[[TicketQuery(status=closed,resolution!=fixed|wontfix|worksforme,format=table,col=summary|type|resolution,order=priority,desc=1,milestone=Orsync 1.0</code><span style="font-family: monospace; font-size: x-small;">,modified=2weeksago..</span><span style="font-size: x-small;">)]]</span><br />
<code style="font-size: small;">
<br />
<br />
}}}<br />
<br />
{{{<br />
#!div style="clear:both"<br />
}}}<br />
<br />
<br />
</code><br />
<br />
There you go. Hope it helps!</div>
Mackehttp://www.blogger.com/profile/06392128938508053935noreply@blogger.com11tag:blogger.com,1999:blog-4343580553277118310.post-72473778953676190302011-03-29T13:26:00.001+02:002011-03-29T13:26:49.459+02:00Process spawned successfullyOur daughter arrived on March 19th, hence the lack of updates lately.<br />
<br />
Normal service may resume in the future, but expect shorter posts in the near time.<br />
<br />
P.S. I'm considering applying as a GSoC mentor for Buildbot, guiding the Javascript UI project together with a few other Buildbot developers.Mackehttp://www.blogger.com/profile/06392128938508053935noreply@blogger.com0tag:blogger.com,1999:blog-4343580553277118310.post-15358388786116125972011-02-26T22:13:00.005+01:002011-06-07T11:53:50.439+02:00Book Review - What Technology Wants by Kevin KellyI decided to order a few non-fiction book from Amazon a few months ago, as a change from the reality-escaping high-fantasy, grand space opera or hard sci-fi that I usually churn through. I suppose it's got something to do with <s>growing up</s> getting older.<br />
<br />
Included in the package was:<br />
<ul><li><i>Managing Humans</i> by <i>Michael Lopp</i> (has a very neat 'geek & manager' blog at <a href="http://randsinrepose.com/">randsinrepose.com</a>)</li>
<li><i>Linchpin </i>by <i>Seth Godin</i> (has a pretty unique 'be awesome now' blog at <a href="http://sethgodin.com/">sethgodin.com</a>)</li>
<li><i>Guns, Germs and Steel </i>by <i>Jared Diamond</i> (the book won the Pulitzer Prize)</li>
<li><i>Crossing the Chasm: Marketing and Selling High-Tech Products to Mainstream Customer </i>by <i>Geoffery A. Moore </i>(was recommended due to the other books I've ordered)</li>
</ul><div>And, the reason for this post:</div><ul><li><i>What Technology Wants </i>by Kevin Kelly (former Wired editor, etc. Website at <a href="http://kk.org/">kk.org</a>)</li>
</ul><div>Apart from the last one, I've also finished the two first and heartily recommend them. I might review them too, but if you can't wait for that, I'm fairly certain you won't regret spending some time with them. Also, I'm currently a third of the way into <i>Guns, Germs and Steel</i> and it has not disappointed the slightest yet.</div><div><br />
</div><div>Anyway, withour further ado, here comes my short and humble review of:</div><div><br />
<span class="Apple-style-span" style="font-size: large;"><i>What Technology Wants </i>by <i>Kevin Kelly</i></span><br />
<span class="Apple-style-span" style="font-size: x-small;"><i>(This review is also posted on <a href="http://www.amazon.com/review/R3G9G1SSSH3YI3/ref=cm_cr_rdp_perm">Amazon.com</a>)</i></span><br />
<div><br />
</div>What Technology Wants is a slightly puzzling title which poked my interest. Can technology actually "want" something? What does that phrase mean? Does it attempt to imply that technology has a will of its own, is it is a "force of nature", or just something inevitable built in the rules of the universe?<br />
<br />
These questions might sound a bit like "hippy-talk" (for lack of a better term) and while reading the first chapters of the book, which try to grasp this rather evasive concept, it felt rather hard to follow out where the author tries to lead. Solid lines of reasoning do emerge eventually, so if the narrative feels a bit vague in the beginning, one should not give up. Getting the grips on the driving force behind all the technology that most of us humans has ready access to, and what this actually means, is to say the least a rather daunting task. Also, I suppose the book tries to cater for many readers, not just the tech-savvy, so it attempts to gather everyone and provide a foundation on which the ideas and theories of subsequent chapters can build on.<br />
<br />
The amount of background research made for the book is phenomenal. He devotes a large part of the book on the Amish, being that they are a successful group that chooses to live outside the "normal" western civilization, actively choosing to abstain from much of today's technology. However, he notes, crucially in my (and his) opinion, that the Amish would not be able to function without the rest of the society, and that they continually lag about 50 years behind.<br />
<br />
This choosing of technology is not specific to Amish though. Everyone is doing it, one way or the other. Often, we are not very consistent in our choices. I.e. we may be on the cutting edge on one part, but several generations back on another, just because we want to.<br />
<br />
Kelly relates this to the fact that Amish seem to live a pretty happy and unstressful life, at least in comparison to many of the rest of us. They perform their honest work and labor with the tools they have, being fairly content with the situation. They choose their tools by waiting for the rest of society (and select individuals of their own) to try out technologies before choosing that which is good and not disruptive to their way of living. This of course relies on the fact to the rest of us continues to provide spare parts for old tech, as well as continuously producing new technology.<br />
<br />
An interesting side-fact (related to the issue of spare parts above) that is stated is that, apparently, no technology ever dies. You can find somewhere to buy a piece of flint and steel, an axe, an abacus, vacuum tubes (for your "this-goes-to-eleven" guitar amp), a vinyl player, etc. It don't doubt it at all, and it does help to choose between various technologies.<br />
<br />
The book also contains a treatise on the unabomber. Being Swedish (and rather young at the time of the event), I knew very little about him before reading this book. There are some excerpts of the unabombers manifesto included (and discussed) in the book, which make the case that technology is inevitable and people cannot escape it. From this, IIRC, the unabomber draws the conclusion that since it's forced upon people by the system, so the system (and/or civilization) such as it is must be destroyed completely for the people to be free. Most of us agree with the first part, but our rejection of the latter conclusion probably separates civilization from apocalypse. (Also, even the unabomber tried to reject civilization and technology for several years, but could not do so completely, since he needed bullets for his rifle, rope for his traps and gasoline for the car to be able to travel to trade these things.)<br />
<br />
Kelly proffers the same statement here, which is that technology in something inevitable, in the same sense that the universe has given us DNA, multi-cellular organisms, mammals, humans and civilization (for better or worse). One simply cannot prevent technology from appearing, given how far everything have gone already, and from where it actually started (i.e. the primordial soup). Complexity, and the perpetual increase thereof, is inherent in the foundations of the universe. We've had natural evolution for almost four billion years, and for the last ten to twenty thousand years (give or take a few), mankind (a product of the above) has been selecting, domesticating, refining and reworking different parts of nature to its liking. Now, we're selecting technology instead, and technology is undergoing evolution under the same criteria that (probably) made us domesticate the wolf rather than the hyena. (It's more beautiful, more intelligent, more adaptable, etc etc.)<br />
<br />
This strive towards beauty, complexity, adaptability, etc etc is going on with technology today. Personally, I see this in the world of computer components, libraries, frameworks, utilities, etc. The open-source ecosystem a good example of this evolutionary process, as libraries come, evolve and leave. Some evolve quickly then stagnate when there is no opposition, then either gets wiped out when a new, better toolkit appear, or they attract sufficient interest (from it's users and developers) to catch up. The book's final chapters summarizes a number of criteria that are selected for in the evolutionary process, that will continue to be the driving force of change as technology evolves into more diverse, specialized, complex, interlinked, adaptable and beautiful manifestations..<br />
<br />
Kelly, rather early, names the entire technological sphere the Technium. In the end, he concludes that what it wants is just to live and prosper, just like any other self-evolvable entity. The difference is that the Technium can evolve a thousand or a million times faster, and that it this speed is because it does not evolve by chance (i.e. mutation), but rather the fact that it is actively driven (you could say developed) towards improvement with every generation. Also, since it's so interlinked, and has perfect memory (i.e. the Internet, more or less), it will build upon itself much faster than evolution (wherein for instance the eye evolved independently eight times) and even faster than human civilization (which could not communicate ideas and inventions especially fast until we had the Internet).<br />
<br />
I think this book is awesome in several ways. The question it attempts to both define, investigate and answer is immense. It is also a most relevant question, as I (and I suspect a few more) wonder where we are heading with all this technology, how it will shape us and what we can do, if anything, to guide it during its evolution. And since it actually manages to pull it off, I cannot by heartily recommend it to anyone that has some kind of interest in the field.<br />
<br />
Having left me me with a sense that there is really no difference between the big bang and the forming solar systems, life and evolution, humans and civilization and finally technology (and thus the Technium, as Kelly names it), I feel that I'm standing slightly more on firmer ground, while the world around us spins ever faster.<br />
<br />
Or maybe, I'm just getting older. ;)<br />
<div style="font-style: italic;"><br />
</div></div>Mackehttp://www.blogger.com/profile/06392128938508053935noreply@blogger.com0tag:blogger.com,1999:blog-4343580553277118310.post-31046916479467335692011-01-17T17:11:00.000+01:002011-01-17T17:11:22.812+01:00Configuring PyQt across unit-testsWe had some <span class="Apple-style-span" style="font-family: inherit;">problems </span>with configuring the PyQt API version across unit-tests. The problems arise because several modules try to call sip.setapi, which you can only do once (even if you call it with the same args, which ought to be idempotent, you'd think.)<br />
<br />
Anyway, what I did was to create a module called 'pyqtconfig' with the following contents:<br />
<br />
<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">import sip</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">sip.setapi('QString', 2)</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">sip.setapi('QVariant', 2)</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">sip.setapi('QTextStream', 2)</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">import PyQt4.Qt</span><br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">Qt = PyQt4.Qt</span><br />
<div><br />
</div><div>Then I simply do 'from pyqtconfig import Qt' in each module. Works like a charm.</div><div><br />
</div><div><i>(If you want to import each module separately, you need to adapt the above code somewhat. I'll leave that as an exercise for the reader. :)</i></div><div><br />
</div>Mackehttp://www.blogger.com/profile/06392128938508053935noreply@blogger.com0tag:blogger.com,1999:blog-4343580553277118310.post-32665715103910425532011-01-17T12:51:00.006+01:002011-09-30T11:14:01.714+02:00Trac and Kanban<span class="Apple-style-span" style="font-family: inherit;">(Don't miss my update <a href="http://mackeblog.blogspot.com/2011/09/trac-and-kanban-redux.html">Trac and Kanban Redux</a>)</span><br />
<span class="Apple-style-span" style="font-family: inherit;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: inherit;">We use a Trac wiki page as a simple kanban board. It's pretty neat, especially when put on a 42" LCD TV in the project room.</span><br />
<span class="Apple-style-span" style="font-family: inherit;"><br />
</span><br />
<span class="Apple-style-span" style="font-family: inherit;">Here's how it looks in the browser:</span><br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://2.bp.blogspot.com/_tlUbUg_x1Rk/TTQtJ8EDJkI/AAAAAAAAADo/V6jIXwiZ52I/s1600/trac.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="180" src="http://2.bp.blogspot.com/_tlUbUg_x1Rk/TTQtJ8EDJkI/AAAAAAAAADo/V6jIXwiZ52I/s400/trac.png" width="400" /></a></div><br />
And here's the wiki code, all built using TickeyQuery-macros, both for counting (to keep an eye on the WIP-limit) and listing the tickets themselves. It's pretty basic, but it works well for our small team and it's easy to understand and maintain.<br />
<span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"><br />
</span><br />
<pre style="font-size: 50%;">This is the ticket dashboard for Orcamp for [milestone:"Orcamp 1.0"] ([/query?milestone=2010+M11&order=priority&col=id&col=summary&col=status&col=type&col=priority&col=milestone&col=component tickets]) -- [wiki:Orcamp/FeatureList current feature list]
{{{
#!div style="float:left; margin-right:1em; width:30%"
= Open Tickets =
Total: [[TicketQuery(status=new|reopened,milestone=Orcamp 1.0,format=count)]]
[[TicketQuery(group=priority,status=new|reopened,milestone=Orcamp 1.0)]]
}}}
{{{
#!div style="float:left; margin-right:1em; width:30%"
= Work In Progress =
Total: [[TicketQuery(status=accepted|testable,milestone=Orcamp 1.0,format=count)]]
== Started ==
[[TicketQuery(status=accepted,order=priority,group=owner,milestone=Orcamp 1.0)]]
== Ready for Test ==
[[TicketQuery(status=testable&review_issues=0&reviewed!=,group=developertest,milestone=Orcamp 1.0)]]
== Reviewed tickets with issues ==
[[TicketQuery(review_issues=1,group=owner,status=testable|closed,resolution=fixed,milestone=Orcamp 1.0)]]
== Unreviewed tickets ==
[[TicketQuery(review_issues!=1,status=closed|testable,group=status,resolution=fixed,reviewed=,milestone=Orcamp 1.0)]]
}}}
{{{
#!div style="float:left; margin-right:1em; width:30%"
= Closed Tickets =
Total fixed: [[TicketQuery(status=closed,milestone=Orcamp 1.0,resolution=fixed,format=count)]]
Other: [[TicketQuery(status=closed,milestone=Orcamp 1.0,resolution!=fixed,format=count)]]
== Fixed tickets ==
[[TicketQuery(status=closed,group=type,resolution=fixed,milestone=Orcamp 1.0)]]
== Other resolutions ==
[[TicketQuery(status=closed,group=resolution,resolution!=fixed,milestone=Orcamp 1.0)]]
}}}
{{{
#!div style="clear:both"
}}}
</pre><span class="Apple-style-span" style="font-family: inherit;">Note that the above wiki code contains some references to custom fields (notably review_issues) that we use to manage code reviews on a ticket-by-ticket basis. In our small team, we've decided to allow commits of unreviewed code, but require code to be reviewed before it's cleared for testing. </span><br />
<span class="Apple-style-span" style="font-family: inherit;"><br />
</span><br />
As always, you should adapt to local conditions. :)Mackehttp://www.blogger.com/profile/06392128938508053935noreply@blogger.com4tag:blogger.com,1999:blog-4343580553277118310.post-87813689387900846272011-01-09T18:43:00.001+01:002011-01-09T18:44:23.759+01:00BuildBotIcon - Python Style (and it's got tests this time!)If you've read earlier posts here, you might know that I'm behind <a href="https://bitbucket.org/marcusl/buildboticon/wiki/Home">BuildBotIcon</a>, a small Java application that sits in the system tray, polling one or more <a href="http://buildbot.net/">BuildBot</a> servers and reports any changes. It also plays sounds and turns lava lamps on or off.<br />
<br />
I've decided to do a version in Python using PyQt instead. It's mostly as a fun exercise swim in the ecosystem of python (setuptools, unittest, mocking, coverage, yaml, etc) but it might come out better than the original.<br />
<br />
PyQs nice, and the new-style signals-slots makes it a breeze to do what you want if, assuming you know your way around Qt. (Not that it's hard to learn, I just knew Qt pretty well before embarking on this project.)<br />
<br />
Writing unittests are a breeze, and I think that it sure helps knowing that the code works, instead of knowing it only compiles. Coming from C++ this is actually pretty nice change. However, you should write with testing from the beginning. Grafting unit-testing later on is difficult and might not be a net win for all components, especially in C++ . (Mostly since you've run them for a while and the obvious (and thus easy-to-test-for) bugs have already been ironed out.)<br />
<br />
However, the greatest fun so far has been using <a href="http://pypi.python.org/pypi/mock/">Mock</a>, yet another mock object module for python that takes a nice and intuitive approach to mocking, IMHO. Instead of record/replay, or setup/run/verify, it's more like mock/run/assert. Read <a href="http://www.voidspace.org.uk/python/articles/mocking.shtml">Mocking, Patching, Stubbing: all that Stuff</a> (and it's link to <a href="http://martinfowler.com/articles/mocksArentStubs.html">Mocks aren't Stubs</a>) for more information on this.<br />
<br />
The ability of substituting <b>any </b>function or object in the APIs you're running makes unittesting immensely powerful and thorough. If you trust the framework (Qt in my case) it's very easy to verify that play() is called on the right QSound (without making noises) or that get() is run with the right URL to access the network.<br />
Of course, more complex behaviours are trickier to mock properly, so it helps to refactor your app into testable parts (which you should do anyway).<br />
<br />
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">Also, I've used <a href="http://yaml.org/">YAML </a>for the settings file this time (using <a href="http://pyyaml.org/">PyYAML</a>). All is good and well, except that it turns out that writing the settings file first and implementing the app second had probably been a better idea. TDD again.</div><br />
It has all fallen out pretty nicely so far, except that, similar to the Java version, I've bungled together the backend that does networking and parsing with the UI. Since I currently have three ways to show build state (tray icon, sound and lava lamps) I should really had put a Listener interface in there. Fixing that now means refactoring the unit tests too, which is less fun. :-|<br />
<br />
Nevertheless, all of this is experience is bound to pay back in my ThrustRacer-project (should I get it off the ground) or at work, where we're about to embark on a year of much Python coding. I don't expect either to get it right from the start, one never does. :)Mackehttp://www.blogger.com/profile/06392128938508053935noreply@blogger.com0tag:blogger.com,1999:blog-4343580553277118310.post-67692822235592267422010-11-27T12:18:00.000+01:002010-11-27T12:18:37.737+01:00Mercurial rebase/merge musings part 2In a <a href="http://mackeblog.blogspot.com/2010/11/mercurial-rebasemerge-musings.html">previous post</a> I wrote about how nice it'd be if <a href="http://mercurial.selenic.com/">Mercurial</a> automatically could choose rebase or merge depending on whether there are conflicts that need manual resolution.<br />
<br />
So, "yesternight", after finishing the <a href="http://mercurial.selenic.com/wiki/CheckFilesExtension">checkfiles extension</a> I wrote about recently I couldn't go back to sleep (despite it being 2:30 AM). So, I got back up and hacked together the <a href="http://mercurial.selenic.com/wiki/RebaseIfExtension">rebaseif extension</a> that adds a command and an option to pull that does exactly that. It turns out it wasn't that hard, although the process could probably be optimized slightly as it currently attempts a rebase, aborts on conflicts and then does a merge. (The better way would be to detect which operation would succeed and perform that.)<br />
<br />
Now, some support for that in <a href="http://tortoisehg.bitbucket.org/">TortoiseHg</a> and we're all set. :)Mackehttp://www.blogger.com/profile/06392128938508053935noreply@blogger.com0tag:blogger.com,1999:blog-4343580553277118310.post-86510929874140675482010-11-27T00:52:00.003+01:002010-11-27T12:27:49.351+01:00Mercurial extension writingI just spent the better part of the evening and early night to hack together a <a href="http://mercurial.selenic.com/">Mercurial </a>extension to check for and fix tabs and trailing whitespace in files to be committed.<br />
<br />
Diving into a (relatively) poorly documented Python API is not easy. My previous attempts with Django (extremely good docs) and BuildBot (perfectly acceptable docs) went much better in terms of tackling the early learning curve.<br />
<br />
As an example, I have not yet managed to figure out how to get the added diff lines of the working directory, or the files included in an upcoming commit (this are slightly different). The MercurialAPI wiki page only gives a brief overview, after that you're on your own and have to use the source. This is pretty fine, except that dynamic languages need more written docs if the source is to be understood better.<br />
<br />
Anyway, the extension works pretty nicely as I decided to check the _entire_ file instead of just the changed lines. Shouldn't make a big difference performance wise, and it will allow a code base to go from mixes-tabs to spaces-only much faster. I can always revisit that part later if the need arises.<br />
<br />
The next part in the plan is to write a small script that auto-injects this into each developers hgrc file on checkout. (I have a script that one must run, more or less, to deploy/unzip third-party libraries and set up environment variable, so it can just as well edit the hgrc file too.) I'm not worried about the security implications currently, as we're four developers on a corporate network, so everyone should play nicely at the moment, and secondly, I can always safeguard the paths to the extension (or put in in a subrepo) to which no-one but a select few, including myself, have commit/push rights (in case of a rogue developer).<br />
<br />
The next little script I write will probably be something like that, to allow deployment of local extensions. I'll also add some extra meta-data to a commit and a central-repo hook to check for that, so that one can't push without the meta-data (ensuring that everyone starts using the hook ASAP) and also run the same checks there (in case someone decides to override the hook).<br />
<br />
Hacking should not happen out of pure malice, but I've had developers disabling the global warnings-in-log-at-exit popup just because they where annoyed by them. (The best way would've been to fix the warnings inside the app, or at least have a chat with me on which log messages are warnings and if it's worthwhile to add a feature where said developer may disable the popup for a few hours.) Since the popup was disabled, many others in the team missed warnings or errors in the log and subsequently the CI build failed with warnings after commit. Sadly quite contraproductive and against the spirit of early-warnings.<br />
<br />
Summing it up, I really enjoy hacking together small things in Python, but for larger projects it seems to become pretty unbearable and you really want to know the types of the variables. I really wish C# and WPF was available on more than one platform, so that we could go with that instead of C++/Python on Qt. It does seem to be a nicer road to walk.<br />
<br />
(Yes, I've thought about Java, and I've worked with it for a few years. And no, I don't really like it, but I suppose SWT is at leats a bearable GUI-API (i.e. better than Swing), but lately with the Oracle takeover I'm very wary of betting on Java going anywhere but down the drain for non-enterprise projects.)<br />
<br />
Obligatory links:<br />
<br />
<ul><li><a href="https://bitbucket.org/marcusl/ml-hgext/src/tip/checkfiles.py">checkfiles.py</a> on BitBucket.org.</li>
<li><a href="http://mercurial.selenic.com/wiki/CheckFilesExtension">CheckFilesExtension</a> on the Mercurial Wiki.</li>
<li><a href="https://www.ohloh.net/p/ml-hgext">ml-hgext</a> project on Ohloh.net</li>
<li><a href="http://open.spotify.com/artist/0Iv00ucAIqr5KVS7bXGFa9">Ozric Tentacles</a> on Spotify, great background coding music (instrumental progressive/trance crossover)</li>
</ul>Mackehttp://www.blogger.com/profile/06392128938508053935noreply@blogger.com0tag:blogger.com,1999:blog-4343580553277118310.post-47356060491418913892010-11-09T22:36:00.000+01:002010-11-09T22:36:08.738+01:00New toy!Also, I got a HTC Desire a few weeks ago. Love it!<br />
<br />
It's really becoming the multi-purpose do-it-all device that we all need to make our life a non-stop information gathering/processing journey from the cradle to the grave. :-PMackehttp://www.blogger.com/profile/06392128938508053935noreply@blogger.com0tag:blogger.com,1999:blog-4343580553277118310.post-38968862137184656932010-11-09T22:33:00.001+01:002010-11-09T22:42:19.478+01:00More on HTML & JavaScript Client AppsIn a <a href="http://mackeblog.blogspot.com/2010/11/logwindow-20.html#links">recent post</a> I wrote about the new log window for our Qt apps at work. Essentially, it delivers the log via AJAX to a web browser, with the html & javascript embedded in and served statically by the app. Works well enough.<br />
<br />
However, there are two more ships on the horizon flying similar flags. <br />
<br />
The first is my (secret) plan to build a debugger for a State Machine for a few years. Since Qt now has a state machine as of 4.6, and it seems both solid, well documented and behaves mostly correct (observing that we've yet to learn all ins and outs of a full state chart machine) is seems a good target for this. I've already written a QStateMachine to GraphViz translator, which outputs an SVG which can (with some good faith and heavy squinting) be interpreted as a UML state chart (cough cough).<br />
<br />
Now, SVG is an XML document which has a DOM, which you can manipulate with Javascript in most modern browsers. Yay! However, this did not, at the time of having the idea, include Google Chrome (my favorite), since <a href="http://code.google.com/p/chromium/issues/detail?id=61412">SVG-DOM updates didn't redraw the image immediately</a>.... They have fixed it since, but I am pretty busy with other things so this small show stopper held me up. I hit a bump, I move in another direction, especially when I'm just going somewhere to see where I end up. Will have to get back on this one ... :)<br />
<br />
Anyway, for the final product, I think a nice event log and some dynamic colouring/resizes of states and transitions would work wonders for debugging the app. Breakpoints via mouse-click events in the graph is then the second step. Then there are some question marks, but the last step is, as always, profit. ;)<br />
<br />
The problem is always to package it so that other people can re-use it. I have to try to get better at that, but it's sadly not on top of the priority pile when working at a startup.<br />
<br />
Secondly, it seems as the <a href="http://www.buildbot.net/">BuildBot</a> project is planning to boot my <a href="http://buildbot.net/trac/wiki/JinjaBranch">Jinja webstatus implementation</a> that replaced the html-string-pasting in python. (I never blogged about that.. oh well.. fun times it was anyway) Anyways, sad as that can be, the new plan is for... a html/javascript-only client! The content can then be served statically (via twisted or your local "real" webserver, for more oomph) and the webpage then communicates with the buildmaster using relatively small AJAX requests.<br />
<br />
I think the idea is pretty neat, and has much in common with the things above, so I might go ahead and tackle that as well. Or procastrinate until someone else does it and steal^h^h^hborrow ideas from that place. Anyways, the jinja-rewrite made it much easier to see the actual HTML, so the new javascript (preferrably using jQuery) client could build on top of the existing layout.<br />
<br />
Small steps in the right direction makes the world a better place. :)<br />
<br />
Also, some day in March, if things go as they should, the household will gain a new member. One very small and in need of almost-constant care. I estimate I will not be able to contribute to open source for a few years starting then. Best do what I can before that. ;)Mackehttp://www.blogger.com/profile/06392128938508053935noreply@blogger.com0tag:blogger.com,1999:blog-4343580553277118310.post-18989783580411599282010-11-04T17:40:00.003+01:002010-11-27T12:20:58.103+01:00Mercurial rebase/merge musingsI asked about <a href="http://stackoverflow.com/questions/4086724/how-do-i-check-for-potential-merge-rebase-conflicts-in-mercurial">how-to-check-for-potential-merge-rebase-conflicts-in-mercurial</a> on <a href="http://stackoverflow.com/">StackOverflow</a> yesterday and got a few interesting responses.<br />
<div><br />
</div><div>I think I'm going to write a small plugin that rebases if there are no file conflicts, and merges otherwise. This is what I want myself and everyone else to do, as it helps with clearing up bad merges afterwards, and avoid "'rebase broke the build and my life sucks" situtations.<br />
<br />
Update 2010-11-27: I've written an extension for this, see <a href="http://mackeblog.blogspot.com/2010/11/mercurial-rebasemerge-musings-part-2.html">this post</a> for details and links.</div>Mackehttp://www.blogger.com/profile/06392128938508053935noreply@blogger.com0tag:blogger.com,1999:blog-4343580553277118310.post-32090313742274331562010-11-04T16:50:00.006+01:002010-11-04T17:00:02.777+01:00LogWindow 2.0 using LibQxtI just implemented a small web server for our Qt apps that use LibQxt's QxtWeb classes to serve the application log over HTML.<div><br /></div><div>Static files are served from QResources and log events are fetched by AJAX/jQuery, using a small javascript code to format them into a table in the web server.</div><div><br /></div><div>Next on the todo-list is to add a dynamic filter by severity/thread/location. Should be pretty easy to get going.</div><div><br /></div><div>We used an in-application LogWindow before, but that sucks performance wise and it doesn't redraw when debugging the GUI-thread, obviously. This is also way easier than creating and managing an logging application as an entirely different process.</div><div><br /></div><div>The most recent LibQxt even has a JSONRPCService, which might've been useful. We're using a slightly older one so I manually implemented a WebService which serves JSON or html/css/js.</div><div><br /></div><div>Anyway, this ought to be cleaned up a bit and contributed back to LibQxt.</div>Mackehttp://www.blogger.com/profile/06392128938508053935noreply@blogger.com0tag:blogger.com,1999:blog-4343580553277118310.post-10092357356240579822010-01-22T19:51:00.008+01:002011-02-27T00:26:33.186+01:00Validating DTDs in python with lxml<div>After replacing the string-pasting in Buildbot's web-backend with proper html-templates (using Jinja), I decided to write a unit-test that follows all links on all pages and make sure they validate and don't contain stale links.</div><div><br />
</div><div>lxml seemed like a nice tool for that, but our XHTML starts with a reference to w3.org's DTD I got this straight away:</div><pre>failed to load HTTP resource</pre>Googling found me the <a href="http://www.hoboes.com/Mimsy/hacks/caching-dtds-using-lxml-and-etree/">Caching DTDs using lxml and etree</a> article by Jimmy Stratton. He had ran into the same problem last fall, dug around for a while (against the forces of nature, in this case represented by authors and documentation for lxml and python), and was nice enough to post his solution on the web.<br />
<br />
The resolver works flawlessly. Thanks Jimmy! I owe you one.<br />
<br />
<b>Update:</b> Since this is a reasonably popular post, here's the <a href="https://github.com/buildbot/buildbot/blob/a1e99533057f3fd56496c26b42f0e0ba9e774b08/buildbot/test/runs/test_webparts.py">unit test</a> for BuildBot, which applies this using the Twisted framework to validate all linked-to pages on the website. (It's was lost from the source tree in one of the many refactorings and cleanups of BuildBot's test suite. Also, it added some troublesome dependencies for the many builds slaves, and was too broad in scope. It should be re-cast as unit-tests for each page's possible states.)Mackehttp://www.blogger.com/profile/06392128938508053935noreply@blogger.com0tag:blogger.com,1999:blog-4343580553277118310.post-23222321162507753052009-08-31T18:55:00.002+02:002009-08-31T18:56:28.690+02:00Dirty coding tricksIf you've gotta ship, you've gotta ship.<br /><br />See <a href="http://www.gamasutra.com/view/feature/4111/dirty_coding_tricks.php">this Gamasutra article</a> for some funny stories. :)Mackehttp://www.blogger.com/profile/06392128938508053935noreply@blogger.com0tag:blogger.com,1999:blog-4343580553277118310.post-49438985982359985132009-07-09T18:12:00.005+02:002009-07-09T18:25:36.825+02:00Teh build is fail!Just to complete the gallery:<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_tlUbUg_x1Rk/SlYXB98fdII/AAAAAAAAACI/7puznKg9NkA/s1600-h/lavalamp-megared.jpg"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 192px; height: 320px;" src="http://3.bp.blogspot.com/_tlUbUg_x1Rk/SlYXB98fdII/AAAAAAAAACI/7puznKg9NkA/s320/lavalamp-megared.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5356494129379374210" /></a><br />The responsible one has left the building but it's been pretty effective so far in getting people to fix the build a.s.a.p. :)<br /><br />Also, the latest version of <a href="http://bitbucket.org/marcusl/buildboticon">BuildBotIcon</a> has optional sound, and currently we have Homer Simpson going 'DOH!' or 'Woohoo!' (rather loud). This makes everyone aware of it instantly.<br /><br />(And yes, I've been reading <a href="http://icanhascheezburger.com">lolcats</a> too much lately..)Mackehttp://www.blogger.com/profile/06392128938508053935noreply@blogger.com0tag:blogger.com,1999:blog-4343580553277118310.post-33384768279184717412009-06-30T18:20:00.002+02:002009-06-30T18:22:07.301+02:00Oh noes, the red light is on!This is how it looks before things get really bad. The build was fixed before the lamp was running at full speed though. :)<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_tlUbUg_x1Rk/Sko7gBs2xQI/AAAAAAAAACA/PXM6hgi0DLo/s1600-h/lavalamp-red.jpg"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 192px; height: 320px;" src="http://3.bp.blogspot.com/_tlUbUg_x1Rk/Sko7gBs2xQI/AAAAAAAAACA/PXM6hgi0DLo/s320/lavalamp-red.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5353156528481682690" /></a>Mackehttp://www.blogger.com/profile/06392128938508053935noreply@blogger.com0tag:blogger.com,1999:blog-4343580553277118310.post-15312291312149937292009-06-24T17:25:00.005+02:002009-06-30T09:57:31.551+02:00Lava lamps - up and running!One picture, 1k words, etc:<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_tlUbUg_x1Rk/SkJFpBnawZI/AAAAAAAAAB4/l4Bt7uT1vG4/s1600-h/lavalamps.jpg"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 192px; height: 320px;" src="http://3.bp.blogspot.com/_tlUbUg_x1Rk/SkJFpBnawZI/AAAAAAAAAB4/l4Bt7uT1vG4/s320/lavalamps.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5350915878380290450" /></a><br /><br />We've also added a timer to turn them off during the night, along with the status monitor (which you can't see), to be nice to the environment.<br /><br />Again, the USB board can be bought from <a href="http://www.active-robots.com/products/phidgets/phidgets-interface.shtml">Active Robots</a> who were very friendly and helpful shipping the package to me, in spite of the efforts of our local post office. ;)Mackehttp://www.blogger.com/profile/06392128938508053935noreply@blogger.com1