Register forum user name Search FAQ

Gammon Forum

Notice: Any messages purporting to come from this site telling you that your password has expired, or that you need to verify your details, confirm your email, resolve issues, making threats, or asking for money, are spam. We do not email users with any such messages. If you have lost your password you can obtain a new one by using the password reset link.

Due to spam on this forum, all posts now need moderator approval.

 Entire forum ➜ MUSHclient ➜ Lua ➜ Robot development framework in Mush Client (Lua)

Robot development framework in Mush Client (Lua)

It is now over 60 days since the last post. This thread is closed.     Refresh page


Posted by Jing Wu   Australia  (35 posts)  Bio
Date Sat 12 Jul 2008 11:43 AM (UTC)

Amended on Sat 12 Jul 2008 11:44 AM (UTC) by Jing Wu

Message
This is a framework I wrote for writing robot with Lua Script in Mush Client.
The framework enables you dealing with every action/command base on the message/event type, and do it in sequence, which means you needn't pay attention to enable or disable a trigger, and just focuing on the logical of your robot.
The idea of the framework is liking a Windows Message/Event Driven mode. A serial of events you can define, and handle them in a callback function. The key point is an event can be defined as triggered how many times within a period or infinite time.
Moreover, a command sender I created helps you send commands for repetition, delaying, or you can extend it to process some special commands by adding codes in Command:Send function. The command sender will ensure all of your commands sent in sequence according to the order you invoke the Command:Add function.


In order to use this framework to develop your robot, you can follow this way:
1. define an event you wanna handle.
2. create a trigger in mush to fire this event.
3. write a call back function for this event.
4. in the callback function, you are able to do some response by sending commands using command sender.

for example:
......

check detail in my blog:
http://www.cnblogs.com/wj/archive/2008/07/12/1241569.html

Kernel Development
Top

Posted by Jing Wu   Australia  (35 posts)  Bio
Date Reply #1 on Sun 13 Jul 2008 04:02 AM (UTC)

Amended on Sun 13 Jul 2008 04:04 AM (UTC) by Jing Wu

Message
In order to use this framework to develop your robot, you can follow this way:
1. define an event you wanna handle.
2. create a trigger in mush to fire this event.
3. write a call back function for this event.
4. in the callback function, you are able to do some response by sending commands using command sender.

for example:
you can create a trigger like:



you can handle events like:

-- handle "event1"
Listener.new():Register(Event.new("event1"), EventHandler1.new())
EventHander1 = class(Callback)
function EventHander1:Do(event, )
    -- do something
    cmdSender:Add("cmd1;#3 cmd2;@2;cmd3") -- #3 means repeat 3 times, @2 means delay 2 seconds
end

-- handle "event2" five times
Listener.new():Register(Event.new("event2, 0, 5"), EventHandler2.new())
EventHander2 = class(Callback)
function EventHander2:Do(event, )
    -- do something
    cmdSender:Add({"cmd4", "@3", "#2 cmd5"}) -- accept commands in a table
end

-- handle "event3" twice within 10 seconds. if it is not triggered twice within 10 seconds, a timeout event is sent.
Listener.new():Register(Event.new("event3, 10, 2"), EventHandler3.new())
EventHander3 = class(Callback)
function EventHander3:Do(event, )
    -- do something
    if (event.isTimeout) then
        cmdSender:Add("cmd6")
    else
        cmdSender:Add({"cmd7", "cmd8"})
    end
end


Here is the codes of this framework, copy it to your lua script file, then you can use it.


---------------------------------------------------------
-- OO, implement class module
---------------------------------------------------------

local _class = {}

function class(super)
    local class_type = {}
    class_type.ctor = false
    class_type.super = super
    class_type.new =
        function()
            local obj = {}
            do
                local create
                create =
                    function(c, )
                        if c.super then
                            create(c.super, )
                        end
                        if c.ctor then
                            c.ctor(obj, )
                        end
                    end
                create(class_type, )
            end
            setmetatable(obj, { __index = _class[class_type] })
            return obj
        end
    local vtbl = {}
    _class[class_type] = vtbl

    setmetatable(class_type, { __newindex =
        function(t, k, v)
            vtbl[k] = v
        end
    })

    if super then
        setmetatable(vtbl, { __index =
            function(t,k)
                local ret = _class[super][k]
                vtbl[k] = ret
                return ret
            end
        })
    end

    return class_type
end

