Thursday, April 29, 2021

How I created the Psion version of my freight management program


I have three other versions of my freight management program programmed with the 4D relational database, Livecode and most recently, Google Sheets but I keep returning to the version which I created on my Psion pocket computer.

 

The advantages of the Psion version are:

  • It is portable. I can take it with me out into the garden - it fits into my jeans pocket
  • I can consult it and update entries in real time rather than having to go indoors and fire up the laptop and consult print-outs
  • I can read the screen which, unlike my phone, gets even clearer in bright sunlight
  • It was cheap. I re-discovered it in an old briefcase in the cupboard under the stairs.
  • It's very easy to program. The OPL programming language is quite high level though not quite as conversational as that used by Livecode.
  • It has a keyboard which I find a lot easier to use than the touch-screen on my phone

The (to me, minor) drawbacks are:

  • The text is quite small - but still quite readable - and no smaller than that on my phone screen
  • It has only 1Mb of RAM and so the programming has to make minimal demands on system memory. However, it does mean I have to be more efficient with my programming

I have detailed the way the program works in my previous blog post, however, since writing that, I have made a few improvements to the way it works and also made amendments to the coding as it would sometimes run out of system memory. 

The changes I have made are:

  • the option for randomising the locations of wagons has been removed
  • individual wagons can be relocated from the Locations List
  • the locations are now identified by number rather than name in the database
  • the weighting system has been streamlined and improved
  • the number and range of wagon types has been changed

I never used the random wagon allocation feature. It's much easier just to deploy wagons according to where they are stored and then update their locations manually, either directly in the database or through the now modified Locations List function. Deleting it recovered some memory.

The Locations List looks almost the same as previously, but now in the first column, each wagon's position in the database is listed in addition to its wagon ID and its description.

This number is now used to enable the user to move it. Typing a number (eg "19"), tells the system that wagon number 19 needs to be moved ....

Typing the location number will then move it to that location.

  1. Beeston Market
  2. Beeston Castle
  3. Peckforton
  4. Mill Siding
  5. Bulkeley
  6. Copper Mine
  7. Bickerton

0 indicates that a wagon is out of service (eg under repair).

Database entries therefore now look slightly different. 

Locations are now simply identified by the relevant numeral - in this case "5" shows this wagon is currently located at Bulkeley. Not only does this speed up data entry, it reduces the amount of processing needed in the program.

 

The type of wagon is identified by a single letter - "m" indicates it is for "mixed" traffic meaning it could roam almost anywhere on the system.

I drew upon my experience of programming the Google Sheets version to determine the weightings for wagon types as a list of numbers. For example, any wagon designated for fruit traffic would be most likely to be sent to Beeston Market and Bulkeley, have a lesser likelihood of visiting Bickerton, Beeston Castle and Peckforton but would never visit the Mill Siding or the Copper Mine.

This produces a weightings profile of:

8,3,4,0,8,0,6

ie

  • an 8/9 chance of going to Beeston Market
  • a 3/9 chance of going to Beeston Castle
  • a 4/9 chance of going to Peckforton
  • a 0/9 chance of going to the Mill Siding
  • an 8/9 chance of going to Bulkeley
  • a 0/9 chance of going to the Copper Mine
  • a 6/9 chance of going to Bickerton

How this is calculated is revealed in the program below.


PROC begin:
GLOBAL k$(1)
GLOBAL w%(15),wn$(15,2),wd$(13,30),wi$(15,4),loc%(15),dest%(15)
LOCAL c%
c%=1
DO
w%(c%)=0 :wn$(c%)="" :wd$(c%)="" wi$(c%)=""
loc%(c%)=0 :dest%(c%)=0
c%=c%+1
UNTIL c%=16
CLS
PRINT
PRINT " FREIGHT MANAGER v4"
PRINT
PRINT " S = Sort"
PRINT " R = Reset"
PRINT " L = List locations"
PRINT " -------------------"
PRINT
PRINT " What type of train?"
PRINT " P = Pickup Goods"
PRINT " M = Mixed"
DO
k$=GET$
UNTIL k$="p" OR k$="m" OR k$="s" OR k$="r" OR k$="l"
IF k$="p" OR k$="s"
train:
ELSEIF k$="l"
list:
begin:
ELSEIF k$="s"
sort:
begin:
ELSE
reset:
begin:
ENDIF
ENDP

