This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
Kryptanyte's profile picture

[File Handling Error]Receiving and writing files

Started by Kryptanyte, 01 March 2013 - 07:47 PM
Kryptanyte #1
Posted 01 March 2013 - 08:47 PM
I'm currently attempting to develop a update system for a project on a server I am an admin on, its pretty basic although it might be confusing, I have actually confused myself with the reading of the files by putting tables in tables I think? I'm not too sure and this could all because I'm being stupid and doing something that isn't possible or doing it wrong, not sure but here's the error;

<filename>:51: attempt to concatenate string and nil

Here's the programs;


Receiver Program
Spoiler

--Updater Recieving Program V1
rednet.open("right")
function progRecieve()
	
	event1, id1, message1 = os.pullEvent()
	
	if event1 == "rednet_message" then
	
		fileNumber = message1
		term.clear()
		term.setCursorPos(1,1)
		print("Number Recieved")

	else
	
	end	
	
	for q = 1, 8 do
	
		event2, id2, message2 = os.pullEvent()
	
		if event2 == "rednet_message" then
	
			fileName = textutils.unserialize(message2)
			print("File Name: "..fileName)

		else
	
		end
		
		event3, id3, message3 = os.pullEvent()
	
		if event3 == "rednet_message" then
		
			progTable = {}
			progTable = textutils.unserialize(message3)
		
		else
	
		end
	
		writeProg(progTable, tostring(fileName))
	
	end
	
end

function writeProg(prog, fileWName)

	
	local file = fs.open("TestDir/"..fileWName, "w")
	
	for i = 1, 1 do
		
		file.write(prog)
		
	end
	file.close()
	fileWName = nil
	
end

program, fileName = progRecieve()
writeProg(program, fileName)


Sender Program (Aka where all the files are stored)
Spoiler

--Updater Sender V1

rednet.open("right")

function sendProg(progsTable, sendID, fileNames)
	
	rednet.send(1, "8")
	sleep(1)
	
	for i = 1, #progsTable do
	
		rednet.send(1, textutils.serialize(fileNames[i]))
		sleep(1)
		rednet.send(1, textutils.serialize(progsTable[i]))
	
	end
	return
end

function convertProg(progs)

progsTable = {}

	for i = 1, #progs do
	
		local oFile = fs.open(tostring(progs[i]),'r')
		prog = {}
		prog = oFile.readAll()
		oFile:close()
		table.insert(progsTable, prog)
		
	end
	
	progsTable = textutils.serialize(progsTable)
	
	return progsTable
	
end

function runMain(filesName, saveNames)

	progsTable = convertProg(filesName)
	sendProgTable = textutils.serialize(progsTable)
	sendProg(sendProgTable, 1, saveNames)

end

progLocs = {"/cake", "/can", "/case", "/computer", "/foot", "/krypt", "/mouse", "/notepad", "/pen", "/test", "/test1"}
progNames = {"cake", "can", "case", "computer", "foot", "krypt", "mouse", "notepad", "pen", "test", "test1"}
runMain(progLocs, progNames)

Sorry for the messy weird code that you may have issues reading etc, but there's also an odd bug that I noted, in the receiving program, for debug reasons, I added a clear and a print in a couple of places to tell me where its at.

Well first off, the term.clear() after its meant to receive how many programs will not fire, not sure whats going on there, then after that the line "print("File Name: "..fileName)" prints the number.

This is quite possibly the weirdest code that I have ever made, it also thinks that when you swap out the 8 in the for statement for "fileNumber" it says something like the for statement has no max amount of times it can repeat.

To anyone who can help, thank you and I would be amazed if anyone could figure this out…. It wouldn't just be my emulator would it? o.O

Cheers

Krypt
theoriginalbit #2
Posted 01 March 2013 - 10:10 PM
I'm not too sure what the loops are doing, and why you are pulling events several times in the loops and such.

Tables for the messages are defs the easiest way to do this. the basic concepts are right too but you just have some mistakes that are causing some issues.

I was feeling extremely bored so I put this together its based off yours, its only basic, it can only do one file at a time, but if you look in the client you can see that you are able to still do multiple, just have to be separate requests (note people don't always do this, make solutions, we aren't code monkeys)

Server
Client

here is the outcome of the program: http://puu.sh/2ac4B

if you have any questions about how the code works or such just ask.
Kryptanyte #3
Posted 02 March 2013 - 01:08 AM
Well this was originally developed as a single file sender, but was intended to be a full OS updater. I'm trying to make it so you can pass it two tables, one with the locations of the files, the second with names of the files so the receiver knows what to save it as, this is what the loops everywhere are for. I'm probably a bit out of it but I'll try to explain it.

I have multiple pull events because I am receiving 3 things, 1st thing; how many files there are. 2nd thing; the name of the program to save to. 3rd thing; the actual program. This is due to the fact that this program is designed to basically be standalone on the server. So say I implement this into an OS, the main OS programs will check for an update, if available it will download the new updater and the updater will then download the files that have been updated. However say there was a bug with one version of the OS, say I created a totally new file, the clients don't know what the file is called, so I send the whole lot to the client from the server standalone. Also helps as the server/s that handle the updating are the mainframe for the whole network.

I have looked at your program, and as you can see from my above explanation, its not exactly what I am trying to however that is exactly what you would want to put in a cloud file server, have it setup so that the server sends the information of each directory and if the client wants to download a file, it requests it.

Also to;

(note people don't always do this, make solutions, we aren't code monkeys)

I know and I do thank you for creating such a program that I will possibly understand in a few months, maybe. xD
theoriginalbit #4
Posted 02 March 2013 - 01:23 AM
Well this was originally developed as a single file sender, but was intended to be a full OS updater. I'm trying to make it so you can pass it two tables, one with the locations of the files, the second with names of the files so the receiver knows what to save it as, this is what the loops everywhere are for. I'm probably a bit out of it but I'll try to explain it.

I have multiple pull events because I am receiving 3 things, 1st thing; how many files there are. 2nd thing; the name of the program to save to. 3rd thing; the actual program. This is due to the fact that this program is designed to basically be standalone on the server. So say I implement this into an OS, the main OS programs will check for an update, if available it will download the new updater and the updater will then download the files that have been updated. However say there was a bug with one version of the OS, say I created a totally new file, the clients don't know what the file is called, so I send the whole lot to the client from the server standalone. Also helps as the server/s that handle the updating are the mainframe for the whole network.
Ok I understand, can be achieved better than relying on getting 3 messages in the correct order. the easiest thing would be to do something like this on the server:

local filesToSend = {}
for i = 1, #filesList do -- files list here is a list of all your file paths
  local filePath = filesList[i]
  local handle = fs.open( filePath, 'r' )
  if handle then
    filesToSend[ filePath ] = handle.readAll()
    handle.close()
  end
end

rednet.send( <id>, textutils.serialize( filesToSend ) ) -- make the files to send table a string and send it

then on the receiving end

-- get the message in
msg = textutils.unserialize(msg) -- change it back to a table
for k,v in pairs( msg ) do -- lets go through the table getting the key (paths) and value (contents) from it
  local h = fs.open(k, 'w')
  if h then
    h.write( v )
    h.close()
  else
    printError("Cannot update file "..k )
  end
end
Or something to that effect. I typed this out here in the editor so I did not test this version unlike my previous one, but the logic should be sound.

I know and I do thank you for creating such a program that I will possibly understand in a few months, maybe. xD
My solution defs isn't that different to yours.
Kryptanyte #5
Posted 02 March 2013 - 01:36 AM
Yeah, I read the first line of your post and thought, should of put it in one table, xD. I tend to do that a lot, I don't know why.

I see how you did it there, the only thing is when the server is getting all the programs, is there a way to make it so that the filepath saved into the table to send is rather a custom filepath? Instead of just where it got the actual program from. For example, say the updates were saved in directory "Updates" and the save directory was "Main Files" or "Configs" or something.

How exactly would to you about doing that? (I know of tables, I know you can do crazy stuff like this, I just don't know how to do. Also sorry for asking this here, my internet is capped and is slow. This is pretty much the only site that doesn't take an hour to load…)
theoriginalbit #6
Posted 02 March 2013 - 01:43 AM
You could have it do
for k,v in pairs(filesList) do
  -- then open and read k, and v is the path that it goes into, then the rest is the same as previous
end
then your table would be say

filesList = {
  ["server/path"] = "/some/install/path"
}


Tables info in the PIL hopefully you can load it
Kryptanyte #7
Posted 02 March 2013 - 01:46 AM
Thank you very much for your help dude (Never EVER thought I would say such a thing to an aussie.) you are an extremely helpful person
theoriginalbit #8
Posted 02 March 2013 - 01:55 AM
your welcome, anytime … watch your mouth there mate talking down about us aussies, might not help next time if thats your attitude. :P/> damn kiwis.
Kryptanyte #9
Posted 02 March 2013 - 01:59 AM
your welcome, anytime … watch your mouth there mate talking down about us aussies, might not help next time if thats your attitude. :P/> damn kiwis.

Thats ok, seems that we are in a stalemate anyway. All we ever seem to do is sit across the street from each other yelling insults xD

*EDIT*

Just opened up that code for storing the programs, this is what I have made with it, just the sending side;

Spoiler

--Updater Sender V1

rednet.open("right")

function convertProg(fileList)

local filesToSend = {}

    for k,v in pairs( filesList ) do -- files list here is a list of all your file paths
    
        local filePath = filesList[i]
        local handle = fs.open( filePath, 'r' )
        
        if handle then
        
            filesToSend[ filePath ] = handle.readAll()
            handle.close()
        
        end
        
    end
    
    rednet.send(1, textutils.serialize(filesToSend))

end

progNames = {
["cake"] = "TestDir/cake",
["can"] = "TestDir/can",
["case"] = "TestDir/case",
["computer"] = "TestDir/computer",
["foot"] = "TestDir/foot",
["krypt"] = "TestDir/krypt",
["mouse"] = "TestDir/mouse",
["notepad"] = "TestDir/notepad",
["pen"] = "TestDir/pen",
["test"] = "TestDir/test",
["test1"] = "TestDir/test1"
}

convertProg((progNames))


Don't mind the names of the function etc, I just deleted most of the original code. I'm picking up an error when its trying to kick start the for loop. It says; bad argument : table expected, got nil. Sorry its almost 3am, am I missing something here? or is it a genuine problem with the syntax, as you said, you didn't test it.
theoriginalbit #10
Posted 02 March 2013 - 02:42 AM
the for loop is looking for filesList
the table name is progNames

also table declarations should be above where they are used

side note: I know its almost 3am, we are only 2hrs behind.
Kryptanyte #11
Posted 02 March 2013 - 11:51 AM
The fact the table is where it is shouldn't matter as it's being called in a function. or does it still have to do this?

*EDIT*

I fixed it somewhat, when on the server side program, you need to change the line



local filePath = filesList[i]

to

local filePath = k


So that was my bad, also need to figure out the custom directory save in the table. At the moment, it just saves to the main directory. Working on this now

*EDIT 2*

Well, figured it out in 5 mins, when reading the files on serverside change the readAll() line to



filesToSend[ v ] = handle.readAll()


Updated code;

Server
Spoiler

--Updater Sender Program V2

rednet.open("right")

function sendFiles(fileList)

local filesToSend = {}

    for k,v in pairs( filesList ) do -- files list here is a list of all your file paths
    
        local filePath = k
        local handle = fs.open( filePath, 'r' )
        
        if handle then
        
            filesToSend[ v ] = handle.readAll()
            handle.close()
        
        end
        
    end
    
    rednet.send(1, textutils.serialize(filesToSend))

end

filesLists = {
 ["cake"] = "/TestDir/cake",
 ["can"] = "/TestDir/can",
 ["case"] = "/TestDir/case",
 ["computer"] = "/TestDir/computer",
 ["foot"] = "/TestDir/foot",
 ["krypt"] = "/TestDir/krypt",
 ["mouse"] = "/TestDir/mouse",
 ["notepad"] = "/TestDir/notepad",
 ["pen"] = "/TestDir/pen",
 ["test"] = "/TestDir/test",
 ["test1"] = "/TestDir/test1"
}

sendFiles(filesLists)

Client Program
Spoiler

--Updater Receiving Program V2

rednet.open("right")

function progRecieve()
    
    event, id, msg, dist = os.pullEvent("rednet_message")
    
    msg = textutils.unserialize(msg) -- change it back to a table
    
    for k,v in pairs( msg ) do -- lets go through the table getting the key (paths) and value (contents) from it
        
        local h = fs.open(k, 'w')
            
            if h then
            
                h.write( v )
                h.close()

            else

                printError("Cannot update file "..k )

            end
            
    end
    
end


program, fileName = progRecieve()


To anyone who uses these codes please give TheOriginalBit and myself credit, mostly TheOriginalBit though.

Cheers

Krypt