mirror of
https://github.com/Doodle3D/doodle3d-firmware.git
synced 2024-12-22 11:03:48 +01:00
Merge branch 'develop'
This commit is contained in:
commit
349210a3fc
@ -1,3 +1,15 @@
|
|||||||
|
Changelog
|
||||||
|
# 0.10.2 (14th mar 2014)
|
||||||
|
- Fixed connection issues to networks with multiple routers sharing the same ssid
|
||||||
|
- Option to update to beta releases (Beta testers are welcome)
|
||||||
|
- Substituted wifiboxid retrievable through info api
|
||||||
|
- Added a "connecting" printer state where it found a printer but couldn't communicate yet. (Not yet implemented for Makerbot's)
|
||||||
|
- When connecting takes to long, we display a warning that the user might have selected the wrong printer type (Not yet implemented for Makerbot's)
|
||||||
|
- API's printer/state doesn't return an error anymore when a printer is just connected
|
||||||
|
- Allowing WiFi channels 12 & 13
|
||||||
|
- Fixed issue that control access wasn't properly reset after print
|
||||||
|
- Fixed another issue where the box in access point wouldn't give ip addresses
|
||||||
|
|
||||||
# 0.10.1 (12th feb 2014)
|
# 0.10.1 (12th feb 2014)
|
||||||
- miniFactory support
|
- miniFactory support
|
||||||
- Fixed most Makerbot display issues
|
- Fixed most Makerbot display issues
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
alias d='ls -la --color=auto'
|
|
||||||
alias wopkg='/usr/share/lua/wifibox/opkg.conf'
|
|
@ -1,188 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
# NOTE: this script generates an index based on images found in the target directory.
|
|
||||||
# So make sure it contains all images ever released (unless you want to actually remove them).
|
|
||||||
# If this is not the case, first create a mirror of doodle3d.com/updates/images.
|
|
||||||
|
|
||||||
#prevent being run as root (which is dangerous)
|
|
||||||
if [ "$(id -u)" == "0" ]; then
|
|
||||||
echo "Don't run this script as root, it is potentially dangerous." 1>&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
# expects path of file to return the size of
|
|
||||||
fileSize() {
|
|
||||||
stat -f %z ${1}
|
|
||||||
}
|
|
||||||
|
|
||||||
# expects arguments: version, devType, sysupgrade|factory
|
|
||||||
# image name will be: '${IMAGE_BASENAME}-<version>-<devType>-<sysupgrade|factory>.bin'
|
|
||||||
constructImageName() {
|
|
||||||
if [ $# -lt 3 ]; then echo "incorrect usage of constructImageName()"; exit 1; fi
|
|
||||||
echo "${IMAGE_BASENAME}-${1}-${2}-${3}.bin"
|
|
||||||
}
|
|
||||||
|
|
||||||
# expects arguments: basePath (where the files are), version, devType
|
|
||||||
generateIndexEntry() {
|
|
||||||
if [ $# -lt 3 ]; then echo "incorrect usage of generateIndexEntry()"; exit 1; fi
|
|
||||||
|
|
||||||
sysupgrade_out_basename=`constructImageName ${2} ${3} sysupgrade`
|
|
||||||
factory_out_basename=`constructImageName ${2} ${3} factory`
|
|
||||||
sysupgrade_out_file=${1}/${sysupgrade_out_basename}
|
|
||||||
factory_out_file=${1}/${factory_out_basename}
|
|
||||||
|
|
||||||
sysupgrade_filesize=`fileSize ${sysupgrade_out_file}`
|
|
||||||
factory_filesize=`fileSize ${factory_out_file}`
|
|
||||||
sysupgrade_md5sum=`md5 -q ${sysupgrade_out_file}`
|
|
||||||
factory_md5sum=`md5 -q ${factory_out_file}`
|
|
||||||
echo "Version: ${2}" >> $IMG_INDEX_FILE
|
|
||||||
echo "Files: ${sysupgrade_out_basename}; ${factory_out_basename}" >> $IMG_INDEX_FILE
|
|
||||||
echo "FileSize: ${sysupgrade_filesize}; ${factory_filesize}" >> $IMG_INDEX_FILE
|
|
||||||
echo "MD5: ${sysupgrade_md5sum}; ${factory_md5sum}" >> $IMG_INDEX_FILE
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
OPENWRT_BASE=.
|
|
||||||
PKG_SRC_DIR=$OPENWRT_BASE/bin/ar71xx/packages
|
|
||||||
PKG_DEST_SUBPATH=updates
|
|
||||||
MAKE_INDEX_SCRIPT=$OPENWRT_BASE/scripts/ipkg-make-index.sh
|
|
||||||
INDEX_FILE=Packages
|
|
||||||
INDEX_GZ_FILE=Packages.gz
|
|
||||||
DEVICE_TYPES="tl-mr3020 tl-wr703"
|
|
||||||
MAX_GOOD_IMAGE_SIZE=3500000
|
|
||||||
IMAGE_BASENAME="doodle3d-wifibox"
|
|
||||||
|
|
||||||
COMPRESS_RESULT=0
|
|
||||||
PKG_DEST_BASE=.
|
|
||||||
|
|
||||||
|
|
||||||
for arg in "$@"; do
|
|
||||||
case $arg in
|
|
||||||
-h)
|
|
||||||
echo "This script creates a directory with wifibox and ultifi ipk files found in the openWrt build environment."
|
|
||||||
echo "The feed dir is called $PKG_DEST_DIR and will be created in the current directory. (currently `pwd`)"
|
|
||||||
echo "If specified, the -z option also compresses the result for easier transfer to a webserver."
|
|
||||||
exit
|
|
||||||
;;
|
|
||||||
-z)
|
|
||||||
COMPRESS_RESULT=1
|
|
||||||
;;
|
|
||||||
-*)
|
|
||||||
echo "Unrecognized option '$arg'"
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
PKG_DEST_BASE=$arg
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
grep "^This is the buildsystem for the OpenWrt Linux distribution\.$" README >/dev/null 2>&1
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
echo "Please run this script from the Openwrt build root (on OSX this is probably an image mounted under /Volumes)."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
#determine the wifibox root path
|
|
||||||
my_rel_dir=`dirname $0`
|
|
||||||
pushd "$my_rel_dir" > /dev/null
|
|
||||||
WIFIBOX_DIR="`pwd`/../.."
|
|
||||||
popd > /dev/null
|
|
||||||
|
|
||||||
FW_VERSION=`cat $WIFIBOX_DIR/src/FIRMWARE-VERSION`
|
|
||||||
echo "Compiling firmware update files for version ${FW_VERSION}"
|
|
||||||
|
|
||||||
|
|
||||||
#setup paths
|
|
||||||
PKG_DEST_DIR=$PKG_DEST_BASE/$PKG_DEST_SUBPATH
|
|
||||||
PKG_FEED_DIR=$PKG_DEST_DIR/feed
|
|
||||||
PKG_IMG_DIR=$PKG_DEST_DIR/images
|
|
||||||
IMG_INDEX_FILE=$PKG_IMG_DIR/wifibox-image.index
|
|
||||||
|
|
||||||
if [ ! -d $PKG_DEST_DIR ]; then mkdir -p $PKG_DEST_DIR; fi
|
|
||||||
echo "Using $PKG_DEST_DIR as target directory"
|
|
||||||
|
|
||||||
|
|
||||||
#clear directory and copy package files
|
|
||||||
if [ ! -d $PKG_FEED_DIR ]; then mkdir $PKG_FEED_DIR; fi
|
|
||||||
cp $PKG_SRC_DIR/wifibox*.ipk $PKG_FEED_DIR
|
|
||||||
cp $PKG_SRC_DIR/doodle3d-client*.ipk $PKG_FEED_DIR
|
|
||||||
cp $PKG_SRC_DIR/print3d*.ipk $PKG_FEED_DIR
|
|
||||||
cp $PKG_SRC_DIR/ultifi*.ipk $PKG_FEED_DIR
|
|
||||||
rm -f $PKG_FEED_DIR/$INDEX_FILE
|
|
||||||
rm -f $PKG_FEED_DIR/$INDEX_GZ_FILE
|
|
||||||
|
|
||||||
|
|
||||||
#copy and rename images
|
|
||||||
if [ ! -d $PKG_IMG_DIR ]; then mkdir $PKG_IMG_DIR; fi
|
|
||||||
rm -f $IMG_INDEX_FILE
|
|
||||||
for devtype in $DEVICE_TYPES; do
|
|
||||||
IMG_SRC_PATH=$OPENWRT_BASE/bin/ar71xx
|
|
||||||
if [ -f $IMG_SRC_PATH/openwrt-ar71xx-generic-${devtype}-v1-squashfs-sysupgrade.bin ]; then
|
|
||||||
sysupgrade_name=$IMG_SRC_PATH/openwrt-ar71xx-generic-${devtype}-v1-squashfs-sysupgrade.bin
|
|
||||||
factory_name=$IMG_SRC_PATH/openwrt-ar71xx-generic-${devtype}-v1-squashfs-factory.bin
|
|
||||||
sysupgrade_size=`fileSize $sysupgrade_name`
|
|
||||||
factory_size=`fileSize $factory_name`
|
|
||||||
|
|
||||||
echo "Copying images for device '${devtype}' (sysupgrade size: ${sysupgrade_size}, factory size: ${factory_size})"
|
|
||||||
cp $sysupgrade_name $PKG_IMG_DIR/`constructImageName ${FW_VERSION} ${devtype} sysupgrade`
|
|
||||||
cp $factory_name $PKG_IMG_DIR/`constructImageName ${FW_VERSION} ${devtype} factory`
|
|
||||||
|
|
||||||
if [ $sysupgrade_size -gt $MAX_GOOD_IMAGE_SIZE ]; then
|
|
||||||
echo "WARNING: the sysupgrade image is larger than $MAX_GOOD_IMAGE_SIZE bytes, which probably means it will cause read/write problems when flashed to a device"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
|
|
||||||
# ok this is ugly, but at least it generates a complete index (the loop assumes for each
|
|
||||||
# sysupgrade image it finds, there is also a factory counterpart)
|
|
||||||
for file in $PKG_IMG_DIR/${IMAGE_BASENAME}-*-sysupgrade.bin; do
|
|
||||||
basefile=`basename ${file}`
|
|
||||||
echo "Considering $basefile (${file})"
|
|
||||||
# sorry for the shell magic
|
|
||||||
devtype=${basefile:`expr ${#IMAGE_BASENAME} + 1`}
|
|
||||||
version=${devtype//-*}
|
|
||||||
devtype=${devtype#*-}
|
|
||||||
devtype=${devtype%-*}
|
|
||||||
generateIndexEntry $PKG_IMG_DIR $version $devtype >> $IMG_INDEX_FILE
|
|
||||||
done
|
|
||||||
|
|
||||||
|
|
||||||
# NOTE: the aliasing construct in the indexing script does not work (and even then, the md5 command defaults to a different output format), so we hack around it here.
|
|
||||||
MD5_HACK_ENABLED=0
|
|
||||||
which md5sum >/dev/null 2>&1
|
|
||||||
if [ $? -eq 1 ]; then
|
|
||||||
MD5_HACK_ENABLED=1
|
|
||||||
TEMPBIN_DIR=/tmp/tempbin23QQDBR
|
|
||||||
mkdir $TEMPBIN_DIR
|
|
||||||
|
|
||||||
cat <<EOF > $TEMPBIN_DIR/md5sum
|
|
||||||
`type -p md5` -q \$1
|
|
||||||
EOF
|
|
||||||
|
|
||||||
chmod +x $TEMPBIN_DIR/md5sum
|
|
||||||
PATH=$PATH:$TEMPBIN_DIR
|
|
||||||
fi
|
|
||||||
|
|
||||||
#this cwd juggling is required to have the package indexer generate correct paths (i.e. no paths) in the Packages file
|
|
||||||
OPENWRT_DIR=`pwd`
|
|
||||||
pushd $PKG_FEED_DIR > /dev/null
|
|
||||||
$OPENWRT_DIR/$MAKE_INDEX_SCRIPT . > $PKG_FEED_DIR/$INDEX_FILE
|
|
||||||
popd > /dev/null
|
|
||||||
|
|
||||||
if [ $MD5_HACK_ENABLED -eq 1 ]; then
|
|
||||||
rm $TEMPBIN_DIR/md5sum
|
|
||||||
rmdir $TEMPBIN_DIR
|
|
||||||
fi
|
|
||||||
|
|
||||||
gzip -c $PKG_FEED_DIR/$INDEX_FILE > $PKG_FEED_DIR/$INDEX_GZ_FILE
|
|
||||||
|
|
||||||
|
|
||||||
if [ $COMPRESS_RESULT -eq 1 ]; then
|
|
||||||
cd $PKG_DEST_BASE
|
|
||||||
echo "Compressing generated package directory..."
|
|
||||||
tar czvf "doodle3d-wifibox-update-dist.tgz" $PKG_DEST_SUBPATH
|
|
||||||
fi
|
|
526
extra/scripts/publish-wifibox-release.lua
Executable file
526
extra/scripts/publish-wifibox-release.lua
Executable file
@ -0,0 +1,526 @@
|
|||||||
|
#!/usr/bin/env lua
|
||||||
|
--#!/usr/bin/env lua -l strict
|
||||||
|
|
||||||
|
-- This script creates a new release by copying openwrt image files and release notes to a local
|
||||||
|
-- directory and updating the relevant index file with a new entry. This directory is
|
||||||
|
-- then synchronized to the release repository online.
|
||||||
|
--
|
||||||
|
-- USAGE:
|
||||||
|
-- The only dependency of this script is the penlight library, which can be installed using
|
||||||
|
-- LuaRocks (http://luarocks.org/) as follows: 'sudo luarocks install penlight'.
|
||||||
|
-- This script will automatically locate the Doodle3D repo's.
|
||||||
|
-- Index files are fetched from the online repository.
|
||||||
|
-- For synchronizing, rsync must have passwordless SSH access to the server, for a
|
||||||
|
-- guide, see: http://www.linuxproblem.org/art_9.html.
|
||||||
|
-- The scrips expects a alias named 'doodle3d.com', you can add this editing the following file:
|
||||||
|
-- .ssh/config
|
||||||
|
-- and adding
|
||||||
|
-- Host doodle3d.com
|
||||||
|
-- User webmaster@doodle3d.com
|
||||||
|
-- HostName ftp.greenhost.nl
|
||||||
|
-- Some basic sanity checks are built in (unique version, updated release notes, 'clean' openwrt config)
|
||||||
|
-- but lots others are still missing (mainly: clean git repo's, freshly built images).
|
||||||
|
-- The script must be run from within the openwrt build root. So it's handy to create a symlink
|
||||||
|
-- to this file. You could to something like from the build root:
|
||||||
|
-- ln -s ~/wrt-wifibox-feed/doodle3d-firmware/extra/scripts/publish-wifibox-release.lua .
|
||||||
|
-- Then you can start with:
|
||||||
|
-- cd trunk ../publish-wifibox-release.lua
|
||||||
|
-- Before anything is actually uploaded, you will be asked if that's really what you want to do.
|
||||||
|
-- It might be wise to make a backup on the server before updating it, there's a script
|
||||||
|
-- to do this on the server: '~/backup-updates-dir.sh'.
|
||||||
|
--
|
||||||
|
-- To play around with or improve on this script, use and modify the variables 'SERVER_HOST'
|
||||||
|
-- and 'SERVER_PATH' below to point to your machine (assuming you have a webserver running there).
|
||||||
|
-- Also uncomment and modify UPDATER_BASE_URL. You will have to init the local 'repo' with at
|
||||||
|
-- least empty index files ('wifibox-image.index' and 'wifibox-image.beta.index'), or you
|
||||||
|
-- could of course mirror the online repository.
|
||||||
|
--
|
||||||
|
-- TODO (in random order):
|
||||||
|
-- * (feature) command-line arguments: overrides, verbosity, allow local mirroring, clear local cache dir, etc.
|
||||||
|
-- * (feature) automatically create a backup of the online repo (there's already a script fir this, as mentioned above)
|
||||||
|
-- * (feature) check whether git repo's are clean and on correct branch
|
||||||
|
-- * (feature) allow local mirroring with a reverse rsync command and rebuilding the indexes
|
||||||
|
-- - update manager 'cache' should then be enabled to prevent fetchIndexTable from downloading files
|
||||||
|
-- * (feature) automatically (re)build openwrt to ensure it is up to date?
|
||||||
|
-- * (feature) update package feed (requires a local mirror for the feed indexing script)
|
||||||
|
-- - in this case sanity checks must also be run on package versions/revisions
|
||||||
|
-- * (feature) automatically tag (and merge?) git commits?
|
||||||
|
-- * (feature) execute as dry-run by default so changes can be reviewed?
|
||||||
|
-- * (refactor) rename awkward vars/funcs regarding entries, versions and caches...
|
||||||
|
-- * (refactor) replace function arguments 'includeBetas' with a set function like setUseCache to improve readability
|
||||||
|
-- * (refactor) replace prints with D() function from update manager or other slightly smarter mechanisms?
|
||||||
|
|
||||||
|
local function ERR(msg) print(msg) end
|
||||||
|
|
||||||
|
local ok, pl = pcall(require, 'pl.import_into')
|
||||||
|
if not ok then
|
||||||
|
ERR('This script requires the Penlight library')
|
||||||
|
os.exit(2)
|
||||||
|
end
|
||||||
|
pl = pl()
|
||||||
|
local um --- update manager module, will be loaded later through @{loadUpdateManager}
|
||||||
|
|
||||||
|
local lfs = require('lfs') -- assume this exists since it's required by penlight as well
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------
|
||||||
|
-- CONSTANTS AND VARIABLES --
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
--local SERVER_HOST = 'localhost'
|
||||||
|
--local SERVER_PATH = '~USERDIR/public_html/wifibox/updates'
|
||||||
|
--local UPDATER_BASE_URL = 'http://localhost/~USERDIR/wifibox/updates'
|
||||||
|
local SERVER_HOST = 'doodle3d.com'
|
||||||
|
local SERVER_PATH = 'doodle3d.com/DEFAULT/updates'
|
||||||
|
--- SERVER_HOST and SERVER_PATH are used by rsync to merge the local working directory
|
||||||
|
-- back into the online repository (requires functioning public key SSH access).
|
||||||
|
-- UPDATER_BASE_URL is used by the d3d-updater script to download the index files
|
||||||
|
-- (over HTTP), it defaults to the doodle3d.com online repo so it should only be
|
||||||
|
-- used for development purposes.
|
||||||
|
|
||||||
|
local D3D_REPO_FIRMWARE_NAME = 'doodle3d-firmware'
|
||||||
|
local D3D_REPO_CLIENT_NAME = 'doodle3d-client'
|
||||||
|
local D3D_REPO_PRINT3D_NAME = 'print3d'
|
||||||
|
local IMAGE_BASENAME = 'doodle3d-wifibox'
|
||||||
|
local BACKUP_FILE_SUFFIX = 'bkp'
|
||||||
|
local RELEASE_NOTES_FILE = "ReleaseNotes.md"
|
||||||
|
local RSYNC_TIMEOUT = 2
|
||||||
|
local MAX_VIABLE_IMAGE_SIZE = 3500000
|
||||||
|
|
||||||
|
local deviceType = 'tl-mr3020' -- or 'tl-wr703'
|
||||||
|
local lock = nil
|
||||||
|
local paths = {}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------
|
||||||
|
-- UTILITY FUNCTIONS --
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
local function loadUpdateManager()
|
||||||
|
package.path = package.path .. ';' .. pl.path.join(paths.firmware, 'src') .. '/?.lua'
|
||||||
|
local argStash = arg
|
||||||
|
arg = nil
|
||||||
|
um = require('script.d3d-updater') -- arg must be nil for the update manager to load as module
|
||||||
|
arg = argStash
|
||||||
|
end
|
||||||
|
|
||||||
|
local function quit(ev)
|
||||||
|
if lock then lock:free() end
|
||||||
|
os.exit(ev or 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function md5sum(file)
|
||||||
|
local rv,_,sum = pl.utils.executeex('md5 -q "' .. file .. '"')
|
||||||
|
|
||||||
|
return rv and sum:sub(1, -2) or nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local function getYesNo(question)
|
||||||
|
local answer
|
||||||
|
repeat
|
||||||
|
io.write(question)
|
||||||
|
io.flush()
|
||||||
|
answer = io.stdin:read('*line'):lower()
|
||||||
|
until answer == 'yes' or answer == 'y' or answer == 'no' or answer == 'n'
|
||||||
|
|
||||||
|
return (answer:sub(1, 1) == 'y') and true or false
|
||||||
|
end
|
||||||
|
|
||||||
|
local function detectRootPrivileges()
|
||||||
|
local rv,_,userId = pl.utils.executeex('id -u')
|
||||||
|
if not rv then return nil end
|
||||||
|
|
||||||
|
return tonumber(userId) == 0 and true or false
|
||||||
|
end
|
||||||
|
|
||||||
|
local function findInFile(needle, file)
|
||||||
|
local f = io.open(file, 'r')
|
||||||
|
if not f then return nil,"could not open file" end
|
||||||
|
|
||||||
|
local t = f:read('*all')
|
||||||
|
return not not t:find(needle, 1, true)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function detectOpenWrtRoot()
|
||||||
|
local f = io.open('Makefile', 'r')
|
||||||
|
local line = f and f:read('*line')
|
||||||
|
local rv = (line and line:find('# Makefile for OpenWrt') == 1) and true or false
|
||||||
|
|
||||||
|
if f then f:close() end
|
||||||
|
return rv
|
||||||
|
end
|
||||||
|
|
||||||
|
-- returns uri (file path) of the wifibox feed, nil if not found or nil+msg on error
|
||||||
|
-- recognized feed names are 'wifibox' and 'doodle3d' (case-insensitive)
|
||||||
|
local function getWifiboxFeedRoot(feedsFile)
|
||||||
|
local typ, nam, uri = nil, nil, nil
|
||||||
|
local lineNo = 1
|
||||||
|
local f = io.open(feedsFile, 'r')
|
||||||
|
|
||||||
|
if not f then return nil, "could not open '" .. feedsFile .. '"' end
|
||||||
|
|
||||||
|
for line in f:lines() do
|
||||||
|
typ, nam, uri = line:match('^([^%s]+)%s+([^%s]+)%s+([^%s]+)$')
|
||||||
|
|
||||||
|
if not (typ and nam and uri) then
|
||||||
|
f:close()
|
||||||
|
return uri or nil, "could not parse line " .. feedsFile .. "#" .. lineNo
|
||||||
|
end
|
||||||
|
|
||||||
|
local commented = (typ:find('#') == 1)
|
||||||
|
if not commented and (nam:lower() == 'wifibox' or nam:lower() == 'doodle3d') then
|
||||||
|
break
|
||||||
|
else
|
||||||
|
typ, nam, uri = nil, nil, nil
|
||||||
|
end
|
||||||
|
|
||||||
|
lineNo = lineNo + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
if uri and not (typ == 'src-link' or typ == 'src-cpy') then return nil, "d3d feed has wrong type '" .. typ .. "', use 'src-link' or 'src-cpy'" end
|
||||||
|
|
||||||
|
f:close()
|
||||||
|
return uri
|
||||||
|
end
|
||||||
|
|
||||||
|
-- TODO: pass table to functions to fill in? if they all return either true or nil+msg, that could be used for display of ok/msg
|
||||||
|
-- returns true on success, false on error, and displays meaningful messages
|
||||||
|
--local function runCheck(msg, processFunc)
|
||||||
|
-- io.write(msg .. "... ")
|
||||||
|
-- return processFunc(--[[ hmm ]]--)
|
||||||
|
--end
|
||||||
|
|
||||||
|
local function runAction(actMsg, errMsg, ev, func)
|
||||||
|
io.write("* " .. actMsg .. "...")
|
||||||
|
local rv,err = func()
|
||||||
|
if not rv then
|
||||||
|
if err then print("Error: " .. errMsg .. " (" .. err .. ")")
|
||||||
|
else print("Error: " .. errMsg)
|
||||||
|
end
|
||||||
|
quit(ev)
|
||||||
|
else
|
||||||
|
print("ok")
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function constructImageName(version, devType, sysupOrFactory)
|
||||||
|
return IMAGE_BASENAME .. '-' .. um.formatVersion(version) .. '-' .. devType .. '-' .. sysupOrFactory .. '.bin'
|
||||||
|
end
|
||||||
|
|
||||||
|
local function imageCachePath()
|
||||||
|
return pl.path.join(paths.cache, 'images')
|
||||||
|
end
|
||||||
|
|
||||||
|
local function ensureFilePresent(src, tgt)
|
||||||
|
-- print("About to copy '" .. src .. "' => '" .. tgt .. "'")
|
||||||
|
local srcMd5, tgtMd5 = md5sum(src), md5sum(tgt)
|
||||||
|
|
||||||
|
if not srcMd5 then return nil,"source file does not exist" end
|
||||||
|
if tgtMd5 and srcMd5 ~= tgtMd5 then return nil,"target file already exists but is different from source file" end
|
||||||
|
|
||||||
|
if not tgtMd5 then
|
||||||
|
if not pl.file.copy(src, tgt, false) then return nil,"could not copy file" end
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--------------------
|
||||||
|
-- MAIN FUNCTIONS --
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
local function prepare()
|
||||||
|
local msg = nil
|
||||||
|
|
||||||
|
io.write("* Checking if working directory is the OpenWrt root... ")
|
||||||
|
local isOpenWrtRoot = detectOpenWrtRoot()
|
||||||
|
if isOpenWrtRoot then
|
||||||
|
paths.wrt = pl.path.currentdir()
|
||||||
|
print("found " .. paths.wrt)
|
||||||
|
else
|
||||||
|
print("unrecognized directory, try changing directories or using -wrt-root")
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
io.write("* Looking for Doodle3D feed path... ")
|
||||||
|
local d3dFeed,msg = getWifiboxFeedRoot('feeds.conf')
|
||||||
|
if d3dFeed then
|
||||||
|
print("found " .. d3dFeed)
|
||||||
|
else
|
||||||
|
if msg then print("not found: " .. msg) else print("not found.") end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
paths.firmware = pl.path.join(d3dFeed, D3D_REPO_FIRMWARE_NAME)
|
||||||
|
paths.client = pl.path.join(d3dFeed, D3D_REPO_CLIENT_NAME)
|
||||||
|
paths.print3d = pl.path.join(d3dFeed, D3D_REPO_PRINT3D_NAME)
|
||||||
|
|
||||||
|
-- if empty, try to choose something sensible
|
||||||
|
if not paths.cache or paths.cache == '' then
|
||||||
|
--paths.cache = pl.app.appfile('')
|
||||||
|
paths.cache = '/tmp/d3d-release-dir'
|
||||||
|
end
|
||||||
|
io.write("* Attempting to use " .. paths.cache .. " as cache dir... ")
|
||||||
|
local rv,msg = pl.dir.makepath(paths.cache)
|
||||||
|
if not rv then
|
||||||
|
print("could not create path (" .. msg .. ").")
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
loadUpdateManager()
|
||||||
|
|
||||||
|
local rv,msg = pl.dir.makepath(imageCachePath())
|
||||||
|
if not rv then
|
||||||
|
print("could not create images dir (" .. msg .. ").")
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
lock,msg = lfs.lock_dir(paths.cache)
|
||||||
|
if not lock then
|
||||||
|
print("could not obtain directory lock (" .. msg .. ").")
|
||||||
|
return nil
|
||||||
|
else
|
||||||
|
print("ok")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- initialize update manager script
|
||||||
|
um.setUseCache(false)
|
||||||
|
um.setVerbosity(1)
|
||||||
|
um.setCachePath(imageCachePath())
|
||||||
|
if type(UPDATER_BASE_URL) == 'string' and UPDATER_BASE_URL:len() > 0 then
|
||||||
|
print("* Using updater base URL: '" .. UPDATER_BASE_URL .. "'")
|
||||||
|
um.setBaseUrl(UPDATER_BASE_URL)
|
||||||
|
else
|
||||||
|
print("* Using updater base URL: d3d-updater default")
|
||||||
|
end
|
||||||
|
|
||||||
|
print("* Using rsync server remote: '" .. SERVER_HOST .. "/" .. SERVER_PATH .. "'")
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
local function collectLocalInfo()
|
||||||
|
local info = {}
|
||||||
|
|
||||||
|
-- temporary fields required for copying image files
|
||||||
|
info.factoryImgPath = pl.path.join(paths.wrt, 'bin/ar71xx/openwrt-ar71xx-generic-' .. deviceType .. '-v1-squashfs-factory.bin')
|
||||||
|
info.sysupgradeImgPath = pl.path.join(paths.wrt, 'bin/ar71xx/openwrt-ar71xx-generic-' .. deviceType .. '-v1-squashfs-sysupgrade.bin')
|
||||||
|
|
||||||
|
info.version = um.parseVersion(pl.file.read(pl.path.join(paths.firmware, 'src/FIRMWARE-VERSION')))
|
||||||
|
if not info.version then return nil,"could not determine current firmware version" end
|
||||||
|
|
||||||
|
info.factoryFileSize = pl.path.getsize(info.factoryImgPath)
|
||||||
|
if not info.factoryFileSize then return nil,"could not determine size for factory image" end
|
||||||
|
|
||||||
|
info.sysupgradeFileSize = pl.path.getsize(info.sysupgradeImgPath)
|
||||||
|
if not info.sysupgradeFileSize then return nil,"could not determine size for sysupgrade image" end
|
||||||
|
|
||||||
|
info.factoryMD5 = md5sum(info.factoryImgPath)
|
||||||
|
info.sysupgradeMD5 = md5sum(info.sysupgradeImgPath)
|
||||||
|
if not info.factoryMD5 or not info.sysupgradeMD5 then return nil,"could not determine MD5 sum for image(s)" end
|
||||||
|
|
||||||
|
info.factoryFilename = constructImageName(info.version, deviceType, 'factory')
|
||||||
|
info.sysupgradeFilename = constructImageName(info.version, deviceType, 'sysupgrade')
|
||||||
|
info.timestamp = os.time()
|
||||||
|
|
||||||
|
return info
|
||||||
|
end
|
||||||
|
|
||||||
|
local function fetchVersionInfo()
|
||||||
|
local msg,stables,betas = nil,nil,nil
|
||||||
|
|
||||||
|
stables,msg = um.getAvailableVersions('stables')
|
||||||
|
if not stables then return nil,msg end
|
||||||
|
|
||||||
|
betas,msg = um.getAvailableVersions('betas')
|
||||||
|
if not betas then return nil,msg end
|
||||||
|
|
||||||
|
return stables, betas
|
||||||
|
end
|
||||||
|
|
||||||
|
local function generateIndex(newVersion, versionTable, isStable)
|
||||||
|
local indexFilename = isStable and um.IMAGE_STABLE_INDEX_FILE or um.IMAGE_BETA_INDEX_FILE
|
||||||
|
versionTable[#versionTable+1] = newVersion
|
||||||
|
local sortedVers = pl.List(versionTable)
|
||||||
|
sortedVers:sort(function(a, b)
|
||||||
|
return um.compareVersions(a.version, b.version, a.timestamp, b.timestamp) < 0
|
||||||
|
end)
|
||||||
|
|
||||||
|
local indexPath = pl.path.join(imageCachePath(), indexFilename)
|
||||||
|
local rv = pl.file.copy(indexPath, pl.path.join(paths.cache, indexFilename..'.'..BACKUP_FILE_SUFFIX))
|
||||||
|
if not rv then return nil,"could not backup index file" end
|
||||||
|
|
||||||
|
local idxFile = io.open(pl.path.join(imageCachePath(), indexFilename), 'w')
|
||||||
|
if not idxFile then return nil,"could not open index file for writing" end
|
||||||
|
|
||||||
|
sortedVers:foreach(function(el)
|
||||||
|
idxFile:write("Version: " .. um.formatVersion(el.version) .. "\n")
|
||||||
|
idxFile:write("Files: " .. el.sysupgradeFilename .. "; " .. el.factoryFilename .. "\n")
|
||||||
|
idxFile:write("FileSize: " .. el.sysupgradeFileSize .. "; " .. el.factoryFileSize .. "\n")
|
||||||
|
idxFile:write("MD5: " .. el.sysupgradeMD5 .. "; " .. el.factoryMD5 .. "\n")
|
||||||
|
if el.timestamp then idxFile:write("ReleaseDate: " .. um.formatDate(el.timestamp) .. "\n") end
|
||||||
|
end)
|
||||||
|
|
||||||
|
idxFile:close()
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
local function copyImages(newVersion)
|
||||||
|
local rv,msg
|
||||||
|
rv,msg = ensureFilePresent(newVersion.factoryImgPath, pl.path.join(imageCachePath(), newVersion.factoryFilename))
|
||||||
|
if not rv then return nil,msg end
|
||||||
|
|
||||||
|
rv,msg = ensureFilePresent(newVersion.sysupgradeImgPath, pl.path.join(imageCachePath(), newVersion.sysupgradeFilename))
|
||||||
|
if not rv then return nil,msg end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
local function copyReleaseNotes(newVersion)
|
||||||
|
local srcReleaseNotesPath = pl.path.join(paths.firmware, RELEASE_NOTES_FILE)
|
||||||
|
local tgtReleaseNotesPath = pl.path.join(imageCachePath(), RELEASE_NOTES_FILE)
|
||||||
|
|
||||||
|
if not findInFile(um.formatVersion(newVersion.version), srcReleaseNotesPath) then
|
||||||
|
return nil,"version not mentioned in release notes file"
|
||||||
|
end
|
||||||
|
|
||||||
|
if pl.path.isfile(tgtReleaseNotesPath) then
|
||||||
|
local rv = pl.file.copy(tgtReleaseNotesPath, tgtReleaseNotesPath..'.'..BACKUP_FILE_SUFFIX)
|
||||||
|
if not rv then return nil,"could not backup file" end
|
||||||
|
end
|
||||||
|
|
||||||
|
local rv = pl.file.copy(srcReleaseNotesPath, tgtReleaseNotesPath)
|
||||||
|
if not rv then return nil,"could not copy file" end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- TODO: the packages are not really used and the openwrt script to generate the
|
||||||
|
-- package index requires all packages to be present so this has been skipped for now
|
||||||
|
local function buildFeedDir()
|
||||||
|
local scriptPath = pl.path.join(paths.wrt, 'scripts/ipkg-make-index.sh')
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local function uploadFiles()
|
||||||
|
local serverUrl = SERVER_HOST..':'..SERVER_PATH
|
||||||
|
-- rsync options are: recursive, preserve perms, symlinks and timestamps, be verbose and use compression
|
||||||
|
local cmd = "rsync -rpltvz -e ssh --progress --timeout=" .. RSYNC_TIMEOUT .. " --exclude '*.bkp' --exclude 'lockfile.lfs' " .. paths.cache .. "/* " .. serverUrl
|
||||||
|
print("Running command: '" .. cmd .. "'")
|
||||||
|
local rv,ev = um.compatexecute(cmd)
|
||||||
|
return rv and true or nil,("rsync failed, exit status: " .. ev)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function checkWrtConfig()
|
||||||
|
local goodConfigPath = pl.path.join(paths.firmware, "extra/openwrt-build/openwrt-diffconfig-extramini")
|
||||||
|
local wrtConfigPath = pl.path.tmpname()
|
||||||
|
--print("diffonfig output file: " .. wrtConfigPath)
|
||||||
|
|
||||||
|
local rv,ev = pl.utils.execute('./scripts/diffconfig.sh > "' .. wrtConfigPath .. '" 2> /dev/null')
|
||||||
|
if not rv then return nil,"could not run diffconfig script (exit status: " .. ev .. ")" end
|
||||||
|
|
||||||
|
local _,rv,output = pl.utils.executeex('diff "' .. wrtConfigPath .. '" "' .. goodConfigPath .. '"')
|
||||||
|
|
||||||
|
if rv == 0 then
|
||||||
|
return true
|
||||||
|
elseif rv == 1 then
|
||||||
|
print("configurations differ:\n-----------------------\n" .. output .. "\n-----------------------")
|
||||||
|
--ask for confirmation?
|
||||||
|
else
|
||||||
|
return nil,"unexpected exit status from diff (" .. rv .. ")"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function main()
|
||||||
|
print("\nDoodle3D release script")
|
||||||
|
if detectRootPrivileges() then
|
||||||
|
print("Error: refusing to run script as root.")
|
||||||
|
quit(99)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- local opts = parseOptions(arg)
|
||||||
|
--
|
||||||
|
-- if opts['wrt-root'] then changedir(opts['wrt-root']) end
|
||||||
|
-- if opts['cache-dir'] then paths.cache = opts['cache-dir'] end
|
||||||
|
-- more options: clear cache, rebuild (download all and generate index from actual files), dry-run, force
|
||||||
|
|
||||||
|
if not prepare() then quit(1) end
|
||||||
|
|
||||||
|
|
||||||
|
local newVersion,msg = collectLocalInfo()
|
||||||
|
if not newVersion then
|
||||||
|
print("Error: could not collect local version information (" .. msg .. ")")
|
||||||
|
quit(3)
|
||||||
|
end
|
||||||
|
|
||||||
|
local stables,betas = fetchVersionInfo()
|
||||||
|
if not stables then
|
||||||
|
print("Error: could not get version information (" .. betas .. ")")
|
||||||
|
quit(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
--TODO: if requested, fetch images and packages (i.e., mirror whole directory)
|
||||||
|
|
||||||
|
|
||||||
|
-- pl.pretty.dump(newVersion)
|
||||||
|
-- print("stables: "); pl.pretty.dump(stables)
|
||||||
|
-- print("===========================");
|
||||||
|
-- print("betas: "); pl.pretty.dump(betas)
|
||||||
|
|
||||||
|
|
||||||
|
print("\nRunning sanity checks")
|
||||||
|
|
||||||
|
runAction("Checking whether version is unique",
|
||||||
|
"firmware version " .. um.formatVersion(newVersion.version) .. " already exists", 3, function()
|
||||||
|
return not (um.findVersion(newVersion.version, nil, stables) or um.findVersion(newVersion.version, nil, betas)) and true or nil
|
||||||
|
end)
|
||||||
|
|
||||||
|
runAction("Checking OpenWrt config", "failed", 3, checkWrtConfig)
|
||||||
|
|
||||||
|
--TODO: check git repos (`git log -n 1 --pretty=format:%ct` gives commit date of last commit (not author date))
|
||||||
|
|
||||||
|
local isStable = (newVersion.version.suffix == nil)
|
||||||
|
print("\nRolling release for firmware version " .. um.formatVersion(newVersion.version) .. " (type: " .. (isStable and "stable" or "beta") .. ").")
|
||||||
|
|
||||||
|
if newVersion.sysupgradeFileSize > MAX_VIABLE_IMAGE_SIZE then
|
||||||
|
print("Error: sysupgrade image file is too large, it will not run well (max. size: " .. MAX_VIABLE_IMAGE_SIZE .. " bytes)")
|
||||||
|
quit(4)
|
||||||
|
end
|
||||||
|
|
||||||
|
runAction("Copying release notes", "failed", 5, function()
|
||||||
|
return copyReleaseNotes(newVersion)
|
||||||
|
end)
|
||||||
|
|
||||||
|
runAction("Generating new index file", "could not generate index", 5, function()
|
||||||
|
return generateIndex(newVersion, isStable and stables or betas, isStable)
|
||||||
|
end)
|
||||||
|
|
||||||
|
runAction("Copying image files", "could not generate index", 5, function()
|
||||||
|
return copyImages(newVersion)
|
||||||
|
end)
|
||||||
|
|
||||||
|
io.write("* Building package feed directory...")
|
||||||
|
print("skipped - not implemented")
|
||||||
|
-- runAction("Building package feed directory", "failed", 5, buildFeedDir)
|
||||||
|
|
||||||
|
|
||||||
|
local answer = getYesNo("? Local updates cache will be synced to remote server, proceed? (y/n) ")
|
||||||
|
if answer ~= true then
|
||||||
|
print("Did not get green light, quitting.")
|
||||||
|
quit(5)
|
||||||
|
end
|
||||||
|
|
||||||
|
runAction("About to sync files to server", "could not upload files", 6, uploadFiles)
|
||||||
|
|
||||||
|
print("Done.")
|
||||||
|
quit()
|
||||||
|
end
|
||||||
|
|
||||||
|
main()
|
@ -1,54 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
# This script copies the packages feed & images directory to ~/Sites (e.g. for use with XAMPP or the like).
|
|
||||||
# Using the -u option, it can also upload to doodle3d.com/updates (make sure ssh automatically uses the correct username, or change the rsync command below).
|
|
||||||
# Modify WIFIBOX_BASE_DIR to point to your wifibox directory tree.
|
|
||||||
|
|
||||||
WIFIBOX_BASE_DIR=~/Files/_devel/eclipse-workspace/wifibox
|
|
||||||
DEST_DIR=~/Sites/wifibox
|
|
||||||
UPDATES_DIR=updates
|
|
||||||
BASE_URL=doodle3d.com
|
|
||||||
|
|
||||||
OPTIONS=$DEST_DIR
|
|
||||||
UPLOAD_FILES=0
|
|
||||||
|
|
||||||
for arg in "$@"; do
|
|
||||||
case $arg in
|
|
||||||
-h)
|
|
||||||
echo "This script calls 'create-wifibox-updates-dir.sh' to generate feed/image directories in $DEST_DIR"
|
|
||||||
echo "Use '-z' to also create a compressed file containing the 'updates' directory."
|
|
||||||
echo "Used '-u' to also upload the directory to doodle3d.com/updates"
|
|
||||||
exit
|
|
||||||
;;
|
|
||||||
-z)
|
|
||||||
OPTIONS="$OPTIONS -z"
|
|
||||||
;;
|
|
||||||
-u)
|
|
||||||
UPLOAD_FILES=1
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "Unrecognized option '$arg'"
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
$WIFIBOX_BASE_DIR/extra/scripts/create-wifibox-updates-dir.sh $OPTIONS
|
|
||||||
RETURN_VALUE=$?
|
|
||||||
if [ $RETURN_VALUE -ne 0 ]; then
|
|
||||||
echo "create-wifibox-updates-dir.sh returned an error (${RETURN_VALUE}), exiting"
|
|
||||||
exit $RETURN_VALUE
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ $UPLOAD_FILES -eq 1 ]; then
|
|
||||||
#UPLOAD_PATH=$BASE_URL:public_html/updates
|
|
||||||
UPLOAD_PATH=$BASE_URL:doodle3d.com/DEFAULT/updates
|
|
||||||
|
|
||||||
cat <<-'EOM' > $DEST_DIR/$UPDATES_DIR/.htaccess
|
|
||||||
Options +Indexes
|
|
||||||
EOM
|
|
||||||
|
|
||||||
echo "Uploading files to $UPLOAD_PATH (if you are asked for your password, please add an entry to your ~/.ssh/config and upload your public ssh key)"
|
|
||||||
#options are: recursive, preserve perms, symlinks and timestamps, be verbose and use compression
|
|
||||||
rsync -rpltvz -e ssh --progress $DEST_DIR/$UPDATES_DIR/.htaccess $DEST_DIR/$UPDATES_DIR/* $UPLOAD_PATH
|
|
||||||
fi
|
|
@ -77,15 +77,20 @@ $IPKG_INSTROOT/etc/init.d/wifibox start
|
|||||||
$IPKG_INSTROOT/etc/init.d/dhcpcheck enable
|
$IPKG_INSTROOT/etc/init.d/dhcpcheck enable
|
||||||
|
|
||||||
if [ -z "$IPKG_INSTROOT" ]; then
|
if [ -z "$IPKG_INSTROOT" ]; then
|
||||||
echo "Enabling wifi device..."
|
echo "Enabling and configuring wifi device..."
|
||||||
uci set wireless.@wifi-device[0].disabled=0
|
uci set wireless.@wifi-device[0].disabled=0
|
||||||
uci set wireless.radio0.country='NL'
|
uci set wireless.radio0.country='NL'
|
||||||
uci commit wireless; wifi
|
uci commit wireless; wifi
|
||||||
|
|
||||||
|
echo "Disabling default route and DNS server for lan network interface..."
|
||||||
|
uci set dhcp.lan.dhcp_option='3 6'
|
||||||
|
uci commit dhcp; /etc/init.d/dnsmasq reload
|
||||||
|
|
||||||
addFirewallNet
|
addFirewallNet
|
||||||
|
|
||||||
echo "Adding network interface 'wlan'..."
|
echo "Adding network interface 'wlan'..."
|
||||||
uci set network.wlan=interface; uci commit network; /etc/init.d/network reload
|
uci set network.wlan=interface
|
||||||
|
uci commit network; /etc/init.d/network reload
|
||||||
|
|
||||||
else
|
else
|
||||||
# Create a script to setup the system as wifibox, it will be deleted after it has been run, except if it returns > 0
|
# Create a script to setup the system as wifibox, it will be deleted after it has been run, except if it returns > 0
|
||||||
@ -102,6 +107,8 @@ else
|
|||||||
uci set wireless.radio0.country='NL'
|
uci set wireless.radio0.country='NL'
|
||||||
# TODO: add firewall net
|
# TODO: add firewall net
|
||||||
uci set network.wlan=interface
|
uci set network.wlan=interface
|
||||||
|
|
||||||
|
uci set dhcp.lan.dhcp_option='3 6'
|
||||||
EOM
|
EOM
|
||||||
|
|
||||||
echo "WARNING: WiFiBox network configuration can only be fully prepared when installing on real device"
|
echo "WARNING: WiFiBox network configuration can only be fully prepared when installing on real device"
|
||||||
|
@ -1 +1 @@
|
|||||||
0.10.1
|
0.10.2
|
@ -19,7 +19,7 @@
|
|||||||
-- - _description_: A descriptive text usable by API clients
|
-- - _description_: A descriptive text usable by API clients
|
||||||
-- - _min_, _max_, _regex_: optional constraints (min and max constrain value for numbers, or length for strings)
|
-- - _min_, _max_, _regex_: optional constraints (min and max constrain value for numbers, or length for strings)
|
||||||
-- - _isValid_: an optional function which should return true for valid values and false for invalid ones
|
-- - _isValid_: an optional function which should return true for valid values and false for invalid ones
|
||||||
-- - _subSection: optional: setting name of which current value is used as the uci section where this setting should be loaded from. Otherwise it's retrieved from the generic section. Setting subsection also means it will first try to get a default from subconf_defaults, if that doesn't exsist it will use the regular default
|
-- - _subSection: optional: setting name of which current value is used as the uci section where this setting should be loaded from. Otherwise it's retrieved from the generic section. Setting subsection also means it will first try to get a default from subconf_defaults, if that doesn't exsist it will use the regular default
|
||||||
-- The configuration keys themselves document themselves rather well, hence they are not included in the generated documentation.
|
-- The configuration keys themselves document themselves rather well, hence they are not included in the generated documentation.
|
||||||
--
|
--
|
||||||
-- NOTE: the all-caps definitions should be changed into configuration keys, or moved to a better location.
|
-- NOTE: the all-caps definitions should be changed into configuration keys, or moved to a better location.
|
||||||
@ -106,7 +106,7 @@ M.printer_dimensions_x = {
|
|||||||
default_delta_rostockmax = 0,
|
default_delta_rostockmax = 0,
|
||||||
default_deltamaker = 0,
|
default_deltamaker = 0,
|
||||||
default_kossel = 0,
|
default_kossel = 0,
|
||||||
default_minifactory = 150,
|
default_minifactory = 150,
|
||||||
subSection = 'printer_type',
|
subSection = 'printer_type',
|
||||||
type = 'int',
|
type = 'int',
|
||||||
description = '',
|
description = '',
|
||||||
@ -335,4 +335,16 @@ M.doodle3d_tour_enabled = {
|
|||||||
description = 'Show tour to new users'
|
description = 'Show tour to new users'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
M.doodle3d_update_includeBetas = {
|
||||||
|
default = false,
|
||||||
|
type = 'bool',
|
||||||
|
description = 'Include beta releases when updating'
|
||||||
|
}
|
||||||
|
|
||||||
|
M.doodle3d_update_baseUrl = {
|
||||||
|
default = 'http://doodle3d.com/updates',
|
||||||
|
type = 'string',
|
||||||
|
description = ''
|
||||||
|
}
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
@ -87,14 +87,14 @@ local function setupAutoWifiMode()
|
|||||||
end
|
end
|
||||||
|
|
||||||
if connectWith then
|
if connectWith then
|
||||||
local rv,msg = netconf.associateSsid(connectWith,nil,nil,true)
|
local rv,msg = netconf.associateSsid(connectWith,nil,nil)
|
||||||
if rv then
|
if rv then
|
||||||
return true, "autowifi: associated -- client mode with ssid '" .. connectWith .. "'"
|
return true, "autowifi: associated -- client mode with ssid '" .. connectWith .. "'"
|
||||||
else
|
else
|
||||||
return nil, "autowifi: could not associate with ssid '" .. connectWith .. "' (" .. msg .. ")"
|
return nil, "autowifi: could not associate with ssid '" .. connectWith .. "' (" .. msg .. ")"
|
||||||
end
|
end
|
||||||
elseif netMode ~= 'ap' or netName ~= apSsid then
|
elseif netMode ~= 'ap' or netName ~= apSsid then
|
||||||
local rv,msg = netconf.setupAccessPoint(apSsid,true)
|
local rv,msg = netconf.setupAccessPoint(apSsid)
|
||||||
if rv then
|
if rv then
|
||||||
return true, "autowifi: configured as access point with ssid '" .. apSsid .. "'"
|
return true, "autowifi: configured as access point with ssid '" .. apSsid .. "'"
|
||||||
else
|
else
|
||||||
|
@ -54,10 +54,9 @@ end
|
|||||||
|
|
||||||
--- Switch configuration between AP and station modes
|
--- Switch configuration between AP and station modes
|
||||||
-- @param table components a table with components as keys with operations as values (add or remove)
|
-- @param table components a table with components as keys with operations as values (add or remove)
|
||||||
-- @param boolean boot If true, the components have to start instead of reloaded (only needed on boot)
|
|
||||||
-- Valid components (each with add and rm operation) are: apnet, staticaddr, dhcppool, wwwredir, dnsredir, wwwcaptive, natreflect.
|
-- Valid components (each with add and rm operation) are: apnet, staticaddr, dhcppool, wwwredir, dnsredir, wwwcaptive, natreflect.
|
||||||
-- and additionally: wifiiface/add, network/reload
|
-- and additionally: wifiiface/add, network/reload
|
||||||
function M.switchConfiguration(components,boot)
|
function M.switchConfiguration(components)
|
||||||
local dirtyList = {} -- laundry list, add config/script name as key with value c (commit), r (reload) or b (both)
|
local dirtyList = {} -- laundry list, add config/script name as key with value c (commit), r (reload) or b (both)
|
||||||
|
|
||||||
for k,v in pairs(components) do
|
for k,v in pairs(components) do
|
||||||
@ -75,7 +74,7 @@ function M.switchConfiguration(components,boot)
|
|||||||
if v == 'c' or v == 'b' then M.commitComponent(k) end
|
if v == 'c' or v == 'b' then M.commitComponent(k) end
|
||||||
end
|
end
|
||||||
for k,v in pairs(dirtyList) do
|
for k,v in pairs(dirtyList) do
|
||||||
if v == 'r' or v == 'b' then M.reloadComponent(k, silent, boot) end
|
if v == 'r' or v == 'b' then M.reloadComponent(k, silent) end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -84,11 +83,9 @@ function M.commitComponent(c)
|
|||||||
uci:commit(c)
|
uci:commit(c)
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.reloadComponent(c, silent, boot)
|
function M.reloadComponent(c, silent)
|
||||||
log:info("reloading component '" .. c .. "'")
|
log:info("reloading component '" .. c .. "'")
|
||||||
local command = 'reload'
|
local command = 'reload'
|
||||||
-- if booting, the services have to be started before they can be reloaded
|
|
||||||
if boot then command = 'start' end
|
|
||||||
local cmd = '/etc/init.d/' .. c .. ' '..command
|
local cmd = '/etc/init.d/' .. c .. ' '..command
|
||||||
if silent ~= nil and silent then
|
if silent ~= nil and silent then
|
||||||
cmd = cmd .. ' &> /dev/null'
|
cmd = cmd .. ' &> /dev/null'
|
||||||
@ -275,20 +272,16 @@ end
|
|||||||
-- Note: this function might belong in the wlanconfig module but that would introduce
|
-- Note: this function might belong in the wlanconfig module but that would introduce
|
||||||
-- a circular dependency, easiest solution is to place the function here.
|
-- a circular dependency, easiest solution is to place the function here.
|
||||||
-- @tparam string ssid The SSID to use for the access point.
|
-- @tparam string ssid The SSID to use for the access point.
|
||||||
-- @tparam boolean boot If true, the components have to start instead of reloaded (only needed on boot)
|
|
||||||
-- @return True on success or nil+msg on error.
|
-- @return True on success or nil+msg on error.
|
||||||
function M.setupAccessPoint(ssid,boot)
|
function M.setupAccessPoint(ssid)
|
||||||
M.setStatus(M.CREATING,"Creating access point '"..ssid.."'...");
|
M.setStatus(M.CREATING,"Creating access point '"..ssid.."'...");
|
||||||
boot = boot or false
|
|
||||||
--boot = false
|
|
||||||
if boot then log:info(" boot mode") end
|
|
||||||
|
|
||||||
-- add access point configuration
|
-- add access point configuration
|
||||||
M.switchConfiguration({apnet="add_noreload"},boot)
|
M.switchConfiguration({apnet="add_noreload"})
|
||||||
wifi.activateConfig(ssid)
|
wifi.activateConfig(ssid)
|
||||||
-- NOTE: dnsmasq must be reloaded after network or it will be unable to serve IP addresses
|
-- NOTE: dnsmasq must be reloaded after network or it will be unable to serve IP addresses
|
||||||
M.switchConfiguration({ wifiiface="add", network="reload", staticaddr="add", dhcppool="add_noreload", wwwredir="add", dnsredir="add" },boot)
|
M.switchConfiguration({ wifiiface="add", network="reload", staticaddr="add", dhcppool="add_noreload", wwwredir="add", dnsredir="add" })
|
||||||
M.switchConfiguration({dhcp="reload"},boot)
|
M.switchConfiguration({dhcp="reload"})
|
||||||
|
|
||||||
M.setStatus(M.CREATED,"Access point created");
|
M.setStatus(M.CREATED,"Access point created");
|
||||||
|
|
||||||
@ -335,11 +328,9 @@ end
|
|||||||
-- @tparam string ssid The SSID to associate with.
|
-- @tparam string ssid The SSID to associate with.
|
||||||
-- @tparam string passphrase The passphrase (if any) to use, may be left out if a UCI configuration exists.
|
-- @tparam string passphrase The passphrase (if any) to use, may be left out if a UCI configuration exists.
|
||||||
-- @tparam boolean recreate If true, a new UCI configuration based on scan data will always be created, otherwise an attempt will be made to use an existing configuration.
|
-- @tparam boolean recreate If true, a new UCI configuration based on scan data will always be created, otherwise an attempt will be made to use an existing configuration.
|
||||||
-- @tparam boolean boot If true, the components have to start instead of reloaded (only needed on boot)
|
|
||||||
-- @return True on success or nil+msg on error.
|
-- @return True on success or nil+msg on error.
|
||||||
function M.associateSsid(ssid, passphrase, recreate, boot)
|
function M.associateSsid(ssid, passphrase, recreate)
|
||||||
log:info("netconfig:associateSsid: "..(ssid or "<nil>")..", "..(recreate or "<nil>"))
|
log:info("netconfig:associateSsid: "..(ssid or "<nil>")..", "..(recreate or "<nil>"))
|
||||||
if boot then log:info(" boot mode") end
|
|
||||||
M.setStatus(M.CONNECTING,"Connecting...");
|
M.setStatus(M.CONNECTING,"Connecting...");
|
||||||
|
|
||||||
-- see if previously configured network for given ssid exists
|
-- see if previously configured network for given ssid exists
|
||||||
@ -369,17 +360,32 @@ function M.associateSsid(ssid, passphrase, recreate, boot)
|
|||||||
--M.switchConfiguration{ wifiiface="add", apnet="rm", staticaddr="rm", dhcppool="rm", wwwredir="rm", dnsredir="rm", wwwcaptive="rm", wireless="reload" }
|
--M.switchConfiguration{ wifiiface="add", apnet="rm", staticaddr="rm", dhcppool="rm", wwwredir="rm", dnsredir="rm", wwwcaptive="rm", wireless="reload" }
|
||||||
--M.switchConfiguration{ wifiiface="add", apnet="rm", staticaddr="rm", dhcppool="rm", wwwredir="rm", dnsredir="rm", wireless="reload" }
|
--M.switchConfiguration{ wifiiface="add", apnet="rm", staticaddr="rm", dhcppool="rm", wwwredir="rm", dnsredir="rm", wireless="reload" }
|
||||||
--M.switchConfiguration{ wifiiface="add", staticaddr="rm", dhcppool="rm", wwwredir="rm", dnsredir="rm", wireless="reload" }
|
--M.switchConfiguration{ wifiiface="add", staticaddr="rm", dhcppool="rm", wwwredir="rm", dnsredir="rm", wireless="reload" }
|
||||||
M.switchConfiguration({ wifiiface="add", staticaddr="rm", dhcppool="rm", wwwredir="rm", dnsredir="rm" },boot)
|
M.switchConfiguration({ wifiiface="add", staticaddr="rm", dhcppool="rm", wwwredir="rm", dnsredir="rm" })
|
||||||
|
|
||||||
-- check if we are actually associated
|
|
||||||
local status = wifi.getDeviceState()
|
|
||||||
if not status.ssid or status.ssid ~= ssid then
|
|
||||||
local msg = "Could not associate with network (incorrect password?)"
|
|
||||||
M.setStatus(M.CONNECTING_FAILED,msg);
|
|
||||||
return nil,msg
|
|
||||||
end
|
|
||||||
|
|
||||||
M.setStatus(M.CONNECTED,"Connected");
|
-- we check if we get a ssid and ip in max 5 seconds
|
||||||
|
-- if not there is probably a issue
|
||||||
|
local attemptInterval = 1
|
||||||
|
local maxAttempts = 5
|
||||||
|
local attempt = 0
|
||||||
|
local nextAttemptTime = os.time()
|
||||||
|
while true do
|
||||||
|
if os.time() > nextAttemptTime then
|
||||||
|
log:debug("associated check "..utils.dump(attempt).."/"..utils.dump(maxAttempts))
|
||||||
|
if wifi.getLocalIP() ~= nil and wifi.getDeviceState().ssid == ssid then
|
||||||
|
break
|
||||||
|
else
|
||||||
|
attempt = attempt+1
|
||||||
|
if attempt >= maxAttempts then
|
||||||
|
-- still no correct ssid; fail
|
||||||
|
local msg = "Could not associate with network (incorrect password?)"
|
||||||
|
M.setStatus(M.CONNECTING_FAILED,msg);
|
||||||
|
return false, msg
|
||||||
|
else
|
||||||
|
nextAttemptTime = os.time() + attemptInterval
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- signin to connect.doodle3d.com
|
-- signin to connect.doodle3d.com
|
||||||
local success, output = signin.signin()
|
local success, output = signin.signin()
|
||||||
@ -388,7 +394,10 @@ function M.associateSsid(ssid, passphrase, recreate, boot)
|
|||||||
else
|
else
|
||||||
log:info("Signing in failed")
|
log:info("Signing in failed")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- report we are connected after signin attempt
|
||||||
|
M.setStatus(M.CONNECTED,"Connected");
|
||||||
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
--- Disassociate wlan device as client from all SSID's.
|
--- Disassociate wlan device as client from all SSID's.
|
||||||
|
@ -174,7 +174,6 @@ function M.removeConfig(ssid)
|
|||||||
if s.ssid == ssid then
|
if s.ssid == ssid then
|
||||||
uci:delete('wireless', s['.name'])
|
uci:delete('wireless', s['.name'])
|
||||||
rv = true
|
rv = true
|
||||||
return false
|
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
uci:commit('wireless')
|
uci:commit('wireless')
|
||||||
@ -229,7 +228,7 @@ function M.createConfigFromScanInfo(info, passphrase, disabled)
|
|||||||
network = M.NET,
|
network = M.NET,
|
||||||
device = 'radio0',
|
device = 'radio0',
|
||||||
ssid = info.ssid,
|
ssid = info.ssid,
|
||||||
bssid = info.bssid,
|
--bssid = info.bssid,
|
||||||
encryption = M.mapEncryptionType(info.encryption),
|
encryption = M.mapEncryptionType(info.encryption),
|
||||||
mode = mode,
|
mode = mode,
|
||||||
}
|
}
|
||||||
@ -237,8 +236,9 @@ function M.createConfigFromScanInfo(info, passphrase, disabled)
|
|||||||
apconfig.disabled = disabled ~= nil and disabled and 1 or 0
|
apconfig.disabled = disabled ~= nil and disabled and 1 or 0
|
||||||
|
|
||||||
uci:foreach('wireless', 'wifi-iface', function(s)
|
uci:foreach('wireless', 'wifi-iface', function(s)
|
||||||
if s.bssid == info.bssid then
|
--if s.bssid == info.bssid then
|
||||||
log:debug("removing old wireless config for net '" .. s.ssid .. "(bssid: " .. s.bssid .. ")'")
|
if s.ssid == info.ssid then
|
||||||
|
log:debug("removing old wireless config for net '" .. s.ssid .. "'")
|
||||||
uci:delete('wireless', s['.name'])
|
uci:delete('wireless', s['.name'])
|
||||||
-- return false --keep looking, just in case multiple entries with this bssid exist
|
-- return false --keep looking, just in case multiple entries with this bssid exist
|
||||||
end
|
end
|
||||||
|
@ -15,12 +15,10 @@ local wifi = require('network.wlanconfig')
|
|||||||
local accessManager = require('util.access')
|
local accessManager = require('util.access')
|
||||||
local printerAPI = require('rest.api.api_printer')
|
local printerAPI = require('rest.api.api_printer')
|
||||||
|
|
||||||
|
|
||||||
local M = {
|
local M = {
|
||||||
isApi = true
|
isApi = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
-- TODO: this function is also defined in 2 other places, combine them (and avoid require loops)
|
-- TODO: this function is also defined in 2 other places, combine them (and avoid require loops)
|
||||||
local function operationsAccessOrFail(request, response)
|
local function operationsAccessOrFail(request, response)
|
||||||
if not accessManager.hasControl(request.remoteAddress) then
|
if not accessManager.hasControl(request.remoteAddress) then
|
||||||
@ -84,6 +82,9 @@ function M._global_POST(request, response)
|
|||||||
|
|
||||||
local substitutedSsid = wifi.getSubstitutedSsid(settings.get('network.ap.ssid'))
|
local substitutedSsid = wifi.getSubstitutedSsid(settings.get('network.ap.ssid'))
|
||||||
response:addData("substituted_ssid",substitutedSsid)
|
response:addData("substituted_ssid",substitutedSsid)
|
||||||
|
|
||||||
|
local substitutedWiFiBoxID = wifi.getSubstitutedSsid(settings.get('network.cl.wifiboxid'))
|
||||||
|
response:addData("substituted_wifiboxid",substitutedWiFiBoxID)
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.all_GET(request, response)
|
function M.all_GET(request, response)
|
||||||
|
@ -14,6 +14,7 @@ local printDriver = require('print3d')
|
|||||||
local printerUtils = require('util.printer')
|
local printerUtils = require('util.printer')
|
||||||
local printerAPI = require('rest.api.api_printer')
|
local printerAPI = require('rest.api.api_printer')
|
||||||
local wifi = require('network.wlanconfig')
|
local wifi = require('network.wlanconfig')
|
||||||
|
local settings = require('util.settings')
|
||||||
|
|
||||||
local TMP_DIR = '/tmp'
|
local TMP_DIR = '/tmp'
|
||||||
local LOG_COLLECT_DIRNAME = 'wifibox-logs'
|
local LOG_COLLECT_DIRNAME = 'wifibox-logs'
|
||||||
@ -48,6 +49,10 @@ local M = {
|
|||||||
|
|
||||||
function M._global(request, response)
|
function M._global(request, response)
|
||||||
response:setSuccess()
|
response:setSuccess()
|
||||||
|
|
||||||
|
local wifiboxid = wifi.getSubstitutedSsid(settings.get('network.cl.wifiboxid'))
|
||||||
|
response:addData('wifiboxid', wifiboxid)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- TODO: redirect stdout+stderr; handle errors
|
-- TODO: redirect stdout+stderr; handle errors
|
||||||
@ -144,26 +149,10 @@ function M.access(request, response)
|
|||||||
--log:info(" remoteAddress: |"..utils.dump(request.remoteAddress).."|");
|
--log:info(" remoteAddress: |"..utils.dump(request.remoteAddress).."|");
|
||||||
--log:info(" controller: |"..utils.dump(accessManager.getController()).."|");
|
--log:info(" controller: |"..utils.dump(accessManager.getController()).."|");
|
||||||
|
|
||||||
-- when there is a controller we check if the printer is idle,
|
|
||||||
-- if so, it should be done printing and we can clear the controller
|
|
||||||
if accessManager.getController() ~= "" then
|
|
||||||
local argId = request:get("id")
|
|
||||||
local printer,msg = printerUtils.createPrinterOrFail(argId, response)
|
|
||||||
local rv,msg = printer:getState()
|
|
||||||
if rv then
|
|
||||||
response:setSuccess()
|
|
||||||
if(state == "idle") then -- TODO: define in constants somewhere
|
|
||||||
accessManager.setController("") -- clear controller
|
|
||||||
end
|
|
||||||
else
|
|
||||||
response:setError(msg)
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local hasControl = accessManager.hasControl(request.remoteAddress)
|
local hasControl = accessManager.hasControl(request.remoteAddress)
|
||||||
|
-- if hasControl then log:info(" hasControl: true")
|
||||||
|
-- else log:info(" hasControl: false") end
|
||||||
response:setSuccess()
|
response:setSuccess()
|
||||||
|
|
||||||
response:addData('has_control', hasControl)
|
response:addData('has_control', hasControl)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
@ -171,14 +160,10 @@ end
|
|||||||
|
|
||||||
function M.status(request, response)
|
function M.status(request, response)
|
||||||
|
|
||||||
local ds = wifi.getDeviceState()
|
local rv, state = printerAPI.state(request, response)
|
||||||
log:debug(" ssid: "..utils.dump(ds.ssid))
|
|
||||||
|
|
||||||
local rv
|
|
||||||
rv, state = printerAPI.state(request, response)
|
|
||||||
if(rv == false) then return end
|
if(rv == false) then return end
|
||||||
|
|
||||||
if(state ~= "disconnected") then
|
if state ~= "disconnected" and state ~= "connecting" then
|
||||||
rv = printerAPI.temperature(request, response)
|
rv = printerAPI.temperature(request, response)
|
||||||
if(rv == false) then return end
|
if(rv == false) then return end
|
||||||
rv = printerAPI.progress(request, response)
|
rv = printerAPI.progress(request, response)
|
||||||
@ -186,7 +171,6 @@ function M.status(request, response)
|
|||||||
rv = M.access(request, response)
|
rv = M.access(request, response)
|
||||||
if(rv == false) then return end
|
if(rv == false) then return end
|
||||||
end
|
end
|
||||||
response:addData('v', 10)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
@ -147,7 +147,7 @@ function M.associate_POST(request, response)
|
|||||||
|
|
||||||
response:setSuccess("wlan is trying to associate")
|
response:setSuccess("wlan is trying to associate")
|
||||||
|
|
||||||
local rv,msg = netconf.associateSsid(argSsid, argPhrase, argRecreate,false)
|
local rv,msg = netconf.associateSsid(argSsid, argPhrase, argRecreate)
|
||||||
if rv then
|
if rv then
|
||||||
log:info("associated to wifi: "..utils.dump(argSsid))
|
log:info("associated to wifi: "..utils.dump(argSsid))
|
||||||
else
|
else
|
||||||
@ -215,10 +215,6 @@ end
|
|||||||
|
|
||||||
function M.alive(request, response)
|
function M.alive(request, response)
|
||||||
response:setSuccess("alive")
|
response:setSuccess("alive")
|
||||||
|
|
||||||
local ds = wifi.getDeviceState()
|
|
||||||
log:debug(" ssid: "..utils.dump(ds.ssid))
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
@ -27,7 +27,7 @@ end
|
|||||||
function M.temperature(request, response)
|
function M.temperature(request, response)
|
||||||
local argId = request:get("id")
|
local argId = request:get("id")
|
||||||
local printer,msg = printerUtils.createPrinterOrFail(argId, response)
|
local printer,msg = printerUtils.createPrinterOrFail(argId, response)
|
||||||
if not printer then return false end
|
if not printer or not printer:hasSocket() then return false end
|
||||||
|
|
||||||
local temperatures,msg = printer:getTemperatures()
|
local temperatures,msg = printer:getTemperatures()
|
||||||
|
|
||||||
@ -49,7 +49,7 @@ end
|
|||||||
function M.progress(request, response)
|
function M.progress(request, response)
|
||||||
local argId = request:get("id")
|
local argId = request:get("id")
|
||||||
local printer,msg = printerUtils.createPrinterOrFail(argId, response)
|
local printer,msg = printerUtils.createPrinterOrFail(argId, response)
|
||||||
if not printer then return false end
|
if not printer or not printer:hasSocket() then return false end
|
||||||
|
|
||||||
-- NOTE: despite their names, `currentLine` is still the error indicator and `bufferedLines` the message in such case.
|
-- NOTE: despite their names, `currentLine` is still the error indicator and `bufferedLines` the message in such case.
|
||||||
local currentLine,bufferedLines,totalLines = printer:getProgress()
|
local currentLine,bufferedLines,totalLines = printer:getProgress()
|
||||||
@ -81,6 +81,14 @@ function M.state(request, response, onlyReturnState)
|
|||||||
response:addData('state', printerState)
|
response:addData('state', printerState)
|
||||||
end
|
end
|
||||||
return true, printerState
|
return true, printerState
|
||||||
|
elseif not printer:hasSocket() then
|
||||||
|
-- while dev is present but no server is running yet, return 'fake' connecting state
|
||||||
|
local printerState = "connecting"
|
||||||
|
if not onlyReturnState then
|
||||||
|
response:setSuccess()
|
||||||
|
response:addData('state', printerState)
|
||||||
|
end
|
||||||
|
return true, printerState
|
||||||
else
|
else
|
||||||
local rv,msg = printer:getState()
|
local rv,msg = printer:getState()
|
||||||
if rv then
|
if rv then
|
||||||
@ -115,7 +123,7 @@ function M.heatup_POST(request, response)
|
|||||||
|
|
||||||
local argId = request:get("id")
|
local argId = request:get("id")
|
||||||
local printer,msg = printerUtils.createPrinterOrFail(argId, response)
|
local printer,msg = printerUtils.createPrinterOrFail(argId, response)
|
||||||
if not printer then return false end
|
if not printer or not printer:hasSocket() then return false end
|
||||||
|
|
||||||
local temperature = settings.get('printer.heatup.temperature')
|
local temperature = settings.get('printer.heatup.temperature')
|
||||||
local rv,msg = printer:heatup(temperature)
|
local rv,msg = printer:heatup(temperature)
|
||||||
@ -138,8 +146,8 @@ function M.stop_POST(request, response)
|
|||||||
local argId = request:get("id")
|
local argId = request:get("id")
|
||||||
local argGcode = request:get("gcode")
|
local argGcode = request:get("gcode")
|
||||||
local printer,msg = printerUtils.createPrinterOrFail(argId, response)
|
local printer,msg = printerUtils.createPrinterOrFail(argId, response)
|
||||||
if not printer then return end
|
if not printer or not printer:hasSocket() then return false end
|
||||||
|
|
||||||
if(argGcode == nil) then
|
if(argGcode == nil) then
|
||||||
argGcode = ""
|
argGcode = ""
|
||||||
end
|
end
|
||||||
@ -176,7 +184,7 @@ function M.print_POST(request, response)
|
|||||||
local argStart = utils.toboolean(request:get("start"))
|
local argStart = utils.toboolean(request:get("start"))
|
||||||
|
|
||||||
local printer,msg = printerUtils.createPrinterOrFail(argId, response)
|
local printer,msg = printerUtils.createPrinterOrFail(argId, response)
|
||||||
if not printer then return end
|
if not printer or not printer:hasSocket() then return false end
|
||||||
|
|
||||||
response:addData('id', argId)
|
response:addData('id', argId)
|
||||||
|
|
||||||
|
@ -47,11 +47,13 @@ end
|
|||||||
|
|
||||||
|
|
||||||
function M.status(request, response)
|
function M.status(request, response)
|
||||||
|
local includeBetas = settings.get('doodle3d.update.includeBetas')
|
||||||
|
local baseUrl = settings.get('doodle3d.update.baseUrl')
|
||||||
updater.setLogger(log)
|
updater.setLogger(log)
|
||||||
|
updater.setBaseUrl(baseUrl)
|
||||||
updater.setUseCache(false)
|
updater.setUseCache(false)
|
||||||
local success,status,msg = updater.getStatus()
|
local success,status,msg = updater.getStatus(includeBetas)
|
||||||
|
|
||||||
--response:addData('current_version', status.currentVersion)
|
|
||||||
response:addData('current_version', updater.formatVersion(status.currentVersion))
|
response:addData('current_version', updater.formatVersion(status.currentVersion))
|
||||||
|
|
||||||
response:addData('state_code', status.stateCode)
|
response:addData('state_code', status.stateCode)
|
||||||
@ -62,10 +64,12 @@ function M.status(request, response)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local canUpdate = updater.compareVersions(status.newestVersion, status.currentVersion) > 0
|
local canUpdate = updater.compareVersions(status.newestVersion, status.currentVersion, status.newestReleaseTimestamp, status.currentReleaseTimestamp) > 0
|
||||||
|
if (status.currentVersion.suffix ~= nil) and not includeBetas then canUpdate = true end -- always allow downgrade from beta to stable if !includeBetas
|
||||||
|
|
||||||
--response:addData('newest_version', status.newestVersion)
|
|
||||||
response:addData('newest_version', updater.formatVersion(status.newestVersion))
|
response:addData('newest_version', updater.formatVersion(status.newestVersion))
|
||||||
|
if status.currentReleaseTimestamp then response:addData('current_release_date', updater.formatDate(status.currentReleaseTimestamp)) end
|
||||||
|
if status.newestReleaseTimestamp then response:addData('newest_release_date', updater.formatDate(status.newestReleaseTimestamp)) end
|
||||||
response:addData('can_update', canUpdate)
|
response:addData('can_update', canUpdate)
|
||||||
|
|
||||||
if status.progress then response:addData('progress', status.progress) end
|
if status.progress then response:addData('progress', status.progress) end
|
||||||
@ -87,14 +91,17 @@ function M.download_POST(request, response)
|
|||||||
-- block access to prevent potential issues with printing (e.g. out of memory)
|
-- block access to prevent potential issues with printing (e.g. out of memory)
|
||||||
if not operationsAccessOrFail(request, response) then return end
|
if not operationsAccessOrFail(request, response) then return end
|
||||||
|
|
||||||
|
local includeBetas = settings.get('doodle3d.update.includeBetas')
|
||||||
|
local baseUrl = settings.get('doodle3d.update.baseUrl')
|
||||||
updater.setLogger(log)
|
updater.setLogger(log)
|
||||||
|
updater.setBaseUrl(baseUrl)
|
||||||
|
|
||||||
updater.setState(updater.STATE.DOWNLOADING,"")
|
updater.setState(updater.STATE.DOWNLOADING,"")
|
||||||
|
|
||||||
local vEnt, rv, msg
|
local vEnt, rv, msg
|
||||||
|
|
||||||
if not argVersion then
|
if not argVersion then
|
||||||
local success,status,msg = updater.getStatus()
|
local success,status,msg = updater.getStatus(includeBetas)
|
||||||
if not success then
|
if not success then
|
||||||
updater.setState(updater.STATE.DOWNLOAD_FAILED, msg)
|
updater.setState(updater.STATE.DOWNLOAD_FAILED, msg)
|
||||||
response:setFail(msg)
|
response:setFail(msg)
|
||||||
@ -124,7 +131,7 @@ function M.download_POST(request, response)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
vEnt,msg = updater.findVersion(argVersion)
|
vEnt,msg = updater.findVersion(argVersion, includeBetas)
|
||||||
if vEnt == nil then
|
if vEnt == nil then
|
||||||
updater.setState(updater.STATE.DOWNLOAD_FAILED, "error searching version index (" .. msg .. ")")
|
updater.setState(updater.STATE.DOWNLOAD_FAILED, "error searching version index (" .. msg .. ")")
|
||||||
response:setFail("error searching version index (" .. msg .. ")")
|
response:setFail("error searching version index (" .. msg .. ")")
|
||||||
@ -153,9 +160,12 @@ function M.install_POST(request, response)
|
|||||||
local argNoRetain = request:get("no_retain")
|
local argNoRetain = request:get("no_retain")
|
||||||
log:info("API:update/install (noRetain: "..utils.dump(argNoRetain)..")")
|
log:info("API:update/install (noRetain: "..utils.dump(argNoRetain)..")")
|
||||||
local noRetain = argNoRetain == 'true'
|
local noRetain = argNoRetain == 'true'
|
||||||
|
|
||||||
if not operationsAccessOrFail(request, response) then return end
|
if not operationsAccessOrFail(request, response) then return end
|
||||||
|
|
||||||
|
local includeBetas = settings.get('doodle3d.update.includeBetas')
|
||||||
|
local baseUrl = settings.get('doodle3d.update.baseUrl')
|
||||||
|
updater.setBaseUrl(baseUrl)
|
||||||
updater.setLogger(log)
|
updater.setLogger(log)
|
||||||
updater.setState(updater.STATE.INSTALLING,"")
|
updater.setState(updater.STATE.INSTALLING,"")
|
||||||
|
|
||||||
@ -163,7 +173,7 @@ function M.install_POST(request, response)
|
|||||||
--local rv,msg = netconf.enableAccessPoint(ssid)
|
--local rv,msg = netconf.enableAccessPoint(ssid)
|
||||||
|
|
||||||
if not argVersion then
|
if not argVersion then
|
||||||
local success,status,msg = updater.getStatus()
|
local success,status,msg = updater.getStatus(includeBetas)
|
||||||
if not success then
|
if not success then
|
||||||
updater.setState(updater.STATE.INSTALL_FAILED, msg)
|
updater.setState(updater.STATE.INSTALL_FAILED, msg)
|
||||||
response:setFail(msg)
|
response:setFail(msg)
|
||||||
@ -173,7 +183,7 @@ function M.install_POST(request, response)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
vEnt,msg = updater.findVersion(argVersion)
|
vEnt,msg = updater.findVersion(argVersion, includeBetas)
|
||||||
if vEnt == nil then
|
if vEnt == nil then
|
||||||
updater.setState(updater.STATE.INSTALL_FAILED, "error searching version index (" .. msg .. ")")
|
updater.setState(updater.STATE.INSTALL_FAILED, "error searching version index (" .. msg .. ")")
|
||||||
response:setFail("error searching version index (" .. msg .. ")")
|
response:setFail("error searching version index (" .. msg .. ")")
|
||||||
|
@ -10,12 +10,12 @@
|
|||||||
--- This script provides an interface to upgrade or downgrade the Doodle3D wifibox.
|
--- This script provides an interface to upgrade or downgrade the Doodle3D wifibox.
|
||||||
-- It can both be used as a standalone command-line tool and as a Lua library.
|
-- It can both be used as a standalone command-line tool and as a Lua library.
|
||||||
|
|
||||||
-- TODO/NOTES:
|
-- TODO/NOTES: (from old script)
|
||||||
-- add to status: validImage: none|<version> (can use checkValidImage for this)
|
-- add to status: validImage: none|<version> (can use checkValidImage for this)
|
||||||
-- any more TODO's across this file?
|
-- any more TODO's across this file?
|
||||||
-- max 1 image tegelijk (moet api doen), en rekening houden met printbuffer (printen blokkeren?)
|
-- max 1 image tegelijk (moet api doen), en rekening houden met printbuffer (printen blokkeren?)
|
||||||
|
|
||||||
-- MAYBE/LATER:
|
-- MAYBE/LATER: (from old script)
|
||||||
-- add API calls to retrieve a list of all versions with their info (i.e., the result of getAvailableVersions)
|
-- add API calls to retrieve a list of all versions with their info (i.e., the result of getAvailableVersions)
|
||||||
-- wget: add provision (in verbose mode?) to use '-v' instead of '-q' and disable output redirection
|
-- wget: add provision (in verbose mode?) to use '-v' instead of '-q' and disable output redirection
|
||||||
-- document index file format (Version first, then in any order: Files: sysup; factory, FileSize: sysup; factory, MD5: sysup; factory, ChangelogStart:, ..., ChangelogEnd:)
|
-- document index file format (Version first, then in any order: Files: sysup; factory, FileSize: sysup; factory, MD5: sysup; factory, ChangelogStart:, ..., ChangelogEnd:)
|
||||||
@ -44,27 +44,30 @@ M.STATE_NAMES = {
|
|||||||
[M.STATE.INSTALLING] = 'installing', [M.STATE.INSTALLED] = 'installed', [M.STATE.INSTALL_FAILED] = 'install_failed'
|
[M.STATE.INSTALLING] = 'installing', [M.STATE.INSTALLED] = 'installed', [M.STATE.INSTALL_FAILED] = 'install_failed'
|
||||||
}
|
}
|
||||||
|
|
||||||
--- The base URL to use for finding update files.
|
--- The default base URL to use for finding update files.
|
||||||
-- This URL will usually contain both an OpenWRT feed directory and an `images`-directory.
|
-- This URL will usually contain both an OpenWRT feed directory and an `images` directory.
|
||||||
-- This script uses only the latter, and expects to find the file @{IMAGE_INDEX_FILE} there.
|
-- This script uses only the latter, and expects to find the files @{IMAGE_STABLE_INDEX_FILE} and @{IMAGE_BETA_INDEX_FILE} there.
|
||||||
M.DEFAULT_BASE_URL = 'http://doodle3d.com/updates'
|
M.DEFAULT_BASE_URL = 'http://doodle3d.com/updates'
|
||||||
--M.DEFAULT_BASE_URL = 'http://localhost/~USERNAME/wifibox/updates'
|
|
||||||
|
|
||||||
--- The index file containing metadata on update images.
|
--- The index file containing metadata on stable update images.
|
||||||
M.IMAGE_INDEX_FILE = 'wifibox-image.index'
|
M.IMAGE_STABLE_INDEX_FILE = 'wifibox-image.index'
|
||||||
|
|
||||||
|
--- The index file containing metadata on beta update images.
|
||||||
|
M.IMAGE_BETA_INDEX_FILE = 'wifibox-image.beta.index'
|
||||||
|
|
||||||
--- Path to the updater cache.
|
--- Path to the updater cache.
|
||||||
M.CACHE_PATH = '/tmp/d3d-updater'
|
M.DEFAULT_CACHE_PATH = '/tmp/d3d-updater'
|
||||||
|
|
||||||
--- Name of the file to store current state in, this file resides in @{CACHE_PATH}.
|
--- Name of the file to store current state in, this file resides in @{cachePath}.
|
||||||
M.STATE_FILE = 'update-state'
|
M.STATE_FILE = 'update-state'
|
||||||
|
|
||||||
M.WGET_OPTIONS = "-q -t 1 -T 30"
|
M.WGET_OPTIONS = "-q -t 1 -T 30"
|
||||||
--M.WGET_OPTIONS = "-v -t 1 -T 30"
|
--M.WGET_OPTIONS = "-v -t 1 -T 30"
|
||||||
|
|
||||||
local verbosity = 0 -- set by parseCommandlineArguments()
|
local verbosity = 0 -- set by parseCommandlineArguments() or @{setVerbosity}
|
||||||
local log = nil -- wifibox API can use M.setLogger to enable this module to use its logger
|
local log = nil -- wifibox API can use M.setLogger to enable this module to use its logger
|
||||||
local useCache = true -- default, can be overwritten using M.setUseCache()
|
local useCache = false -- default, can be overwritten using @{setUseCache}
|
||||||
|
local cachePath = M.DEFAULT_CACHE_PATH -- default, can be change using @{setCachePath}
|
||||||
local baseUrl = M.DEFAULT_BASE_URL -- default, can be overwritten by M.setBaseUrl()
|
local baseUrl = M.DEFAULT_BASE_URL -- default, can be overwritten by M.setBaseUrl()
|
||||||
|
|
||||||
|
|
||||||
@ -101,7 +104,7 @@ local function E(msg)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Splits the return status from `os.execute`, which consists of two bytes.
|
--- Splits the return status from `os.execute` (only Lua <= 5.1), which consists of two bytes.
|
||||||
--
|
--
|
||||||
-- `os.execute` internally calls [system](http://linux.die.net/man/3/system),
|
-- `os.execute` internally calls [system](http://linux.die.net/man/3/system),
|
||||||
-- which usually returns the command exit status as high byte (see [WEXITSTATUS](http://linux.die.net/man/2/wait)).
|
-- which usually returns the command exit status as high byte (see [WEXITSTATUS](http://linux.die.net/man/2/wait)).
|
||||||
@ -120,11 +123,12 @@ end
|
|||||||
-- @number exitStatus An exit status from wget.
|
-- @number exitStatus An exit status from wget.
|
||||||
-- @treturn string|number Either the status followed by a description, or a message indicating the call was interrupted, or just the status if it was not recognized.
|
-- @treturn string|number Either the status followed by a description, or a message indicating the call was interrupted, or just the status if it was not recognized.
|
||||||
local function wgetStatusToString(exitStatus)
|
local function wgetStatusToString(exitStatus)
|
||||||
local wgetStatus,systemStatus = splitExitStatus(exitStatus)
|
-- local wgetStatus,systemStatus = splitExitStatus(exitStatus)
|
||||||
|
local wgetStatus = exitStatus
|
||||||
|
|
||||||
if systemStatus ~= 0 then
|
-- if systemStatus ~= 0 then
|
||||||
return "interrupted:" .. systemStatus
|
-- return "interrupted: " .. systemStatus
|
||||||
end
|
-- end
|
||||||
|
|
||||||
-- adapted from man(1) wget on OSX
|
-- adapted from man(1) wget on OSX
|
||||||
local statusTexts = {
|
local statusTexts = {
|
||||||
@ -149,8 +153,9 @@ end
|
|||||||
-- @return bool|nil True, or nil on error.
|
-- @return bool|nil True, or nil on error.
|
||||||
-- @return ?string A message in case of error.
|
-- @return ?string A message in case of error.
|
||||||
local function createCacheDirectory()
|
local function createCacheDirectory()
|
||||||
if os.execute('mkdir -p ' .. M.CACHE_PATH) ~= 0 then
|
local _,rv = M.compatexecute('mkdir -p ' .. cachePath)
|
||||||
return nil,"Error: could not create cache directory '" .. M.CACHE_PATH .. "'"
|
if rv ~= 0 then
|
||||||
|
return nil,"Error: could not create cache directory '" .. cachePath .. "'"
|
||||||
end
|
end
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
@ -159,7 +164,7 @@ end
|
|||||||
-- @treturn STATE The current state code (@{STATE}.NONE if no state has been set).
|
-- @treturn STATE The current state code (@{STATE}.NONE if no state has been set).
|
||||||
-- @treturn string The current state message (empty string if no state has been set).
|
-- @treturn string The current state message (empty string if no state has been set).
|
||||||
local function getState()
|
local function getState()
|
||||||
local file,msg = io.open(M.CACHE_PATH .. '/' .. M.STATE_FILE, 'r')
|
local file,msg = io.open(cachePath .. '/' .. M.STATE_FILE, 'r')
|
||||||
if not file then return M.STATE.NONE,"" end
|
if not file then return M.STATE.NONE,"" end
|
||||||
|
|
||||||
local state = file:read('*a')
|
local state = file:read('*a')
|
||||||
@ -179,7 +184,7 @@ end
|
|||||||
|
|
||||||
--- Read the contents of a file.
|
--- Read the contents of a file.
|
||||||
--
|
--
|
||||||
-- TODO: this file has been copied from @{util.utils}.lua and should be merged again.
|
-- TODO: this file has been copied from @{util.utils}.lua and should be merged back.
|
||||||
-- @string filePath The file to read.
|
-- @string filePath The file to read.
|
||||||
-- @bool trimResult Whether or not to trim the read data.
|
-- @bool trimResult Whether or not to trim the read data.
|
||||||
-- @treturn bool|nil True, or nil on error.
|
-- @treturn bool|nil True, or nil on error.
|
||||||
@ -201,7 +206,7 @@ end
|
|||||||
|
|
||||||
--- Reports whether or not a file exists.
|
--- Reports whether or not a file exists.
|
||||||
--
|
--
|
||||||
-- TODO: this file has been copied from @{util.utils}.lua and should be merged again.
|
-- TODO: this file has been copied from @{util.utils}.lua and should be merged back.
|
||||||
-- @string file The file to report about.
|
-- @string file The file to report about.
|
||||||
-- @treturn bool True if the file exists, false otherwise.
|
-- @treturn bool True if the file exists, false otherwise.
|
||||||
local function exists(file)
|
local function exists(file)
|
||||||
@ -216,7 +221,7 @@ end
|
|||||||
|
|
||||||
--- Reports the size of a file or file handle.
|
--- Reports the size of a file or file handle.
|
||||||
--
|
--
|
||||||
-- TODO: this file has been copied from @{util.utils}.lua and should be merged again.
|
-- TODO: this file has been copied from @{util.utils}.lua and should be merged back.
|
||||||
-- @param file A file path or open file handle.
|
-- @param file A file path or open file handle.
|
||||||
-- @treturn number Size of the file.
|
-- @treturn number Size of the file.
|
||||||
local function fileSize(file)
|
local function fileSize(file)
|
||||||
@ -241,8 +246,9 @@ end
|
|||||||
-- @bool dryRun Only log a message if true, otherwise run the command and log a message.
|
-- @bool dryRun Only log a message if true, otherwise run the command and log a message.
|
||||||
-- @treturn number Exit status of of command or -1 if dryRun is true.
|
-- @treturn number Exit status of of command or -1 if dryRun is true.
|
||||||
local function runCommand(command, dryRun)
|
local function runCommand(command, dryRun)
|
||||||
D("about to run: '" .. command .. "'")
|
--D("about to run: '" .. command .. "'")
|
||||||
return (not dryRun) and os.execute(command) or -1
|
if dryRun then return -1 end
|
||||||
|
return M.compatexecute(command)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Removes a file.
|
--- Removes a file.
|
||||||
@ -260,6 +266,7 @@ end
|
|||||||
local function downloadFile(url, saveDir, filename)
|
local function downloadFile(url, saveDir, filename)
|
||||||
if not saveDir or saveDir:len() == 0 then return nil, "saveDir must be non-empty" end
|
if not saveDir or saveDir:len() == 0 then return nil, "saveDir must be non-empty" end
|
||||||
local outArg = (filename:len() > 0) and (' -O' .. filename) or ''
|
local outArg = (filename:len() > 0) and (' -O' .. filename) or ''
|
||||||
|
D("Downloading file '" .. url .. "'")
|
||||||
if filename:len() > 0 then
|
if filename:len() > 0 then
|
||||||
return runCommand('wget ' .. M.WGET_OPTIONS .. ' -O ' .. saveDir .. '/' .. filename .. ' ' .. url .. ' 2> /dev/null')
|
return runCommand('wget ' .. M.WGET_OPTIONS .. ' -O ' .. saveDir .. '/' .. filename .. ' ' .. url .. ' 2> /dev/null')
|
||||||
else
|
else
|
||||||
@ -269,38 +276,39 @@ end
|
|||||||
|
|
||||||
--- Parses command-line arguments and returns a table containing information distilled from them.
|
--- Parses command-line arguments and returns a table containing information distilled from them.
|
||||||
-- @tparam table arglist A table in the same form as the [arg table](http://www.lua.org/pil/1.4.html) created by Lua.
|
-- @tparam table arglist A table in the same form as the [arg table](http://www.lua.org/pil/1.4.html) created by Lua.
|
||||||
|
-- @tparam table defaults A table with defaults settings (actually the basis for the returned table)
|
||||||
-- @treturn table|nil A table containing information on what to do, or nil if invalid arguments were specified.
|
-- @treturn table|nil A table containing information on what to do, or nil if invalid arguments were specified.
|
||||||
-- @treturn ?string Descriptive message on error.
|
-- @treturn ?string Descriptive message on error.
|
||||||
local function parseCommandlineArguments(arglist)
|
local function parseCommandlineArguments(arglist, defaults)
|
||||||
local result = { verbosity = 0, baseUrl = M.DEFAULT_BASE_URL, action = nil }
|
|
||||||
local nextIsVersion, nextIsUrl = false, false
|
local nextIsVersion, nextIsUrl = false, false
|
||||||
for index,argument in ipairs(arglist) do
|
for index,argument in ipairs(arglist) do
|
||||||
if nextIsVersion then
|
if nextIsVersion then
|
||||||
result.version = argument; nextIsVersion = false
|
defaults.version = argument; nextIsVersion = false
|
||||||
elseif nextIsUrl then
|
elseif nextIsUrl then
|
||||||
result.baseUrl = argument; nextIsUrl = false
|
defaults.baseUrl = argument; nextIsUrl = false
|
||||||
else
|
else
|
||||||
if argument == '-h' then result.action = 'showHelp'
|
if argument == '-h' then defaults.action = 'showHelp'
|
||||||
elseif argument == '-q' then result.verbosity = -1
|
elseif argument == '-q' then defaults.verbosity = -1
|
||||||
elseif argument == '-V' then result.verbosity = 1
|
elseif argument == '-V' then defaults.verbosity = 1
|
||||||
elseif argument == '-c' then result.useCache = true
|
elseif argument == '-c' then defaults.useCache = true
|
||||||
elseif argument == '-C' then result.useCache = false
|
elseif argument == '-C' then defaults.useCache = false
|
||||||
elseif argument == '-u' then nextIsUrl = true
|
elseif argument == '-u' then nextIsUrl = true
|
||||||
elseif argument == '-v' then result.action = 'showCurrentVersion'
|
elseif argument == '-b' then defaults.includeBetas = true
|
||||||
elseif argument == '-s' then result.action = 'showStatus'
|
elseif argument == '-v' then defaults.action = 'showCurrentVersion'
|
||||||
elseif argument == '-l' then result.action = 'showAvailableVersions'
|
elseif argument == '-s' then defaults.action = 'showStatus'
|
||||||
elseif argument == '-i' then result.action = 'showVersionInfo'; nextIsVersion = true
|
elseif argument == '-l' then defaults.action = 'showAvailableVersions'
|
||||||
elseif argument == '-d' then result.action = 'imageDownload'; nextIsVersion = true
|
elseif argument == '-i' then defaults.action = 'showVersionInfo'; nextIsVersion = true
|
||||||
elseif argument == '-f' then result.action = 'imageInstall'; nextIsVersion = true
|
elseif argument == '-d' then defaults.action = 'imageDownload'; nextIsVersion = true
|
||||||
elseif argument == '-r' then result.action = 'clear'
|
elseif argument == '-f' then defaults.action = 'imageInstall'; nextIsVersion = true
|
||||||
|
elseif argument == '-r' then defaults.action = 'clear'
|
||||||
else return nil,"unrecognized argument '" .. argument .. "'"
|
else return nil,"unrecognized argument '" .. argument .. "'"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if result.version then
|
if defaults.version then
|
||||||
result.version = M.parseVersion(result.version)
|
defaults.version = M.parseVersion(defaults.version)
|
||||||
if not result.version then
|
if not defaults.version then
|
||||||
return nil,"error parsing specified version"
|
return nil,"error parsing specified version"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -308,7 +316,23 @@ local function parseCommandlineArguments(arglist)
|
|||||||
if nextIsVersion then return nil, "missing required version argument" end
|
if nextIsVersion then return nil, "missing required version argument" end
|
||||||
if nextIsUrl then return nil, "missing required URL argument" end
|
if nextIsUrl then return nil, "missing required URL argument" end
|
||||||
|
|
||||||
return result
|
return defaults
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Determines if the system is OpenWrt or not by checking if `/etc/openwrt_release` exists.
|
||||||
|
-- @treturn bool True if the OS is OpenWrt.
|
||||||
|
local function isOpenWrt()
|
||||||
|
local flag = nil
|
||||||
|
return function()
|
||||||
|
if flag == nil then
|
||||||
|
local relFile = io.open('/etc/openwrt_release', 'r')
|
||||||
|
flag = not not relFile
|
||||||
|
if relFile then relFile:close() end
|
||||||
|
return flag
|
||||||
|
else
|
||||||
|
return flag
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Returns the [MD5](http://en.wikipedia.org/wiki/MD5) hash for a given file.
|
--- Returns the [MD5](http://en.wikipedia.org/wiki/MD5) hash for a given file.
|
||||||
@ -317,10 +341,23 @@ end
|
|||||||
-- @string filepath The path of which to calculate the MD5-sum.
|
-- @string filepath The path of which to calculate the MD5-sum.
|
||||||
-- @treturn nil
|
-- @treturn nil
|
||||||
local function md5sum(filepath)
|
local function md5sum(filepath)
|
||||||
return nil
|
local sfile
|
||||||
-- TODO [osx: md5 -q <file>], [linux: ?]
|
|
||||||
end
|
|
||||||
|
|
||||||
|
if not isOpenWrt() then
|
||||||
|
sfile = io.popen('md5 -q "' .. filepath .. '"')
|
||||||
|
else
|
||||||
|
sfile = io.popen('md5sum "' .. filepath .. '" 2>/dev/null', 'r')
|
||||||
|
end
|
||||||
|
|
||||||
|
local sum = sfile:read('*all')
|
||||||
|
sfile:close()
|
||||||
|
|
||||||
|
if not sum then return nil,"could not obtain MD5 sum" end
|
||||||
|
|
||||||
|
sum = sum:match('[%da-fA-F]+')
|
||||||
|
|
||||||
|
return sum
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -328,6 +365,31 @@ end
|
|||||||
-- MODULE FUNCTIONS --
|
-- MODULE FUNCTIONS --
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
|
local compatlua51 = _VERSION == 'Lua 5.1'
|
||||||
|
|
||||||
|
--- execute a shell command. Taken from penlight library.
|
||||||
|
-- This is a compatibility function that returns the same for Lua 5.1 and Lua 5.2
|
||||||
|
-- @param cmd a shell command
|
||||||
|
-- @return true if successful
|
||||||
|
-- @return actual return code
|
||||||
|
function M.compatexecute(cmd)
|
||||||
|
local res1,res2,res3 = os.execute(cmd)
|
||||||
|
if compatlua51 then
|
||||||
|
local cmd, sys = splitExitStatus(res1)
|
||||||
|
return (res1 == 0) and true,cmd or nil,cmd
|
||||||
|
else
|
||||||
|
return res1, res3
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set verbosity (log level) that determines which messages do get logged and which do not.
|
||||||
|
-- @tparam number level The level to set, between -1 and 1.
|
||||||
|
function M.setVerbosity(level)
|
||||||
|
if level and level >= -1 and level <= 1 then
|
||||||
|
verbosity = level
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
--- Enables use of the given @{util.logger} object, otherwise `stdout`/`stderr` will be used.
|
--- Enables use of the given @{util.logger} object, otherwise `stdout`/`stderr` will be used.
|
||||||
-- @tparam util.logger logger The logger to log future messages to.
|
-- @tparam util.logger logger The logger to log future messages to.
|
||||||
function M.setLogger(logger)
|
function M.setLogger(logger)
|
||||||
@ -348,16 +410,23 @@ function M.setBaseUrl(url)
|
|||||||
baseUrl = url
|
baseUrl = url
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Sets the filesystem path to use as cache for downloaded index and image files.
|
||||||
|
-- @string path The path to use, use nil to restore default @{DEFAULT_CACHE_PATH}.
|
||||||
|
function M.setCachePath(path)
|
||||||
|
cachePath = path or M.DEFAULT_CACHE_PATH
|
||||||
|
end
|
||||||
|
|
||||||
--- Returns a table with information about current update status of the wifibox.
|
--- Returns a table with information about current update status of the wifibox.
|
||||||
--
|
--
|
||||||
-- The result table will contain at least the current version, current state code and text.
|
-- The result table will contain at least the current version, current state code and text.
|
||||||
-- If the box has internet access, it will also include the newest version available.
|
-- If the box has internet access, it will also include the newest version available.
|
||||||
-- If an image is currently being downloaded, progress information will also be included.
|
-- If an image is currently being downloaded, progress information will also be included.
|
||||||
--
|
--
|
||||||
|
-- @tparam bool[opt] withBetas Consider beta releases when looking for newest version.
|
||||||
-- @treturn bool True if status has been determined fully, false if not.
|
-- @treturn bool True if status has been determined fully, false if not.
|
||||||
-- @treturn table The result table.
|
-- @treturn table The result table.
|
||||||
-- @treturn ?string Descriptive message in case the result table is not complete.
|
-- @treturn ?string Descriptive message in case the result table is not complete.
|
||||||
function M.getStatus()
|
function M.getStatus(withBetas)
|
||||||
if not baseUrl then baseUrl = M.DEFAULT_BASE_URL end
|
if not baseUrl then baseUrl = M.DEFAULT_BASE_URL end
|
||||||
local unknownVersion = { major = 0, minor = 0, patch = 0 }
|
local unknownVersion = { major = 0, minor = 0, patch = 0 }
|
||||||
local result = {}
|
local result = {}
|
||||||
@ -366,18 +435,39 @@ function M.getStatus()
|
|||||||
result.stateCode, result.stateText = getState()
|
result.stateCode, result.stateText = getState()
|
||||||
result.stateCode = tonumber(result.stateCode)
|
result.stateCode = tonumber(result.stateCode)
|
||||||
|
|
||||||
local verTable,msg = M.getAvailableVersions()
|
local verTable,msg = M.getAvailableVersions(withBetas and 'both' or 'stables')
|
||||||
if not verTable then
|
if not verTable then
|
||||||
D("could not obtain available versions (" .. msg .. ")")
|
D("error: could not obtain available versions (" .. msg .. ")")
|
||||||
-- TODO: set an error state in result to signify we probably do not have internet access?
|
|
||||||
return false, result, msg
|
return false, result, msg
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- NOTE: to look up the current version we need a table containing all versions
|
||||||
|
local allVersionsTable,msg
|
||||||
|
if not withBetas then
|
||||||
|
allVersionsTable,msg = M.getAvailableVersions('both')
|
||||||
|
if not allVersionsTable then
|
||||||
|
D("error: could not obtain available versions including betas (" .. msg .. ")")
|
||||||
|
return false, result, msg
|
||||||
|
end
|
||||||
|
else
|
||||||
|
allVersionsTable = verTable
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
local newest = verTable and verTable[#verTable]
|
local newest = verTable and verTable[#verTable]
|
||||||
result.newestVersion = newest and newest.version or unknownVersion
|
result.newestVersion = newest and newest.version or unknownVersion
|
||||||
|
result.newestReleaseTimestamp = newest and newest.timestamp
|
||||||
|
|
||||||
|
-- look up timestamp of current version
|
||||||
|
local cEnt = M.findVersion(result.currentVersion, nil, allVersionsTable)
|
||||||
|
if cEnt then
|
||||||
|
result.currentReleaseTimestamp = cEnt.timestamp
|
||||||
|
else
|
||||||
|
D("warning: could not find current wifibox version in release index, beta setting disabled after having beta installed?")
|
||||||
|
end
|
||||||
|
|
||||||
if result.stateCode == M.STATE.DOWNLOADING then
|
if result.stateCode == M.STATE.DOWNLOADING then
|
||||||
result.progress = fileSize(M.CACHE_PATH .. '/' .. newest.sysupgradeFilename)
|
result.progress = fileSize(cachePath .. '/' .. newest.sysupgradeFilename)
|
||||||
if not result.progress then result.progress = 0 end -- in case the file does not exist yet (which yields nil)
|
if not result.progress then result.progress = 0 end -- in case the file does not exist yet (which yields nil)
|
||||||
result.imageSize = newest.sysupgradeFileSize
|
result.imageSize = newest.sysupgradeFileSize
|
||||||
end
|
end
|
||||||
@ -387,55 +477,107 @@ end
|
|||||||
|
|
||||||
--- Turns a plain-text version as returned by @{formatVersion} into a table.
|
--- Turns a plain-text version as returned by @{formatVersion} into a table.
|
||||||
-- @tparam string|table versionText The version string to parse, if it is already a table, it is returned as-is.
|
-- @tparam string|table versionText The version string to parse, if it is already a table, it is returned as-is.
|
||||||
-- @treturn table A parse version.
|
-- @treturn table A parsed version or nil on incorrect argument.
|
||||||
function M.parseVersion(versionText)
|
function M.parseVersion(versionText)
|
||||||
|
if not versionText then return nil end
|
||||||
if type(versionText) == 'table' then return versionText end
|
if type(versionText) == 'table' then return versionText end
|
||||||
if not versionText or versionText:len() == 0 then return nil end
|
if not versionText or versionText:len() == 0 then return nil end
|
||||||
|
|
||||||
local major,minor,patch = versionText:match("^%s*(%d+)%.(%d+)%.(%d+)%s*$")
|
local major,minor,patch,suffix = versionText:match("^%s*(%d+)%.(%d+)%.(%d+)(-?%w*)%s*$")
|
||||||
if not major or not minor or not patch then return nil end
|
if not major or not minor or not patch then return nil end -- suffix not required
|
||||||
|
|
||||||
return { ['major'] = major, ['minor'] = minor, ['patch'] = patch }
|
if type(suffix) == 'string' and suffix:len() > 0 then
|
||||||
|
if suffix:sub(1, 1) ~= '-' then return nil end
|
||||||
|
suffix = suffix:sub(2)
|
||||||
|
else
|
||||||
|
suffix = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return { ['major'] = major, ['minor'] = minor, ['patch'] = patch, ['suffix'] = suffix }
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Formats a version as returned by @{parseVersion}.
|
--- Formats a version as returned by @{parseVersion}.
|
||||||
-- @tparam table|string version The version to format, if it is already a string, that will be returned unmodified.
|
-- @tparam table|string version The version to format, if it is already a string, that will be returned unmodified.
|
||||||
-- @treturn string A formatted version.
|
-- @treturn string A formatted version or nil on incorrect argument.
|
||||||
function M.formatVersion(version)
|
function M.formatVersion(version)
|
||||||
|
if not version then return nil end
|
||||||
if type(version) == 'string' then return version end
|
if type(version) == 'string' then return version end
|
||||||
return version.major .. "." .. version.minor .. "." .. version.patch
|
|
||||||
|
local ver = version.major .. "." .. version.minor .. "." .. version.patch
|
||||||
|
if version.suffix then ver = ver .. '-' .. version.suffix end
|
||||||
|
|
||||||
|
return ver
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Compares two versions.
|
--- Compares two versions. Note that the second return value must be used for equality testing.
|
||||||
|
-- If given, the timestamps have higher priority than the versions. Suffixes are ignored.
|
||||||
-- @tparam table versionA A version as returned by @{parseVersion}.
|
-- @tparam table versionA A version as returned by @{parseVersion}.
|
||||||
-- @tparam table versionB A version as returned by @{parseVersion}.
|
-- @tparam table versionB A version as returned by @{parseVersion}.
|
||||||
-- @treturn number -1 if versionA is smaller than versionB, 0 if versions are equal or 1 if versionA is larger than versionB.
|
-- @param timestampA[opt] A timestamp as returned by @{parseDate}.
|
||||||
function M.compareVersions(versionA, versionB)
|
-- @param timestampB[opt] A timestamp as returned by @{parseDate}.
|
||||||
|
-- @treturn number -1 if versionA/timestampA is smaller/older than versionB/timestampB, 0 if versions are equal (or undecided) or 1 if A is larger/newer than B.
|
||||||
|
-- @treturn bool True if versions are really equal (first return value can be 0 if everything but the suffix is equal)
|
||||||
|
function M.compareVersions(versionA, versionB, timestampA, timestampB)
|
||||||
if type(versionA) ~= 'table' or type(versionB) ~= 'table' then return nil end
|
if type(versionA) ~= 'table' or type(versionB) ~= 'table' then return nil end
|
||||||
local diff = versionA.major - versionB.major
|
|
||||||
if diff == 0 then diff = versionA.minor - versionB.minor end
|
local diff = 0
|
||||||
if diff == 0 then diff = versionA.patch - versionB.patch end
|
if timestampA and timestampB then diff = timestampA - timestampB end
|
||||||
return diff > 0 and 1 or (diff < 0 and -1 or 0)
|
if diff == 0 then
|
||||||
|
diff = versionA.major - versionB.major
|
||||||
|
if diff == 0 then diff = versionA.minor - versionB.minor end
|
||||||
|
if diff == 0 then diff = versionA.patch - versionB.patch end
|
||||||
|
end
|
||||||
|
|
||||||
|
local result = diff > 0 and 1 or (diff < 0 and -1 or 0)
|
||||||
|
local reallyEqual = (diff == 0) and (versionA.suffix == versionB.suffix)
|
||||||
|
|
||||||
|
return result, (reallyEqual and true or false)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Checks if versions are exactly equal.
|
||||||
|
-- It returns the second return value of @{compareVersions} and accepts the same arguments.
|
||||||
|
-- @treturn bool True if versions are equal, false otherwise.
|
||||||
|
function M.versionsEqual(versionA, versionB, timestampA, timestampB)
|
||||||
|
return select(2, M.compareVersions(versionA, versionB, timestampA, timestampB))
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Returns information on a version if it can be found in a collection of versions as returned by @{getAvailableVersions}.
|
--- Returns information on a version if it can be found in a collection of versions as returned by @{getAvailableVersions}.
|
||||||
-- @tparam table version The version to look for.
|
-- @tparam table version The version to look for.
|
||||||
|
-- @tparam bool[opt] withBetas If verTable is not given, download versions including beta releases
|
||||||
-- @tparam table[opt] verTable A table containing a collection of versions, if not passed in, it will be obtained using @{getAvailableVersions}.
|
-- @tparam table[opt] verTable A table containing a collection of versions, if not passed in, it will be obtained using @{getAvailableVersions}.
|
||||||
|
-- @param timestamp[opt] Specific timestamp to look for.
|
||||||
-- @treturn table|nil Version information table found in the collection, or nil on error or if not found.
|
-- @treturn table|nil Version information table found in the collection, or nil on error or if not found.
|
||||||
-- @treturn string Descriptive message in case of error or if the version could not be found.
|
-- @treturn string Descriptive message in case of error or if the version could not be found.
|
||||||
function M.findVersion(version, verTable)
|
function M.findVersion(version, withBetas, verTable, timestamp)
|
||||||
local msg = nil
|
local msg = nil
|
||||||
version = M.parseVersion(version)
|
version = M.parseVersion(version)
|
||||||
if not verTable then verTable,msg = M.getAvailableVersions() end
|
if not verTable then verTable,msg = M.getAvailableVersions(withBetas and 'both' or 'stables') end
|
||||||
|
|
||||||
if not verTable then return nil,msg end
|
if not verTable then return nil,msg end
|
||||||
|
|
||||||
for _,ent in pairs(verTable) do
|
for _,ent in pairs(verTable) do
|
||||||
if M.compareVersions(ent.version, version) == 0 then return ent end
|
if M.versionsEqual(ent.version, version, ent.timestamp, timestamp) == true then return ent end
|
||||||
end
|
end
|
||||||
return nil,"no such version"
|
return nil,"no such version"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Turns a date of the format 'yyyymmdd' into a timestamp as returned by os.time.
|
||||||
|
-- @tparam string dateText The date to parse.
|
||||||
|
-- @return A timestamp or nil if the argument does not have correct format.
|
||||||
|
function M.parseDate(dateText)
|
||||||
|
if type(dateText) ~= 'string' or dateText:len() ~= 8 or dateText:find('[^%d]') ~= nil then return nil end
|
||||||
|
|
||||||
|
return os.time({ year = dateText:sub(1, 4), month = dateText:sub(5, 6), day = dateText:sub(7,8) })
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Formats a timestamp as returned by os.time to a date of the form 'yyyymmdd'.
|
||||||
|
-- @param timestamp The timestamp to format.
|
||||||
|
-- @return A formatted date or nil if the argument is nil.
|
||||||
|
function M.formatDate(timestamp)
|
||||||
|
if not timestamp then return nil end
|
||||||
|
return os.date('%Y%m%d', timestamp)
|
||||||
|
end
|
||||||
|
|
||||||
--- Creates an image file name based on given properties.
|
--- Creates an image file name based on given properties.
|
||||||
-- The generated name has the following form: `doodle3d-wifibox-<version>-<deviceType>-<'factory'|'sysupgrade'>.bin`.
|
-- The generated name has the following form: `doodle3d-wifibox-<version>-<deviceType>-<'factory'|'sysupgrade'>.bin`.
|
||||||
-- @tparam table|string version The version of the image.
|
-- @tparam table|string version The version of the image.
|
||||||
@ -449,7 +591,7 @@ function M.constructImageFilename(version, devType, isFactory)
|
|||||||
return 'doodle3d-wifibox-' .. M.formatVersion(v) .. '-' .. dt .. '-' .. sf .. '.bin'
|
return 'doodle3d-wifibox-' .. M.formatVersion(v) .. '-' .. dt .. '-' .. sf .. '.bin'
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Checks whether a valid image file is present in @{CACHE_PATH} for the given image properties.
|
--- Checks whether a valid image file is present in @{cachePath} for the given image properties.
|
||||||
-- The versionEntry table will be augmented with an `isValid` key.
|
-- The versionEntry table will be augmented with an `isValid` key.
|
||||||
--
|
--
|
||||||
-- NOTE: currently, this function only checks the image exists and has the correct size.
|
-- NOTE: currently, this function only checks the image exists and has the correct size.
|
||||||
@ -459,12 +601,20 @@ end
|
|||||||
-- @string[opt] devType Image device type, see @{constructImageFilename}.
|
-- @string[opt] devType Image device type, see @{constructImageFilename}.
|
||||||
-- @bool[opt] isFactory Image type, see @{constructImageFilename}.
|
-- @bool[opt] isFactory Image type, see @{constructImageFilename}.
|
||||||
-- @treturn bool True if a valid image is present, false otherwise.
|
-- @treturn bool True if a valid image is present, false otherwise.
|
||||||
|
-- @treturn string|nil Reason for being invalid if first return value is false.
|
||||||
function M.checkValidImage(versionEntry, devType, isFactory)
|
function M.checkValidImage(versionEntry, devType, isFactory)
|
||||||
local filename = M.constructImageFilename(versionEntry.version, devType, isFactory)
|
local filename = M.constructImageFilename(versionEntry.version, devType, isFactory)
|
||||||
--return versionEntry.md5 == md5sum(M.CACHE_PATH .. '/' .. filename)
|
|
||||||
local size = fileSize(M.CACHE_PATH .. '/' .. filename)
|
local entSize = isFactory and versionEntry.factoryFileSize or versionEntry.sysupgradeFileSize
|
||||||
versionEntry.isValid = versionEntry.sysupgradeFileSize == size
|
local entMd5 = isFactory and versionEntry.factoryMd5 or versionEntry.sysupgradeMD5
|
||||||
return versionEntry.isValid
|
|
||||||
|
versionEntry.isValid = entMd5 == md5sum(cachePath .. '/' .. filename)
|
||||||
|
if not versionEntry.isValid then return false,"incorrect MD5 checksum" end
|
||||||
|
|
||||||
|
versionEntry.isValid = entSize == fileSize(cachePath .. '/' .. filename)
|
||||||
|
if not versionEntry.isValid then return false,"incorrect file size" end
|
||||||
|
|
||||||
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Returns the current wifibox version text, extracted from `/etc/wifibox-version`.
|
--- Returns the current wifibox version text, extracted from `/etc/wifibox-version`.
|
||||||
@ -482,18 +632,14 @@ function M.getCurrentVersion()
|
|||||||
end
|
end
|
||||||
|
|
||||||
--- Returns an indexed and sorted table containing version information tables.
|
--- Returns an indexed and sorted table containing version information tables.
|
||||||
-- The information is obtained from the either cached or downloaded image index (@{IMAGE_INDEX_FILE}).
|
-- The information is obtained from the either cached or downloaded image index file.
|
||||||
-- @treturn table A table with a collection of version information tables.
|
local function fetchIndexTable(indexFile, cachePath)
|
||||||
function M.getAvailableVersions()
|
|
||||||
if not baseUrl then baseUrl = M.DEFAULT_BASE_URL end
|
if not baseUrl then baseUrl = M.DEFAULT_BASE_URL end
|
||||||
local indexFilename = M.CACHE_PATH .. '/' .. M.IMAGE_INDEX_FILE
|
local indexFilename = cachePath .. '/' .. indexFile
|
||||||
|
|
||||||
local ccRv,ccMsg = createCacheDirectory()
|
|
||||||
if not ccRv then return nil,ccMsg end
|
|
||||||
|
|
||||||
if not useCache or not exists(indexFilename) then
|
if not useCache or not exists(indexFilename) then
|
||||||
local rv = downloadFile(baseUrl .. '/images/' .. M.IMAGE_INDEX_FILE, M.CACHE_PATH, M.IMAGE_INDEX_FILE)
|
local rv1,rv2 = downloadFile(baseUrl .. '/images/' .. indexFile, cachePath, indexFile)
|
||||||
if rv ~= 0 then return nil,"could not download image index file (" .. wgetStatusToString(rv) .. ")" end
|
if not rv1 then return nil,"could not download image index file (" .. wgetStatusToString(rv2) .. ")" end
|
||||||
end
|
end
|
||||||
|
|
||||||
local status,idxLines = pcall(io.lines, indexFilename)
|
local status,idxLines = pcall(io.lines, indexFilename)
|
||||||
@ -505,7 +651,7 @@ function M.getAvailableVersions()
|
|||||||
for line in idxLines do
|
for line in idxLines do
|
||||||
local k,v = line:match('^(.-):(.*)$')
|
local k,v = line:match('^(.-):(.*)$')
|
||||||
k,v = trim(k), trim(v)
|
k,v = trim(k), trim(v)
|
||||||
if not log then D("#" .. lineno .. ": considering '" .. line .. "' (" .. (k or '<nil>') .. " / " .. (v or '<nil>') .. ")") end
|
--if not log then D("#" .. lineno .. ": considering '" .. line .. "' (" .. (k or '<nil>') .. " / " .. (v or '<nil>') .. ")") end
|
||||||
if not changelogMode and (not k or not v) then return nil,"incorrectly formatted line in index file (line " .. lineno .. ")" end
|
if not changelogMode and (not k or not v) then return nil,"incorrectly formatted line in index file (line " .. lineno .. ")" end
|
||||||
|
|
||||||
if k == 'ChangelogEnd' then
|
if k == 'ChangelogEnd' then
|
||||||
@ -537,6 +683,13 @@ function M.getAvailableVersions()
|
|||||||
sSum,fSum = trim(sSum), trim(fSum)
|
sSum,fSum = trim(sSum), trim(fSum)
|
||||||
if sSum then entry.sysupgradeMD5 = sSum end
|
if sSum then entry.sysupgradeMD5 = sSum end
|
||||||
if fSum then entry.factoryMD5 = fSum end
|
if fSum then entry.factoryMD5 = fSum end
|
||||||
|
elseif k == 'ReleaseDate' then
|
||||||
|
local ts = M.parseDate(v)
|
||||||
|
if not ts then
|
||||||
|
P(0, "ignoring incorrectly formatted ReleaseDate field (line " .. lineno .. ")")
|
||||||
|
else
|
||||||
|
entry.timestamp = ts
|
||||||
|
end
|
||||||
else
|
else
|
||||||
P(-1, "ignoring unrecognized field in index file '" .. k .. "' (line " .. lineno .. ")")
|
P(-1, "ignoring unrecognized field in index file '" .. k .. "' (line " .. lineno .. ")")
|
||||||
end
|
end
|
||||||
@ -553,6 +706,36 @@ function M.getAvailableVersions()
|
|||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Returns an indexed and sorted table containing version information tables.
|
||||||
|
-- The information is obtained from the either cached or downloaded image index (@{IMAGE_STABLE_INDEX_FILE}).
|
||||||
|
-- @tparam which[opt] Which type of versions to fetch, either 'stables' (default), 'betas' or both.
|
||||||
|
-- @treturn table A table with a collection of version information tables.
|
||||||
|
function M.getAvailableVersions(which)
|
||||||
|
local ccRv,ccMsg = createCacheDirectory()
|
||||||
|
if not ccRv then return nil,ccMsg end
|
||||||
|
|
||||||
|
local verTable, msg = {}, nil
|
||||||
|
|
||||||
|
if which == 'stables' or which == 'both' then
|
||||||
|
verTable,msg = fetchIndexTable(M.IMAGE_STABLE_INDEX_FILE, cachePath)
|
||||||
|
if not verTable then return nil,msg end
|
||||||
|
end
|
||||||
|
|
||||||
|
if which == 'betas' or which == 'both' then
|
||||||
|
local betas,msg = fetchIndexTable(M.IMAGE_BETA_INDEX_FILE, cachePath)
|
||||||
|
if not betas then return nil,msg end
|
||||||
|
|
||||||
|
for k,v in pairs(betas) do verTable[k] = v end
|
||||||
|
end
|
||||||
|
|
||||||
|
table.sort(verTable, function(a, b)
|
||||||
|
return M.compareVersions(a.version, b.version, a.timestamp, b.timestamp) < 0
|
||||||
|
end)
|
||||||
|
|
||||||
|
|
||||||
|
return verTable
|
||||||
|
end
|
||||||
|
|
||||||
--- Attempts to download an image file with the requested properties.
|
--- Attempts to download an image file with the requested properties.
|
||||||
-- @tparam table versionEntry A version information table.
|
-- @tparam table versionEntry A version information table.
|
||||||
-- @string[opt] devType Image device type, see @{constructImageFilename}.
|
-- @string[opt] devType Image device type, see @{constructImageFilename}.
|
||||||
@ -574,25 +757,26 @@ function M.downloadImageFile(versionEntry, devType, isFactory)
|
|||||||
if versionEntry.isValid == false then doDownload = true end
|
if versionEntry.isValid == false then doDownload = true end
|
||||||
end
|
end
|
||||||
|
|
||||||
local rv = 0
|
local rv1,rv2 = 0,0
|
||||||
if doDownload then
|
if doDownload then
|
||||||
M.setState(M.STATE.DOWNLOADING, "Downloading image (" .. filename .. ")")
|
M.setState(M.STATE.DOWNLOADING, "Downloading image (" .. filename .. ")")
|
||||||
rv = downloadFile(baseUrl .. '/images/' .. filename, M.CACHE_PATH, filename)
|
rv1,rv2 = downloadFile(baseUrl .. '/images/' .. filename, cachePath, filename)
|
||||||
end
|
end
|
||||||
|
|
||||||
if rv == 0 then
|
if rv1 then
|
||||||
if M.checkValidImage(versionEntry, devType, isFactory) then
|
local valid,msg = M.checkValidImage(versionEntry, devType, isFactory)
|
||||||
|
if valid then
|
||||||
M.setState(M.STATE.IMAGE_READY, "Image downloaded, ready to install (image name: " .. filename .. ")")
|
M.setState(M.STATE.IMAGE_READY, "Image downloaded, ready to install (image name: " .. filename .. ")")
|
||||||
return true
|
return true
|
||||||
else
|
else
|
||||||
removeFile(M.CACHE_PATH .. '/' .. filename)
|
removeFile(cachePath .. '/' .. filename)
|
||||||
local ws = "Image download failed (invalid image)"
|
local ws = "Image download failed (invalid image: " .. msg .. ")"
|
||||||
M.setState(M.STATE.DOWNLOAD_FAILED, ws)
|
M.setState(M.STATE.DOWNLOAD_FAILED, ws)
|
||||||
return nil,ws
|
return nil,ws
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
local ws = wgetStatusToString(rv)
|
local ws = wgetStatusToString(rv2)
|
||||||
removeFile(M.CACHE_PATH .. '/' .. filename)
|
removeFile(cachePath .. '/' .. filename)
|
||||||
M.setState(M.STATE.DOWNLOAD_FAILED, "Image download failed (wget error: " .. ws .. ")")
|
M.setState(M.STATE.DOWNLOAD_FAILED, "Image download failed (wget error: " .. ws .. ")")
|
||||||
return nil,ws
|
return nil,ws
|
||||||
end
|
end
|
||||||
@ -608,10 +792,10 @@ end
|
|||||||
-- @treturn bool|nil True on success (with the 'exception' as noted above) or nil on error.
|
-- @treturn bool|nil True on success (with the 'exception' as noted above) or nil on error.
|
||||||
-- @treturn ?string|number (optional) Descriptive message or sysupgrade exit status on error.
|
-- @treturn ?string|number (optional) Descriptive message or sysupgrade exit status on error.
|
||||||
function M.flashImageVersion(versionEntry, noRetain, devType, isFactory)
|
function M.flashImageVersion(versionEntry, noRetain, devType, isFactory)
|
||||||
log:info("flashImageVersion")
|
if log then log:info("flashImageVersion") end
|
||||||
local imgName = M.constructImageFilename(versionEntry.version, devType, isFactory)
|
local imgName = M.constructImageFilename(versionEntry.version, devType, isFactory)
|
||||||
local cmd = noRetain and 'sysupgrade -n ' or 'sysupgrade '
|
local cmd = noRetain and 'sysupgrade -n ' or 'sysupgrade '
|
||||||
cmd = cmd .. M.CACHE_PATH .. '/' .. imgName
|
cmd = cmd .. cachePath .. '/' .. imgName
|
||||||
|
|
||||||
local ccRv,ccMsg = createCacheDirectory()
|
local ccRv,ccMsg = createCacheDirectory()
|
||||||
if not ccRv then return nil,ccMsg end
|
if not ccRv then return nil,ccMsg end
|
||||||
@ -634,22 +818,30 @@ function M.flashImageVersion(versionEntry, noRetain, devType, isFactory)
|
|||||||
return (rv == 0) and true or nil,rv
|
return (rv == 0) and true or nil,rv
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Clears '*.bin' in the @{CACHE_PATH} directory.
|
--- Clears '*.bin' and both index files in the @{cachePath} directory.
|
||||||
-- @treturn bool|nil True on success, or nil on error.
|
-- @treturn bool|nil True on success, or nil on error.
|
||||||
-- @treturn ?string Descriptive message on error.
|
-- @treturn ?string Descriptive message on error.
|
||||||
function M.clear()
|
function M.clear()
|
||||||
local ccRv,ccMsg = createCacheDirectory()
|
local ccRv,ccMsg = createCacheDirectory()
|
||||||
if not ccRv then return nil,ccMsg end
|
if not ccRv then return nil,ccMsg end
|
||||||
|
|
||||||
D("Removing " .. M.CACHE_PATH .. "/doodle3d-wifibox-*.bin")
|
D("Removing " .. cachePath .. "/doodle3d-wifibox-*.bin")
|
||||||
M.setState(M.STATE.NONE, "")
|
M.setState(M.STATE.NONE, "")
|
||||||
local rv = os.execute('rm -f ' .. M.CACHE_PATH .. '/doodle3d-wifibox-*.bin')
|
local success = true
|
||||||
return (rv == 0) and true or nil,"could not remove image files"
|
local rv = M.compatexecute('rm -f ' .. cachePath .. '/doodle3d-wifibox-*.bin')
|
||||||
|
success = success and (rv == 0)
|
||||||
|
local rv = M.compatexecute('rm -f ' .. cachePath .. '/' .. M.IMAGE_STABLE_INDEX_FILE)
|
||||||
|
success = success and (rv == 0)
|
||||||
|
local rv = M.compatexecute('rm -f ' .. cachePath .. '/' .. M.IMAGE_BETA_INDEX_FILE)
|
||||||
|
success = success and (rv == 0)
|
||||||
|
|
||||||
|
--return success,"could not delete all files"
|
||||||
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Set updater state.
|
--- Set updater state.
|
||||||
--
|
--
|
||||||
-- NOTE: make sure the cache directory @{CACHE_PATH} exists before calling this function or it will fail.
|
-- NOTE: make sure the cache directory @{cachePath} exists before calling this function or it will fail.
|
||||||
--
|
--
|
||||||
-- NOTE: this function _can_ fail but this is not expected to happen so the return value is mostly ignored for now.
|
-- NOTE: this function _can_ fail but this is not expected to happen so the return value is mostly ignored for now.
|
||||||
--
|
--
|
||||||
@ -659,7 +851,7 @@ end
|
|||||||
function M.setState(code, msg)
|
function M.setState(code, msg)
|
||||||
local s = code .. '|' .. msg
|
local s = code .. '|' .. msg
|
||||||
D("set update state: " .. M.STATE_NAMES[code] .. " ('" .. s .. "')")
|
D("set update state: " .. M.STATE_NAMES[code] .. " ('" .. s .. "')")
|
||||||
local file,msg = io.open(M.CACHE_PATH .. '/' .. M.STATE_FILE, 'w')
|
local file,msg = io.open(cachePath .. '/' .. M.STATE_FILE, 'w')
|
||||||
|
|
||||||
if not file then
|
if not file then
|
||||||
E("error: could not open state file for writing (" .. msg .. ")")
|
E("error: could not open state file for writing (" .. msg .. ")")
|
||||||
@ -682,7 +874,15 @@ end
|
|||||||
-- so this file can also be used as a library.
|
-- so this file can also be used as a library.
|
||||||
-- Command-line arguments are expected to be present in the global `arg` variable.
|
-- Command-line arguments are expected to be present in the global `arg` variable.
|
||||||
local function main()
|
local function main()
|
||||||
local argTable,msg = parseCommandlineArguments(arg)
|
-- NOTE: this require must be local to functions which are only executed on the wifibox (i.e., where we have uci)
|
||||||
|
package.path = package.path .. ';/usr/share/lua/wifibox/?.lua'
|
||||||
|
local settings = require('util.settings')
|
||||||
|
|
||||||
|
local defaults = { verbosity = 0, baseUrl = M.DEFAULT_BASE_URL, includeBetas = false, action = nil }
|
||||||
|
local confBaseUrl = settings.get('doodle3d.update.baseUrl')
|
||||||
|
if confBaseUrl and confBaseUrl:len() > 0 then defaults.baseUrl = confBaseUrl end
|
||||||
|
|
||||||
|
local argTable,msg = parseCommandlineArguments(arg, defaults)
|
||||||
|
|
||||||
if not argTable then
|
if not argTable then
|
||||||
E("error interpreting command-line arguments, try '-h' for help (".. msg ..")")
|
E("error interpreting command-line arguments, try '-h' for help (".. msg ..")")
|
||||||
@ -690,7 +890,9 @@ local function main()
|
|||||||
end
|
end
|
||||||
|
|
||||||
verbosity = argTable.verbosity
|
verbosity = argTable.verbosity
|
||||||
|
includeBetas = argTable.includeBetas
|
||||||
if argTable.useCache ~= nil then useCache = argTable.useCache end
|
if argTable.useCache ~= nil then useCache = argTable.useCache end
|
||||||
|
if argTable.baseUrl ~= nil then baseUrl = argTable.baseUrl end
|
||||||
|
|
||||||
P(0, "Doodle3D Wifibox firmware updater")
|
P(0, "Doodle3D Wifibox firmware updater")
|
||||||
local cacheCreated,msg = createCacheDirectory()
|
local cacheCreated,msg = createCacheDirectory()
|
||||||
@ -699,13 +901,16 @@ local function main()
|
|||||||
os.exit(1)
|
os.exit(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
P(0, (includeBetas and "Considering" or "Not considering") .. " beta releases.")
|
||||||
|
|
||||||
if argTable.action == 'showHelp' then
|
if argTable.action == 'showHelp' then
|
||||||
P(1, "\t-h\t\tShow this help message")
|
P(1, "\t-h\t\tShow this help message")
|
||||||
P(1, "\t-q\t\tquiet mode")
|
P(1, "\t-q\t\tquiet mode")
|
||||||
P(1, "\t-V\t\tverbose mode")
|
P(1, "\t-V\t\tverbose mode")
|
||||||
P(1, "\t-c\t\tUse cache as much as possible")
|
P(1, "\t-c\t\tUse cache as much as possible")
|
||||||
P(1, "\t-C\t\tDo not use the cache")
|
P(1, "\t-C\t\tDo not use the cache (default)")
|
||||||
P(1, "\t-u <base_url>\tUse specified base URL (default: " .. M.DEFAULT_BASE_URL .. ")")
|
P(1, "\t-u <base_url>\tUse specified base URL (default: " .. M.DEFAULT_BASE_URL .. ")")
|
||||||
|
P(1, "\t-b\t\tInclude beta releases")
|
||||||
P(1, "\t-v\t\tShow current image version")
|
P(1, "\t-v\t\tShow current image version")
|
||||||
P(1, "\t-s\t\tShow current update status")
|
P(1, "\t-s\t\tShow current update status")
|
||||||
P(1, "\t-l\t\tShow list of available image versions (and which one has been downloaded, if any)")
|
P(1, "\t-l\t\tShow list of available image versions (and which one has been downloaded, if any)")
|
||||||
@ -723,7 +928,7 @@ local function main()
|
|||||||
P(1, "version: " .. M.formatVersion(v))
|
P(1, "version: " .. M.formatVersion(v))
|
||||||
|
|
||||||
elseif argTable.action == 'showStatus' then
|
elseif argTable.action == 'showStatus' then
|
||||||
local status = M.getStatus()
|
local success,status,msg = M.getStatus(includeBetas)
|
||||||
P(0, "Current update status:")
|
P(0, "Current update status:")
|
||||||
P(1, " currentVersion:\t" .. (M.formatVersion(status.currentVersion) or '?'))
|
P(1, " currentVersion:\t" .. (M.formatVersion(status.currentVersion) or '?'))
|
||||||
P(1, " newestVersion:\t" .. (M.formatVersion(status.newestVersion) or '?'))
|
P(1, " newestVersion:\t" .. (M.formatVersion(status.newestVersion) or '?'))
|
||||||
@ -740,7 +945,7 @@ local function main()
|
|||||||
end
|
end
|
||||||
|
|
||||||
elseif argTable.action == 'showAvailableVersions' then
|
elseif argTable.action == 'showAvailableVersions' then
|
||||||
local verTable,msg = M.getAvailableVersions()
|
local verTable,msg = M.getAvailableVersions(includeBetas and 'both' or 'stables')
|
||||||
if not verTable then
|
if not verTable then
|
||||||
E("error collecting version information (" .. msg .. ")")
|
E("error collecting version information (" .. msg .. ")")
|
||||||
os.exit(2)
|
os.exit(2)
|
||||||
@ -750,7 +955,7 @@ local function main()
|
|||||||
for _,ent in ipairs(verTable) do P(1, M.formatVersion(ent.version)) end
|
for _,ent in ipairs(verTable) do P(1, M.formatVersion(ent.version)) end
|
||||||
|
|
||||||
elseif argTable.action == 'showVersionInfo' then
|
elseif argTable.action == 'showVersionInfo' then
|
||||||
local vEnt,msg = M.findVersion(argTable.version)
|
local vEnt,msg = M.findVersion(argTable.version, includeBetas)
|
||||||
|
|
||||||
if vEnt then
|
if vEnt then
|
||||||
P(0, "Information on version:")
|
P(0, "Information on version:")
|
||||||
@ -761,6 +966,7 @@ local function main()
|
|||||||
P(1, " factoryFilename:\t" .. (vEnt.factoryFilename or '-'))
|
P(1, " factoryFilename:\t" .. (vEnt.factoryFilename or '-'))
|
||||||
P(1, " factoryFileSize:\t" .. (vEnt.factoryFileSize or '-'))
|
P(1, " factoryFileSize:\t" .. (vEnt.factoryFileSize or '-'))
|
||||||
P(1, " factoryMD5:\t\t" .. (vEnt.factoryMD5 or '-'))
|
P(1, " factoryMD5:\t\t" .. (vEnt.factoryMD5 or '-'))
|
||||||
|
P(1, " releaseDate:\t\t" .. (vEnt.timestamp and M.formatDate(vEnt.timestamp) or '-'))
|
||||||
if vEnt.changelog then
|
if vEnt.changelog then
|
||||||
P(1, "\n--- Changelog ---\n" .. vEnt.changelog .. '---')
|
P(1, "\n--- Changelog ---\n" .. vEnt.changelog .. '---')
|
||||||
else
|
else
|
||||||
@ -775,7 +981,7 @@ local function main()
|
|||||||
end
|
end
|
||||||
|
|
||||||
elseif argTable.action == 'imageDownload' then
|
elseif argTable.action == 'imageDownload' then
|
||||||
local vEnt,msg = M.findVersion(argTable.version)
|
local vEnt,msg = M.findVersion(argTable.version, includeBetas)
|
||||||
if vEnt == false then
|
if vEnt == false then
|
||||||
P(1, "no such version")
|
P(1, "no such version")
|
||||||
os.exit(4)
|
os.exit(4)
|
||||||
@ -797,7 +1003,7 @@ local function main()
|
|||||||
|
|
||||||
elseif argTable.action == 'imageInstall' then
|
elseif argTable.action == 'imageInstall' then
|
||||||
local vEnt, msg = nil, nil
|
local vEnt, msg = nil, nil
|
||||||
vEnt,msg = M.findVersion(argTable.version)
|
vEnt,msg = M.findVersion(argTable.version, includeBetas)
|
||||||
if vEnt == false then
|
if vEnt == false then
|
||||||
P(1, "no such version")
|
P(1, "no such version")
|
||||||
os.exit(4)
|
os.exit(4)
|
||||||
|
@ -6,7 +6,8 @@
|
|||||||
# See file LICENSE.txt or visit http://www.gnu.org/licenses/gpl.html for full license details.
|
# See file LICENSE.txt or visit http://www.gnu.org/licenses/gpl.html for full license details.
|
||||||
|
|
||||||
# start after networking
|
# start after networking
|
||||||
START=22
|
#START=22
|
||||||
|
START=61
|
||||||
|
|
||||||
LOGGER="logger -t autowifi -p 6"
|
LOGGER="logger -t autowifi -p 6"
|
||||||
|
|
||||||
|
@ -8,12 +8,34 @@
|
|||||||
|
|
||||||
local log = require('util.logger')
|
local log = require('util.logger')
|
||||||
local utils = require('util.utils')
|
local utils = require('util.utils')
|
||||||
|
local printerUtils = require('util.printer')
|
||||||
|
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
function M.hasControl(ip)
|
function M.hasControl(ip)
|
||||||
local controllerIP = M.getController()
|
local controllerIP = M.getController()
|
||||||
return (controllerIP == "" or (controllerIP ~= "" and controllerIP == ip))
|
|
||||||
|
-- no controller stored? we have control
|
||||||
|
if controllerIP == "" then return true end;
|
||||||
|
|
||||||
|
-- controller stored is same as our (requesting) ip? we have control
|
||||||
|
if(controllerIP == ip) then return true end;
|
||||||
|
|
||||||
|
-- no printer connected? we have control
|
||||||
|
local printer,msg = printerUtils.createPrinterOrFail()
|
||||||
|
if not printer or not printer:hasSocket() then
|
||||||
|
M.setController("") -- clear the controller
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- printer is idle (done printing)? we have control
|
||||||
|
local state = printer:getState()
|
||||||
|
if state == "idle" then -- TODO: define in constants somewhere
|
||||||
|
M.setController("") -- clear controller
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.getController()
|
function M.getController()
|
||||||
|
@ -78,8 +78,10 @@ function M.createPrinterOrFail(deviceId, response)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if not printer then
|
if not printer then
|
||||||
response:setError("could not open printer driver (" .. msg .. ")")
|
if response ~= nil then
|
||||||
response:addData('id', deviceId)
|
response:setError("could not open printer driver (" .. msg .. ")")
|
||||||
|
response:addData('id', deviceId)
|
||||||
|
end
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
58
test/test-d3d-updater.lua
Normal file
58
test/test-d3d-updater.lua
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
-- This script contains a number of impromptu tests to check version comparisons, kept around in case real unit tests will be created one day.
|
||||||
|
argStash = arg
|
||||||
|
arg = nil
|
||||||
|
local upd = require('d3d-updater')
|
||||||
|
arg = argStash
|
||||||
|
|
||||||
|
local function dump(o)
|
||||||
|
if type(o) == 'table' then
|
||||||
|
local s = '{ '
|
||||||
|
for k,v in pairs(o) do
|
||||||
|
if type(k) ~= 'number' then k = '"'..k..'"' end
|
||||||
|
s = s .. '['..k..'] = ' .. dump(v) .. ','
|
||||||
|
end
|
||||||
|
return s .. '} '
|
||||||
|
else
|
||||||
|
return tostring(o)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local input = '19990213'
|
||||||
|
local ts = upd.parseDate(input)
|
||||||
|
print("parse " .. input .. ": " .. ts)
|
||||||
|
print("format " .. ts .. ": " .. upd.formatDate(ts))
|
||||||
|
|
||||||
|
local vertex1 = '0.2.3'
|
||||||
|
local vertab1 = upd.parseVersion(vertex1)
|
||||||
|
print("parse " .. vertex1 .. ": " .. dump(vertab1))
|
||||||
|
print("formatted: " .. upd.formatVersion(vertab1))
|
||||||
|
|
||||||
|
local vertex2 = '0.2.3-text'
|
||||||
|
local vertab2 = upd.parseVersion(vertex2)
|
||||||
|
print("parse " .. vertex2 .. ": " .. dump(vertab2))
|
||||||
|
print("formatted: " .. upd.formatVersion(vertab2))
|
||||||
|
|
||||||
|
local vA, vB = upd.parseVersion("1.4.5-alpha"), upd.parseVersion("1.4.4-rc1")
|
||||||
|
local tsA, tsB = 100000, 100005
|
||||||
|
|
||||||
|
local cmp1,cmp2 = upd.compareVersions(vA, vB)
|
||||||
|
print("vA <=> vB: " .. cmp1 .. " / " .. dump(cmp2))
|
||||||
|
cmp1,cmp2 = upd.compareVersions(vA, vB, tsA, tsB)
|
||||||
|
print("vA/tsA <=> vB/tsB: " .. cmp1 .. " / " .. dump(cmp2))
|
||||||
|
cmp1,cmp2 = upd.compareVersions(vB, vA, tsA, tsB)
|
||||||
|
print("vB/tsA <=> vA/tsB: " .. cmp1 .. " / " .. dump(cmp2))
|
||||||
|
cmp1,cmp2 = upd.compareVersions(vA, vB, tsB, tsA)
|
||||||
|
print("vA/tsB <=> vB/tsA: " .. cmp1 .. " / " .. dump(cmp2))
|
||||||
|
|
||||||
|
local vWithout,vWith = upd.parseVersion('1.2.3'), upd.parseVersion('1.2.3-sfx')
|
||||||
|
--print("vWithout: " .. dump(vWithout) .. "; vWith: " .. dump(vWith))
|
||||||
|
cmp1,cmp2 = upd.compareVersions(vWithout, vWithout)
|
||||||
|
print("1.2.3 <=> 1.2.3: " .. cmp1 .. " / " .. dump(cmp2))
|
||||||
|
cmp1,cmp2 = upd.compareVersions(vWithout, vWith)
|
||||||
|
print("1.2.3 <=> 1.2.3-sfx: " .. cmp1 .. " / " .. dump(cmp2))
|
||||||
|
cmp1,cmp2 = upd.compareVersions(vWith, vWith)
|
||||||
|
print("1.2.3-sfx <=> 1.2.3-sfx: " .. cmp1 .. " / " .. dump(cmp2))
|
||||||
|
|
||||||
|
print("nn equal? " .. dump(upd.versionsEqual(vWithout, vWithout)))
|
||||||
|
print("ny equal? " .. dump(upd.versionsEqual(vWithout, vWith)))
|
||||||
|
print("yy equal? " .. dump(upd.versionsEqual(vWith, vWith)))
|
Loading…
Reference in New Issue
Block a user