PROC train:
LOCAL w$(7),r$(16)
LOCAL c%,d%,e%,g%,l$(14),t%,s%,wt%,nl%,nw%
BUSY "Please wait ...."
l$="BMBCPKMSByCMBn" REM The short (2 char) names for locations used in the database
c%=1
t%=1
OPEN "A:\dat\Wagons2.dbf",A,n$,d$,i$,t$,l$,m$
CLS
FIRST
DO
redo::
s%=1
w%(c%)=1+(RND*COUNT)
IF c%>1
DO
IF w%(c%)=w%(s%)
GOTO redo
ENDIF
s%=s%+1
UNTIL s%=c%
ENDIF
c%=c%+1
UNTIL c%=16
c%=1
d%=1
DO
POSITION w%(d%)
IF a.l$="0"
d%=d%+1
UPDATE
CONTINUE
ENDIF
wn$(d%)=A.n$
wd$(d%)=A.d$
wi$(d%)=A.i$
loc%(d%)=VAL(A.l$)
wt%=0
nl%=0
nw%=0
e%=1
IF k$="p"
g%=15
ELSE
g%=4
ENDIF
DO
IF e%<>VAL(A.l$)
IF A.t$="t":w$="7260404"
ELSEIF A.t$="p":w$="6060060"
ELSEIF A.t$="m":w$="8664616"
ELSEIF A.t$="c":w$="9653545"
ELSEIF A.t$="b":w$="8640404"
ELSEIF A.t$="f":w$="9040805"
ELSEIF A.t$="g":w$="8000080"
ELSEIF A.t$="s":w$="8007405"
ENDIF
wt%=INT(RND*VAL(MID$((w$),e%,1)))
IF wt%=nw%
wt%=INT(RND)+wt%
ENDIF
IF wt%>nw%
nw%=wt%
dest%(d%)=e%
ENDIF
ENDIF
e%=e%+1
UNTIL e%=8
d%=d%+1
CLS
AT 5,8
PRINT" ":REPT$("***",1+g%-d%)
UNTIL d%=g%+1
CLS
IF k$="p"
PRINT" Pickup goods" :PRINT
ELSE
PRINT " Mixed train" :PRINT
ENDIF
s%=1
DO
c%=1
DO
IF loc%(c%)=s%
PRINT"*",wi$(c%),wd$(c%);REPT$(".",(46-(LEN(wd$(c%)))));MID$(l$,(s%*2)-1,2),"to".MID$(l$,dest%(c%)*2)-1,2)
ENDIF
IF c%=4 AND k$="m"
c%=15
ENDIF
c%=c%+1
UNTIL c%=16
s%=s%+1
UNTIL c%=16
BUSY OFF
GET
CLOSE
dINIT "ACCEPT TRAIN?"
dBUTTONS "No",%N,"Yes".%Y
IF DIALOG = %n
train:
ENDIF
stations:
ENDP

