Opening and closing a single record file

I have an application that includes the use of a single record file that contains various settings, ie. paths to various data files, program options, program activation details. My issue is that my code is not allowing the record to be saved. I am using Clarion9 with Topspeed data files. The file name is S_SysData
The code I am using to open the record at the Window Events > Opening Windows embed point is as follows:

! Initial Entry to S_SysData 
!----------------------------

IF Records(S_SysData) = 0                       !If no record
  Access:S_SysData.Insert()                     !add blank entry
END
Set(S_SysData)
Access:S_SysData.Next()

The code I am using to close the record at the Control Event > Ok button > Accepted is as follows:

!Update Last Modified Date Time & User
!-------------------------------------

    SYD:RMD     = TODAY()
    SYD:RMT     = CLOCK()
    SYD:RMB     = 'SYSADMIN'

Access:S_SysData.Update()
BREAK

The error message I get when trying to save the record is "An error (Record Not Available (33)) was experienced when making changes to the M:\APP02\DATA\S_SysData file.

What should I have done to ensure the record was available?

Any suggestions would be appreciated.

Fetch the record and test its the right record before updating it to avoid error 33.

For more control, use language functions eg add, put etc in your own function or class.

Additionally, the Insert() can fail if you have some field validations on your DCT and you’re adding an empty record.
It’s better to use the ADD function…

Most file actions will trigger an errorcode of some kind. Check for errors as you go.

  PROGRAM

MyFileName                    CSTRING(FILE:MaxFilePath+1)

S_SysData                     FILE,DRIVER('TOPSPEED'),PRE(SYS),CREATE,NAME(MyFileName)
RECORD                          RECORD
RMD                               LONG
RMT                               LONG
RMB                               STRING(60)
Note                              STRING(100)
                                END
                              END

  MAP
  END

  CODE

  MyFileName = LONGPATH('.\SysData.tps') ! Assuming for this example that we're not in a protected area here
  
  LOOP 2 TIMES
    OPEN(S_SysData)
    CASE ERRORCODE() 
    OF 2 ! File not found
      CREATE(S_SysData)
      IF ERRORCODE()
        MESSAGE('Could not create "' & MyFileName & '"|' & ERROR())
        RETURN
      END
      CYCLE      
    OF 0
      BREAK
    ELSE
      MESSAGE('Could not open "' & MyFileName & '"|' & ERROR())
      RETURN
    END
  END  

  SET(S_SysData)
  NEXT(S_SysData)
  IF ERRORCODE()
    SYS:RMD = TODAY()
    SYS:RMT = CLOCK()
    SYS:RMB = 'SYSADMIN'
    ADD(S_SysData)
    IF ERRORCODE()
      MESSAGE('Could not add a record to "' & MyFileName & '"|' & ERROR())
      RETURN
    END
  END
  
  SYS:Note = 'Last Run ' & CLIP(LEFT(FORMAT(TODAY(), @d2b))) & ' ' & CLIP(LEFT(FORMAT(CLOCK(),@t1)))
  PUT(S_SysData)
  IF ERRORCODE()
    MESSAGE('There was a problem writing the record: ' & ERROR() & ' ' & ERRORCODE())
    RETURN
  END
  
  MESSAGE('SYS:RMD=' & FORMAT(SYS:RMD,@d2b) & '|SYS:RMT=' & FORMAT(SYS:RMT,@t3) & '|SYS:RMB=' & SYS:RMB & '|SYS:Note=" ' & CLIP(SYS:Note) & '"')
      
  
  RETURN    
1 Like

You can override the field validation in the global embeds File, Field section with an low priority Return Level:Benign, even better you can wrap that Return Level:Benign in a condition to make field validation optional based on the conditions coded for.

1 Like

Off topic, but in my experience a single record file is a PIA!

Please pardon my ignorance, but what is a PIA?

Pain in the Arse!

I said it was off-topic because it doesn’t help your original problem. But to elaborate on my comment it is my belief that a “System” file is better structured with several fields and a key that points to the info you need. e.g.

Sys:PropertyName
Sys:Description
Sys:Value
Sys:Expiry
etc etc

Then, when you want a value you get it by finding the Sys:PropertyNameKey.

I believe this is preferable to a single record containing all your values because, with Topspeed in particular, you need to change your dct and rebuild your file every time you add a new value to your System file.

1 Like

FWIW, I have used a blob in TPS with XML or JSON, and it worked fine.

1 Like

Likewise.

And in my unwashed youth (before even SOAP) I included several spare LONGs and a large STRING that I could OVER when I needed to add new configuration items.

1 Like

One way to handle this is to create normal Form template procedure that handles the file updates.

Then you just need to create a Source procedure that does the things a Browse does in the Insert and Change buttons.

This code was written on my phone and from memory so others please note what’s wrong:

Access:S_SysData.Open()
Access:S_SysData.UseFile() ! So file is opened for Clarion statements

IF Records(S_SysData) = 0                       !If no record
   GET(S_SysData,0)
   CLEAR(S_SysData )
   GlobalRequest = InsertRecord
ELSE
   Set(S_SysData)
   WATCH(S_SysData) !Form optimistic concurrency
   Next(S_SysData)
   GlobalRequest = ChangeRecord
END

S_SysData_FormProcedure()

Access:S_SysData.Close()
RETURN

2 Likes

Hi,
I use a single record for control in my program - all the way from Clarion for DOS to C11.
I supply the application with a single record in the file. I do not let the system add it.

After my Main Window opens I call a routine - first to make sure the expiration date is not exceeded:

CK_EXPIRE ROUTINE           !MAKE SURE THE PROGRAM HAS NOT EXPIRED
  share(maindATA)
  get(maindata,1)
  CLOSE(MAINDATA)
   if mai:expire <> ' '                          ! if date entered
    if today() - mai:expire > 0                  !expired
     halt(0,'THIS PROGRAM HAS EXPIRED')
    .
   .

Then I call my CONTROL record and modify settings - again, one record - no inserts - only edits:

ctl_check ROUTINE    !check status
     DAYNAME = 'DAY' & (SUB(YEAR(TODAY()),-2,2)) !****SET TO current YEAR
     SUMNAME = 'SUM' & (SUB(YEAR(TODAY()),-2,2)) !****SET TO current YEAR
    share(control)
    LOOP
      HOLD(CONTROL)
      Set(CONTROL)
      Next(control) ! no access
      IF ERRORCODE() = 43 |   !HELD
      OR CON:REPAIR           !OR = 1 --> REPAIR IN PROGRESS
        RELEASE(CONTROL)
        CYCLE
      ELSE
         if CON:date <> today()                  !if not todays date
           if CON:ctlstatus =6   !ERROR CODE FOR NOT DAYJOURNAL NOT PRINTED
             MEM:STATUS = 6
             release(control)
             do err_6  !will return after screen show
           .
            share(DAYJOURN)
            share(SUMMARY)
           set(dayjourn)
            previous(dayjourn)
            if error() then stop('error getting previous dayjournal').
            Access:DAYJOURN.Close()
           set(summary)
            previous(summary)
            if error() then stop('error getting previous summary').
             CLOSE(SUMMARY)
            if day:tdate <> sum:date !journal was not printed for previous date
              RELEASE(CONTROL)
              DO NOT_DONE
            else
              IF TODAY() < SUM:DATE  !IF SYSTEM DATE IS BEFORE THE LAST RECORD
                 release(control)
                 do w_date
   !@@@              return  !** Check for return value required **
              .
              CON:date = today()
              CON:ctlstatus = 0   !set to no records added
              MEM:STATUS = 0
              CON:ACTIVE = 0      !NO ACTIVE STATIONS  RESET IN CASE DUMP OUT ON BREAK OR ERROR!
            .
         else                                    !if it is todays date
           if CON:ctlstatus = 0                  !no records added yet
             MEM:STATUS = 0
           elsif CON:ctlstatus =1                !records added
             MEM:STATUS = 1
           elsif CON:ctlstatus =5                !dayjournal already printed for today
             MEM:STATUS = 5
           elsif CON:ctlstatus =6                !ERROR CODE FOR NOT DAYJOURNAL NOT PRINTED
             MEM:STATUS = 6
             release(control)
             do err_6  !will return after screen show
           .
         .
         PUT(CONTROL)
         RELEASE(CONTROL)
        CLOSE(CONTROL)
         BREAK
    . .

This simple code just keeps chugging on in my windows app.
Ron

Per the Help on HOLD you will not get Error 43 “Record Is Already Held”
unless you use HOLD(file, Time Out Seconds).

Your code will wait forever for the record to be released. It would be good to check for other Errors. Like maybe the file did not open, or is corrupt.