Jump to content




[help]wireless peripheral api


  • You cannot reply to this topic
25 replies to this topic

#1 KingofGamesYami

  • Members
  • 3,002 posts
  • LocationUnited States of America

Posted 06 May 2014 - 09:30 PM

I've decided (because of pocket computers) to create an api that allows you to control peripherals wirelessly through a "slave" computer. I would like to know how peripherals work, and how I would overwrite them the way I want (meaning: I want to keep the same commands).

I currently have:

main
and
slave
Note: a & b are undefined for now... I'm not sure if I will have the user set them or if they will be pre-determined.

Edited by KingofGamesYami, 07 May 2014 - 09:35 PM.


#2 CometWolf

  • Members
  • 1,283 posts

Posted 06 May 2014 - 09:57 PM

I've never done such a thing myself, but i'd imagine it's pretty simple. One thing i noted with your code, was how you try to create the new peripheral function table.
	    for k, v in pairs(msg[4]) do
			    per[k] = modem.transmit(channela, channelb, v)
	    end
This won't work, as you're storing the result of modem.transmit in per[k], not an actual function.
	    for k, v in pairs(msg[4]) do
			    per[k] = function(...)
				  modem.transmit(channela, channelb, {call = v,params = {...}})
				  return {os.pullEvent("modem_message")}[5]
			    end
	    end
Obviously you'd want some timeout, connection confirm and id confirmation on this stuff aswell, but you get the idea. It needs to be a function

#3 KingofGamesYami

  • Members
  • 3,002 posts
  • LocationUnited States of America

Posted 06 May 2014 - 10:52 PM

View PostCometWolf, on 06 May 2014 - 09:57 PM, said:

I've never done such a thing myself, but i'd imagine it's pretty simple. One thing i noted with your code, was how you try to create the new peripheral function table.
		for k, v in pairs(msg[4]) do
				per[k] = modem.transmit(channela, channelb, v)
		end
This won't work, as you're storing the result of modem.transmit in per[k], not an actual function.
		for k, v in pairs(msg[4]) do
				per[k] = function(...)
				  modem.transmit(channela, channelb, {call = v,params = {...}})
				  return {os.pullEvent("modem_message")}[5]
				end
		end
Obviously you'd want some timeout, connection confirm and id confirmation on this stuff aswell, but you get the idea. It needs to be a function
Thanks for the hint! I have some experience overwriting an api, as I did create a TurtleTracker with a "returnHome" function.
I don't think it's complicated, but the modem_message part is the most difficult.
PS: is there a way to check if the request timed out? I know you do os.pullEvent("modem_message", 5) for a 5 sec wait, but I want my code to error when it doesn't get a response in that amount of time.

Edited by KingofGamesYami, 06 May 2014 - 10:54 PM.


#4 HometownPotato

  • Members
  • 62 posts

Posted 06 May 2014 - 11:01 PM

You may want to change 'return {os.pullEvent("modem_message")}[5]' to ' 'return ({os.pullEvent("modem_message")})[5]' since I always seem to have problems in all versions of Lua doing {...}[x] as opposed to ({...})[x]

Yeah, I meant change sorry.

Edited by HometownPotato, 06 May 2014 - 11:27 PM.


#5 KingofGamesYami

  • Members
  • 3,002 posts
  • LocationUnited States of America

Posted 06 May 2014 - 11:13 PM

View PostHometownPotato, on 06 May 2014 - 11:01 PM, said:

You may want to chance 'return {os.pullEvent("modem_message")}[5]' to ' 'return ({os.pullEvent("modem_message")})[5]' since I always seem to have problems in all versions of Lua doing {...}[x] as opposed to ({...})[x]
I'm going to assume you ment change instead of chance, and thanks for the advice.

What I really need now is how to tell the slave which wrapped peripheral it should use... or rather how to get the info out of this example:

mon = wp.wrap("right", 10, 10)
mon.write("hello") --how do I get that this is calling the peripheral associated with "right" on the 10 frequency?

Edited by KingofGamesYami, 06 May 2014 - 11:18 PM.


#6 Bomb Bloke

    Hobbyist Coder

  • Moderators
  • 7,099 posts
  • LocationTasmania (AU)

Posted 06 May 2014 - 11:40 PM

In the "wrap" function, before you added in your remote-call functions, you might do this:

per.side = side
per.channela = channela
per.channelb = channelb

Now that data should be accessible via self.

#7 KingofGamesYami

  • Members
  • 3,002 posts
  • LocationUnited States of America

Posted 06 May 2014 - 11:45 PM

View PostBomb Bloke, on 06 May 2014 - 11:40 PM, said:

In the "wrap" function, before you added in your remote-call functions, you might do this:

per.side = side
per.channela = channela
per.channelb = channelb

Now that data should be accessible via self.
ugh... I thought to use self you had to use
per = {
command = function(self) --#do stuff 
end
}
per:command()
but then, thats what I get from teaching myself by looking at other peoples code (Lyqyd's touchpoint api)
edit: updated wrap function
function wrap(side, channela, channelb)
	local per = {
	side = side,
	channela = channela,
	channelb = channelb,
}
	modem.open(channelb)
	modem.transmit(channela, channelb, side)
	msg = {os.pullEvent("modem_message")}
	for k, v in pairs(msg[4]) do
		per[k] = function(...)
			modem.transmit(channela, channelb, {call = v, params = {...}})
			return ({os.pullEvent("modem_message")})[4]
		end
	end
	return per
end

Edited by KingofGamesYami, 06 May 2014 - 11:52 PM.


#8 Bomb Bloke

    Hobbyist Coder

  • Moderators
  • 7,099 posts
  • LocationTasmania (AU)

Posted 07 May 2014 - 12:06 AM

Something like that. Or "per.command(per)".

Also;

        for k, v in pairs(msg[4]) do
                per[k] = function(self, ...)
                        modem.transmit(self.channela, self.channelb, {call = self.side, params = {...}})
                        return ({os.pullEvent("modem_message")})[4]
                end
        end

Or some such thing. I must confess this isn't something I've tried (at least in Lua). You'll need to tweak your slave's wrapping code, by the way - it doesn't seem to properly make use of what's sent to it. Or you could just forget about wrapping things on the slave side and just use peripheral.call for everything. Yeah, probably go with that.

I suspect metatables might offer something more to your liking here, but if so I'll leave the explanation to someone with more experience in the area.

#9 KingofGamesYami

  • Members
  • 3,002 posts
  • LocationUnited States of America

Posted 07 May 2014 - 12:37 AM

View PostBomb Bloke, on 07 May 2014 - 12:06 AM, said:

Something like that. Or "per.command(per)".

Also;

        for k, v in pairs(msg[4]) do
                per[k] = function(self, ...)
                        modem.transmit(self.channela, self.channelb, {call = self.side, params = {...}})
                        return ({os.pullEvent("modem_message")})[4]
                end
        end

Or some such thing. I must confess this isn't something I've tried (at least in Lua). You'll need to tweak your slave's wrapping code, by the way - it doesn't seem to properly make use of what's sent to it. Or you could just forget about wrapping things on the slave side and just use peripheral.call for everything. Yeah, probably go with that.

I suspect metatables might offer something more to your liking here, but if so I'll leave the explanation to someone with more experience in the area.
I've been updating the slave, just haven't posted what I have (yet). I know how/what a metatable is/does whatever, and it would be a better idea in this case I think.

edit:
setmetatable(per, {__index = msg[4]})
something like this?

nevermind... with that, I wouldn't be able to add the fact that it is sending/recieving a message.

Edited by KingofGamesYami, 07 May 2014 - 12:43 AM.


#10 KingofGamesYami

  • Members
  • 3,002 posts
  • LocationUnited States of America

Posted 07 May 2014 - 01:04 AM

I just thought of this... How would one get a "mouse_click" event with this? The program needs something that constantly checks for updates from the slave, but I don't want to screw up someone's program by making them use parallel and creating a "waitforevent" function.

Edited by KingofGamesYami, 07 May 2014 - 01:06 AM.


#11 Bomb Bloke

    Hobbyist Coder

  • Moderators
  • 7,099 posts
  • LocationTasmania (AU)

Posted 07 May 2014 - 01:25 AM

It's either a co-routine, or you implement a customised os.pullEvent() function.

For example: You're probably aware that the RedNet API is a wrapper for the API available to modems. When you send a modem message, a rednet message event also gets generated.

This is done by a function in the rednet API that sits there waiting for modem messages to come in. When it spots one, it generates a corresponding rednet event. When a given computer boots, it runs this function in parallel with the shell, so that rednet API function is loaded and active at all times (regardless as to whether the system even has a modem).

In your case, you'd likely find it easier to embed two functions in your API: one which overrides os.pullEvent(), and one that restores the original version of os.pullEvent(). The override would basically call the original function, check to see whether the incoming event was a peripheral-related modem message, and if so queue a related "spoof" event (returning the received event unaltered if not).

#12 KingofGamesYami

  • Members
  • 3,002 posts
  • LocationUnited States of America

Posted 07 May 2014 - 01:37 AM

View PostBomb Bloke, on 07 May 2014 - 01:25 AM, said:

It's either a co-routine, or you implement a customised os.pullEvent() function.

For example: You're probably aware that the RedNet API is a wrapper for the API available to modems. When you send a modem message, a rednet message event also gets generated.

This is done by a function in the rednet API that sits there waiting for modem messages to come in. When it spots one, it generates a corresponding rednet event. When a given computer boots, it runs this function in parallel with the shell, so that rednet API function is loaded and active at all times (regardless as to whether the system even has a modem).

In your case, you'd likely find it easier to embed two functions in your API: one which overrides os.pullEvent(), and one that restores the original version of os.pullEvent(). The override would basically call the original function, check to see whether the incoming event was a peripheral-related modem message, and if so queue a related "spoof" event (returning the received event unaltered if not).
something like
oldos = {}
for k, v in pairs(os) do
 oldos[k] = v
end
function os.pullEvent()
 event = {oldos.pullEvent()}
 if event[1] == "modem_message" and event[5][1] == "wpe" then
  return unpack(event[5][2])
 else
  return unpack(event)
 end
end

Edited by KingofGamesYami, 07 May 2014 - 01:37 AM.


#13 Bomb Bloke

    Hobbyist Coder

  • Moderators
  • 7,099 posts
  • LocationTasmania (AU)

Posted 07 May 2014 - 02:49 AM

Something like that. You wouldn't need to backup all the pointers in "os" (just os.pullEvent), you'd require an additional check to ensure event[5] is a table before trying to index into it, and you'd need to account for event filters.

#14 KingofGamesYami

  • Members
  • 3,002 posts
  • LocationUnited States of America

Posted 07 May 2014 - 11:51 AM

View PostBomb Bloke, on 07 May 2014 - 02:49 AM, said:

Something like that. You wouldn't need to backup all the pointers in "os" (just os.pullEvent), you'd require an additional check to ensure event[5] is a table before trying to index into it, and you'd need to account for event filters.
Would I also need to overwrite os.pullEventRaw? some programs will use it.

oldos = {}
for k, v in pairs(os) do
	oldos[k] = v
end
function os.pullEvent(...)
	local args = {...}
	if args[1] == "wpe" then
		event = oldos.pullEvent("modem_message")
		if type(event[5]) == "table" and event[5][1] == "wpe" then
			return unpack(event[5][2])
		else
			return os.pullEvent(unpack(args))
		end
	else
		local event = {oldos.pullEvent(unpack(args))}
	end
 	if event[1] == "modem_message" and type(event[5]) == "table" and event[5][1] == "wpe" and (#args == 0 or (#args = 1 and args[1] = "wpe")) then
  		return unpack(event[5][2])
	else
 		return unpack(event)
 	end
end

Edited by KingofGamesYami, 07 May 2014 - 12:01 PM.


#15 Bomb Bloke

    Hobbyist Coder

  • Moderators
  • 7,099 posts
  • LocationTasmania (AU)

Posted 07 May 2014 - 12:03 PM

That entirely up to you. If you do so, however, then there's no need to override os.pullEvent (as that also uses os.pullEventRaw - os.pullEvent only really exists to do this same sort of thing; it looks for a certain event ("terminate") and performs a special action when it spots it ("kills your script")).

#16 KingofGamesYami

  • Members
  • 3,002 posts
  • LocationUnited States of America

Posted 07 May 2014 - 12:10 PM

If I overwrite os.pullEventRaw, and do this:
return os.pullEventRaw(unpack(args))
will this turn os.pullEvent() into an os.pullEventRaw(), thus unintentionally making peoples scripts un-terminatable?

#17 Bomb Bloke

    Hobbyist Coder

  • Moderators
  • 7,099 posts
  • LocationTasmania (AU)

Posted 07 May 2014 - 12:16 PM

You'd only need to worry about that if you specifically rigged os.pullEventRaw to look for "terminate" events and prevented it from passing them on to os.pullEvent. That is to say, you'd need to go out of your way to break that functionality.

#18 KingofGamesYami

  • Members
  • 3,002 posts
  • LocationUnited States of America

Posted 07 May 2014 - 12:27 PM

View PostBomb Bloke, on 07 May 2014 - 12:16 PM, said:

You'd only need to worry about that if you specifically rigged os.pullEventRaw to look for "terminate" events and prevented it from passing them on to os.pullEvent. That is to say, you'd need to go out of your way to break that functionality.
Ok thanks. Wasn't sure how that would work.

#19 KingofGamesYami

  • Members
  • 3,002 posts
  • LocationUnited States of America

Posted 07 May 2014 - 09:57 PM

My "slave" script is erroring now;
-snip- I'm dumb...

Edited by KingofGamesYami, 07 May 2014 - 10:09 PM.


#20 Bomb Bloke

    Hobbyist Coder

  • Moderators
  • 7,099 posts
  • LocationTasmania (AU)

Posted 07 May 2014 - 10:24 PM

Hmm. Can't really see a problem there, but there's a missing "then" on line 28.

By the way, aren't all args guarenteed to be strings - I'm not sure why you're checking for this? I also got the impression modem.open() doesn't take them - wouldn't you need to use tonumber(args[1])?





1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users