PROC stations:
LOCAL c%,f%,g%,t%,s%,s$(2),f$(15,4),t%(15,4),l$(14)
CLS
PRINT " Which station?"
PRINT
PRINT " * 1 * Beeston Market"
PRINT " * 2 * Beeston (C)astle"
PRINT " * 3 * (P)eckforton"
PRINT " * 4 * Mill (S)iding"
PRINT " * 5 * Bulkele(Y)"
PRINT " * 6 * Coppe(R) Mine"
PRINT " * 7 * Bickerto(N)"
PRINT
PRINT "* Or SPACE to exit *"
DO
s%=GET
UNTIL s%=32 OR (s%>48 AND s%<56)
IF s%=32
dINIT "Are you sure?"
dTEXT "","This will restart program",2dBUTTONS "No",%N,"Yes",%Y
IF DIALOG =y%
begin:
ENDIF
:stations
ENDIF
OPEN "A:\dat\Wagons2.dbf",A,n$,d$,i$,t$,l$
l$="BMBCPKMSByCNBn"
CLS
s%=s%-48
IF s%=1
PRINT "Beeston Market"
ELSEIF s%=2
PRINT "Beeston Castle"
ELSEIF s%=3
PRINT "Peckforton"
ELSEIF s%=4"
PRINT "Mill Siding"
ELSEIF s%=5
PRINT "Bulkeley"
ELSEIF s%=6
PRINT "Copper Mine"
ELSE
PRINT "Bickerton"
ENDIF
c%=1
DO
f$(c%)=""
c%=c%+1
UNTIL c%=16
again::
c%=1
f%=1
t%=1
PRINT "From",s$
DO
IF loc%(c%)=s%
PRINT t%,wi$(c%),"-",wd$(c%),"to".MID$(l$,(dest%(c%)*2)-1,2)
t%=t%+1
ENDIF
c%=c%+1
UNTIL c%=5
IF k$="p"
DO
IF loc%(c%)=s%
PRINT t%,wi$(c%),"-",wd$(c%),"to".MID$(l$,(dest%(c%)*2)-1,2)
t%=t%+1
ENDIF
c%=c%+1
UNTIL c%=16
ENDIF
c%=1
t%=1
PRINT
PRINT "To",s$,"(Letter=accept wagon * T=accept ALL)"
c%=1
DO
IF dest%(c%)=s%
IF f$(t%)="OK"
wi$(c%)="OK"
ENDIF
PRINT CHR$(t%+96),wi$(c%),"-",wd$(c%),"from",MID$(l$(loc%(c%)*2)-1,2)
t$(t%)=wi$(c%)
t%=t%+1
ENDIF
IF k$="m" and c%=4
c%=15
ENDIF
c%=c%+1
UNTIL c%=16
PRINT
PRINT "Or press SPACE to move on"
g%=GET
IF g%=32
CLOSE
stations:
ENDIF
IF g%=116
c%=1
DO
IF (t$(c%)<>"OK")
FIRST
FIND (t$(c%))
A.l$=CHR$(s%+48)
UPDATE
f$(c%)="OK"
ENDIF
c%=c%+1
UNTIL c%=t%
CLS
GOTO again
ENDIF
IF g%>113 OR g%<97
BEEP 5,300
CLS
GOTO again
ENDIF
IF g%-96<t% AND g%>0
IF f$(g%-96)<>"OK"
FIRST
FIND (t$(g%-96))
A.l$=CHR$(s%+48)
UPDATE
f$(g%-96)="OK"
CLS
GOTO again
ENDIF
ENDIF
CLOSE
stations:
ENDP

PROC sort:
LOCAL c%,d%,e%,e$(10,2),fin$(2)
OPEN "A:\dat\Wagons2.dbf",A,n$,d$,i$,t$,l$
BUSY "Sorting ...."
c%=17
DO
FIRST
IF c%<10
fin$=NUM$(c%,1)
fin$="0"+fin$
ELSE
fin$=NUM$(c%,2)
ENDIF
FINDFIELD (fin$,1,1,16)
TRAP UPDATE
e%=ERR
IF e%=-36
d%=d%+1
e$(d%)=fin$
e%=0
ENDIF
c%=c%+1
UNTIL c%=COUNT+1
IF d%>0
DO
FIRST
A.n$=e$(d%)
UPDATE
d%=d%-1
UNTIL d%=0
d%=77
ENDIF
CLOSE
IF d%=77
sort:
ENDIF
BUSY OFF
ENDP

PROC reset:
LOCAL c%
OPEN "A:\dat\Wagons2.dbf",A,n$,d$,i$,t$,l$
BUSY "Resetting ....."
c%=1
DO
FIRST
IF A.l$<>"0"
A.l$="1"
UPDATE
ELSE
UPDATE
ENDIF
c%=c%+1
UNTIL c%=c%+1
CLOSE
BUSY OFF
ENDP

