Thursday, December 30, 2010

All quiet on the blogging front

After the extreme excitement of the last blog, things have quietened down somewhat. 

On Saturday, I continued reading 'Information Technology for Manufacturing' (ITM) only to discover that the remaining chapters weren't quite so pertinent to my needs as the opening three. I only leafed through the book, but it was clear that the subjects chosen weren't what I needed to read. I'm not saying goodbye to my 'new friend', but it's clear that our friendship won't be quite as close as I had originally imagined.

As I wrote last week, "We have the woodwork factory whose production time is measured in days, where it's hard to assign specific tasks to specify people and where it seems that the methods of following production were designed by someone divorced from the way the factory works (or more correctly, divorced from the way the factory works now). As a result, the plant manager has to resort to maintaining an Excel spreadsheet which keeps all the data which are relevant to him."

I  figured out how I could extend our ERP program to maintain some of the data which he keeps in the spreadsheet; the whole idea of time slices suddenly became much easier and simpler. I wrote with some excitement to the woodwork factory manager on Sunday explaining my solution but he was unable to grasp what I was suggesting. So yesterday, I hauled myself up to the north of Israel and sat with him for a few hours. 

I didn't make the sale. My solution was for only part of what his Excel spreadsheet contains; the spreadsheet is heterogenous in that it contains data both about orders and batches (one batch contains one or more orders), and in Priority it is difficult if not impossible to produce heterogenous reports. I suggested a few alternatives, but these require an order to be linked to a batch as soon as possible and for reasons which I don't comprehend, they attach an order to a batch as late as possible.

As I say, I didn't make the sale. We agreed to part as friends.

Another, more promising area for exploration is external optimisation: ERP's work order says to use a certain amount of raw material, but via the use of an external optimiser, the workers are able to produce the same amount of finished product with less than the dictated amount of raw material. This causes problems with stock keeping and creates what Priority calls 'unflushed inventory' (I haven't managed to find any references to this on the Internet). This is definitely an ERP failure and definitely something which I intend to investigate, and hopefully solve. The problem exists (one way or another) in three out of the four business units in my company, so there should be plenty of examples.

It occurs to me that it might be a good idea to try and contact a company who has implemented Priority abroad; I won't be perceived as a competitor, and I can learn what the various terms are called in English.

Friday, December 24, 2010

New friend


It seems that I have a new friend in my life: a hardbound book called 'Information Technology for Manufacturing' by Kevin Ake, John Clemons and Mark Cubine. If indeed it is available for $39.39 from American Amazon then go for it, because I paid nearly the same price in British pounds (from the Book Depository). I am going to call this book 'ITM' and create a tag for it so that in the future it will be easy to find references to this book in the blog.

I've only read the first three chapters of ITM but it seems to be an ideal source of information and ideas on how to improve the use of one's ERP system (the subtitle of the book is 'reducing costs and expanding possibilities').

I bought the book because I am seriously considering studying for a doctorate degree in business administration (DBA) when I finish my MBA degree, and it's good to start as soon as possible in finding ideas which need to be solved. As opposed to the MBA, in which students are taught the material, the DBA allows the student to display his prior knowledge whilst making a contribution of his own to the literature.

At the moment, it looks like my topic will be addressing the failures of 'standard' ERP and showing how these failures can be ameliorated by the use of 'extended' ERP. It occurs to me that I would not be able to do so had not Priority, the ERP system with which I work, not been extensible (it allows the user to define his own tables, data entry screens and reports).

