Skip to content

bryson404/test

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

28 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

-- ============================================ -- KIRITO SOFTWORKS -- JBOD Network Storage System v3.5 - BUG FIX -- For CC: Tweaked (ComputerCraft) -- ============================================

local JBOD = { version = "3.5", drives = {}, debug = false }

-- ============================================ -- SUPPORTED PERIPHERAL TYPES -- ============================================

JBOD.VALID_TYPES = { "drive", "disk_drive", "diskraid", "disk_raid", "advanced_disk_raid", "storage", "drive_bay", "media_drive", }

function JBOD:isValidStorageType(peripheralType) if not peripheralType then return false end peripheralType = peripheralType:lower()

for _, validType in ipairs(self.VALID_TYPES) do
    if peripheralType:find(validType) then
        return true
    end
end
return false

end

-- ============================================ -- DRIVE DETECTION - FIXED -- ============================================

function JBOD:scanDrives() self.drives = {} local peripherals = peripheral.getNames()

print("Scanning for storage devices...")

for _, name in ipairs(peripherals) do
    local pType = peripheral.getType(name)
    
    if self:isValidStorageType(pType) then
        local drive = peripheral.wrap(name)
        local hasMedia = false
        
        -- SAFE check for media presence
        if drive.isDiskPresent then
            -- Wrap in pcall to prevent errors
            local ok, result = pcall(function()
                return drive.isDiskPresent()
            end)
            if ok then
                hasMedia = result
            else
                -- If isDiskPresent errors, assume no media or try other methods
                hasMedia = false
            end
        end
        
        -- If no isDiskPresent or it returned false, check other methods
        if not hasMedia then
            if drive.hasDisk then
                local ok, result = pcall(function()
                    return drive.hasDisk()
                end)
                if ok then hasMedia = result end
                
            elseif drive.isMediaPresent then
                local ok, result = pcall(function()
                    return drive.isMediaPresent()
                end)
                if ok then hasMedia = result end
                
            elseif drive.getDiskID then
                -- Try to get disk ID - if it works, media is present
                local ok, id = pcall(function()
                    return drive.getDiskID()
                end)
                hasMedia = ok and id ~= nil
            else
                -- No way to check - assume present for raid blocks
                hasMedia = true
            end
        end
        
        if hasMedia then
            local mountPath = nil
            
            -- SAFE getMountPath
            if drive.getMountPath then
                local ok, result = pcall(function()
                    return drive.getMountPath()
                end)
                if ok then mountPath = result end
            end
            
            -- For raid blocks without mount paths
            if not mountPath and (drive.list or drive.getCapacity) then
                mountPath = name
            end
            
            if mountPath or drive.list then
                -- SAFE get disk ID
                local diskId = "N/A"
                if drive.getDiskID then
                    local ok, id = pcall(function()
                        return drive.getDiskID()
                    end)
                    if ok and id then diskId = tostring(id) end
                elseif drive.getID then
                    local ok, id = pcall(function()
                        return drive.getID()
                    end)
                    if ok and id then diskId = tostring(id) end
                end
                
                table.insert(self.drives, {
                    name = name,
                    peripheral = drive,
                    pType = pType,
                    mountPath = mountPath or name,
                    diskId = diskId,
                    fullPath = mountPath and ("/" .. mountPath) or nil,
                    isRaid = pType:find("raid") ~= nil
                })
                
                if self.debug then
                    print("  Found: " .. name .. " [" .. pType .. "]")
                end
            end
        else
            if self.debug then
                print("  " .. name .. " [" .. pType .. "] - no media")
            end
        end
    end
end