PROC list:
LOCAL g%,c%,l$(1)
GLOBAL k%
CLS
OPEN "A:\dat\Wagons2.dbf",A,n$,d$,i$,t$,l$
c%=1
DO
IF c%=1
PRINT "1 - Beeston Market"
l$="1"
ELSEIF c%=2
PRINT "2 - Beeston Castle"
l$="2"
ELSEIF c%=3
PRINT "3 - Peckforton"
l$="3"
ELSEIF c%=4
PRINT "4 - Mill Siding"
l$="4"
ELSEIF c%=5
PRINT "5 - Bulkeley"
l$="5"
ELSEIF c%=6
PRINT "6 - Copper Mine"
l$="6"
ELSEIF c%=7
PRINT "7 - Bickerton"
l$="7"
ELSEIF c%=0
PRINT "0 - Out of Service"
l$="0"
ENDIF
g%=1
FIRST
DO
FINDFIELD (l$,5,1,1)
PRINT A.n$,"-",A$.i$,A.d$
g%=g%+1
IF g%=15 OR g%=30 OR g%=45
BUSY "More ...."
k%=GET
IF k%>47 AND k%<58
movewag:
ENDIF
BUSY OFF
ENDIF
NEXT
UNTIL EOF
PRINT "-----------------------"
k%=GET
IF k%>47 AND k%<58
movewag:
ENDIF
c%=c%+1
UNTIL c%=9
CLOSE
begin:
ENDP

PROC movewag:
LOCAL m$(10),f$(2),t$(1)
BEEP 5,300
f$=CHR$(k%)
m$="Move "+f$
BUSY m$
k%=GET
f$=f$+CHR$(k%)
m$=m$+f$+" to"
BUSY m$
k%=GET
t$=CHR$(k%)
FINDFIELD (f$,1,1,16)
A.l$=t$
UPDATE
CLOSE
BUSY OFF
list:
ENDP

 So, how does it work?

 

The BEGIN procedure


PROC begin:
GLOBAL k$(1)
GLOBAL w%(15),wn$(15,2),wd$(13,30),wi$(15,4),loc%(15),dest%(15)
LOCAL c%
c%=1
DO
w%(c%)=0 :wn$(c%)="" :wd$(c%)="" wi$(c%)=""
loc%(c%)=0 :dest%(c%)=0
c%=c%+1
UNTIL c%=16
CLS
PRINT
PRINT " FREIGHT MANAGER v4"
PRINT
PRINT " S = Sort"
PRINT " R = Reset"
PRINT " L = List locations"
PRINT " -------------------"
PRINT
PRINT " What type of train?"
PRINT " P = Pickup Goods"
PRINT " M = Mixed"
DO
k$=GET$
UNTIL k$="p" OR k$="m" OR k$="s" OR k$="r" OR k$="l"
IF k$="p" OR k$="s"
train:
ELSEIF k$="l"
list:
begin:
ELSEIF k$="s"
sort:
begin:
ELSE
reset:
begin:
ENDIF
ENDP

 This initially defines a series of global variables so that data about wagons can be transferred from one procedure to another

It then presents the user with the first menu screen asking which action they want to take


It then stores the key press in the global variable k$ and calls the relevant procedure

 

 The TRAIN procedure

 This procedure firstly picks a wagon from the database at random and then calculates its destination using the weightings shown above.