My aim at the moment is two-fold: to identify such areas (I've already noted two) and to show how it is possible to handle them.

Why do I write that this book is my new friend? Because it looks like ITM will be accompanying me intimately for a long trip into the future. Because I woke up this morning at 4:30am with an idea which came from the intellectual stimulation caused by my new friend!

As is expounded in the first three chapters of ITM, 'standard' ERP was developed for the corporate staff; it handles accountancy, purchasing, sales, manpower, etc very well, but does not have the level of support necessary for the data collected at the plant level. That's not to say that there is no such support, but rather the existing support is designed and implemented from the corporate point of view (an interesting example of bias).

I woke up so early this morning with an idea which can be summed up in two words: time slices.  ERP offers no standard method of storing time slice data. Whilst writing those words, it occurs to me that in the chair factory, we know every day the status of each order: how many of the chairs in each order have been produced and by whom, by virtue of the fact that the manager of the production line sits down at the end of the day and enters this data into the standard program.

But chairs are discrete objects whose production time is measured in minutes. On the other hand, we have the woodwork factory whose production time is measured in days, where it's hard to assign specific tasks to specify people and where it seems that the methods of following production were designed by someone divorced from the way the factory works (or more correctly, divorced from the way the factory works now). As a result, the plant manager has to resort to maintaining an Excel spreadsheet which keeps all the data which are relevant to him.

The very word 'Excel' acts as a springboard: as I have written before, 'whenever I hear the word Excel, I reach for my gun'. Here, the word Excel means that there is a lacuna in ERP which cannot be solved by traditional methods but can be solved by 'extended' ERP (enter cartoon character ERPman stage left). What we need to do is to convert this spreadsheet into a 'time slice' data table which then can be analysed using all the strong methods and advantages of the SQL database.

The plant manager sent me a copy of his spreadsheet a few months ago but I returned it, saying that there was no way to maintain that kind of data in Priority. That's a true statement if one is working in the blinkered standard ERP but not true when one enters the brave new world of extended ERP. On Sunday morning I shall dig out that spreadsheet and start examining how I can transfer it: what data fields are needed, which will come automatically from the database and which will be entered manually, how to present a suitable input screen to the user, etc.

The ideas are running around my head - which is why I woke up at 4:30am. There's no point in going back to bed as it is now 5:30am which is when I would have woken up anyway.

Thursday, December 23, 2010

Size matters

Once upon a time (in fact, until about three months ago), the computer monitors on which my programs run were all the same size. This made it very easy for me when designing screens/forms for my programs: if they looked ok on my monitor, then they would be ok on my client's monitors.

With the advent (and purchase) of different sized monitors with different display ratios and resolutions, this state of affairs is now in the past. The client (OP) has a monitor which is capable of displaying many more pixels than my monitor displays, and she wants to make use of that extra space. So I had to start learning about resizeable forms. As it happens, these work in synergy with MDI child forms - but not with dialog boxes - and a few of my recent programs follow the MDI model, so the possibility of conversion to resizeable forms definitely exists.

The starting point of this discussion will be a simple MDI child form with a database grid and a panel hosting a few buttons. Something like this:


My first attempts at creating a resizeable form included the following steps:
  1. Change the form's border style from 'bsSingle' to 'bsSizeable'
  2. Add a status bar at the bottom of the form in order to show the 'stretchable' bitmap hint
Whilst the form could be resized, the individual components of the form stayed where they were. The next stage in the learning process was the anchor: both the grid and the panel have their anchors set to akLeft and akTop, or in English, the left and top coordinates remain at a fixed offset from the form's top and left. As resizing is generally to the bottom and to the right, the result is that the components don't resize. The key to this conundrum was to set the akBottom anchor property to true - the bottom of the component will be at a fixed offset from the form's bottom. If the form's height increases, the height of the components will increase as well.

Whilst all was good at the design stage, running the program produced different results when resizing the form, specifically regarding the width of the form. A MDI child form whose border style was 'bsSizeable' was displaying at a different width than the form's defined width and this was annoying. I programmed a work around by forcing the form to be a specific width in the FormShow and FormResize methods, but it was clear that this wasn't the solution.

Looking around on the internet, I came across the SizeGrip component; I think that this component originated here, although I actually found it on a different blog which quoted the original word for word without attribution (very bad practice). The sizegrip component allows a form to be resizeable without the need of a statusbar; for reasons which I don't understand, the form's border style can remain bsSingle yet still resize, thus solving the display width problem.

Along with anchors, one is supposed to use the constraints property which define the minimum size for the form. Once I had allowed a form to resize, it seemed only polite to store the resized height in the registry so that the form could be displayed at the same height the next time it was invoked. As a result, I started adding the following code to units
procedure TDoTables.FormCreate(Sender: TObject);
const
 mywidth = 416;
 myheight = 496;

begin
 height:= reg.ReadInteger (progname, 'dotables', myheight);
 constraints.MinWidth:= mywidth;
 constraints.MinHeight:= myheight;
end;

procedure TDoTables.FormClose(Sender: TObject;
  var Action: TCloseAction);
begin
 reg.WriteInteger (progname, 'dotables', height);
 action:= caFree;
 TrimMemory
end;
'DoTables' is the name of the form; the registry stores the height of each individual form.The 'myheight' and 'mywidth' constants were defined in the design stage - what looks good on my monitor.

But: there is still room for improvement. If I set the grid and panel's anchor.akBottom propery to true and enlarge the form, the form looks as follows


Whilst the panel has resized, the buttons have remained in the same position. Obviously the top button should remain in the same position, but the second and third buttons' position should change. The bottom button's top property can easily be calculated as it will be the panel's new height less the height of the button less 8 (the offset from the bottom of the panel), but what about the middle button? And what happens if there are four buttons on the panel? Basically, what is needed is the equivalent of the alignment palette's space equally vertically command. As I don't know how to access this at runtime, I have to mimic its action.

After a bit of muddling around, I found the following method which sits in the form's FormResize method. First of all, calculate the increment to the panel's height (the original design time height is stored as a constant), then divide this height by the number of buttons less one. The second button's top has to increase by this calculated increment, and the third button's top has to increase by this calculated increment times two. Translated into code, it looks like this:
procedure TDoTables.FormResize(Sender: TObject);
const
 ph = 441;

var
 incr: word;

begin
 closebtn.top:= panel1.Height - closebtn.Height - 8;
 incr:= panel1.height - ph;
 if rankbtn.Visible then  // five buttons on screen
  begin
   incr:= incr div 4;
   editbtn.Top:= 106 + incr;
   contactsbtn.top:= 204 + incr * 2;
   rankbtn.top:= 302 + incr * 3
  end
 else
  begin
   incr:= incr div 3;
   editbtn.Top:= 138 + incr;
   contactsbtn.top:= 269 + incr * 2;
  end;
end;
The above is probably more complicated than need be, but I thought I'd quote the form's code ad verbatim. The complication is caused by the fact that sometimes the form displays five buttons (rankbtn will be visible) and sometimes only four buttons (no rankbtn). Editbtn is the second button on the panel and contactsbtn the third; Closebtn is always the final button on the panel so its top can be calculated without knowing the increment in panel height. The magic numbers in the code (106, 204, 302, 138, 269) are the buttons' tops at design time.

I'm going to let this code sit for a while (like good wine); maybe there is room for further improvement.

Tuesday, December 21, 2010

The gumption trap

Thomas Edison famously remarked that "invention is 1% inspiration and 99% perspiration". Unfortunately, it's the same regarding debugging.

I bought a wide LED screen for the children's computer and on Sunday connected it up. The screen didn't work. After eventually finding the power switch and turning the screen on, it still didn't work. As it was fairly late and I was very tired, I decided not to continue but to leave the problem for another day.

On Monday evening, I started again with the screen and verified that it wasn't displaying a picture. I then disconnected it from the children's computer and connected it to mine - voila! There is  a picture on screen, so we know that there is not a problem with the screen itself. I moved the screen back to the other computer - no picture. I connected my screen to their computer - no picture. I even swapped the video cable; the new screen's video cable worked fine on my computer whereas my video cable didn't work on their computer.

At this point I did the sensible thing and took the dog for a walk. Getting out of the house - or more importantly, getting away from the problem - helps me see more clearly and theorise what to do. Obviously it's a problem with the computer's video card.

When I got back (and after eating something: food also helps the brain), I opened up the computer and verified that the screen card was sitting properly in its slot. I also managed to cut my thumb on the sharp edges of metal which have been cut to allow a fan to sit on the video card. I connected the video cable to the video card, connected the electricity - and saw a picture on the screen. I then disconnected the electricity and video cables, connected a disconnected wall fan inside the computer, put the side of the computer case back on, connected everything up - and no picture appears on the screen.

The wall fan was making a large amount of noise so I disconnected the computer again, opened it up, disconnected the wall fan - and then I had the epiphany which I should have had much earlier. I had been connecting the video cable to the onboard video connector instead of to the external video card! Once this was noted, I closed the computer up again, reconnected everything properly - including the video cable - and could begin working.

Well, not quite. There was an icon on the screen saying that the network card was disconnected. I checked the connections - wall first, computer second - before the inevitable epiphany occurred again: I had connected the network cable to the internal ethernet socket, which had been disabled, instead of to the external ethernet socket. Easily solved.

Conclusions:
  1. Write on the computer case to use the external video and ethernet sockets in the future
  2. Take the dog for a walk more frequently
This morning, whilst thinking over this affair, I remember "Zen and the art of motorcycle maintenance" which discusses what I would term 'debugging' and the importance of the 'gumption trap'. When debugging, one often gets into a mental state which leads in one direction whilst ignoring other directions (one of which will be the correct direction). Thus one wastes a great deal of time whilst increasing one's frustration as one goes in the wrong direction.

I am about to order a book which discusses this issue more thoroughly - "How we know what isn't so: fallibility of human reason in everyday life", by Thomas Gilovich. Ignore the price which is going to be displayed by the Amazon link: British Amazon is selling the book for 6.66 GBP but charging something like 8.25 GBP postage, whereas the Book Depository will sell me the book for 10.94 GBP including postage. Why pay 36% more?

Here's the blurb of the book: "When can we trust what we believe - that "teams and players have winning streaks", that "flattery works", or that "the more people who agree, the more likely they are to be right" - and when are such beliefs suspect? Thomas Gilovich offers a guide to the fallacy of the obvious in everyday life. Illustrating his points with examples, and supporting them with the latest research findings, he documents the cognitive, social and motivational processes that distort our thoughts, beliefs, judgements and decisions. In a rapidly changing world, the biases and stereotypes that help us process an overload of complex information inevitably distort what we would like to believe is reality. Awareness of our propensity to make these systematic errors, Gilovich argues, is the first step to more effective analysis and action.

I look forward to reading this book. 

Monday, December 20, 2010

Frustration in the computer lab

The last two weeks have been very volatile: some good events have happened, countered by some not so good events. I think that things have evened out now, but all those ups and downs have left me very tired.

I choose to write about the problems we have had in updating computer services in the occupational psychologist's office. Until about a month ago, the setup was as follows:
  1. a venerable NT server was hosting all data files (mainly word files and pdf), as well as hosting the Firebird server and its associated data files
  2. Two "main user" computers for the OP and her secretary, which accessed the files on the server. These computers run XP.
  3. Seven "examinee" computers which run the various computerised exams and store their results on the server; five of these computers run XP and one Windows 98.
  4. One "helper" computer which runs a program every ten minutes during the day; this program reads the results of the computerised exams, inputs them into Firebird data tables and creates Word files containing the developed results of those exams. This computer runs XP.
Due to various problems (probably speed and clutter), it was decided to buy new computers to replace the two 'main user' computers; these naturally came with Windows 7. After playing with these computers for about two weeks, we discovered that they were having problems saving files on the NT server. So we decided to buy another Win7 machine which would replace the NT server: all it had to do was store files and run Firebird.

This is where the fun begins. It took several frustrating hours before I could even get Firebird (v1.5.5) to run on this new computer and to access the database files locally. I was totally unable to set up the other computers to access the databases, so we decided to scrub Win7 and install XP. Again, it took several hours, but eventually on Saturday night, I had Firebird running on this new computer and the Win7 computers could access the databases.

But the remaining XP computers couldn't access those databases! This means that the 'helper' computer was unable to do its work, and I was considering moving its functions to the new server computer so that it would update its databases locally. But in order to do that, it would need Office installed. Even though the Win7 machines were accessing the databases on Saturday night, they often complained of problems: one program would complain about not having access to temporary files being created on the new server, and another program displayed a timing problem. 

All day Sunday, the OP and her secretary were having problems with the databases. The files created by the people who were examined that day could not be entered into the databases because the helper computer couldn't help. So in the evening, we decided to put everything back almost the way it was. 

The only real difference is that the NT server no longer serves as a file repository - the new computer does this. It still hosts the Firebird server and all the database programs run properly. The helper computer now does its job.

All of the above has taken a large amount of time and frustration (see opening paragraph). As I often say, "computers were invented to teach us the value of patience", a deliberately ironic statement seeing as computers do routine tasks so fast that we (supposedly) don't have to wait.

I bought the NT server in 1998 if I remember correctly, and it's still going strong, which says something about the operating system. One day, though, it's going to stop working, and then we'll have to find a way of migrating the databases successfully to another computer.

Wednesday, December 08, 2010

Project management exam

Tonight we lit the eighth candle of Chanuka. The reason for lighting eight candles is that, according to the legend, there was only enough oil for a lamp in the Temple to stay alight for one day; despite this, the lamp stayed lit for eight days. This is called the miracle of Chanuka.

I had my own small Chanuka miracle today when the Project Management exam was not too hard. I'm not saying that it was easy, though; I finished the exam after two hours and 40 minutes, whereas previous exams have taken at the most two hours and 20 minutes.

I'm fairly confident that I did well on the exam (let's say 70%), although the more I think about the exam, the more I can think of things which I didn't answer well. The case study was about an oil exploration company, which is a lot simpler than some of the example case studies that I have seen. Drawing the organisational breakdown structure (OBS) wasn't too hard, although it may well be that I misunderstood one of the questions about the contracts involved.

