Writing Lua libraries (split from Night-time time-lapse) - LUA Scripting - CHDK Forum  

Writing Lua libraries (split from Night-time time-lapse)

  • 10 Replies
  • 7293 Views
*

Offline reyalp

  • ******
  • 12106
Writing Lua libraries (split from Night-time time-lapse)
« on: 26 / October / 2008, 20:31:03 »
Advertisements
Fudgey:

You can make the date stuff much simpler by using os.date

Code: (lua) [Select]
os.date("%X") -- gives you HH:MM:SS
os.date("%x") -- gives you MM/DD/YY (yeah, that's yank format, should depend on your locale but cams probably don't set it)
-- You can combine multiple format options, so
os.date("%d/%m/%y %X") -- would give you DD/MM/YY HH:MM:SS

You can see all the available formats in the llibtest.log

You should also be aware that if you accidentally pass a value that isn't a string or number (such as nil) to write() it will cause an error, unlike print which calls tostring() on everything. This isn't necessarily a problem for your script, but something to keep in mind when you are switching between the two.

If you want the log module to be general purpose, you should probably use
log.file:write(...) and not set io.output.

Rather than inserting require "dummy" in every module, I'd suggest that you arrange for your test script to load everything it needs first, and then dofile() on the script to be tested.
Don't forget what the H stands for.

*

Offline fudgey

  • *****
  • 1705
  • a570is
Writing Lua libraries (split from
« Reply #1 on: 27 / October / 2008, 12:46:00 »
Thanks for the education reyalp! I'm surprised you didn't find more gravely faulty things to fix (or maybe you're just being polite ;)).

You can make the date stuff much simpler by using os.date

Yea, I noticed that from your test script after I had already done it the other way. Didn't have the time to dig into it then and I figured it'd be easy to replace those functions later and that maybe it's good to wait until they are when io and os functions are more widely tested. Or, do you think os.date is foolproof on all cameras compared to io.*?

After all, originally I only intended to fix that black framing thing using half shoot magic, but I got "a bit" carried away waiting for all those test runs to finish. :D

You should also be aware that if you accidentally pass a value that isn't a string or number (such as nil) to write() it will cause an error, unlike print which calls tostring() on everything. This isn't necessarily a problem for your script, but something to keep in mind when you are switching between the two.

If you want the log module to be general purpose, you should probably use
log.file:write(...) and not set io.output.

What else is there besides string or number (that someone could be giving inside log.print() paranthesis?). Actually I first tried to do something like io.write(...,"\n") because print() always inserts newline as well but that failed (fell free to tell me why), and then something like io.write(unpack(arg),"\n") which didn't work either so I just gave up. Anyway, I suppose that would've always forced one character to write().

Should I call tostring() on it or do a nil check (and how does one do that check ... for nil, those two failures above make me feel ... is special in more than one way?)

About log.file:write(...), Ok. I understand the difference. Not that I really know why that syntax would work, but that's just me not having read the manual. I think we should have generic logging functions with usage similar to mine in trunk but preferably with optional multiple simultaneous file support in one way or another.

Rather than inserting require "dummy" in every module, I'd suggest that you arrange for your test script to load everything it needs first, and then dofile() on the script to be tested.

Only sunsetF15.lua needs that dummy. The other ones are actually left over from when I first tested the functions standalone running the libary lua files. I deleted the test calls from the end of each library but apparently not the dummy requires.

*

Offline PhyrePhoX

  • *****
  • 2254
  • make RAW not WAR
    • PhyreWorX
Writing Lua libraries (split from
« Reply #2 on: 27 / October / 2008, 14:14:32 »
perhaps i can extend the get_mode command to at least return m- or av- or the other modes. though i think that in some cameras, the table isnt properly set up.

*

Offline fudgey

  • *****
  • 1705
  • a570is
Writing Lua libraries (split from
« Reply #3 on: 27 / October / 2008, 15:28:42 »
perhaps i can extend the get_mode command to at least return m- or av- or the other modes. though i think that in some cameras, the table isnt properly set up.

This is something that is maybe better off in a generated lua library like the new propcase.lua as it's just a propcase interpretation table, isn't it? The problem with creating it may be that the source code (assuming it's all correct which it probably isn't, but that's just a matter of getting our cuddly users run some tests to fix what's broken) has different values than what get_prop returns.

At least in ubasic it does, I believe due to different selection of word width or signedness among the two.


*

Offline reyalp

  • ******
  • 12106
Writing Lua libraries (split from
« Reply #4 on: 27 / October / 2008, 22:45:59 »
What else is there besides string or number (that someone could be giving inside log.print() paranthesis?). Actually I first tried to do something like io.write(...,"\n") because print() always inserts newline as well but that failed (fell free to tell me why), and then something like io.write(unpack(arg),"\n") which didn't work either so I just gave up. Anyway, I suppose that would've always forced one character to write().
Yes, the ... has to be at the end. You can expand ... into a table, and then unpack that. I think you could do
Code: [Select]
write(unpack({...}),"\n")
However, if all you want to do is add a newline, just do
Code: [Select]
write(...)
write("\n")
io is buffered, so the extra call isn't a concern.

The typical one that gets me is logging the result of something that returns nil.

The other option is to call tostring in your log calls on anything that might be nil. This is what I do in the test script.
Quote
Should I call tostring() on it or do a nil check (and how does one do that check ... for nil, those two failures above make me feel ... is special in more than one way?)
There isn't a pretty way way to do it. You'd have to unpack it to a table and then iterate through it.

Quote
About log.file:write(...), Ok. I understand the difference. Not that I really know why that syntax would work, but that's just me not having read the manual. I think we should have generic logging functions with usage similar to mine in trunk but preferably with optional multiple simultaneous file support in one way or another.
Yup, this is a good candidate for a standard module. I look forward to stealing your work for inclusion in the standard modules :)

when you do io.open, the thing returned is a "file object", meaning a table that has some functions attached to it. so file:write(...) means write to that file.

file:write(...) actually just translates to file.write(file,...)

I'd say os.date is pretty safe. Actually, given that every camera tested so far has worked fine with the libs, I'd say chances are good it will work on the rest.

Hrrm, we're dragging fbonomis thread off topic. If someone wants to split the general lua discussion out, that might be polite :)
Don't forget what the H stands for.

*

Offline fudgey

  • *****
  • 1705
  • a570is
Re: Writing Lua libraries (split from
« Reply #5 on: 28 / October / 2008, 13:51:42 »
However, if all you want to do is add a newline, just do
Code: [Select]
write(...)
write("\n")
io is buffered, so the extra call isn't a concern.

Now that's ingenious. Didn't even cross my mind to do that. I need more sleep, obviously. :D I think I'd rather have a simple log function in a standard library that always includes a newline. In any case write() is always available even while using log.print whenever a newline is not desired...

The other option is to call tostring in your log calls on anything that might be nil. This is what I do in the test script.

I think that'll be good enough, all that unpacking sounds like something that'll take more than a  few instructions to complete.

Quote
Yup, this is a good candidate for a standard module. I look forward to stealing your work for inclusion in the standard modules :)

I intend to please!

Quote
Hrrm, we're dragging fbonomis thread off topic. If someone wants to split the general lua discussion out, that might be polite :)

Done... I hope I didn't screw anything up too badly while at it...that was my first test of the almighty and powerful moderator buttons.  8)

*

Offline fudgey

  • *****
  • 1705
  • a570is
Re: Writing Lua libraries (split from Night-time time-lapse)
« Reply #6 on: 01 / November / 2008, 09:26:28 »
I improved the logging library a bit... this one can open and switch between multiple files and find a free log file name all while still being able to use print_screen when desired or if io is not available.

Not very thoroughly tested, comments welcome.

*

Offline reyalp

  • ******
  • 12106
Re: Writing Lua libraries (split from Night-time time-lapse)
« Reply #7 on: 01 / November / 2008, 17:59:12 »
Rather than log.switch, I'd suggest making something that returns a log object.
So you would do something like
require("log")
mylog=log.open("optional file name")
mylog:print("log message")
mylog:close()

The reason to do this is so that modules can do their own logging, without worrying about what the main program or other modules are doing. Problems might arise if multiple modules try to log to the same file.

Note that if you make the log.lua file return a value, this becomes the return value of require. Assuming you return the log table, you can do

mylog=require("log").open("mylog.log")

For lua modules, it generally considered good practice to do this anyway, so modules don't have to pollute the global namespace. The table containing the module can be a local in the module code, and the user can put it in whatever global they want. This is what I with the propcase.lua modules.

Note that if you "require" a module that has already been loaded, require simply returns the last return value instead of loading the module again, so you can freely use it to obtain access to the module.
Don't forget what the H stands for.


*

Offline fudgey

  • *****
  • 1705
  • a570is
Re: Writing Lua libraries (split from Night-time time-lapse)
« Reply #8 on: 02 / November / 2008, 17:37:52 »
You mean something like this?

I didn't actually run this one in the camera yet because it's out shooting a timelapse...

I didn't try implementing that mylog=require("log").open("mylog.log") part, frankly it looks too scary (as in to use a library like that, I'd always have to cut&paste and example)...


Btw, is there a logic in how print() works? In my experience, it usually uses tab delimiters for a comma separated list (while write() doesn't), but if there is a concatenated string in the comma separated list, it will use space as the separator instead of a tab. In the camera, that is.


*

Offline reyalp

  • ******
  • 12106
Re: Writing Lua libraries (split from Night-time time-lapse)
« Reply #9 on: 02 / November / 2008, 19:04:36 »
Print in recent versions should just put a space between each item (it used to use tabs, and if you are running lua on your PC, it will obviously still use them) I changed this because the CHDK console doesn't actually understand tabs, and with the limited space, it wouldn't be useful to implement them. This is still annoyingly different from write, which doesn't do any formatting.

For consistency, it may be worth writing a wrapper for write that behaves like print (add spaces and tostring anything that needs it.) Actually, in the long run stdout:write() should just go to the console ;)

Your recent version is was pretty much what I had in mind.  I agree that mylog=require("log").open("mylog.log")  is probably too scary for the general scripter.

What follows is purely for educational purposes on a fairly advanced lua concept. If it doesn't make sense, don't worry too much.

There is an important difference between:
Code: [Select]
function foo()
  local t={}
  function t.bar()
    -- stuff
  end
  return t
end
and
Code: [Select]
local function bar()
 -- stuff
end
function foo()
  local t={}
  t.bar = bar
  return t
end
The first version creates a new function (actually, a closure, see also lua specific description) for each call to foo, so bar in each returned t refers to a different function, and that function gets it's own copy of any local variables it uses from the enclosing foo. The second assigns the same function to the bar member of every t, and that function has no access to the local variables of foo.

Note above that I've made bar local, which makes it private to the enclosing block (modules loaded by require are implicitly blocks). This does not affect the lifetime of the function: It will exist for as long as something has a reference to it, but nothing outside the module will be able to use it. For functions internal to a module, this is preferable to using a global, or putting it in the table that forms the interface of the module.

If you plan to use object syntax (t:bar()) you'd have to explicitly include the self parameter in the declaration, e.g.
Code: [Select]
local function bar(self,...)
end
Finally, note that because you've used closures, you don't actually need the self parameter, and so using object syntax is just extra baggage. This is demonstrated by the fact you used newlog.handle:write() instead of self.handle:write(). If you use closures, you may as well just have your users use log.print() because each copy of the print function knows who it belongs to.

If you don't need the special characteristics of closures, the second method is more efficient. OTOH, closures are an incredibly powerful concept, and arguably more elegant the lua "syntactic sugar" object system.

In your case, this doesn't matter which method you use, because the functions are trivial, and people are at most going to open a handful of logs.
Don't forget what the H stands for.

 

Related Topics