PROC train:
LOCAL w$(7),r$(16)
LOCAL c%,d%,e%,g%,l$(14),t%,s%,wt%,nl%,nw%
BUSY "Please wait ...."
l$="BMBCPKMSByCMBn" REM The short (2 char) names for locations used in the database
c%=1
t%=1
OPEN "A:\dat\Wagons2.dbf",A,n$,d$,i$,t$,l$,m$
CLS
FIRST
DO
redo::
s%=1
w%(c%)=1+(RND*COUNT)
IF c%>1
DO
IF w%(c%)=w%(s%)
GOTO redo
ENDIF
s%=s%+1
UNTIL s%=c%
ENDIF
c%=c%+1
UNTIL c%=16
c%=1
d%=1
DO
POSITION w%(d%)
IF a.l$="0"
d%=d%+1
UPDATE
CONTINUE
ENDIF
wn$(d%)=A.n$
wd$(d%)=A.d$
wi$(d%)=A.i$
loc%(d%)=VAL(A.l$)
wt%=0
nl%=0
nw%=0
e%=1
IF k$="p"
g%=15
ELSE
g%=4
ENDIF
DO
IF e%<>VAL(A.l$)
IF A.t$="t":w$="7260404"
ELSEIF A.t$="p":w$="6060060"
ELSEIF A.t$="m":w$="8664616"
ELSEIF A.t$="c":w$="9653545"
ELSEIF A.t$="b":w$="8640404"
ELSEIF A.t$="f":w$="9040805"
ELSEIF A.t$="g":w$="8000080"
ELSEIF A.t$="s":w$="8007405"
ENDIF
wt%=INT(RND*VAL(MID$((w$),e%,1)))
IF wt%=nw%
wt%=INT(RND)+wt%
ENDIF
IF wt%>nw%
nw%=wt%
dest%(d%)=e%
ENDIF
ENDIF
e%=e%+1
UNTIL e%=8
d%=d%+1
CLS
AT 5,8
PRINT" ":REPT$("***",1+g%-d%)
UNTIL d%=g%+1
CLS
IF k$="p"
PRINT" Pickup goods" :PRINT
ELSE
PRINT " Mixed train" :PRINT
ENDIF
s%=1
DO
c%=1
DO
IF loc%(c%)=s%
PRINT"*",wi$(c%),wd$(c%);REPT$(".",(46-(LEN(wd$(c%)))));MID$(l$,(s%*2)-1,2),"to".MID$(l$,dest%(c%)*2)-1,2)
ENDIF
IF c%=4 AND k$="m"
c%=15
ENDIF
c%=c%+1
UNTIL c%=16
s%=s%+1
UNTIL c%=16
BUSY OFF
GET
CLOSE
dINIT "ACCEPT TRAIN?"
dBUTTONS "No",%N,"Yes".%Y
IF DIALOG = %n
train:
ENDIF
stations:
ENDP

After defining the LOCAL variables used in the procedure, it then populates variable l$ with the short (two character) names for each location (ie BM = Beeston Market, BC = Beeston Castle, PK = Peckforton, and so on).

It then OPENs the wagons database. Note, to save memory, I have stored the wagons database on a Flash RAM card in slot A which is why the name is preceded with A:  - each field is then allocated a variable name (ie n$ for number, d$ for description, i$ for ID, etc)

The next DO loop picks 15 wagons at random. The IF loop checks whether the wagon is already in the list and, if so, picks another. NOTE: The more wagons there are in the database, the less likely there will be repeats and hence the quicker the selection process will be.

The next DO loop retrieves the data for each of the selected wagons from the database and stores it in the global variables and then uses the weightings to calculate the intended location for each wagon on the list.

The final main DO loop then presents the user with the list of wagons with intended destinations and asks if the user wants to accept the train or generate another.

 

 The STATIONS procedure

 This procedure asks users to select a location and then presents them with the train movements at that location. Wagons can then be moved to that station with a key press.

PROC stations:
LOCAL c%,f%,g%,t%,s%,s$(2),f$(15,4),t%(15,4),l$(14)
CLS
PRINT " Which station?"
PRINT
PRINT " * 1 * Beeston Market"
PRINT " * 2 * Beeston Castle"
PRINT " * 3 * Peckforton"
PRINT " * 4 * Mill Siding"
PRINT " * 5 * Bulkeley"
PRINT " * 6 * Copper Mine"
PRINT " * 7 * Bickerton"
PRINT
PRINT "* Or SPACE to exit *"
DO
s%=GET
UNTIL s%=32 OR (s%>48 AND s%<56)
IF s%=32
dINIT "Are you sure?"
dTEXT "","This will restart program",2dBUTTONS "No",%N,"Yes",%Y
IF DIALOG =y%
begin:
ENDIF
:stations
ENDIF
OPEN "A:\dat\Wagons2.dbf",A,n$,d$,i$,t$,l$
l$="BMBCPKMSByCNBn"
CLS
s%=s%-48
IF s%=1
PRINT "Beeston Market"
ELSEIF s%=2
PRINT "Beeston Castle"
ELSEIF s%=3
PRINT "Peckforton"
ELSEIF s%=4"
PRINT "Mill Siding"
ELSEIF s%=5
PRINT "Bulkeley"
ELSEIF s%=6
PRINT "Copper Mine"
ELSE
PRINT "Bickerton"
ENDIF
c%=1
DO
f$(c%)=""
c%=c%+1
UNTIL c%=16
again::
c%=1
f%=1
t%=1
PRINT "From",s$
DO
IF loc%(c%)=s%
PRINT t%,wi$(c%),"-",wd$(c%),"to".MID$(l$,(dest%(c%)*2)-1,2)
t%=t%+1
ENDIF
c%=c%+1
UNTIL c%=5
IF k$="p"
DO
IF loc%(c%)=s%
PRINT t%,wi$(c%),"-",wd$(c%),"to".MID$(l$,(dest%(c%)*2)-1,2)
t%=t%+1
ENDIF
c%=c%+1
UNTIL c%=16
ENDIF
c%=1
t%=1
PRINT
PRINT "To",s$,"(Letter=accept wagon * T=accept ALL)"
c%=1
DO
IF dest%(c%)=s%
IF f$(t%)="OK"
wi$(c%)="OK"
ENDIF
PRINT CHR$(t%+96),wi$(c%),"-",wd$(c%),"from",MID$(l$(loc%(c%)*2)-1,2)
t$(t%)=wi$(c%)
t%=t%+1
ENDIF
IF k$="m" and c%=4
c%=15
ENDIF
c%=c%+1
UNTIL c%=16
PRINT
PRINT "Or press SPACE to move on"
g%=GET
IF g%=32
CLOSE
stations:
ENDIF
IF g%=116
c%=1
DO
IF (t$(c%)<>"OK")
FIRST
FIND (t$(c%))
A.l$=CHR$(s%+48)
UPDATE
f$(c%)="OK"
ENDIF
c%=c%+1
UNTIL c%=t%
CLS
GOTO again
ENDIF
IF g%>113 OR g%<97
BEEP 5,300
CLS
GOTO again
ENDIF
IF g%-96<t% AND g%>0
IF f$(g%-96)<>"OK"
FIRST
FIND (t$(g%-96))
A.l$=CHR$(s%+48)
UPDATE
f$(g%-96)="OK"
CLS
GOTO again
ENDIF
ENDIF
CLOSE
stations:
ENDP

The first part of the procedure presents users with the list of locations and invites them to select one by typing the relevant number


The next section then presents the relevant location and shows the wagons which either need to be moved FROM that location or need to be sent TO it.

Beside each wagon needing to be sent there is a letter enabling it to be identified on the keyboard.

Pressing the relevant letter will change the wagon ID (eg T4 or TK1) or OK, indicating that the system has moved it to the new location.

 

 Pressing the T key will move all the wagons to that location in the database.

 

The SORT procedure

This simply sorts the wagons into numerical order in the database. Each time a wagon is UPDATED (ie moved to a new location) by the program, its record is moved to the end of the database and so loses its position. This makes life easier when viewing the wagons database. It's interesting that the database app in the Psion doesn't include a menu option for sorting the records.

NOTE: It assumes that the wagon number field contains sequential two digit numbers, starting with 01, 02, ..... and so on.

The TRAP UPDATE command prevents the procedure from stopping and creating an error message if it doesn't find one of the numbers. For example, if the numbering of the wagons isn't sequential or if there's a repeat of the same number.

This error is then detected (using ERR) and the offending wagon numbers are stored in the e$ array until the end of the procedure. Any wagon entries which have not been sorted will remain at the start of the database file and so these are each allocated one of the missing numbers from the list stored in the e$ array.


PROC sort:
LOCAL c%,d%,e%,e$(10,2),fin$(2)
OPEN "A:\dat\Wagons2.dbf",A,n$,d$,i$,t$,l$
BUSY "Sorting ...."
c%=17
DO
FIRST
IF c%<10
fin$=NUM$(c%,1)
fin$="0"+fin$
ELSE
fin$=NUM$(c%,2)
ENDIF
FINDFIELD (fin$,1,1,16)
TRAP UPDATE
e%=ERR
IF e%=-36
d%=d%+1
e$(d%)=fin$
e%=0
ENDIF
c%=c%+1
UNTIL c%=COUNT+1
IF d%>0
DO
FIRST
A.n$=e$(d%)
UPDATE
d%=d%-1
UNTIL d%=0
d%=77
ENDIF
CLOSE
IF d%=77
sort:
ENDIF
BUSY OFF
ENDP


The RESET procedure

