Due to memory problem I looked once again at the code and.. I found that I’m not using classes properly.
Why? Because every function and attribute were static. I didn’t use self or anything similar.
I scratched my head, looked into google and rewritten temperature sensor.

It can create class instances with self values and static functions.

Thermometer

Classes in LUA are quite specific. If you want to read more details look here
This is how new 18b20 file looks:

local Temp18b20 = {}
Temp18b20.__index = Temp18b20
Temp18b20.device_id = {0x10, 0x28}

setmetatable(Temp18b20, {
    __call = function (cls, ...)
        return cls.new(...)
    end,
})

function Temp18b20.new(pin)
    local self = setmetatable({}, Temp18b20)
    self.pin = pin
    self.addr = nil    
    return self
end  

This code allows us to instance a class by calling its name. What is funny only parameters attached to self (in new function) are class depended, rest are static.
What is all this fuss with self? If you call function as

class.function(a, b)

You are using static call with parameters. But when you do:

class:function(a, b)

Due to all tricks above it translates to

class.function(class, a, b)

And this is the class/self we are looking for:) It takes static function and pass self variables and another arguments.

Such construction is called metatable-based class. It shares function with all instances but we can define some parameter as self. It is good for memory.
How to use such classes:

temp = require "18b20"
pin = 3
thermometer = temp(pin)

And pin is defined for each instance and not for all.

Lets look at functions:

function Temp18b20:init()    
    ow.setup(self.pin) 
    count = 0
    repeat
      count = count + 1
      addr = ow.reset_search(self.pin)
      addr = ow.search(self.pin)
      tmr.wdclr()
    until (addr ~= nil) or (count > 100)
    if addr == nil then
        print("Device not found")
    else
        self.addr = addr
        crc = ow.crc8(string.sub(self.addr,1,7))
        if crc == self.addr:byte(8) then
            if (not Temp18b20.is_device(self.addr:byte(1))) then
                print("Device is not a DS18B20 family device.")
                self.addr = nil
            end
        else
            print("CRC invalid - not a device ?")
            self.addr = nil
        end
    end
end

function Temp18b20.is_device(data)
    for i, v in ipairs(Temp18b20.device_id) do        
        if v == data then
            return true
        end
    end

    return false
end

Here are some nice tricks. See that we see : before function init. This translate into init(self) where self is an instance of our class. That is why we use self.pin and self.addr.
What is better we can still use const functions like is_device.
Final part, also use self and :

function Temp18b20:get_temperature(round)
    ow.reset(self.pin)
    ow.select(self.pin, self.addr)
    ow.write(self.pin, 0x44, 1)
    present = ow.reset(self.pin)
    ow.select(self.pin, self.addr)
    ow.write(self.pin,0xBE,1)
    data = string.char(ow.read(self.pin))
    for i = 1, 8 do
        data = data .. string.char(ow.read(self.pin))
    end    
    crc = ow.crc8(string.sub(data,1,8))
    if crc == data:byte(9) then
        t = (data:byte(1) + data:byte(2) * 256) * 625
        t1 = t / 10000
        t2 = t % 10000        
        if round ~= nil and round ~= false then return t1 else return t1.."."..t2 end
    else
        return nil
    end       
end

return Temp18b20

And full usage:


temp = require "18b20"
pin = 3
thermometer = temp(pin)
thermometer:init()
print(thermometer:get_temperature())

Ok. Worker has been rewritten. Time to do same with handler.

Handler

This one was quick:

local Temp18b20_handler = {}
Temp18b20_handler.__index = Temp18b20_handler

setmetatable(Temp18b20_handler, {
    __call = function (cls, ...)
        return cls.new(...)
    end,
})

function Temp18b20_handler.new(node, round)
    local self = setmetatable({}, Temp18b20_handler)
    self.node = node
    self.round = round
    return self
end    

function Temp18b20_handler:handle(socket, message)
    response = false
    if message ~= nil and message.event ~= nil then
        if message.event == 'temperature.current' then
            r = self.round
            if type(message['parameters']) == 'table' and type(message.parameters['round'] ~= nil)then
                r = message.parameters['round'] 
            end
            message = network_message.prepareMessage()
            message.response = self.node:get_temperature(r)
            network_message.sendMessage(socket, message)
            response = true
        end 
    end

    return response
end

return Temp18b20_handler

Listener

This one require one small change, instate of handle.handle we have handle:handle

Summary

This was unexpected but welcomed. I have to rewrite all modules to proper classes. With some luck it should allow me to hook two screens into one node. If not, well C is the answer.
Next module to rewrite: lcd and its drivers.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s