table.sort(self.drives, function(a, b) return a.name < b.name end)
print("Total storage devices: " .. #self.drives)
return #self.drives

end

-- ============================================ -- FILE OPERATIONS -- ============================================

function JBOD:listFiles() local files = {}

for driveIndex, drive in ipairs(self.drives) do
    local p = drive.peripheral
    
    -- Method 1: Standard mount path
    if drive.fullPath and fs.isDir(drive.fullPath) then
        for _, name in ipairs(fs.list(drive.fullPath)) do
            local fp = drive.fullPath .. "/" .. name
            if not fs.isDir(fp) then
                table.insert(files, {
                    name = name,
                    size = fs.getSize(fp),
                    driveIndex = driveIndex,
                    drive = drive.name,
                    driveType = drive.pType
                })
            end
        end
        
    -- Method 2: Direct peripheral methods (raid blocks)
    elseif p.list then
        local ok, items = pcall(function()
            return p.list("/")
        end)
        if ok and items then
            for name, size in pairs(items) do
                if type(size) == "number" then
                    table.insert(files, {
                        name = name,
                        size = size,
                        driveIndex = driveIndex,
                        drive = drive.name,
                        driveType = drive.pType
                    })
                end
            end
        end
    end
end

table.sort(files, function(a, b) return a.name:lower() < b.name:lower() end)
return files

end

function JBOD:findFile(name) for _, f in ipairs(self:listFiles()) do if f.name == name then return f end end return nil end

function JBOD:read(name) local f = self:findFile(name) if not f then return nil, "File not found" end

local drive = self.drives[f.driveIndex]
local p = drive.peripheral

-- Method 1: Standard fs
if drive.fullPath then
    local path = drive.fullPath .. "/" .. name
    local h = fs.open(path, "r")
    if h then
        local data = h.readAll()
        h.close()
        return data
    end
end

-- Method 2: Direct read
if p.read then
    local ok, data = pcall(function()
        local h = p.read(name)
        if h then
            local d = h.readAll()
            h.close()
            return d
        end
    end)
    if ok and data then return data end
end

return nil, "Read failed"

end

function JBOD:write(name, data) local existing = self:findFile(name) local targetDrive = nil

if existing then
    targetDrive = existing.driveIndex
else
    local bestFree = -1
    for i, d in ipairs(self.drives) do
        local free = self:getDriveFreeSpace(i)
        if free > bestFree then
            bestFree = free
            targetDrive = i
        end
    end
end

if not targetDrive then return false, "No drives available" end

if self:getDriveFreeSpace(targetDrive) < #data then
    return false, "Not enough space"
end

local drive = self.drives[targetDrive]

-- Method 1: Standard fs
if drive.fullPath then
    local path = drive.fullPath .. "/" .. name
    local h = fs.open(path, "w")
    if h then
        h.write(data)
        h.close()
        return true
    end
end

-- Method 2: Direct write
if drive.peripheral.write then
    local ok = pcall(function()
        local h = drive.peripheral.write(name)
        if h then
            h.write(data)
            h.close()
            return true
        end
    end)
    if ok then return true end
end

return false, "Write failed"

end

function JBOD:delete(name) local f = self:findFile(name) if not f then return false, "Not found" end

local drive = self.drives[f.driveIndex]

if drive.fullPath then
    fs.delete(drive.fullPath .. "/" .. name)
    return true
end

if drive.peripheral.delete then
    local ok = pcall(function()
        drive.peripheral.delete(name)
    end)
    return ok
end

return false

end

-- ============================================ -- SPACE CALCULATION -- ============================================

function JBOD:getDriveFreeSpace(index) local drive = self.drives[index] if not drive then return 0 end

-- Method 1: fs.getFreeSpace
if drive.fullPath then
    local ok, free = pcall(function()
        return fs.getFreeSpace(drive.fullPath)
    end)
    if ok then return free end
end

-- Method 2: peripheral method
if drive.peripheral.getFreeSpace then
    local ok, free = pcall(function()
        return drive.peripheral.getFreeSpace()
    end)
    if ok then return free end
end

-- Method 3: Calculate from capacity - used
if drive.peripheral.getCapacity and drive.peripheral.getUsedSpace then
    local ok1, cap = pcall(function()
        return drive.peripheral.getCapacity()
    end)
    local ok2, used = pcall(function()
        return drive.peripheral.getUsedSpace()
    end)
    if ok1 and ok2 then return cap - used end
end

return 0

end

function JBOD:getDriveTotalSpace(index) local drive = self.drives[index] if not drive then return 0 end

if drive.peripheral.getCapacity then
    local ok, cap = pcall(function()
        return drive.peripheral.getCapacity()
    end)
    if ok then return cap end
end

if fs.getCapacity and drive.fullPath then
    local ok, cap = pcall(function()
        return fs.getCapacity(drive.fullPath)
    end)
    if ok then return cap end
end

if drive.isRaid then return 10000000 end
return 2000000

end

function JBOD:getPoolStats() local total, used, free = 0, 0, 0

for i = 1, #self.drives do
    local cap = self:getDriveTotalSpace(i)
    local avail = self:getDriveFreeSpace(i)
    total = total + cap
    free = free + avail
end

used = total - free
local percent = total > 0 and math.floor((used / total) * 100) or 0

return {
    total = total,
    used = used,
    free = free,
    percent = percent,
    drives = #self.drives
}

end

-- ============================================ -- USER INTERFACE - KIRITO SOFTWORKS -- ============================================

function JBOD:formatBytes(b) if b >= 1000000000 then return string.format("%.2f GB", b/1000000000) elseif b >= 1000000 then return string.format("%.2f MB", b/1000000) elseif b >= 1000 then return string.format("%.2f KB", b/1000) else return b .. " B" end end

function JBOD:drawBar(pct, w) local filled = math.floor((pct/100) * w) return "[" .. string.rep("=", filled) .. string.rep("-", w-filled) .. "]" end

function JBOD:drawHeader() print("===================================================") print(" KIRITO SOFTWORKS PRESENTS") print("===================================================") print() end

function JBOD:drawMainScreen() term.clear() term.setCursorPos(1,1)

self:drawHeader()

local s = self:getPoolStats()

print("JBOD NETWORK STORAGE POOL")
print("---------------------------------------------------")
print(string.format("  Storage Devices: %d", s.drives))
print()
print(string.format("  %s %d%%", self:drawBar(s.percent, 30), s.percent))
print()
print(string.format("  Used:  %s", self:formatBytes(s.used)))
print(string.format("  Free:  %s", self:formatBytes(s.free)))
print(string.format("  Total: %s", self:formatBytes(s.total)))
print("---------------------------------------------------")
print()

local files = self:listFiles()
print("FILES (" .. #files .. " total):")
if #files == 0 then
    print("  (pool empty)")
else
    for i = 1, math.min(8, #files) do
        local f = files[i]
        print(string.format("  %-20s %s", f.name:sub(1,20), self:formatBytes(f.size)))
    end
    if #files > 8 then print("  ... +" .. (#files-8) .. " more") end
end

print()
print("Commands: [L]ist  [R]ead  [W]rite  [D]elete  [I]nfo  [Q]uit")

end

function JBOD:showDriveInfo() term.clear() term.setCursorPos(1,1)

self:drawHeader()
print("INDIVIDUAL STORAGE DEVICES")
print("===================================================")
print()

if #self.drives == 0 then
    print("No storage devices detected!")
else
    for i, d in ipairs(self.drives) do
        local total = self:getDriveTotalSpace(i)
        local free = self:getDriveFreeSpace(i)
        local used = total - free
        local pct = math.floor((used/total)*100)
        
        print(string.format("[%d] %s", i, d.name))
        print(string.format("    Type: %s%s", 
            d.pType, 
            d.isRaid and " [RAID]" or ""
        ))
        print(string.format("    ID: %s", d.diskId))
        print(string.format("    %s %d%%", self:drawBar(pct, 20), pct))
        print(string.format("    %s / %s used", self:formatBytes(used), self:formatBytes(total)))
        print(string.format("    %s free", self:formatBytes(free)))
        print()
    end
end

print("===================================================")
print("Press Enter to return...")
read()

end

-- ============================================ -- INTERACTIVE SHELL -- ============================================

function JBOD:run() self:scanDrives()

while true do
    self:drawMainScreen()
    
    term.setCursorPos(1, 22)
    write("> ")
    local cmd = read():lower():sub(1,1)
    
    if cmd == "q" then
        print("Goodbye!")
        break
        
    elseif cmd == "i" then
        self:showDriveInfo()
        
    elseif cmd == "l" then
        print("\nAll files:")
        for _, f in ipairs(self:listFiles()) do
            print(string.format("  %-25s %10s  [%s]", f.name, self:formatBytes(f.size), f.drive))
        end
        print("\nPress Enter...")
        read()
        
    elseif cmd == "r" then
        write("File to read: ")
        local name = read()
        write("Save as: ")
        local dest = read()
        
        local data, err = self:read(name)
        if data then
            local h = fs.open(dest, "w")
            if h then
                h.write(data)
                h.close()
                print("Saved to " .. dest)
            else
                print("Save failed")
            end
        else
            print("Error: " .. tostring(err))
        end
        sleep(1)
        
    elseif cmd == "w" then
        write("Local file: ")
        local src = read()
        if not fs.exists(src) or fs.isDir(src) then
            print("Not found or is directory")
        else
            local h = fs.open(src, "r")
            local data = h.readAll()
            h.close()
            
            write("Save as [" .. fs.getName(src) .. "]: ")
            local name = read()
            if name == "" then name = fs.getName(src) end
            
            local ok, err = self:write(name, data)
            print(ok and "Saved!" or "Failed: " .. tostring(err))
        end
        sleep(1)
        
    elseif cmd == "d" then
        write("File to delete: ")
        local name = read()
        write("Confirm (yes): ")
        if read():lower() == "yes" then
            local ok = self:delete(name)
            print(ok and "Deleted" or "Failed")
        else
            print("Cancelled")
        end
        sleep(1)
    end
end

end

-- ============================================ -- API EXPORT -- ============================================

function JBOD:exportAPI() _G.JBOD = { list = function() return self:listFiles() end, read = function(n) return self:read(n) end, write = function(n,d) return self:write(n,d) end, delete = function(n) return self:delete(n) end, stats = function() return self:getPoolStats() end, drives = function() return self.drives end, scan = function() return self:scanDrives() end } end

-- ============================================ -- MAIN -- ============================================

local args = {...} if args[1] == "api" then JBOD:scanDrives() JBOD:exportAPI() print("KIRITO SOFTWORKS JBOD API ready") elseif args[1] == "daemon" then JBOD:scanDrives() JBOD:exportAPI() print("KIRITO SOFTWORKS JBOD Daemon running") while true do sleep(60) JBOD:scanDrives() end else JBOD:run() end

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors