| 1 | --[[ |
|---|
| 2 | Copyright (C) 2010-2011 <reyalp (at) gmail dot com> |
|---|
| 3 | |
|---|
| 4 | This program is free software; you can redistribute it and/or modify |
|---|
| 5 | it under the terms of the GNU General Public License version 2 as |
|---|
| 6 | published by the Free Software Foundation. |
|---|
| 7 | |
|---|
| 8 | This program is distributed in the hope that it will be useful, |
|---|
| 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 11 | GNU General Public License for more details. |
|---|
| 12 | |
|---|
| 13 | You should have received a copy of the GNU General Public License |
|---|
| 14 | along with this program; if not, write to the Free Software |
|---|
| 15 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|---|
| 16 | --]] |
|---|
| 17 | |
|---|
| 18 | local cli = { |
|---|
| 19 | cmds={}, |
|---|
| 20 | names={}, |
|---|
| 21 | finished = false, |
|---|
| 22 | } |
|---|
| 23 | |
|---|
| 24 | cli.cmd_proto = { |
|---|
| 25 | get_help = function(self) |
|---|
| 26 | local namestr = self.names[1] |
|---|
| 27 | if #self.names > 1 then |
|---|
| 28 | namestr = namestr .. " (" .. self.names[2] |
|---|
| 29 | for i=3,#self.names do |
|---|
| 30 | namestr = namestr .. "," .. self.names[i] |
|---|
| 31 | end |
|---|
| 32 | namestr = namestr .. ")" |
|---|
| 33 | end |
|---|
| 34 | return string.format("%-12s %-12s: - %s\n",namestr,self.arghelp,self.help) |
|---|
| 35 | end, |
|---|
| 36 | } |
|---|
| 37 | |
|---|
| 38 | cli.cmd_meta = { |
|---|
| 39 | __index = function(cmd, key) |
|---|
| 40 | return cli.cmd_proto[key] |
|---|
| 41 | end, |
|---|
| 42 | __call = function(cmd,...) |
|---|
| 43 | return cmd:func(...) |
|---|
| 44 | end, |
|---|
| 45 | } |
|---|
| 46 | |
|---|
| 47 | function cli:add_commands(cmds) |
|---|
| 48 | for i = 1, #cmds do |
|---|
| 49 | cmd = cmds[i] |
|---|
| 50 | table.insert(self.cmds,cmd) |
|---|
| 51 | if not cmd.arghelp then |
|---|
| 52 | cmd.arghelp = '' |
|---|
| 53 | end |
|---|
| 54 | for _,name in ipairs(cmd.names) do |
|---|
| 55 | if self.names[name] then |
|---|
| 56 | warnf("duplicate command name %s\n",name) |
|---|
| 57 | else |
|---|
| 58 | self.names[name] = cmd |
|---|
| 59 | end |
|---|
| 60 | end |
|---|
| 61 | setmetatable(cmd,cli.cmd_meta) |
|---|
| 62 | end |
|---|
| 63 | end |
|---|
| 64 | |
|---|
| 65 | function cli:prompt() |
|---|
| 66 | if con:is_connected() then |
|---|
| 67 | local script_id = con:get_script_id() |
|---|
| 68 | if script_id then |
|---|
| 69 | printf("con %d> ",script_id) |
|---|
| 70 | else |
|---|
| 71 | printf("con> ") |
|---|
| 72 | end |
|---|
| 73 | else |
|---|
| 74 | printf("___> ") |
|---|
| 75 | end |
|---|
| 76 | end |
|---|
| 77 | |
|---|
| 78 | -- execute command given by a single line |
|---|
| 79 | -- returns status,message |
|---|
| 80 | -- message is an error message or printable value |
|---|
| 81 | function cli:execute(line) |
|---|
| 82 | -- single char shortcuts |
|---|
| 83 | local s,e,cmd = string.find(line,'^[%c%s]*([!?.#=])[%c%s]*') |
|---|
| 84 | if not cmd then |
|---|
| 85 | s,e,cmd = string.find(line,'^[%c%s]*([%w_]+)[%c%s]*') |
|---|
| 86 | end |
|---|
| 87 | if s then |
|---|
| 88 | local args = string.sub(line,e+1) |
|---|
| 89 | if self.names[cmd] then |
|---|
| 90 | local status,msg = self.names[cmd](args) |
|---|
| 91 | if not status and not msg then |
|---|
| 92 | msg=cmd .. " failed" |
|---|
| 93 | end |
|---|
| 94 | return status,msg |
|---|
| 95 | else |
|---|
| 96 | return false,string.format("unknown command '%s'\n",cmd) |
|---|
| 97 | end |
|---|
| 98 | elseif string.find(line,'[^%c%s]') then |
|---|
| 99 | return false, string.format("bad input '%s'\n",line) |
|---|
| 100 | end |
|---|
| 101 | -- blank input is OK |
|---|
| 102 | return true,"" |
|---|
| 103 | end |
|---|
| 104 | |
|---|
| 105 | function cli:print_status(status,msg) |
|---|
| 106 | if not status then |
|---|
| 107 | errf("%s\n",tostring(msg)) |
|---|
| 108 | elseif msg and string.len(msg) ~= 0 then |
|---|
| 109 | printf("%s",msg) |
|---|
| 110 | if string.sub(msg,-1,-1) ~= '\n' then |
|---|
| 111 | printf("\n") |
|---|
| 112 | end |
|---|
| 113 | end |
|---|
| 114 | end |
|---|
| 115 | |
|---|
| 116 | function cli:run() |
|---|
| 117 | self:prompt() |
|---|
| 118 | for line in io.lines() do |
|---|
| 119 | self:print_status(self:execute(line)) |
|---|
| 120 | if self.finished then |
|---|
| 121 | break |
|---|
| 122 | end |
|---|
| 123 | self:prompt() |
|---|
| 124 | end |
|---|
| 125 | end |
|---|
| 126 | |
|---|
| 127 | -- add/correct A/ as needed, replace \ with / |
|---|
| 128 | function cli:make_camera_path(path) |
|---|
| 129 | if not path then |
|---|
| 130 | return 'A/' |
|---|
| 131 | end |
|---|
| 132 | -- fix slashes |
|---|
| 133 | path = string.gsub(path,'\\','/') |
|---|
| 134 | local pfx = string.sub(path,1,2) |
|---|
| 135 | if pfx == 'A/' then |
|---|
| 136 | return path |
|---|
| 137 | end |
|---|
| 138 | if pfx == 'a/' then |
|---|
| 139 | return 'A' .. string.sub(path,2,-1) |
|---|
| 140 | end |
|---|
| 141 | return 'A/' .. path |
|---|
| 142 | end |
|---|
| 143 | |
|---|
| 144 | -- returns <str>, <remaining arg string> |
|---|
| 145 | -- accepts quoted strings or space delimited |
|---|
| 146 | function cli:get_string_arg(arg) |
|---|
| 147 | if type(arg) ~= 'string' then |
|---|
| 148 | return |
|---|
| 149 | end |
|---|
| 150 | local path |
|---|
| 151 | -- trim leading spaces |
|---|
| 152 | local s, e = string.find(arg,'^[%c%s]*') |
|---|
| 153 | arg = string.sub(arg,e+1) |
|---|
| 154 | -- check for quotes |
|---|
| 155 | s, e, str = string.find(arg,'^["]([^"]+)["]') |
|---|
| 156 | if s then |
|---|
| 157 | return str, string.sub(arg,e+1) |
|---|
| 158 | end |
|---|
| 159 | -- try without quotes |
|---|
| 160 | s, e, str = string.find(arg,'^([^%c%s]+)') |
|---|
| 161 | if s then |
|---|
| 162 | return str, string.sub(arg,e+1) |
|---|
| 163 | end |
|---|
| 164 | return nil |
|---|
| 165 | end |
|---|
| 166 | |
|---|
| 167 | --[[ |
|---|
| 168 | t,args=cli:get_opts(args,optspec) |
|---|
| 169 | optspec is an array of option letters |
|---|
| 170 | returns table of option values |
|---|
| 171 | plus arg string with recognized opts removed |
|---|
| 172 | TODO should unify command line processing with main.lua args |
|---|
| 173 | ]] |
|---|
| 174 | function cli:get_opts(arg,optspec) |
|---|
| 175 | local r={} |
|---|
| 176 | for i,v in ipairs(optspec) do |
|---|
| 177 | arg = string.gsub(arg,'-'..v,function() |
|---|
| 178 | r[v]=true |
|---|
| 179 | return '' |
|---|
| 180 | end) |
|---|
| 181 | end |
|---|
| 182 | return r,arg |
|---|
| 183 | end |
|---|
| 184 | |
|---|
| 185 | -- returns num, <remaining arg string> |
|---|
| 186 | -- num can be signed hex or decimal |
|---|
| 187 | function cli:get_num_arg(arg) |
|---|
| 188 | if type(arg) ~= 'string' then |
|---|
| 189 | return |
|---|
| 190 | end |
|---|
| 191 | local hex,num |
|---|
| 192 | local s, e, neg=string.find(arg,'^[%c%s]*(-?)') |
|---|
| 193 | if not s then |
|---|
| 194 | neg = '' |
|---|
| 195 | end |
|---|
| 196 | arg = string.sub(arg,e+1) |
|---|
| 197 | s, e, hex=string.find(arg,'^(0[Xx])') |
|---|
| 198 | if s then |
|---|
| 199 | arg = string.sub(arg,e+1) |
|---|
| 200 | s, e, num=string.find(arg,'^([%x]+)') |
|---|
| 201 | else |
|---|
| 202 | hex = '' |
|---|
| 203 | s, e, num=string.find(arg,'^([%d]+)') |
|---|
| 204 | end |
|---|
| 205 | if s then |
|---|
| 206 | return tonumber(neg..hex..num), string.sub(arg,e+1) |
|---|
| 207 | end |
|---|
| 208 | end |
|---|
| 209 | |
|---|
| 210 | cli:add_commands{ |
|---|
| 211 | { |
|---|
| 212 | names={'help','h','?'}, |
|---|
| 213 | arghelp = '[command]'; |
|---|
| 214 | help='help on [command] or all commands', |
|---|
| 215 | func=function(self,args) |
|---|
| 216 | if cli.names[args] then |
|---|
| 217 | return true, cli.names[args]:get_help() |
|---|
| 218 | end |
|---|
| 219 | if args and args ~= "" then |
|---|
| 220 | return false, string.format("unknown command '%s'\n",args) |
|---|
| 221 | end |
|---|
| 222 | msg = "" |
|---|
| 223 | for i,c in ipairs(cli.cmds) do |
|---|
| 224 | msg = msg .. c:get_help() |
|---|
| 225 | end |
|---|
| 226 | return true, msg |
|---|
| 227 | end, |
|---|
| 228 | }, |
|---|
| 229 | { |
|---|
| 230 | names={'#'}, |
|---|
| 231 | help='comment', |
|---|
| 232 | func=function(self,args) |
|---|
| 233 | return true |
|---|
| 234 | end, |
|---|
| 235 | }, |
|---|
| 236 | { |
|---|
| 237 | names={'exec','!'}, |
|---|
| 238 | help='execute local lua', |
|---|
| 239 | arghelp='<lua code>', |
|---|
| 240 | func=function(self,args) |
|---|
| 241 | local f,r = loadstring(args) |
|---|
| 242 | if f then |
|---|
| 243 | r={pcall(f)}; |
|---|
| 244 | if not r[1] then |
|---|
| 245 | return false, string.format("call failed:%s\n",r[2]) |
|---|
| 246 | end |
|---|
| 247 | local s |
|---|
| 248 | if #r > 1 then |
|---|
| 249 | s='=' .. serialize(r[2],{pretty=true,err_type=false,err_cycle=false}) |
|---|
| 250 | for i = 3, #r do |
|---|
| 251 | s = s .. ',' .. serialize(r[i],{pretty=true,err_type=false,err_cycle=false}) |
|---|
| 252 | end |
|---|
| 253 | end |
|---|
| 254 | return true, s |
|---|
| 255 | else |
|---|
| 256 | return false, string.format("compile failed:%s\n",r) |
|---|
| 257 | end |
|---|
| 258 | end, |
|---|
| 259 | }, |
|---|
| 260 | { |
|---|
| 261 | names={'quit','q'}, |
|---|
| 262 | help='quit program', |
|---|
| 263 | func=function() |
|---|
| 264 | cli.finished = true |
|---|
| 265 | return true,"bye" |
|---|
| 266 | end, |
|---|
| 267 | }, |
|---|
| 268 | { |
|---|
| 269 | names={'lua','l','.'}, |
|---|
| 270 | help='execute remote lua', |
|---|
| 271 | arghelp='<lua code>', |
|---|
| 272 | func=function(self,args) |
|---|
| 273 | return con:exec(args) |
|---|
| 274 | end, |
|---|
| 275 | }, |
|---|
| 276 | { |
|---|
| 277 | names={'getm'}, |
|---|
| 278 | help='get messages', |
|---|
| 279 | func=function(self,args) |
|---|
| 280 | local msgs='' |
|---|
| 281 | local msg,err |
|---|
| 282 | while true do |
|---|
| 283 | msg,err=con:read_msg() |
|---|
| 284 | if type(msg) ~= 'table' then |
|---|
| 285 | return false,msgs..err |
|---|
| 286 | end |
|---|
| 287 | if msg.type == 'none' then |
|---|
| 288 | return true,msgs |
|---|
| 289 | end |
|---|
| 290 | msgs = msgs .. chdku.format_script_msg(msg) .. "\n" |
|---|
| 291 | end |
|---|
| 292 | end, |
|---|
| 293 | }, |
|---|
| 294 | { |
|---|
| 295 | names={'putm'}, |
|---|
| 296 | help='send message', |
|---|
| 297 | arghelp='<msg string>', |
|---|
| 298 | func=function(self,args) |
|---|
| 299 | return con:write_msg(args) |
|---|
| 300 | end, |
|---|
| 301 | }, |
|---|
| 302 | { |
|---|
| 303 | names={'luar','='}, |
|---|
| 304 | help='execute remote lua, wait for result', |
|---|
| 305 | arghelp='<lua code>', |
|---|
| 306 | func=function(self,args) |
|---|
| 307 | local rets={} |
|---|
| 308 | local msgs={} |
|---|
| 309 | local status,err = con:execwait(args,{rets=rets,msgs=msgs}) |
|---|
| 310 | if not status then |
|---|
| 311 | return false,err |
|---|
| 312 | end |
|---|
| 313 | local r='' |
|---|
| 314 | for i=1,#msgs do |
|---|
| 315 | r=r .. chdku.format_script_msg(msgs[i]) .. '\n' |
|---|
| 316 | end |
|---|
| 317 | for i=1,table.maxn(rets) do |
|---|
| 318 | r=r .. chdku.format_script_msg(rets[i]) .. '\n' |
|---|
| 319 | end |
|---|
| 320 | return true,r |
|---|
| 321 | end, |
|---|
| 322 | }, |
|---|
| 323 | { |
|---|
| 324 | -- TODO support display as words |
|---|
| 325 | names={'rmem'}, |
|---|
| 326 | help=' read memory', |
|---|
| 327 | arghelp='<address> [count]', |
|---|
| 328 | func=function(self,args) |
|---|
| 329 | local addr |
|---|
| 330 | addr,args = cli:get_num_arg(args) |
|---|
| 331 | local count = cli:get_num_arg(args) |
|---|
| 332 | if not addr then |
|---|
| 333 | return false, "bad args" |
|---|
| 334 | end |
|---|
| 335 | if not count then |
|---|
| 336 | count = 1 |
|---|
| 337 | end |
|---|
| 338 | printf("0x%x %u\n",addr,count) |
|---|
| 339 | r,msg = con:getmem(addr,count) |
|---|
| 340 | if not r then |
|---|
| 341 | return false,msg |
|---|
| 342 | end |
|---|
| 343 | return true,hexdump(r,addr) |
|---|
| 344 | end, |
|---|
| 345 | }, |
|---|
| 346 | { |
|---|
| 347 | names={'list'}, |
|---|
| 348 | help='list devices', |
|---|
| 349 | func=function() |
|---|
| 350 | local msg = '' |
|---|
| 351 | local devs = chdk.list_usb_devices() |
|---|
| 352 | for i,desc in ipairs(devs) do |
|---|
| 353 | local lcon = chdku.connection(desc) |
|---|
| 354 | local usb_info = lcon:get_usb_devinfo() |
|---|
| 355 | local tempcon = false |
|---|
| 356 | local status = "CONNECTED" |
|---|
| 357 | if not lcon:is_connected() then |
|---|
| 358 | tempcon = true |
|---|
| 359 | status = "NOT CONNECTED" |
|---|
| 360 | lcon:connect() |
|---|
| 361 | end |
|---|
| 362 | local ptp_info = lcon:get_ptp_devinfo() |
|---|
| 363 | if not ptp_info then |
|---|
| 364 | ptp_info = { model = "<unknown>" } |
|---|
| 365 | end |
|---|
| 366 | |
|---|
| 367 | if lcon == con then |
|---|
| 368 | status = status..", CLI" |
|---|
| 369 | end |
|---|
| 370 | |
|---|
| 371 | msg = msg .. string.format("%d: %s bus:%s dev:%s vendor:%x pid:%x [%s]\n", |
|---|
| 372 | i, ptp_info.model, |
|---|
| 373 | usb_info.bus, usb_info.dev, |
|---|
| 374 | usb_info.vendor_id, usb_info.product_id, |
|---|
| 375 | status) |
|---|
| 376 | if tempcon then |
|---|
| 377 | lcon:disconnect() |
|---|
| 378 | end |
|---|
| 379 | end |
|---|
| 380 | return true,msg |
|---|
| 381 | end, |
|---|
| 382 | }, |
|---|
| 383 | { |
|---|
| 384 | names={'upload','u'}, |
|---|
| 385 | help='upload a file to the camera', |
|---|
| 386 | arghelp="<local> [remote]", |
|---|
| 387 | func=function(self,args) |
|---|
| 388 | local src,args = cli:get_string_arg(args) |
|---|
| 389 | if not src then |
|---|
| 390 | return false, "missing source" |
|---|
| 391 | end |
|---|
| 392 | local dst = cli:get_string_arg(args) |
|---|
| 393 | -- no dst, use filename of source |
|---|
| 394 | if not dst then |
|---|
| 395 | dst = util.basename(src) |
|---|
| 396 | -- trailing slash, append filename of source |
|---|
| 397 | elseif string.find(dst,'[\\/]$') then |
|---|
| 398 | dst = dst .. util.basename(src) |
|---|
| 399 | end |
|---|
| 400 | if not (src and dst) then |
|---|
| 401 | return false, "bad/missing args ?" |
|---|
| 402 | end |
|---|
| 403 | dst = cli:make_camera_path(dst) |
|---|
| 404 | local msg=string.format("%s->%s\n",src,dst) |
|---|
| 405 | local r, msg2 = con:upload(src,dst) |
|---|
| 406 | if msg2 then |
|---|
| 407 | msg = msg .. msg2 |
|---|
| 408 | end |
|---|
| 409 | return r, msg |
|---|
| 410 | end, |
|---|
| 411 | }, |
|---|
| 412 | { |
|---|
| 413 | names={'download','d'}, |
|---|
| 414 | help='download a file from the camera', |
|---|
| 415 | arghelp="<remote> [local]", |
|---|
| 416 | func=function(self,args) |
|---|
| 417 | local src,args = cli:get_string_arg(args) |
|---|
| 418 | if not src then |
|---|
| 419 | return false, "missing source" |
|---|
| 420 | end |
|---|
| 421 | local dst = cli:get_string_arg(args) |
|---|
| 422 | -- use final component |
|---|
| 423 | if not dst then |
|---|
| 424 | dst = util.basename(src) |
|---|
| 425 | -- trailing slash, append filename of source |
|---|
| 426 | -- TODO should use stat to figure out if target is a directory |
|---|
| 427 | elseif string.find(dst,'[\\/]$') then |
|---|
| 428 | dst = dst .. util.basename(src) |
|---|
| 429 | end |
|---|
| 430 | if not dst then |
|---|
| 431 | return false, "bad/missing args ?" |
|---|
| 432 | end |
|---|
| 433 | src = cli:make_camera_path(src) |
|---|
| 434 | local msg=string.format("%s->%s\n",src,dst) |
|---|
| 435 | local r, msg2 = con:download(src,dst) |
|---|
| 436 | if msg2 then |
|---|
| 437 | msg = msg .. msg2 |
|---|
| 438 | end |
|---|
| 439 | return r, msg |
|---|
| 440 | end, |
|---|
| 441 | }, |
|---|
| 442 | { |
|---|
| 443 | names={'version','ver'}, |
|---|
| 444 | help='print API versions', |
|---|
| 445 | func=function(self,args) |
|---|
| 446 | local host_ver = string.format("host:%d.%d cam:",chdk.host_api_version()) |
|---|
| 447 | if con:is_connected() then |
|---|
| 448 | local cam_major, cam_minor = con:camera_api_version() |
|---|
| 449 | if not cam_major then |
|---|
| 450 | return false, host_ver .. string.format("error %s",cam_minor) |
|---|
| 451 | end |
|---|
| 452 | return true, host_ver .. string.format("%d.%d",cam_major,cam_minor) |
|---|
| 453 | else |
|---|
| 454 | return true, host_ver .. "not connected" |
|---|
| 455 | end |
|---|
| 456 | end, |
|---|
| 457 | }, |
|---|
| 458 | { |
|---|
| 459 | names={'connect','c'}, |
|---|
| 460 | help='connect to device', |
|---|
| 461 | func=function(self,args) |
|---|
| 462 | if con:is_connected() then |
|---|
| 463 | con:disconnect() |
|---|
| 464 | end |
|---|
| 465 | -- TODO temp just connect to the default device for now |
|---|
| 466 | local devs = chdk.list_usb_devices() |
|---|
| 467 | if #devs > 0 then |
|---|
| 468 | con = chdku.connection(devs[1]) |
|---|
| 469 | return con:connect() |
|---|
| 470 | end |
|---|
| 471 | return false,"no devices available" |
|---|
| 472 | end, |
|---|
| 473 | }, |
|---|
| 474 | --[[ |
|---|
| 475 | -- TODO this isn't useful - on win device name can change on reset ? |
|---|
| 476 | { |
|---|
| 477 | names={'reconnect','r'}, |
|---|
| 478 | help='reconnect to current device', |
|---|
| 479 | func=function(self,args) |
|---|
| 480 | if con:is_connected() then |
|---|
| 481 | con:disconnect() |
|---|
| 482 | end |
|---|
| 483 | return con:connect() |
|---|
| 484 | end, |
|---|
| 485 | }, |
|---|
| 486 | ]] |
|---|
| 487 | { |
|---|
| 488 | names={'disconnect','dis'}, |
|---|
| 489 | help='disconnect from device', |
|---|
| 490 | func=function(self,args) |
|---|
| 491 | return con:disconnect() |
|---|
| 492 | end, |
|---|
| 493 | }, |
|---|
| 494 | { |
|---|
| 495 | names={'ls'}, |
|---|
| 496 | help='list files/directories on camera', |
|---|
| 497 | arghelp="[-l] [path]", |
|---|
| 498 | func=function(self,args) |
|---|
| 499 | local opts,listops |
|---|
| 500 | opts,args=cli:get_opts(args,{'l'}) |
|---|
| 501 | local path=cli:get_string_arg(args) |
|---|
| 502 | path = cli:make_camera_path(path) |
|---|
| 503 | if opts.l then |
|---|
| 504 | listopts = { stat='*' } |
|---|
| 505 | else |
|---|
| 506 | listopts = { stat='/' } |
|---|
| 507 | end |
|---|
| 508 | local list,msg = con:listdir(path,listopts) |
|---|
| 509 | if type(list) == 'table' then |
|---|
| 510 | local r = '' |
|---|
| 511 | if opts.l then |
|---|
| 512 | -- alphabetic sort TODO sorting/grouping options |
|---|
| 513 | chdku.sortdir_stat(list) |
|---|
| 514 | for i,st in ipairs(list) do |
|---|
| 515 | if st.is_dir then |
|---|
| 516 | r = r .. string.format("%s/\n",st.name) |
|---|
| 517 | else |
|---|
| 518 | r = r .. string.format("%-13s %10d\n",st.name,st.size) |
|---|
| 519 | end |
|---|
| 520 | end |
|---|
| 521 | else |
|---|
| 522 | table.sort(list) |
|---|
| 523 | for i,name in ipairs(list) do |
|---|
| 524 | r = r .. name .. '\n' |
|---|
| 525 | end |
|---|
| 526 | end |
|---|
| 527 | |
|---|
| 528 | return true,r |
|---|
| 529 | end |
|---|
| 530 | return false,msg |
|---|
| 531 | end, |
|---|
| 532 | }, |
|---|
| 533 | { |
|---|
| 534 | names={'reboot'}, |
|---|
| 535 | help='reboot the camera', |
|---|
| 536 | arghelp="[file]", |
|---|
| 537 | func=function(self,args) |
|---|
| 538 | local bootfile=cli:get_string_arg(args) |
|---|
| 539 | if bootfile then |
|---|
| 540 | bootfile = cli:make_camera_path(bootfile) |
|---|
| 541 | bootfile = string.format("'%s'",bootfile) |
|---|
| 542 | else |
|---|
| 543 | bootfile = '' |
|---|
| 544 | end |
|---|
| 545 | -- sleep and disconnect to avoid later connection problems on some cameras |
|---|
| 546 | -- clobber because we don't care about memory leaks |
|---|
| 547 | local status,err=con:exec('sleep(1000);reboot('..bootfile..')',{clobber=true}) |
|---|
| 548 | if not status then |
|---|
| 549 | return false,err |
|---|
| 550 | end |
|---|
| 551 | con:disconnect() |
|---|
| 552 | -- sleep locally to avoid clobbering the reboot, and allow time for the camera to come up before trying to connect |
|---|
| 553 | sys.sleep(3000) |
|---|
| 554 | |
|---|
| 555 | return con:connect() |
|---|
| 556 | end, |
|---|
| 557 | }, |
|---|
| 558 | }; |
|---|
| 559 | |
|---|
| 560 | return cli; |
|---|