---------------------------------------------------------
-- event
--- type: type of an event
--- timeout: in a particular time(in seconds) didn't receive the event will fire a timeout event
--- times: the event will be triggered how many times, then will be self removed
---------------------------------------------------------

Event = class()

function Event:ctor(type, timeout, times)
    self.type = type
    if (timeout == nil and times == nil) then
        -- if both timeout and times are not set, then can be triggered any times (set times to zero)
        self.timeout = 0
        self.times = 0
    elseif (timeout ~= nil and times == nil) then
        -- if timeout is set, times is not set, then can be trigger only once
        self.timeout = timeout
        self.times = 1
    else
        -- if both timeout and times are set, then can be trigger any times within timeout
        self.timeout = timeout
        self.times = times
    end
    self.isTimeout = false
    self.triggered = 0
end

function Event:Reset()
    self.isTimeout = false
    self.triggered = 0
end

Kernel Development
Top

Posted by Jing Wu   Australia  (35 posts)  Bio
Date Reply #2 on Sun 13 Jul 2008 04:02 AM (UTC)

Amended on Sun 13 Jul 2008 04:03 AM (UTC) by Jing Wu

Message

---------------------------------------------------------
-- callback: callback function when receved an event
---------------------------------------------------------

Callback = class()

function Callback:ctor(insideFunc)
    self.func = insideFunc
end

function Callback:Invoke(event, )
    -- logging
    helper:Print("Event:", event.type, " Timeout:", event.isTimeout, " Triggered:", event.triggered)
    -- call handler
    self:Do(event, )
end

function Callback:Do(event, )
    helper:Print("Do Noting")
end

---------------------------------------------------------
-- listener
---------------------------------------------------------

Listener = class()

function Listener:ctor()
    self.id = CreateGUID()
end

function Listener:Register(event, callback)
    assert(event.type ~= nil, "event type is nil")
    self.event = event
    self.callback = callback
    -- create timer if has timeout
    if (event.timeout ~= 0) then -- create timer using type as timer name
        helper:AddTimer(self.event.type, self.event.timeout)
    end
    -- add self in listener list
    dispatcher:AddListener(self)
end

function Listener:Remove()
    assert(self.event ~= nil, "have to register event then remove it")
    -- if has timer and the timer is not timeout, delete it
    if (self.event.timeout ~= 0 and not self.event.isTimeout) then
        helper:RemoveTimer(self.event.type)
    end
    -- remove self in listener list
    dispatcher:RemoveListener(self)
end

function Listener:OnEvent()
    -- add triggered times
    self.event.triggered = self.event.triggered + 1
    -- check if reach triggered times
    if (self.event.times ~= 0 and self.event.triggered == self.event.times) then
        self:Remove()
    end
    -- call back
    self.callback:Invoke(self.event, )
end

function Listener:OnTimeout()
    -- set isTimeout and call back
    self.event.isTimeout = true
    -- delete listener
    self:Remove()
    -- call back
    self.callback:Invoke(self.event)
end

Kernel Development
Top

Posted by Jing Wu   Australia  (35 posts)  Bio
Date Reply #3 on Sun 13 Jul 2008 04:03 AM (UTC)
Message

---------------------------------------------------------
-- event dispatcher
---------------------------------------------------------

EventDispatcher = class()

function EventDispatcher:ctor()
    self.listeners = {}
end

function EventDispatcher:AddListener(listener)
    self.listeners[listener.id] = listener
end

function EventDispatcher:RemoveListener(listener)
    self.listeners[listener.id] = nil
end

function EventDispatcher:IsListening(listener)
    return (self.listeners[listener.id] ~= nil)
end

function EventDispatcher:Match(eventType)
    local matchs = {}
    for k, v in pairs (self.listeners) do
        if (v.event.type == eventType) then
            table.insert(matchs, v)
        end
    end
    return matchs
end

function EventDispatcher:SendEvent(eventType, )
    local matchs = self:Match(eventType)
    if (#matchs ~= 0) then
        for k, v in pairs (matchs) do
            v:OnEvent()
        end
    end
end

function EventDispatcher:SendTimeout(timerName)
    local matchs = self:Match(timerName)
    if (#matchs ~= 0) then
        for k, v in pairs (matchs) do
            v:OnTimeout()
        end
    end
end

-- only one instance
dispatcher = EventDispatcher.new()

Kernel Development
Top

Posted by Jing Wu   Australia  (35 posts)  Bio
Date Reply #4 on Sun 13 Jul 2008 04:06 AM (UTC)
Message

---------------------------------------------------------
-- Helper
---------------------------------------------------------

Helper = class()

function Helper:ctor()
    self.isPrint = false
    self.cmds = {}
end

function Helper:Print()
    if self.isPrint then
        Note()
    end
end

function Helper:AddTimer(name, interval)
    local hours = math.floor(interval / 3600)
    interval = interval - (hours * 3600)
    local minutes = math.floor(interval / 60)
    local seconds = interval - (minutes * 60)
    local status = AddTimer (name, hours, minutes, seconds, "dispatcher:SendTimeout(\"" .. name .. "\")", timer_flag.OneShot + timer_flag.Temporary + timer_flag.Replace, "")
    assert(status == error_code.eOK, "fail to create timer:" .. name)
    SetTimerOption(name, "send_to", 12)
    EnableTimer(name, true)
    ResetTimer(name)
end

function Helper:ResetTimer(name, interval)
    assert(IsTimer(name), "timer doesn't exist")
    EnableTimer(name, false)
    local hours = math.floor(interval / 3600)
    interval = interval - (hours * 3600)
    local minutes = math.floor(interval / 60)
    local seconds = interval - (minutes * 60)
    SetTimerOption(name, "hour", hours)
    SetTimerOption(name, "minute", minutes)
    SetTimerOption(name, "second", seconds)
    EnableTimer(name, true)
    ResetTimer(name)
end

function Helper:RemoveTimer(name)
    EnableTimer(name, false)
    DeleteTimer(name)
end

-- only one instance
helper = Helper.new()

---------------------------------------------------------
-- Command
--- Repeat: #4 xx (repeat 4 times for command xx)
--- Delay: @3 (delay 3 seconds)
---------------------------------------------------------

Command = class()

function Command:ctor()
    self.cmds = {}
    self.isRunning = false
    self.thread = nil
end

function Command:ToTable(cmds)
    assert(type(cmds) == "string", "commands must be string type")
    local retVal = {}
    for k, v in pairs(utils.split(cmds, ";")) do
        if (string.sub(v, 1, 1) == "#") then -- convert repeat command
            local sb, se = string.find(v, "%s+")
            assert(sb ~= nil and se ~= nil, "wrong repeat command format")
            local times = tonumber(string.sub(v, 2, sb - 1))
            local cmd = string.sub(v, se + 1)
            for i = 1, times, 1 do
                retVal[#retVal + 1] = cmd
            end
        else
            retVal[#retVal + 1] = v
        end
    end
    return retVal
end

function Command:Add(cmds)
    if (type(cmds) == "string") then
        cmds = self:ToTable(cmds)
    end
    assert(type(cmds) == "table", "commands must be string or table type")
    -- add cmds
    for k, v in pairs (cmds) do
        self.cmds[#self.cmds + 1] = v
    end
    -- wakeup to process
    self:Wakeup()
end

function Command:Clear()
    self.cmds = {}
end

function Command:Wakeup()
    if (self.thread == nil) then
        cmdSender.thread = coroutine.create(cmdSender.Do)
    end
    if (not self.isRunning) then
        coroutine.resume(self.thread)
    end
end

function Command:Do()
    while true do
        local cmd = nil
        if (#cmdSender.cmds ~= 0) then
            cmd = cmdSender.cmds[1] -- pick cmd in queue
            table.remove (cmdSender.cmds, 1) -- remove cmd in queue
        end
        if (cmd ~= nil) then
            cmdSender.isRunning = true
            if (string.sub(cmd, 1, 1) == "@") then
                local interval = tonumber(string.sub(cmd, 2))
                if (interval > 0) then
                    helper:Print("delay:", interval)
                    DoAfterSpecial(interval, "coroutine.resume(cmdSender.thread)", 12)
                    coroutine.yield()
                end
            else
                cmdSender:Send(cmd)
            end
        else
            cmdSender.isRunning = false
            coroutine.yield()
        end
    end
end

function Command:Send(cmd)
    helper:Print("cmd:", cmd)
    if (IsAlias(cmd) == error_code.eOK) then
        SendImmediate(GetAliasInfo(cmd, 2))
    else
        SendImmediate(cmd)
    end
end

cmdSender = Command.new()

Kernel Development
Top

The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).

To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.


18,067 views.

It is now over 60 days since the last post. This thread is closed.     Refresh page

Go to topic:           Search the forum


[Go to top] top

Information and images on this site are licensed under the Creative Commons Attribution 3.0 Australia License unless stated otherwise.