Project transfer iPad to iPad

For those of you that have 2 iPads with Codea, here’s a program to send projects from one iPad to the other on the same network. I got tired of sending projects thru email or Dropbox to the other iPad, so I started to play with sockets. While playing with this, I ran into a few problems. One problem was with the packet size. I’m not sure if it’s changeable or how it’s changed if it can be, but there was a limit of 8192 bytes. If the project size was less than 8192 bytes, there wasn’t any problem. If it was greater, then it wouldn’t work. I had to break up the file into packets with less than 8192 bytes. I got the split working, but ran into another problem. When I tried to send multiple packets, the other iPad wouldn’t receive all of them. I wasn’t sure if the problem was in sending or receiving the packets. I added a delay in between sending the packets and that worked. I then ran into a third problem with projects that had multiple tabs. If the code in a tab didn’t have a blank line at the beginning or end, then the code of one tab was on the same line as the last line of the previous tab and caused an error when that project was executed. I got around that by adding a new line in between any tab when I read them in to send. So, here’s how to use this code. Load this on each iPad and start them. It will show you what your IP address is. Then you need to press the parameter button get their ip on both iPads. If successful, it will display their IP address. Enter the name of the project you want to send in the parameter text area project and press send. It will show you the number of bytes sent, and the other iPad will show the number of bytes received and that the project was copied to pasteboard.text . Exit the program and then create a new project and do a paste. If the code has the --# type name, then you can do a paste into and it will create each tab. If it doesn’t have the --#, then all the tabs will be in the Main tab.

function setup()  
    size=8000
    delay=.1
    socket=require("socket")
    theirIp=""
    getMyIp()
    setMySocket()
    setTheirSocket()
    parameter.action("get their ip",getTheirIp)
    parameter.text("project")
    parameter.action("send", sendMessage)
    parameter.boolean("display",false)
end

function draw()
    background(0)
    receiveTheirMessage()
end

function setMySocket()    
    server=socket.udp()
    server:setsockname(myIp,5544)
    server:settimeout(0)
end

function setTheirSocket()
    client=socket.udp()
    client:settimeout(0)
end

function getMyIp()
    server=socket.udp()
    server:setpeername("1.1.1.1",80)
    myIp,myPort=server:getsockname()
    ip1,ip2=string.match(myIp,"(%d+.%d+.%d+.)(%d+)")
    print("myIp "..myIp)
end

function getTheirIp()
    client=socket.udp()
    client:settimeout(0)
    -- send a message to everyone on this network except to myself
    for z=1,255 do
        if z~=tonumber(ip2) then
            client:setpeername(ip1..z,5544)
            client:send("sent")
        end
    end
end

function receiveTheirMessage()
    local data,msg,port=server:receivefrom()
    if data~=nil then
        if data=="sent" then
            client=socket.udp()
            client:settimeout(0)
            client:setpeername(msg,5544)
            client:send("recv"..string.sub(data,5,#data))
        elseif data=="recv" then
            print("Their Ip  "..msg)
            theirIp=msg
        elseif data=="start message" then
            output.clear()
            pasteboard.text=""
        elseif data=="end message" then
            if display then
                print(pasteboard.text)
            end
            print("\
\
"..#pasteboard.text.." bytes received and \
 copied to pasteboard.text")
        else
            pasteboard.text=pasteboard.text..data
        end
    end
end

function readProject()
    tabs=listProjectTabs(project)
    str=""
    for a,b in pairs(tabs) do
        str=str.."\
"..readProjectTab(project..":"..b)
    end
end

function sendMessage()
    output.clear()
    readProject()
    if project=="" then
        print("no project entered")
        return
    end
    if str=="" then
        print("Project  "..project.."  not found")
        return
    end
    if theirIp=="" then
        print("their Ip not received")
        return
    end
    client = socket.udp()
    client:setpeername(theirIp,5544)
    client:settimeout(0)
    client:send("start message")
    sPos=1
    ePos=0
    cnt=0
    sendData=true
    while sendData do
        ePos=ePos+size
        if ePos>=#str then
            ePos=#str
            sendData=false
        end
        tempStr=string.sub(str,sPos,ePos)
        client:send(tempStr)
        cnt=cnt+1
        print("\
\
message "..cnt.." sent "..#tempStr.." bytes")
        sPos=sPos+size
        sTime=socket.gettime()+delay
        while socket.gettime()<sTime do  
            -- create a delay between multiple sends      
        end
    end
    print(#str.." bytes sent")
    client:send("end message")
end

Just a note for the above program. I was able to transfer Cargo-Bot from one iPad to the other. That was 261,000+ bytes and the program ran OK after I pasted it into a new project. Since it didn’t have the --# tab names, Cargo-Bot was all in the Main tab.

Great! I was using up to now your “old” (from april 10, 2017) backup/restore code for transferring my projects to my new iPad Pro.

I’m thinking that once the beta version is released to everyone, I’ll change the program to create the project on the other iPad instead of just saving it to pasteboard.text. The beta version has new functions that allow you to create, delete, and to check if a project already exists. That should save a little time. Or I could add another parameter.text area to let you enter a project name that will be created on the other iPad. I’ll also have to work out creating tabs in the receiving project if the send project had tabs.

@quezadav When I got my second iPad, I modified my backup/restore program to do a full restore. That way, I was able to restore all of my projects (500+) on the new iPad in about 30 seconds.

@dave1707 listProjects, createProject and deleteProject have been available since 2.3.3. They were working for me in 2.3.6 before I installed the beta.

@XanDDemoX You are correct, they are in the 2.3.3 version. I’ve been using beta versions for so long now, that I don’t know what the latest version for the general users is. If that’s the case, I guess I’ll start changing my code.

I created new code using my program above. The new code will create a project on the destination iPad with the same name as the original project being sent. If that project already exists on the destination iPad, a message will be printed and nothing will be saved. There is a parameter.boolean switch that will allow you to overwrite an existing project. If you don’t want to overwrite the existing project but you still want the project to be sent, you can enter a new project name that will be created. If the original project contains tabs, the new project will also contain tabs. I’m still testing the code and will post it when I’m done, but I’m open to any suggestions. The version of Codea your using must have the new functions, createProject, hasProject, and deleteProject. I’ll have to check, but I’m not sure if the function hasProject was released yet or it’s only in the beta version…

@dave1707 - thanks for the code, really useful already used it on my pads no problems. Looking forward to your update.

@Bri_G Glad it’s useful. Just did more tests on my latest code. I sent the project Cargo-Bot to another iPad. I didn’t want to overwrite the Cargo-Bot project on the destination iPad, so I entered a new project name. The new project was created with all of the tabs and the copied code ran. So far so good.

Here’s a version of my latest code. The project from the sending iPad will be created on the receiving iPad. No more creating a project and pasting the code. If the project on the receiving iPad already exists, a message will be printed and nothing happens. To overwrite the code, the overwrite slider on the receiving iPad must be set to the right. If you don’t want to overwrite the code, you can enter a new project name on the receiving iPad. I tested this with Cargo-Bot and all 47 tabs were created on the receiving iPad. I used a new project name on the receiving iPad so I wouldn’t overwrite the existing project. Cargo-Bot under the new project name ran OK. To use this code, you must have the Codea version with the new functions createProject, deleteProject, and hasProject. It’s possible the function hasProject was added in the beta version now being tested. If that’s the case, the section of if hasProject in createProj() code can be commented out, but you have to make sure the project being sent doesn’t exist on the receiving iPad. If you try this, let me know if you have problems or if everything is working OK for you.

function setup()  
    size=8000
    delay=.1
    socket=require("socket")
    theirIp=""    
    getMyIp()
    setMySocket()
    setTheirSocket()
    parameter.action("get their ip",getTheirIp)
    parameter.text("projectToSend")
    parameter.boolean("overwrite",false)
    parameter.text("newProjectName")
    parameter.action("send", sendMessage)
end

function draw()
    background(0)
    receiveTheirMessage()
end

function setMySocket()    
    server=socket.udp()
    server:setsockname(myIp,5544)
    server:settimeout(0)
end

function setTheirSocket()
    client=socket.udp()
    client:settimeout(0)
end

function getMyIp()
    server=socket.udp()
    server:setpeername("1.1.1.1",80)
    myIp,myPort=server:getsockname()
    ip1,ip2=string.match(myIp,"(%d+.%d+.%d+.)(%d+)")
    print("myIp "..myIp)
end

function getTheirIp()
    client=socket.udp()
    client:settimeout(0)
    -- send a message to everyone on this network except to myself
    for z=1,255 do
        if z~=tonumber(ip2) then
            client:setpeername(ip1..z,5544)
            client:send("sent")
        end
    end
end

function receiveTheirMessage()
    local data,msg,port=server:receivefrom()
    if data~=nil then
        if data=="sent" then
            client=socket.udp()
            client:settimeout(0)
            client:setpeername(msg,5544)
            client:send("recv"..string.sub(data,5,#data))
        elseif data=="recv" then
            print("Their Ip  "..msg)
            theirIp=msg
        elseif data=="start message" then
            output.clear()
            newCode=""
        elseif data=="end message" then
            print("\
\
"..#newCode.." bytes received")
            createProj()            
        else
            newCode=newCode..data
        end
    end
end

function createProj()
    -- get project name and create the project
    for a in string.gmatch(newCode,"--PROJECT=(.-)=NAME") do
        projName=a
        if newProjectName~="" then
            projName=newProjectName
        end
        if projName~=nil then
            if hasProject(projName) then
                if overwrite then
                    print("deleted project "..projName)
                    deleteProject(projName)
                else
                    print("project "..projName.." already exists.")
                    return
                end
            end
            print("Creating project  "..projName)
            createProject(projName)
        else
            print(projName.."  not found in new code.")
        end
    end    
    -- get tab name and create the tabs and code
    for tabName,code in string.gmatch(newCode,"--TAB=(.-)=START\
(.-)(\
)--TAB==END") do
        print("created tab  "..tabName)
        saveProjectTab(projName..":"..tabName,code)
    end
end    

function readProject()
    origProject="--PROJECT="..projectToSend.."=NAME\
"
    tabs=listProjectTabs(projectToSend)
    for a,b in pairs(tabs) do
        origProject=origProject.."--TAB="..b.."=START\
"..readProjectTab(projectToSend..":"..b).."\
--TAB==END\
"
    end
end

function sendMessage()
    output.clear()
    readProject()
    if project=="" then
        print("no project entered")
        return
    end
    if origProject=="" then
        print("Project  "..project.."  not found")
        return
    end
    if theirIp=="" then
        print("their Ip not received")
        return
    end
    client = socket.udp()
    client:setpeername(theirIp,5544)
    client:settimeout(0)
    client:send("start message")
    sPos=1
    ePos=0
    cnt=0
    sendData=true
    while sendData do
        ePos=ePos+size
        if ePos>=#origProject then
            ePos=#origProject
            sendData=false
        end
        tempStr=string.sub(origProject,sPos,ePos)
        client:send(tempStr)
        cnt=cnt+1
        print("\
\
message "..cnt.." sent "..#tempStr.." bytes")
        sPos=sPos+size
        sTime=socket.gettime()+delay
        while socket.gettime()<sTime do  
            -- create a delay between multiple sends      
        end
    end
    print(#origProject.." bytes sent")
    client:send("end message")
end

@dave1707 - thanks again for the update. Transferred one project without problem, second one gave following error:

Main 67: attempt to get length of a value (global 'newCode) etc…

Attempt from pad2 to padPro

@Bri_G The only way I could duplicate the error was to comment out newCode="" and newCode=newCode..data in the function receiveTheirMessage. That would tell me that when you did the send, the receiving iPad only received the end message record and there was nothing in newCode. When I was first writing this, if I sent a message larger than the buffer size, I wouldn’t receive a message. If you get that error again, look at the print area and see if the message count is in order and size adds up to what was sent. I don’t know how the buffer size is set or if it’s the same on every network. On mine, the size looks like it’s 8192 bytes which is why I set size to 8000. You could try reducing the size variable at the beginning of the code if you keep having trouble.

@Bri_G I don’t know that much about sockets. This code was more or less trial by error. When I was trying to figure out how big of a record I could send with test code, I started at 1000 bytes and kept increasing it by 1000. When I sent 8000 or less, I received what I sent. When I sent 9000 bytes, my receive count said 8192. When I sent 10,000 or more, I received nothing. The 8192 count is an even 8K and seemed reasonable. Like I said above, I don’t know how that value is set and if it’s the same on every network.

@Bri_G Here’s a program to verify your packet size. This runs on a single iPad and sends a record to itself. Tap on sendMessage to send a message. Print statements will show how many bytes were sent and received. Tap again and the size sent will increase by 500. Keep tapping and you should see where your limit is. You should also see a record sent, but nothing received as the size increases.

function setup()  
    socket=require("socket")
    getMyIp()
    setMySocket()
    setTheirSocket()
    size=0
    parameter.action("send", sendMessage)
end

function draw()
    background(0)
    receiveTheirMessage()
end

function setMySocket()    
    server=socket.udp()
    server:setsockname(myIp,5544)
    server:settimeout(0)
end

function setTheirSocket()
    client=socket.udp()
    client:settimeout(0)
end

function getMyIp()
    server=socket.udp()
    server:setpeername("1.1.1.1",80)
    myIp,myPort=server:getsockname()
    print("myIp "..myIp)
end

function receiveTheirMessage()
    local data,msg,port=server:receivefrom()
    if data~=nil then
        print("size received  "..#data)
    end
end

function sendMessage()
    output.clear()
    size=size+500
    tab={}
    for z=1,size do
        table.insert(tab,"a")
    end
    str=table.concat(tab)
    client = socket.udp()
    client:setpeername(myIp,5544)
    client:settimeout(0)
    client:send(str)
    print("size sent was  "..#str)
end

@dave1707 8192 bytes is a limit imposed on receiving udp datagrams by LuaSocket itself for some reason. http://w3.impa.br/~diego/software/luasocket/udp.html#receive

@XanDDemoX Thanks for the info. I was searching for router packet sizes and wasn’t finding anything useful. @Bri_G I looked a your post again and saw the iPad2. I’m using an iPad Air which is faster, so maybe you need to increase the delay value. I had to add the delay because I was dropping records when sending at full speed. Let me know if you get it working so others can make changes if they run into problems too.

@dave1707 - update, tried the reverse with padPro to pad2, different code, and ran ok. Noticed sometimes, when finding the other IP can take a varied length of time sometimes quick and sometimes slow.

Ran your packet size code - both followed your own trials maxed out at 8192. Logical I think it has to be a multiple of 256.

@Bri_G Did you try increasing the delay value and see if that fixed the problem of going from the pad2 to pro.

@dave1707 - not yet tried the timer as, on retesting, have been able to get the transfer to work from pad2 to padPro.

Ran into another problem, on retrying used the alternative name field to get another file on padPro and it came back with an error that the original file existed on the padPro (which it did) and wouldn’t transfer under a new file name.

Will run timing trials any advice on limits?