This procedure simply sends all the wagons back to location number 1 (in my case Beeston Market). It ignores wagons currently Out of Service (ie Location 0)


PROC reset:
LOCAL c%
OPEN "A:\dat\Wagons2.dbf",A,n$,d$,i$,t$,l$
BUSY "Resetting ....."
c%=1
DO
FIRST
IF A.l$<>"0"
A.l$="1"
UPDATE
ELSE
UPDATE
ENDIF
c%=c%+1
UNTIL c%=c%+1
CLOSE
BUSY OFF
ENDP


The LIST procedure

This lists the wagons at each location and allows individual wagons to be moved from one location to another. I find this useful at the start of a session to make sure wagons are where they are supposed to be.

PROC list:
LOCAL g%,c%,l$(1)
GLOBAL k%
CLS
OPEN "A:\dat\Wagons2.dbf",A,n$,d$,i$,t$,l$
c%=1
DO
IF c%=1
PRINT "1 - Beeston Market"
l$="1"
ELSEIF c%=2
PRINT "2 - Beeston Castle"
l$="2"
ELSEIF c%=3
PRINT "3 - Peckforton"
l$="3"
ELSEIF c%=4
PRINT "4 - Mill Siding"
l$="4"
ELSEIF c%=5
PRINT "5 - Bulkeley"
l$="5"
ELSEIF c%=6
PRINT "6 - Copper Mine"
l$="6"
ELSEIF c%=7
PRINT "7 - Bickerton"
l$="7"
ELSEIF c%=0
PRINT "0 - Out of Service"
l$="0"
ENDIF
g%=1
FIRST
DO
FINDFIELD (l$,5,1,1)
PRINT A.n$,"-",A$.i$,A.d$
g%=g%+1
IF g%=15 OR g%=30 OR g%=45
BUSY "More ...."
k%=GET
IF k%>47 AND k%<58
movewag:
ENDIF
BUSY OFF
ENDIF
NEXT
UNTIL EOF
PRINT "-----------------------"
k%=GET
IF k%>47 AND k%<58
movewag:
ENDIF
c%=c%+1
UNTIL c%=9
CLOSE
begin:
ENDP

The main DO loop works its way progressively through the locations, finding wagons in the database for each location then moving on to the next.

 

IF the number of wagons at any one location is more than 15 (ie a screen-full), then it pauses and waits for the user to press a key (using the GET statement).

IF the user presses a number key, then it calls the MOVEWAG procedure (see below) to move that particular wagon to a new location.


 Similarly, when all the wagons at a particular location have been presented, the program pauses to await a key press.

If the key press is the space bar (ie character code 32), then the program continues.


The MOVEWAG procedure

As indicated above, if a number key is pressed in the LIST procedure, this procedure asks for another number (all wagons have two digit numbers) and then asks for a location (ie between 1 and 7).

It then changes the location of the wagon in the database.

PROC movewag:
LOCAL m$(10),f$(2),t$(1)
BEEP 5,300
f$=CHR$(k%)
m$="Move "+f$
BUSY m$
k%=GET
f$=f$+CHR$(k%)
m$=m$+f$+" to"
BUSY m$
k%=GET
t$=CHR$(k%)
FINDFIELD (f$,1,1,16)
A.l$=t$
UPDATE
CLOSE
BUSY OFF
list:
ENDP


Conclusion

Nothing in this world is perfect and this version of my freight manager program is a compromise between the ideal and what is achievable on the Psion with its limited 1Mb of working memory. However, of all the different versions, this best suits my needs. I have used it now for several operating sessions and find it very helpful in identifying the wagons which need to be moved.

Unlike the versions which are laptop based and rely on print outs if, for some reason such as the weather deteriorating, I have to abandon a running session before all the wagons in a train have been moved to their intended location, this version allows for that eventuality.

I also find it helpful to see which wagons have already been moved and which are still on the list. Occasionally, it's easy to confuse one wagon with another - one open wagon full of coal looks very much like another.

It seems to me a great pity that nothing seems to have replaced the Psion. It was (and still is) a great little computer. If the resale prices on eBay are any indicator, then there is still a market out there for a decent little computer with a proper keyboard which fits into your pocket!




No comments: