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

[1.51+][Codesnippet] Auto Detect Peripheral Side

Started by Sharidan, 22 July 2013 - 06:13 AM
Sharidan #1
Posted 22 July 2013 - 08:13 AM
I have been viewing a lot of posts and code here and for the most part, most applications hardwire which side a specific peripheral has to be on. For the most part, this is most likely due to the author just setting it up quick for his or her purpose.

This little code snippet can automatically detect which side the peripheral is attached to and return the side thus transforming a hardwired side to a customizable feature. Added bonus is that you don't have to mind which side it has to sit on and that you don't have to edit the code if you change the side.


local function getDeviceSide(deviceType)
  -- List of all sides
  local lstSides = {"left","right","up","down","front","back"};
  -- Now loop through all the sides
  -- "i" holds a table counter
  -- "side" will hold the actual side text
  for i, side in pairs(lstSides) do
    if (peripheral.isPresent(side)) then
      -- Yup, there is something on this side
      -- Check the type against the device
      -- string.lower() just ensures that the passed
      -- device type is in lower case.
      -- peripheral.getType() always returns lower case texts
      if (peripheral.getType(side) == string.lower(deviceType)) then
        -- Yes, this is the device type we need, so return the side
        return side;
        -- Note, that this call to "return" also terminates
        -- further running of this function
      end -- if .getType()
    end -- if .isPresent()
  end -- for-do
  -- If we reach this point, it means that we didnt find
  -- the specified device anywhere, so return nil indicating
  -- that it doesnt exist.
  return nil;
end -- function()

Ok. Now that we have something that can detect which side a specific peripheral is connected on, it would be nice to know how to acutally use this thing. Coming right up …

In this example I will use a modem just for the fun of it.


-- Where is my modem connected, if any?
local modemSide = getDeviceSide("modem");
if (modemSide) then
  -- We did get a side, so there is a modem connected
  rednet.open(modemside);
else
  print("Please attach a modem!")
end
-- ...
-- ...
if (modemSide) then
  -- Close up the connection again
  rednet.close(modemSide);
end

That's pretty much it. Using this method, you can customize which side the peripheral is attached to and you don't have to worry about it anywhere else in your code.
Your code no longer has to rely on any device being attached to any specific side of the computer.

Just for the fun of it, here's a nifty way you can use the .getDeviceSide() function to detect a crafty turtle.


local wbSide = getDeviceSide("workbench")
if (wbSide and turtle) then
  print("Yay! I'm a Crafty Turtle.");
end

For those wanting the code without comments…
Spoiler

local function getDeviceSide(deviceType)
  local lstSides = {"left","right","up","down","front","back"};
  for i, side in pairs(lstSides) do
    if (peripheral.isPresent(side)) then
      if (peripheral.getType(side) == string.lower(deviceType)) then
        return side;
      end
    end
  end -- for-do
  return nil;
end

Happy coding :)/>

EDIT: Altered the text slightly, because the forum color coded wrong and dumped a bunch of HTML end-tag codes.
Edited on 22 July 2013 - 06:24 AM
theoriginalbit #2
Posted 22 July 2013 - 11:58 AM
Nice little tutorial, simple, straight to the point and covers something that I agree people should use in released programs.

Just a few things;
SpoilerHard-coding the sides

local lstSides = {"left","right","up","down","front","back"};
while that works, it's nicer to just use

local lstSides = rs.getSides()
which gives you that list

also checking for peripheral presence isn't required, when there is no peripheral using getType will return nil, and a string is never equal to nil.

lastly, I suggest removing the string lowering of the type, as some mods may add capitalisations on their peripherals, either that or you lower both strings (meaning you would need to keep the isPresent check in, since the nil's would throw an error when attempting to lower)……..

Just a bit of insight, this is the function I like to use…

local function findPeripheral( t, e )
  t = t:lower()
  for _,s in pairs(rs.getSides()) do
	if peripheral.getType(s) == t then
	  return peripheral.wrap(s)
	end
  end
  error("No "..(e or t).." attached to the computer.", 0)
end
Now you might say, why do you have 2 parameters… this is why

local monitor = findPeripheral("monitor") --# when not found "No monitor attached to this computer"
local chat = findPeripheral("chat") --# when not found "No chat attached to this computer", this doesn't work, chat is the chatbox instead
local chat = findPeripheral("chat", "chatbox") --# now it's "No chatbox attached to this computer"
--# another example
local bridge = findPeripheral("terminal_glasses_bridge", "openperipheral terminal glasses bridge") --# much nicer if I don't say so myself
Also it should be noted that

error("text", 0)
will print the text in red but no line number or file name, that way you can quickly exit a program, print in red, but not make the user think it's a bug with your program :)/>

EDIT: Oh, also maybe add a function or some way for it to be able to search for remote peripherals as a last resort, and then explain it. ;)/> xD
Edited on 22 July 2013 - 10:05 AM
Sharidan #3
Posted 22 July 2013 - 12:50 PM
Hmm … rs.getSides() ???

I havnt come across the "rs" namespace anywhere yet, so I didnt know about that particular function. That's why I hardwired the sides in the search table. Nice tip on the rs.getSides().

I was not aware of the fact that some mods use upper case lettering in their peripheral typing, but yes: you are absolutely correct, this is something my snippet should be able to handle fully. Thanks for bringing this to my attention.

On the subject of using error() to bug out:

My snippet doesnt bug out using error() for a number of reasons. As you can see, it'll return nil if the device wasnt found. I've used this snippet for my turtle network monitoring system. Just getting a nil will allow my network code to still run, but internally ignore using the rednet API, if no modem was attached. I've got some "peripheral attached" checking code, that then re-calls the getDeviceSide() function and then enables my network. Having the function bug out using error() would not allow this particular dynamic to work. I use the same dynamics for my bigscreen monitor display, which allows the receiving computer the freedom of still running all the services, even though the externals are not connected right now. Basically I can setup my service computer, boot up the program, then attach the modem to any side and put up the monitor next to it, pretty much in any order I want … user friendly :)/>

I'll look into changing the snippet to be able to handle remote peripherals aswell.

I'll update the original post as soon as I've got all this sorted :)/>

Thanks for all the feedback theoriginalbit.
theoriginalbit #4
Posted 22 July 2013 - 01:04 PM
Hmm … rs.getSides() ???
I havnt come across the "rs" namespace anywhere yet, so I didnt know about that particular function. That's why I hardwired the sides in the search table. Nice tip on the rs.getSides().
`rs` is API shorthand for the `redstone` API, both are valid. so redstone.getSides() will return the list.

I was not aware of the fact that some mods use upper case lettering in their peripheral typing, but yes: you are absolutely correct, this is something my snippet should be able to handle fully. Thanks for bringing this to my attention.
they aren't forced to use lowercase but most use lowercase, I think I've only ever seen one use upper and it was ICBM i think…

On the subject of using error() to bug out:
Yeh wasn't saying to use it, was just showing the one I like to use in my programs. just for a bit of diversity. xD

I'll look into changing the snippet to be able to handle remote peripherals aswell.
Shouldn't be too hard, just iterate through `p.getNamesRemote()` and use `p.getTypeRemote(name)` to check if it is of the required type.

Thanks for all the feedback theoriginalbit.
no problems :)/>
MudkipTheEpic #5
Posted 22 July 2013 - 02:38 PM
When remote peripherals came out, the devs added a few features to the peripheral API to make handling remote peripherals easier.

Simply use peripheral.getNames() to get the side/registered names of all connected peripherals.

They also made peripheral.call/wrap/getType/isPresent, etc. work for remote peripherals just the same as directly connected ones. If there was a peripheral_# on a network connected to the computer, you could still peripheral.wrap("peripheral_#").

To add remote functionality to your code, replace rs.getSides with peripheral.getNames.
Sharidan #6
Posted 22 July 2013 - 02:59 PM
Thanks for all the great suggestions.

I had already started scouting the wiki for additional information and found most of what I need. All these great suggestions, have inspired me to write a full "hardware" detector. I will however post the full hardwareDetector as a seperate tutorial as it extends past what I intended with this tutorial.

Thanks again guys :)/>