As expected, question 2 was a critical path method question with crashing until a certain limit. This question held no problems for me, although there was a twist in one of the subquestions: after the project has been fully crashed, how can it be speeded up even more? I can think of two ways: compromising on quality (which means that each step won't take so long), and fast tracking, in which one activity is started before its predecessor has finished. I may write to the course designer to ask whether there are any other ways of speeding up the project.

The third question, as expected, was about EVA; this too held no problems but it involved a great deal of writing.

The fourth and final question was about risk. Fortunately I had been reading the course book chapter about risk in the morning, so most of the material was fresh in my mind. This question could have been about something else in which case I might not have been able to answer. To quote the opening sentence,
Explain the difference between risk and uncertainty
I'll leave with that sentence hanging in the air.

Monday, December 06, 2010

Rain

It is finally raining here. It is a great shame that this rain - for which we have waited for, longed for, prayed for - could not have fallen a week ago. Had it done so, then the forest on the Carmel mountain would have been wet, or at least hydrated, and so would not have gone up in flames the way that it did. The entire country mourns the trees.

My mind is trying to concentrate on the Project Management exam which will be held in another two days, but it's very difficult, for 
  • My wife told me that there will be two funerals held on the kibbutz today  
  • I have just discovered that the annual license for my motorbike expired two days ago
  • I have been trying to install the Firebird server on a new computer running Windows 7 and am not succeeding
  • I have just received the telephone bill for my trip abroad at the end of September and I have exceeded (by far) the amount that my company allows me each month
The icing on the cake is that I have an appointment for the dental hygienist in three hours time. At least the state of my teeth and gums is much improved from what it was a year or two ago.

When things are down, then everything is down. At least there will be an improvement by Thursday.

Thursday, December 02, 2010

Chanuka through the years

Last night we lit the first Chanuka candle, so this seems like a good opportunity to exercise my memory by trying to remember Chanuka through the years.





I know that we lit Chanuka candles all the years when I was at school and I know that we never had a Christmas tree, but apart from that, my mind comes up as a complete blank for those years regarding the festival. As opposed to other festivals, not much happens except lighting the candles, eating doughnuts (sufganiyot) and saying certain prayers, so it's not unreasonable to assume that this is all that we did.

In 1973, I was in Israel during Chanuka, but again my mind is completely blank as to what happened then. Considering that this would have been one or two months after the end of the Yom Kippur war, the entire country would have been in a very sad state of mind, and celebrations were kept to a minimum.

My first specific memories about Chanuka would be from 1975. As I recall, that year I (along with others) 'performed' every night of the festival, singing the special festival songs and bringing cheer. One day would have been at the Friern Barnet Old Age Home (or something similar) where the local Habonim group sang for the residents. Another night, we were outside the Russian Embassy, singing the songs and protesting about the Russian government's emigration policy (as if we would have made a difference). Quite possibly we also appeared at other rallies during the week.

In 1976, I decided to start a tradition by inviting about twenty people to a party at the communal house where I lived and cooking for them. I remember cooking pineapple chicken (which was fine) and fried banana (which was ridiculously ambitious for me to cook for so many people). Apart from the cooking, I don't remember anything else, but we must have had a fine time.

I repeated the tradition the following year, although fewer people attended. I don't remember what I cooked but I do remember that I invited a girl from outside our circle to attend. I had met her at university; she was in the first year intake of my course whilst I was in the final year. She was Jewish and lived not too far away (in London terms - near Swiss Cottage, whereas I lived across the road from the Hendon Way). The following day I phoned her to ask how she was, and whether she had enjoyed herself the previous night; we proceeded to have a conversation for about twenty minutes with a much warmer tone that I had expected. I mentioned this to one of my fellow flatmates with a note of surprise; about five minutes afterwards, the girl phoned me - it turns out that she thought that I was someone else whom she knew better, hence the tone. Although we did hang out for a while, this relationship (if it might be called that) didn't go anywhere. [Mind you, I felt that year as if I was living on borrowed time as I knew that I would be emigrating in the summer of 1978. Typically, after a few years of drought, I met some nice Jewish girls that year but was unable to do very much in the romantic direction.]

In 1978, I was living permanently in Israel. I remember going to Tel Aviv one day before Chanuka and buying a chanukia - my first and only. I doubt very much that buying the chanukia was the reason for my trip but nothing else comes to mind. That year, a few of my fellow immigrants and I performed for the kibbutz, singing some chanuka songs in two, three and even four part harmonies which I had arranged. Every now and then, I pull out my recording of that event and marvel at our ability - and again at my ambition for such vocal writing.

The next few years are again a blank, although I can almost picture them. The kibbutz would have held a Chanuka party at which I probably would have performed, but towards the end of the period of my first kibbutz, these affairs almost certainly would have become chillier and less enjoyable.

In 1989, we moved to a different kibbutz. The Chanuka party that year was one of the most enjoyable in my life, and I remember being moved to tears. This was the kind of party of which I had dreamed when I was still in the youth movement, and finally it was happening in front of my eyes. Many people participated in the music (including myself), every family brought their chanukia, and basically it was the Chanuka party to end all Chanuka parties.

The same pattern continued for the next few years, although naturally my enjoyment decreased. There were also Chanuka parties held in the kindergarten in which our children participated; one year I was asked to say the blessings for lighting the candles and to my eternal embarrassment I became confused in the middle and forgot the words.

Once the children entered school, our active participation decreased and decreased. One year I remember well, for the music was provided by the local school big band and my daughter appeared in a dance troupe. The dancing was fine but the writing was on the wall: we don't need you to play the guitar any more.

Since then, I don't think that we've been to a kibbutz Chanuka party. Should my children get married, have children and still live on the kibbutz, then we will attend again, but that's not going to be for some time - and who says that the parties will be in the same format?


So here we are, alone -
our children have grown up and moved away.
living their own lives, they say...
it all seems very strange to me.
         
I don't understand their ways:
our children amaze me all the time
and I often wonder why they make me feel
so sad and suddenly old.
         
Now we're left with an empty home,
from our nest all the birds have flown for foreign skies.
We're discarded, of no further use,
though we gave our kids all our youth and all our lives -
we really tried.
         
Now there's only my wife and me;
we used to have a family - now that's gone
and only memories linger on...
it all seems very wrong to me.
(Peter Hammill, "Autumn") 

It's not quite as bad as that; whilst our children have their own rooms on the kibbutz, they visit us most days to eat, watch television, use the computers and get clean laundry. But last night our daughter was working off the kibbutz and our son was at the Kibbutz party with his friends - and my wife and I were alone. So we phoned our families and blessed them.

Sunday, November 28, 2010

Sumptuous Sunday 2

This really should be 'Sumptuous Saturday' as I'm not doing any cooking today....

For Friday night, I prepared a beef stew, basically cholent, in the slow cooker. Unfortunately, this was not a success. Although each ingredient was cooked, the dish as a whole didn't come together. I think that I'm going to abandon this dish and go back to the simple minced meat casserole which I used to cook on the gas.

On Saturday, I cooked a whole chicken in the slow cooker. As ever, I was dubious about placing a whole chicken into the cooker with no liquid, but the results were astounding. Along with the chicken, I placed potato and onion slices in the pot. These came out very nicely cooked and next time, I'll put in more potato, in larger pieces. Although I used some celery as a spice, this wasn't very noticeable in the final result. The chicken was cooked for four hours on high.

I decided to find out what 'high' and 'low' really mean. 'High' is 300 degrees Fahrenheit, which translates to 149 degrees Centigrade; 200 degrees F is 93 degrees C. Both are somewhat higher figures than I had expected. I wonder what the advantages are of cooking a whole chicken at 149 degrees for four hours as opposed to roasting it for two hours at 175 degrees. I suppose that it depends on how much electricity (how many watts) the slow cooker and oven are using. Finding this out for the slow cooker should be easy, but I don't know whether I can find out the oven's requirements.

Thursday, November 25, 2010

Algorithm for quickly drawing activity networks

Caveat: this post is probably going to be long and tedious; it certainly will be boring. If you're not interested in drawing activity networks (the first step in CPM or PERT analyses in project management), then don't read on. You have been warned....

The exam in Project Management is about two weeks in the future. At the moment I am somewhat under-employed at work, so I am using the time to go over exam questions. I have noticed that it takes me about three or four attempts to draw an activity network, time which could be better spent elsewhere. I thus need a method which will allow me to draw the network correctly on the first attempt.

After googling several sites, I finally found what I was looking for. As Heriot Watt displays the data in a slightly different manner, I modified the algorithm displayed therein. After playing with the algorithm a few times, I discovered that I can significantly speed it up whilst not losing any accuracy. So I'm going to present my version of the algorithm here - all errors are thus mine!

Here is the data:

Table 1: Estimated activity times for works to be completed by Contractor X
ActivityOptimistic time (weeks)Likely time (£)Pessimistic time (weeks)
A–B123
B–C369
B–H456
C–D81012
D–E345
D–F234
E–G345
F–J123
G–H135
G–I468
G–K135
H–L123
I–L123
I–M4710
J–K123
K–M123
L–M234
M–N234
As there are optimistic and pessimistic times quoted in the data, one can assume that this is going to be PERT analysis. This makes no difference when drawing the activity network.

Here's the algorithm:
repeat
1. List all the current nodes (A, B, C, etc. This list will shorten during execution)
2. Cross off each destination node which can be reached by a node on the list (if the activity is AB, then one crosses B off the list)
3. Remove from the list all uncrossed nodes and put them aside until the list is empty

So: the initial list is all the nodes: A, B, C, D, E, F, G, H, I, J, K, L, M, N.
First iteration: everything gets crossed off the list bar A (not surprisingly as this is the starting node).
Second iteration: everything gets crossed off the list bar B (as A is no longer on the list, no node has B as a destination node)
Third iteration: everything gets crossed off the list bar C
Fourth iteration: everything gets crossed off the list bar D (I warned you this would be tedious)
Fifth iteration: everything gets crossed off the list bar E and F
Sixth iteration: everything gets crossed off the list bar G and J
Seventh iteration: everything gets crossed off the list bar H, I and K
Eighth iteration: everything gets crossed off the list bar L
Ninth iteration: everything gets crossed off the list bar M
Tenth iteration: everything gets crossed off the list bar N

The result of the above is a grid with ten columns and three rows, like this:




EGHL
ABCDFJIMN
K


In practice, one doesn't actually draw the grid. Here one discovers that the algorithm isn't sufficient and that one needs two heuristics. When there are two (or even three) nodes in a column, how does one know their vertical placement? One heuristic which I developed is that the node with the most activities should be placed in the middle. If one considers H, I and J: H only leads to L, whereas I leads to L and M; K leads only to M. So I has to be in the middle. Why are H and K the way they are? Because G leads to H whereas J leads to K, and this is the easiest way of drawing the connections. As I leads both to L and M, L has to be "above" M. The second heuristic is that the final position of each node in the grid is dependent on the preceding and succeeding nodes and can only be determined when actually drawing.

Here's the activity network as given in the worked answer
Here, E is effectively in row 2 and F in row 3, as opposed to 1 and 2, but that's not important.

For reasons which I don't entirely understand, we are not allowed to use scrap paper during the exam; everything has to be written in the bound answers book, although scrap workings can be crossed out and labeled 'scrap'. Using this technique will save the amount of scrap working that I have to produce, and will naturally save time. Unfortunately, it's going to be a bit difficult copying the clean network from the scrap workings.

Wednesday, November 24, 2010

Thinking of templates

Gerard: What are you doing, Newman?
Newman: Thinking.
Gerard: Well, think me up a cup of coffee.
(from The Fugitive,  starring Harrison Ford and Tommy Lee Jones).

The Occupational Psychologist has upgraded a few of her office computers to Windows 7 and Office 2010, whereas I work with Windows XP and Office 2003. All of the programs which I have written for her work without change ... as long as they don't try to open Word and display data there. As it happens, most of them do have to open Word, which means that we have a serious problem.

I thought that maybe the automation interface had changed, but that seems not to be the case. I looked around on the internet for solutions; one suggested putting in a 'sleep' statement after the call to open Word, another suggested changing protections. Neither solution worked. Suddenly, out of the blue, a word popped into my head - templates. My programs are written to open Word using a certain template and Word 2010 doesn't know where to find that template.

I quickly wrote a small test program which opens a document and types a few words without specifying a template. After transferring the program to the OP's office, we ran the program successfully. In other words, my hypothesis about the non-functioning Word link was correct: Word doesn't know where to find the correct template.

Of course, knowing the solution to the problem doesn't always help in implementing that solution. I've sort of found the correct place in Word to define the templates, but the dialog box has changed from Office 2003, but aside from that, the text appears in small Hebrew letters which are hard to read, and often Microsoft speaks a special dialect of Hebrew which most people can't understand.

So: if anyone reading this is versed in using Word 2010 automation with templates, please let me know.

Update:
It's just as well that I wrote this blog in advance of publishing it. Via the help of the wonderful stack overflow site, I found the place where I can define the default location of templates in Word 2010. If anyone's interested, it's Files|Options|Advanced|File locations. The button which activates the file locations dialog is located at the bottom of a very long dialog box and is almost hidden away. No wonder I didn't find it before (especially as I was looking in the wrong place). Methinks Microsoft is trying to tell us something.

Tuesday, November 23, 2010

Copper socks 2

I discovered that one of the most popular posts that I have written was about copper socks. A few days after writing that entry, I felt that I might have been too enthusiastic, as I was having problems with the socks: they were too tight. I checked whether I had ordered the wrong size, but no, I had ordered 'large' (and being on the small size, my feet are naturally small). It might have been that my wife's laundering caused the socks to shrink slightly; there were no special washing instructions on the packet in which the socks came.

A month later I can say that my problems with the tightness was probably more due to me being unused to wearing shoes and socks after six months in sandals, although I still feel that the socks are a little tight. Until a few days ago, I couldn't wait to get home and take the shoes and socks off, but last night I felt quite comfortable in them.

As for my feet: the fungi seem to have disappeared, although the skin is very dry. I'm going to start rubbing in foot cream before putting on the socks in the morning to alleviate this problem.

I received another letter from Cupron the other day, telling me that they have copper pillowsheets (if I remember correctly) on sale somewhere in Israel. Although I'm pleased that they've started selling to their natural market, it wasn't a product in which I was interested, so I deleted the email.

The gravy boat

My logo states that the blog contains random comments from a working programmer, an amateur chef and a frustrated musician. The previous blog was from the frustrated musician and the one before that was from the working programmer, so I suppose that this blog has to be written by the amateur chef.

I didn't cook supper on Friday night; a few days previously, my 22 year old daughter said that she wanted to cook cottage pie for us. Her interest in cooking started when she was in Australia a few years ago and had to start looking after herself; she now avidly watches cooking programs on television (Gordon Ramsay is a particular favourite, although I'm not too sure how much a neophyte chef can learn from him). The pie itself was a bit watery; when I asked her about this, she said that she added water to the meat. ??? I asked myself.

On Saturday, I cooked again chicken breasts in the slow cooker. The supermarket didn't have the usual frozen chicken breasts so I had to make do with chicken shnitzel. This is basically the same, but the cuts are thinner and so cooked more quickly, a fact which I did not take into account. The sauce is counter-intuitive: mayonnaise, jam and mustard. I am tempted to add some peanut butter to this mix to see what effect that has.

Having started making sauces, I have been looking for the past month or so for a gravy boat in which to serve them; I've looked in several shops which sell kitchen utensils but without success.
During my searches, I also discovered what it's called in Hebrew, although I suspect that many people won't know the word (although it's derived from the word for sauce and thus immediately understandable). In the end, I started looking for a local supplier on the Internet and found one rapidly enough (the above picture is from their website). The postage is more expensive than the boat itself, but together they cost barely more than a kilo of beef, so I'm not complaining. It arrived in the post yesterday and today my wife will pick it up; on Friday, I'll serve something in it (although I haven't yet decided what to serve).

Monday, November 22, 2010

The road to Antibes

I wrote a little about this song a few days ago. Since writing, I have completed the arrangement, lowered the song's key so that I can sing it, recorded the vocal track and uploaded the song to Soundclick where it can be found in all its glory. Please give it a play.

A few words about the arrangement: when I first started sequencing the song, I gave it a fairly mundane introduction of alternating Em and Bm chords. Later on I changed this for a sequence Am|Bm7|C|Bm7|Am|Bm7|C|D, which then led into the Em verse.This introduction stayed for about three days, until suddenly I played a little oboe lick with a flattened fifth. This sounded so good that I had to start with this lick - upon which the rest of the introduction seemed out of place, so I dropped it. Starting in Am also reminded me very strongly of my song 'In silence' which effectively uses the same trick of starting in a different key from the song, so I transposed the intro down to Em.

I looked at the words which I had written on 15 December 1972; I remember being unsatisfied with them then but being unable to write anything better. Of the two extant verses, I kept the first four lines and threw away the rest, meaning that on Friday night I basically wrote all the  song's lyrics from scratch. They're still not particularly good but they're not that bad. The third verse references the jazz festival -
The roads of Antibes at festival time
Jazz percolates from the streets
The sounds of Antibes head straight for the heart
Arpeggios for free
Saxophones and oboes too
What's my wish? Well, here's a clue
Take me back
As it happens, the arrangement was featuring my two favourite wind instruments, the flute and the oboe, but if the lyrics are referencing the saxophone (surely the instrument most identified with jazz) then the arrangement too will have to feature a saxophone. So the flute became a soprano saxophone and all was well with the world again.

I always forget how to record my vocals in order to get a good result. The first recording on Saturday morning was fine from a technique point of view but did not sound good; no amount of effects (eq, reverb etc) could give me the sound that I wanted. So I sang the song again, very quietly, from the back of my throat. This produces a very warm sound which can be treated in order to create the final vocal.

Sunday, November 21, 2010

Improving the dual list box dialog

Database theories state that there are three types of relations: one to one, one to many, and many to many. The first two are easy to model, and the third is generally implemented by some kind of link table. This link table can either masquerade as a normal table, if it includes other information above the simple many to many link, or it can be degenerate, in which it includes solely a link to one table and a link to the other table.

In the programs which I have written for the Occupational Psychologist (OP), there have always been at least one such degenerate link table. I've been trying to think of several past examples, but they all require understanding of the kibbutz way of life to make sense, so I'll  give an example from the medical world. If a doctor suspects that a person is suffering from complaint A, then he will order a blood test which will include testing for A1 (say glucose), A2 (say urea) and A3 (creatinine). The doctor might order a blood test for set B which will include A1 as well as B1, B2 and B3. The many to many relationship is that one specific blood test (glucose) can appear in many 'blood test sets', and each 'blood test set' can include several individual blood tests.

When one of the sets (in this case, the individual blood test) contains only a small, finite number of members (let's say 30), I often implement this with a 'dual list box dialog'. This is one of the standard dialog boxes in Delphi which I have been using for years and looks like this


Over the years I have refined this dialog box to work in Hebrew and to load its items from database tables. The right hand list box would be populated by (in our example) the individual blood tests which have already been assigned to the blood test set, and the left hand list box would be populated by the tests which have not been assigned to this set.

A database table would be defined with two fields: one containing the key of each blood test set and one containing the key of the individual blood tests. Thus in the simple example given above, the link table would look like this (using the actual names of the entities as opposed to their key numbers; of course, in real life, the keys are used)


SetTest
AA1
AA2
AA3
BA1
BB1
BB2

Populating the right hand list box is easy: all one needs to do is write an SQL query which returns the tests when the set is A. Populating the left hand list box is less easy and for years I had been using the following method: iterate over a list of all the tests in the test table (ie A1, A2 etc), check whether the current value is in the right hand list box, and if it isn't, then add it to the left hand list box. Whilst this is clearly inefficient, I excused the inefficiency by noting that the list shouldn't contain more than 30 members.

When implementing this dialog box for the n'th time yesterday, it finally dawned on me that I could populate the left hand list box much more efficiently by means of a theta query : return only the values of the blood tests which aren't joined to any blood set test. This is easy to say but slightly difficult to define in SQL -
select items.name, items.id
from items
where not exists
(select 'x' from blood2item
where blood2item.item = items.id
and blood2item.blood = :p1)
'Blood2Item' is the name of the link table, 'blood' is the field holding the blood set key and 'item' is the individual blood test.

Another improvement suggested itself: the dialog box came with code which handles moving an item from one list box to the other -
procedure TDualListDlg.MoveSelected(List: TCustomListBox; Items: TStrings);
var
  I: Integer;
begin
  for I := List.Items.Count - 1 downto 0 do
    if List.Selected[I] then
    begin
      Items.AddObject(List.Items[I], List.Items.Objects[I]);
      List.Items.Delete(I);
    end;
end;
I had never really looked at this code, but yesterday I noticed that it moved an object, not just the name. Several months ago, I wrote about adding objects to a combo box; on the basis of this code I was able to improve my dual list box code when adding to the left hand list box
with qSrcList do
  begin
   params[0].asinteger:= akey;
   open;
   while not eof do
    begin
     srclist.items.addobject (fieldbyname ('name').asstring,
     tobject (fieldbyname ('id').asinteger));
     next
    end;
   close
  end;
Previously, when retrieving the value of an item, I would have to perform a table lookup to establish the value of an item's key from its name, but now the key has already been added to the list box and retrieving it means accessing the item's object value. The scales remaining in dstlist are ones which weren't there before we started, so add them to the 'blood2item' table

 with dstlist do
  for index:= 1 to items.count do
   with qInsert do
    begin
     close;
     parambyname ('p1').asinteger:= gid;
     parambyname ('p2').asinteger:= longint (Items.Objects[index - 1]);
     execsql;
    end;

I realise that this explanation is a bit muddled, but I'm fairly sure that anyone who uses this dual list box dialog will understand. The main thing is that I understand, so now I can improve all the previous uses of this dialog box and improve (once again) the efficiency of my programs.

Thursday, November 18, 2010

Alesis Q49

I haven't been producing any music at home for a long time. One reason is that my creative mind seems to be dwarfed by the intellectual need of my MBA courses, but the real reason is that I don't have the means to create music. My old MIDI keyboard controller simply will not connect to my computer via a USB interface. After successfully ignoring this fact for several months, I thought it about time that I do something about it, and so instructed my wife that the next time that she is in Tel Aviv, she should enter a certain musical instrument shop and buy me a keyboard.

She was in Tel Aviv on Monday and bought me an Alesis Q49 usb/midi keyboard controller, for the princely sum of about 650 NIS (about $180); this might seem a lot, but the keyboard is going to last for years. As its name hints, the keyboard has 49 keys, which makes it at least twice the size of my previous keyboard. I can put it on the computer desk in front of the monitor and play it there, but storing it is going to be a slight problem. The main thing is that the keyboard works perfectly with all my software; sometimes there are small problems in detecting the keyboard, but I'm learning to overcome those.

In honour of the new keyboard, I am in the process of resurrecting a very old song which I wrote in 1972 called 'The road to Antibes'. The song itself is not particularly special, which is probably why I haven't given it a second thought for the last 38 years. So what suddenly brought it to mind?

A few weeks ago, I decided to burn a compilation disk composed of ballads performed by King Crimson. Yes, them heavy people knew how to turn out a deft ballad. Listening to this to and from my studies reminded me what a deft hand Robert Fripp had (still has?) with the acoustic guitar. What jigged my memory was the song "Cascade and Cascade"; I was very enamoured of this song when it came out in 1970, so much so that a year or so later, I had the idea of creating a band whose musical direction would be similar to that of C&C, along with Sandy's "Who knows where the time goes" and "The sea" - lots of open guitar chords along with a sunny flute (true, there's no flute on the Sandy songs, but the guitar obbligatos could easily be transformed to warbling on the flute). The group would be called Antibes - because of the jazz festival held in the city on the French riviera (Côte d'Azur). It seemed the perfect sunny name for the sunny music that I had in mind.

The idea never reached fruition primarily because I didn't know anyone who played the flute! Somehow a female violinist was suggested, and I remember that one rehearsal was held with the violinist and a bassist (and me on acoustic guitar), playing my song "Sunday Rain", but nothing else came of this. I know that I wrote 'The road to Antibes' so that the group would have some material, but looking back on it now, TRTA was not really the right kind of song.

Exhuming the song, I performed a kind of Lennon-McCartney trick; one would bring the other an almost completed song, whereupon the other would change a chord here or there, add a middle section and possibly write the words for another verse. That's what I've done: almost automatically I changed the chords for one sequence, making it much more interesting. I have yet to add a middle section (which is sorely lacking) and I may well write another verse. I'm trying to give the song a sunny arrangement, but that seems more difficult than it sounds.

I wonder what it's like in Antibes.

How to save money when ordering books from abroad

Not living in Britain or anywhere else which has 'super saving shipping' or whatever it's called, ordering books from Amazon can be an expensive business. True, the books themselves are sold at reasonable discounts from their list price, but the shipping can be a killer.

For example, I have just been looking at the book 'Cider with Roadies' by Stuart Maconie, which would very much seem to be my kind of book. The list price is 7.99 GBP, and Amazon (in Britain) is selling it for 5.11 GBP, which works out to a 36% discount. Not bad ... until one considers how much the shipping to Israel costs. I don't know how they calculate the shipping: whether it depends on the weight of the items, their size, their cost or the day of the month. Anyway, almost completing my Amazon order with this book and a disk brought the shipping costs to 10.00 GBP! Assuming that the shipping cost for one book would be 5 GBP (not necessarily an assumption which holds), this means that I could purchase the book in Israel for 10.11 GBP, or 1.26 times its list price.

I don't know whether that book has been imported into Israel and distributed by the major book chain here, Steimatzky, but if it were, I imagine that the book would cost around 80 NIS, which works out at around 13.8 GBP at current exchange rates. So despite the hefty postage cost, it's still cheaper to order directly from Amazon.

Or so I thought until a few weeks ago, when I read on a blog discussing the fact that it's almost impossible to use an i-pad in Israel. Someone suggested ordering books from The Book Depository, which promises free delivery worldwide for all books. I checked into the site, saw that the above book was selling for 7.19 GBP and ordered it, paying by PayPal. I was not charged for postage, meaning that I bought the book with a 10% discount from its list price, but more importantly, at least 29% less than it would have cost from Amazon (depending on the postage, natch). Need I say more?

The only downside to this deal is that the Book Depository does not seem to sell DVDs nor musical disks, meaning that I will still have to order from Amazon and pay their inflationary postage prices whenever I want a non-book item. Consider the fact that Amazon started selling books.

Monday, November 15, 2010

Project Management lectures

As mentioned previously, last week there were three evening lectures held in the Project Management course, delivered by the course supervisor from Edinburgh. I am told that such lectures occur for each course about once every two years; out of the four courses that I have taken so far, three have had guest lecturers. After a quick check, I see that I wrote a year ago about attending the lectures for the accountancy course.

In my opinion, these lectures are a great help and those who don't attend can only blame themselves. The reason for their non-attendance is probably in my blind spot: maybe they have difficulty in understanding spoken English, and even more of a problem in understanding English spoken in a Scottish accent.

The PM exam seems to be more rigidly structured than the other subjects: the first question demands an OBS (organisational breakdown structure), the second is either CPM or PERT, the third is EVA and the fourth is a general essay question, normally about risk management. There are no multiple choice questions. This makes preparing for the exam somewhat easier than it might have otherwise been. As the lecturer pointed out, one only needs 50% in order to pass, and most numerically inclined people should have no problem in achieving the maximum marks from the CPM/PERT and EVA questions.

But of course we have to aim for the sky! The OBS question also should not be too hard, especially as we now know all the downfalls that await us. The lecturer had put at our disposal thirty six different files (I know - I printed them!) which give background and foreground material; one of these documents discusses where students went wrong in the exam. So I know, for example, that it is a very good idea to take several coloured pens with me into the exam in order to differentiate the types of links which I will have to draw in the OBS.

Possibly, revising for the exam will boil down to committing my thirteen pages of notes to memory (and two pages of those are CPM and PERT, for which I don't really need notes).

I wrote to the lecturer today, thanking him for the lectures. I noted that these lectures always have the same psychological affect on the students who participate: their self-confidence increases, and a certain amount of social binding occurs. Unfortunately, there is very little socialising in class and I rarely know anybody's name, so this affect is welcome. Actually, our regular lecturer asked in the first lecture for everybody to say their name and a few words about themselves, but social cretin that I am, most of what was said was quickly forgotten.

The Scottish lecturer several times used his flight back to Heathrow as an example for PERT: when El Al buys a landing window for a huge sum, it has to be confident that it can land 95% of its planes within that window. Planes landing outside of that window will mean fines for the company, an expense which has to be weighed up against the cost of widening the landing window. As it happens, the "plane missed its allocated landing slot! An air conditioning unit failed while the aircraft was still on the runway so there was a delay of about one hour at Ben Gurion. The pilot made up about half an hour en route but then we had to circle for over an hour at Heathrow in the ‘stacking queue’ resulting from extreme high winds over the South of England".

As a side affect of these lectures, I know see everything as a project!

Sunday, November 14, 2010

Sumptuous Sunday

One music blog to which I subscribe uses alliterative titles for the days of the week, eg Traditional Thursday. On that basis, today's entry will be Sumptuous Sunday, in which I discuss the various dishes which I cooked over the weekend.

The slow cooker was definitely the star this weekend. For Friday night, I prepared again chicken drumsticks and rice (10 drumsticks, a cup and a half of rice, 1 litre coca cola, one diced onion: place in slow cooker and cook for three hours on high, one hour on low), which my wife enjoyed. The children (now young adults) didn't like the way the rice turned out, so I won't cook this dish again for a Friday.

For lunch yesterday, I tried out a recipe which I had found on an Israeli slow cooking blog. From the supermarket, I bought a packet of frozen chicken breasts; once thawed, this revealed four breasts, which I sliced lengthways, making eight portions. The weight was around 1.2 kg. I diced an onion, mixed the pieces with two tablespoons of olive oil, and then spread the resulting mix onto the bottom of the slow cooker. On top of the onions I placed the chicken breasts, and on top of the breasts I smeared a mixture of mayonnaise and jam (two tablespoons each). The original recipe also called for another two tablespoons of mustard, but we didn't have any at home (I thought that we did have, but my wife threw the container out). I set the slow cooker to cook for four hours on low. I served the breasts with cabbage and my patent mashed potato.

The result was sublime. I had been worried about the lack of liquid available, but instead of having too much free liquid bubbling, the recipe produced a sauce of a nice consistency. Next time around, I'm going to reduce the amount of onion; it will be interesting to see what contribution the mustard makes to the sauce.

Friday, November 12, 2010

What are the 10 Songs that defined one's tastes?

I haven't written much about music lately, primarily because music has had to take a back seat in my life in the past few months, and there hasn't been any new music that has entered my life. In lieu of something more up to date, I include here a posting which I sent to the Richard Thompson mailing list a fortnight ago which answers the above question.

I suppose one has to define what a "song that defined one's taste" means before answering. I take this to mean the songs that made the biggest impression on me when I started listening to music "properly" (aged 13-14), which for me would be 1970/1 (I list the years below). Each song defines for me a genre (although I'm not so sure about King Crimson - VdGG meant and still means much more to me).

1. The Beatles - I want to hold your hand (1963)
2. The Beach Boys - God only knows (1966)
3. Fairport Convention - Who knows where the time goes (Unhalfbricking version) (1970)
4. Van der Graaf Generator - Refugees (1970)
5. Frank Zappa - Peaches en regalia (1970)
6. King Crimson - Epitaph (1970)
7. Randy Newman - I think it's going to rain today (1971)
8. Dave Evans - Grey lady morning (1971)
9. The Band - It makes no difference (1978)
10. Robin Frederick - Water falls down (2003)

Looking at this list today, I would probably substitute "Winter wine" by Caravan for "God only knows" - WW was my entree into Canterbury music.

Saturday, November 06, 2010

Trains and meals

The last few days have been very hectic, not really affording me the small amount of time necessary to write a few words here. Next week is going to be even worse: one evening, I will be the 'duty driver' for the kibbutz, from 5-10pm, and three evenings I will be attending lectures in Project Management, courtesy of a guest lecturer from the home university.

I am known as a vocal supporter of the Israeli train system; most of the time it's very good, but occasionally there are problems. On Wednesday I was due to make the familiar trip to Haifa Bay; when I went to buy the ticket, the station had mysteriously disappeared from the list of destinations available from the automatic ticket machine. When I bought my ticket from a human cashier, I was told that there were no trains north of Haifa. This happens for a few days every now and then, but this time I hadn't noticed and hadn't bothered to plan my trip for a day when there is no maintenance being performed on the line. Although this news disappointed me, I wasn't totally upset as this meant that I could catch an earlier train from Tel Aviv to Haifa and thus save twenty minutes which would be devoted to the problem of catching a bus from Haifa to the bay (normally this is two buses).

After buying a return ticket to Haifa and passing through the turnstiles, I prepared to wait the ten minutes until the train to Tel Aviv was due to arrive. I hadn't been there for more than a minute before an announcement came over the tannoy that the train was twenty minutes late. Everybody groaned. Whilst this wasn't much of a set-back to me (the train should arrive in Tel Aviv in time to make my normal connection), it was a problem for those who have to be at work by a certain time.

Eventually the train arrived and off we went. By the time we arrived at Lod, so many people had come aboard that the train operator had to make several announcements asking people to disembark as the train was too heavy and could not move. There are trains every few minutes from Lod to Tel Aviv so there was no real reason for people to get on our train, but even so it took about another ten minutes before sufficient people disembarked and the train could continue its journey.

A few minutes after arriving in Tel Aviv, a train arrived which took me to Haifa. This train was one of the modern design with electrical sockets distributed generously; as the train was barely occupied, this was an excellent opportunity to get some work done. I had barely set a foot outside the train station in Haifa when I noticed a minibus waiting for passengers to take further North. Whilst I had to wait a while for the minibus to fill, it took me reasonably quickly to within 400 metres of my destination - although traversing the packed streets of Haifa made me remember how much easier the train makes this trip.

I arrived at my destination about 25 minutes later than I would have arrived normally. Not too bad considering the disturbances.

Returning home, I left the office a bit early, but again was fortunate to arrive at the main road just in time for a minibus to stop and pick me up. Whilst this bus went back to Haifa, it did not go to the train station but rather what's called the 'upper city' (Haifa is built on the side of a hill), so I had to walk for about 15 minutes before arriving at the train station, just in time for a train back to Tel Aviv. Fortunately I knew where I was in Haifa, so it wasn't difficult to find my way to the station.

I decided a few days ago that today I would cook chicken drumsticks and prunes in the slow cooker. I literally jumped out of bed at 6:30am; put the drumsticks in the slow cooker, covered them with about a litre of coca cola, added a cup and a bit of rice, then added diced raw onion and diced prunes. I put the slow cooker on 'high' for two hours then lowered the heat to 'low' for another three.

I knew that the chicken would be ok but I wasn't sure how the rice would be. I had checked a recipe for slow cooked rice the day before with proportions 1:1 (ie one cup of rice/one cup of water) whereas normally I use 1:2. Here I was using a proportion of about 1:5! The result was fine: the rice was indeed more like a pudding than distinct grains, but that doesn't bother us. The onion and prunes weren't particularly evident, and the chicken was cooked to perfection. My wife gave it the thumbs up, and it's much easier cooking this dish in the slow cooker than in the oven. Another recipe for the book.

Tuesday, November 02, 2010

Sorting up and down

Back to basics today: the occupational psychologist (OP) wanted a report in which she could sort by each column, like in Excel. Clicking the first column would sort by this column, clicking the second would sort by the second column, etc. This isn't difficult to do in Delphi; it requires one to understand the dbGridTitleClick method and how to build indices for a clientdataset.

The other day, she asked me whether it would be possible once the report is sorted by a column to click again on the titlebar and get the column to sort itself in reverse. I was non-committal, but a little thought and work gave me the answer.

I am enclosing the code to a very simple but complete example program. The program uses a dbgrid, a datasource and a clientdataset, all inter-connected. For the example, I am using a standalone clientdataset, in which I define the fields and populate them; the indexing technique works exactly the same when the clientdataset is populated via queries.

As the data can be sorted by several columns (in the example, only two) and each column has to 'remember' in which direction it was last sorted (so that clicking on the grid's title bar will cause the data to be sorted in the reverse order), an array of boolean has to be declared, one element per column (this is called 'directions' in the example). 

For every sortable column, two indices have to be created - one for ascending sort and one for descending sort. Each index is created using the 'AddIndex' method of the clientdataset; a descending index has the third parameter set to ixDescending, whereas an ascending index has the third parameter empty.

Every time the grid's title bar is clicked, the index of the clientdataset is changed. But how does the program know which index to use, especially as there are two indices per column? The elegant solution that I invented was to number each index, where the number is the column number doubled; if the index is descending, then the number is incremented. Thus for column 0, the ascending index will be idx0 (0 times 2 = 0, no increment), whereas the descending index will be idx1. For column 1, the ascending index will be idx2 and the descending index idx3. This simple scheme will allow the indices to be chosen as a function of the column number.

Here is the program...
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, DB, DBClient, Grids, DBGrids, StdCtrls;

type
  TForm1 = class(TForm)
    DBGrid1: TDBGrid;
    qTest: TClientDataSet;
    DataSource1: TDataSource;
    procedure FormShow(Sender: TObject);
    procedure DBGrid1TitleClick(Column: TColumn);
  private
    { Private declarations }
    directions: array [0..1] of boolean;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormShow(Sender: TObject);
var
 strings: tstrings;

begin
 directions[0]:= true; directions[1]:= true;
 with qTest do
  begin
   fielddefs.add ('num', ftInteger, 0, false);
   fielddefs.add ('extra', ftInteger, 0, false);
   createdataset;
   open;
   addindex ('idx0', 'num', [], '', '', 0);
   addindex ('idx1', 'num', [ixDescending], '', '', 0);
   addindex ('idx2', 'extra', [], '', '', 0);
   addindex ('idx3', 'extra', [ixDescending], '', '', 0);
   strings:= tstringlist.create;
   getindexnames (strings);   // this line seems to be essential!
   strings.free;
   append;
   fieldbyname ('num').asinteger:= 2;
   fieldbyname ('extra').AsInteger:= 10;
   post;
   append;
   fieldbyname ('num').asinteger:= 1;
   fieldbyname ('extra').AsInteger:= 14;
   post;
   append;
   fieldbyname ('num').asinteger:= 3;
   fieldbyname ('extra').AsInteger:= 20;
   post;
  end;
end;

procedure TForm1.DBGrid1TitleClick(Column: TColumn);
var
 n, ex: word;

begin
 n:= column.Index;
 directions[n]:= not directions[n];
 ex:= n * 2;
 if directions[n] then inc (ex);
 with qTest do
  try
   disablecontrols;
   close;
   indexname:= 'idx' + inttostr (ex);
   open
  finally
   enablecontrols
  end;
end;

end.
There is one small 'gotcha' - the cliendataset 'GetIndexNames' method has to be called (its results are stored in a temporary string list). Without this, one receives an error message 'qTest: Index idx0 not found' should one click on the grid's title bar.