Text input/outputstream help

Hello :slight_smile:

I’m a Xojo beginner so bear with me if I ask stupid questions along the way :sweat_smile: I have managed to make an app for Raspberry that has different properties updated by a timer.
For example if the MainPump is active the timer will +1 every second to the property MainPumpCounter.

I have four properties stored as integers:
MainPumpCounter
VibrationCounter
SpaCounter
MachineTimeCounter

However I have ran against the wall when trying to add two event handlers to the app.

Eventhandler1)
When the program is closed I would like it to save the properties to a textfile in a folder on the desktop called Miberi
The textfile I would like to be named MachineProperties
In the file should then be stored the four properties
If the file already exits it should be overwritten (the .create function as far as I understand)
This process I’m trying to do without the showdialog (the program should do it in the background without user envolvement)

Eventhandler2)
When the program is opened I would like the program to read the text file (if it exits) and overwrite the current value of the four properties

I hope your bright minds can help me on this puzzle as I have spend hours googling and reading the forums + xojo docs but can’t seem to put the puzzle together on how to get it to work :cold_sweat:

Thanks in advance for your kind help

Eventhandler1 would be the close/closing event of your application and look something like

Var t As TextOutputStream = TextOutputStream.Create(SpecialFolder.Desktop.Child("MachineProperties"))
t.WriteLine(MachineTimeCounter.ToString)
t.WriteLine(MainPumpCounter.ToString)
t.WriteLine(SpaCounter.ToString)
t.WriteLine(VibrationCounter.ToString)
t.Close

EventHandler2 would be the open/opening event of your app and look something like

Var f As FolderItem = SpecialFolder.Desktop.Child("MachineProperties")
If f.Exists Then
  Var t As TextInputStream = TextInputStream.Open(f)
  MachineTimeCounter = t.ReadLine.ToInteger
  MainPumpCounter = t.ReadLine.ToInteger
  SpaCounter = t.ReadLine.ToInteger
  VibrationCounter = t.ReadLine.ToInteger
  t.Close
End If

The order that you write/read your properties doesn’t matter so long as they are the same.

HTH
Wayne

I‚Äôd change to ‚Äúif f<>nil and f.exists then‚ÄĚ.

I didn’t check for Nil as this is a desktop project and I can’t conceive of a situation where specialfolder.desktop doesn’t exist without the OS being corrupt. If the file were in a folder on the desktop then yes that may be useful.

However it can’t hurt.

I can do it without corrupting the OS (and did it years ago on some of my computers).
In the Finder, select your desktop folder (from your Home folder), get info and remove all permissions (read, write and execute).
SpecialFolder.Desktop would still exist, that’s not the problem. Accessing anything in it would return nil.

The OP did mention that the file would be in a folder on the desktop. So I would amend Wayne’s code as

var f as FolderItem = SpecialFolder.Desktop.Child("Miberi")
if f <> Nil and f.Exists then f = f.Child("MachineProperties")
if f <> Nil then
   Var t As TextOutputStream = TextOutputStream.Create(f)
   t.WriteLine(MachineTimeCounter.ToString)
   t.WriteLine(MainPumpCounter.ToString)
   t.WriteLine(SpaCounter.ToString)
   t.WriteLine(VibrationCounter.ToString)
   t.Close
End
var f as FolderItem = SpecialFolder.Desktop.Child("Miberi")
if f <> Nil and f.Exists then f = f.Child("MachineProperties")
if f <> Nil and f.Exists then
  Var t As TextInputStream = TextInputStream.Open(f)
  MachineTimeCounter = t.ReadLine.ToInteger
  MainPumpCounter = t.ReadLine.ToInteger
  SpaCounter = t.ReadLine.ToInteger
  VibrationCounter = t.ReadLine.ToInteger
  t.Close
End

Somewhat more advanced but worth considering is using JSON or XML for your properties file (still text, so file operations are the same). This will make your properties file self-explanatory since each property will have a human-readable key identifying what it is. It also makes the read/write order unimportant, so you can never screw it up by unthinkingly changing the order, since each property will be accessed by its key rather than its position in the file.

If you’ve only got four properties it’s probably not a big deal but if your app grows, the benefits will increase.

1 Like

+1 on key/value pairs in the file format.

1 Like

Thank you very much everybody :smiley:

I can see I hit the wall on this part: SpecialFolder.Desktop.Child(‚ÄúMiberi‚ÄĚ)

I will test the code in the weekend, so thank you all sooo much for pointing me in the right direction :pray:

@Tim_Hare I got it working with the help of your code so thank you very much :relaxed:
What do you mean with ‚Äú+1 on key/value pairs in the file format‚ÄĚ?
Sorry but I don’t understand :sweat_smile:

I was agreeing with Julia’s comment. If you ever start to add more fields to the file, you should consider some format where each value is labeled. It makes it a lot more flexible and easier to read if you need to inspect the file in a text editor.

Okay I understand :smiley: thanks again to all

Hello
I have the suggested code from Tim Hare running, but sometimes (seem to random when) I get an error crashing the program:
An exception of class NilObjectException was not handled. The application must be shut down.

When clicking ok (which is the only option) the program closes.

I have read about exception handling in Xojo docs and done a lot of googling but can’t figure out a way to catch the exception and then simply delete (and/or) ignore it and let the program continue.

Can anyone help :pray:

Where is the exception occurring? Run the app under the debugger and ensure that in the Project menu you have Break on Exceptions set. Then see where the exception occurs. That’s the first step. Then post back here with the code around the exception line.

I will give it a try but my problem is that there can go a week or two without any exceptions. That’s why I’m trying to figure out if it is possible to simple catch the exception and then ignore it without showing a messagebox…

The code I have when the app opens:

var f as FolderItem = SpecialFolder.Desktop.Child(‚ÄúAHTproperties‚ÄĚ)
if f <> Nil and f.Exists then f = f.Child(‚ÄúAHTcounters‚ÄĚ)
if f <> Nil and f.Exists then
Var t As TextInputStream = TextInputStream.Open(f)
App.TællerMaskineIgang = t.ReadLine.ToInteger
App.T√¶llerL√łbeb√•nd = t.ReadLine.ToInteger
App.TællerVibration = t.ReadLine.ToInteger
App.TællerHovedpumpe = t.ReadLine.ToInteger
App.TællerSandfilterStdTank = t.ReadLine.ToInteger
App.TællerSandfilterKoldTank = t.ReadLine.ToInteger
App.TællerSolarie = t.ReadLine.ToInteger
App.TællerSpa = t.ReadLine.ToInteger
App.TællerFintFilter = t.ReadLine.ToInteger
App.TællerGrovFilter = t.ReadLine.ToInteger
t.Close
End

The code I have when the app closes:

var f as FolderItem = SpecialFolder.Desktop.Child(‚ÄúAHTproperties‚ÄĚ)
if f <> Nil and f.Exists then f = f.Child(‚ÄúAHTcounters‚ÄĚ)
if f <> Nil then
Var t As TextOutputStream = TextOutputStream.Create(f)
t.WriteLine(App.TællerMaskineIgang.ToString)
t.WriteLine(App.T√¶llerL√łbeb√•nd.ToString)
t.WriteLine(App.TællerVibration.ToString)
t.WriteLine(App.TællerHovedpumpe.ToString)
t.WriteLine(App.TællerSandfilterStdTank.ToString)
t.WriteLine(App.TællerSandfilterKoldTank.ToString)
t.WriteLine(App.TællerSolarie.ToString)
t.WriteLine(App.TællerSpa.ToString)
t.WriteLine(App.TællerFintFilter.ToString)
t.WriteLine(App.TællerGrovFilter.ToString)
t.Close
End

for simple error handling see
https://documentation.xojo.com/api/exceptions/exception.html

have you edit this file maybe? (just because any byte order mark at the beginning (BOM))
do not recycle variables
use one for the path the other for the file.
if the path not exists create it. https://documentation.xojo.com/api/files/folderitem.html.CreateFolder
it is not necessary to test nil if you got a object.

The file AHTcounters has not been edited and I have checked that permissions for folder and file are set to anyone… I know with certainty that the folder AHTproperties exist as it is created on the Desktop in advance manually when I set up the pi…
I have the debugger running now, so I’m just hoping it will not take weeks for this error to occur.

My problem is though that I have been through all xojo documentation and searched hours on google without any luck.

When the nilobjectexception occours I would like to ignore it and let the app continue. I‚Äôm simply a beginner so I can‚Äôt figure out how to do that as all examples are with ‚Äúshow messagebox‚ÄĚ instead off ‚Äúignore exception‚ÄĚ

I hope it makes sense :sweat_smile:

on error resume is not good.
first you need the method where the nilobject exception happens.

as example here
Var t As TextOutputStream
you access this t direct
if open fails you got no stream
t would be nil
t.ReadLine fail, would be nil.Readline
you can use app wide error handling here (not for resume next, just to know why and where)


unhandled means all places without try catch around :wink:
if your app run 24/7 i not think that this load/save is your issue.

You can catch exceptions to handle them and prevent the application from failing and exiting. However, you have to know where they are. Have you seen the NilObjectException in debug? Do you know whereabouts it could be happening?

App.UnhandledException should be a last resort and not your go-to resolution. Do not implement this event and return true simply to avoid this one issue.

If you don’t know where the NilObjectException is occurring, you could wrap your whole routine in a try…catch and that would still be a more appropriately scoped solution than App.UnhandledException.

try
  var f as FolderItem = SpecialFolder.Desktop.Child(‚ÄúAHTproperties‚ÄĚ)
  if f <> Nil and f.Exists then f = f.Child(‚ÄúAHTcounters‚ÄĚ)
  if f <> Nil then
  Var t As TextOutputStream = TextOutputStream.Create(f)
  t.WriteLine(App.TællerMaskineIgang.ToString)
  t.WriteLine(App.T√¶llerL√łbeb√•nd.ToString)
  t.WriteLine(App.TællerVibration.ToString)
  t.WriteLine(App.TællerHovedpumpe.ToString)
  t.WriteLine(App.TællerSandfilterStdTank.ToString)
  t.WriteLine(App.TællerSandfilterKoldTank.ToString)
  t.WriteLine(App.TællerSolarie.ToString)
  t.WriteLine(App.TællerSpa.ToString)
  t.WriteLine(App.TællerFintFilter.ToString)
  t.WriteLine(App.TællerGrovFilter.ToString)
  t.Close
  End

catch ex as NilObjectException
  // Something went terribly wrong, the data was not saved.

end try
2 Likes

Lookup Try/Catch in the documentation:

http://documentation.xojo.com/api/code_execution/try.html

Trouble is, if you therefore avoid executing some important code, you could get a crash later.

I’d suggest, if you do use Try/Catch, that in the Catch code you log where the error has occurred.