sock.lua
--- A networking library for Lua games -- @module sock -- @usage local sock = require "sock" local sock = { _VERSION = 'sock.lua v0.1.0', _DESCRIPTION = 'A networking library for Lua games', _URL = 'https://github.com/camchenry/sock.lua', _LICENSE = [[ MIT License Copyright (c) 2016 Cameron McHenry Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ]] } local currentFolder = (...):match("(.-)[^%.]+$") require "enet" -- bitser is expected to be in the same directory as sock.lua local bitser = require(currentFolder .. "bitser") -- links variables to keys based on their order -- note that it only works for boolean and number values, not strings local function zipTable(items, keys) local data = {} -- convert variable at index 1 into the value for the key value at index 1, and so on for i, value in ipairs(items) do local key = keys[i] data[key] = value end return data end --- All of the possible connection statuses for a client connection. -- @see Client:getState local CONNECTION_STATES = { "disconnected", -- Disconnected from the server. "connecting", -- In the process of connecting to the server. "acknowledging_connect", -- "connection_pending", -- "connection_succeeded", -- "connected", -- Successfully connected to the server. "disconnect_later", -- Disconnecting, but only after sending all queued packets. "disconnecting", -- In the process of disconnecting from the server. "acknowledging_disconnect", -- "zombie", -- "unknown", -- } --- Valid modes for sending messages. local SEND_MODES = { "reliable", -- Message is guaranteed to arrive, and arrive in the order in which it is sent. "unsequenced", -- Message has no guarantee on the order that it arrives. "unreliable", -- Message is not guaranteed to arrive. } local function isValidSendMode(mode) for i, validMode in pairs(SEND_MODES) do if mode == validMode then return true end end return false end local Logger = {} local Logger_mt = {__index = Logger} function newLogger(source) local logger = setmetatable({ source = source, messages = {}, -- Makes print info more concise, but should still log the full line shortenLines = true, -- Print all incoming event data printEventData = false, printErrors = true, printWarnings = true, }, Logger_mt) return logger end function Logger:log(event, data) local time = os.date("%X") -- something like 24:59:59 local shortLine = ("[%s] %s"):format(event, data) local fullLine = ("[%s][%s][%s] %s"):format(self.source, time, event, data) -- The printed message may or may not be the full message local line = fullLine if self.shortenLines then line = shortLine end if self.printEventData then print(line) elseif self.printErrors and event == "error" then print(line) elseif self.printWarnings and event == "warning" then print(line) end -- The logged message is always the full message table.insert(self.messages, fullLine) -- TODO: Dump to a log file end local Listener = {} local Listener_mt = {__index = Listener} function newListener() local listener = setmetatable({ triggers = {}, formats = {}, }, Listener_mt) return listener end -- Adds a callback to a trigger -- Returns: the callback function function Listener:addCallback(event, callback) if not self.triggers[event] then self.triggers[event] = {} end table.insert(self.triggers[event], callback) return callback end -- Removes a callback on a given trigger -- Returns a boolean indicating if the callback was removed function Listener:removeCallback(callback) for event, triggers in pairs(self.triggers) do for i, trigger in pairs(triggers) do if trigger == callback then table.remove(triggers, i) return true end end end return false end -- Accepts: event (string), format (table) -- Returns: nothing function Listener:setDataFormat(event, format) self.formats[event] = format end -- Activates all callbacks for a trigger -- Returns a boolean indicating if any callbacks were triggered function Listener:trigger(event, data, client) if self.triggers[event] then for i, trigger in pairs(self.triggers[event]) do -- Event has a pre-existing format defined if self.formats[event] then data = zipTable(data, self.formats[event]) end trigger(data, client) end return true else return false end end --- Manages all clients and receives network events. local Server = {} local Server_mt = {__index = Server} --- Gets the Client object associated with an enet peer. -- @tparam peer peer An enet peer. -- @treturn Client Object associated with the peer. function Server:getClient(peer) for i, client in pairs(self.clients) do if peer == client.connection then return client end end end --- Gets the Client object that has the given connection id. -- @tparam number connectId The unique client connection id. -- @treturn Client function Server:getClientByConnectId(connectId) for i, client in pairs(self.clients) do if connectId == client.connectId then return client end end end --- Get the Client object that has the given peer index. -- @treturn Client function Server:getClientByIndex(index) for i, client in pairs(self.clients) do if index == client:getIndex() then return client end end end --- Get the enet_peer that has the given index. -- @treturn enet_peer The underlying enet peer object. function Server:getPeerByIndex(index) return self.host:get_peer(index) end --- Set the send mode for the next outgoing message. -- The mode will be reset after the next message is sent. The initial default -- is "reliable". -- @tparam string mode A valid send mode. -- @see SEND_MODES function Server:setSendMode(mode) if not isValidSendMode(mode) then self:log("warning", "Tried to use invalid send mode: '" .. mode .. "'. Defaulting to reliable.") mode = "reliable" end self.sendMode = mode end --- Set the default send mode for all future outgoing messages. -- The initial default is "reliable". -- @tparam string mode A valid send mode. -- @see SEND_MODES function Server:setDefaultSendMode(mode) if not isValidSendMode(mode) then self:log("error", "Tried to set default send mode to invalid mode: '" .. mode .. "'") error("Tried to set default send mode to invalid mode: '" .. mode .. "'") end self.defaultSendMode = mode end --- Set the send channel for the next outgoing message. -- The channel will be reset after the next message. Channels are zero-indexed -- and cannot exceed the maximum number of channels allocated. The initial -- default is 0. -- @tparam number channel Channel to send data on. function Server:setSendChannel(channel) if channel > (self.maxChannels - 1) then self:log("warning", "Tried to use invalid channel: " .. channel .. " (max is " .. self.maxChannels - 1 .. "). Defaulting to 0.") channel = 0 end self.sendChannel = channel end --- Set the default send channel for all future outgoing messages. -- The initial default is 0. -- @tparam number channel Channel to send data on. function Server:setDefaultSendChannel(channel) self.defaultSendChannel = channel end --- Reset all send options to their default values. function Server:resetSendSettings() self.sendMode = self.defaultSendMode self.sendChannel = self.defaultSendChannel end --- Check for network events and handle them. function Server:update() local event = self.host:service(self.messageTimeout) while event do if event.type == "connect" then local eventClient = sock.newClient(event.peer) table.insert(self.peers, event.peer) table.insert(self.clients, eventClient) self:_activateTriggers("connect", event.data, eventClient) self:log(event.type, tostring(event.peer) .. " connected") elseif event.type == "receive" then local message = bitser.loads(event.data) local eventClient = self:getClient(event.peer) local event = message[1] local data = message[2] self:_activateTriggers(event, data, eventClient) self:log(event, data) elseif event.type == "disconnect" then -- remove from the active peer list for i, peer in pairs(self.peers) do if peer == event.peer then table.remove(self.peers, i) end end local eventClient = self:getClient(event.peer) for i, client in pairs(self.clients) do if client == eventClient then table.remove(self.clients, i) end end self:_activateTriggers("disconnect", event.data, eventClient) self:log(event.type, tostring(event.peer) .. " disconnected") end event = self.host:service() end end --- Send a message to all peers, except one. -- Useful for when the client does something locally, but other clients -- need to be updated at the same time. This way avoids duplicating objects by -- never sending its own event to itself in the first place. -- @todo This function is bugged (I think.) It should accept clients, not peers. -- @tparam enet_peer peer The peer to not receive the message. -- @tparam string event The event to trigger with this message. -- @param data The data to send. function Server:emitToAllBut(peer, event, data) local message = {event, data} local serializedMessage = nil -- 'Data' = binary data class in Love if type(data) == "userdata" and data.type and data:typeOf("Data") then message[2] = data:getString() serializedMessage = bitser.dumps(message) else serializedMessage = bitser.dumps(message) end for i, p in pairs(self.peers) do if p ~= peer then self.packetsSent = self.packetsSent + 1 p:send(serializedMessage, self.sendChannel, self.sendMode) end end self:resetSendSettings() end --- Send a message to all peers. -- @tparam string event The event to trigger with this message. -- @param data The data to send. function Server:emitToAll(event, data) local message = {event, data} local serializedMessage = nil -- 'Data' = binary data class in Love if type(data) == "userdata" and data.type and data:typeOf("Data") then message[2] = data:getString() serializedMessage = bitser.dumps(message) else serializedMessage = bitser.dumps(message) end self.packetsSent = self.packetsSent + #self.peers self.host:broadcast(serializedMessage, self.sendChannel, self.sendMode) self:resetSendSettings() end --- Add a callback to an event. -- @tparam string event The event that will trigger the callback. -- @tparam function callback The callback to be triggered. -- @treturn function The callback that was passed in. --@usage --server:on("connect", function(data, client) -- print("Client connected!") --end) function Server:on(event, callback) return self.listener:addCallback(event, callback) end --- Set the data format for an event. -- @tparam string event The event to set the data format for. -- @tparam table format The data format. function Server:setDataFormat(event, format) return self.listener:setDataFormat(event, format) end function Server:_activateTriggers(event, data, client) local result = self.listener:trigger(event, data, client) self.packetsReceived = self.packetsReceived + 1 if not result then self:log("warning", "Tried to activate trigger: '" .. tostring(event) .. "' but it does not exist.") end end --- Remove a specific callback for an event. -- @tparam function callback The callback to remove. -- @treturn boolean Whether or not the callback was removed. --@usage --local callback = server:on("chatMessage", function(message) -- print(message) --end) --server:removeCallback(callback) function Server:removeCallback(callback) return self.listener:removeCallback(callback) end --- Log an event. -- Alias for Server.logger:log. -- @tparam string event The type of event that happened. -- @tparam string data The message to log. --@usage --if somethingBadHappened then -- server:log("error", "Something bad happened!") --end function Server:log(event, data) return self.logger:log(event, data) end --- Get the total sent data since the server was created. -- @treturn number The total sent data in bytes. function Server:getTotalSentData() return self.host:total_sent_data() end --- Get the total received data since the server was created. -- @treturn number The total received data in bytes. function Server:getTotalReceivedData() return self.host:total_received_data() end --- Get the total number of packets (messages) sent since the server was created. -- Everytime a message is sent or received, the corresponding figure is incremented. -- Therefore, this is not necessarily an accurate indicator of how many packets were actually -- exchanged over the network. -- @treturn number The total number of sent packets. function Server:getTotalSentPackets() return self.packetsSent end --- Get the total number of packets (messages) received since the server was created. -- @treturn number The total number of received packets. -- @see Server:getTotalSentPackets function Server:getTotalReceivedPackets() return self.packetsReceived end --- Set the incoming and outgoing bandwidth limits. -- @tparam number incoming The maximum incoming bandwidth in bytes. -- @tparam number outgoing The maximum outgoing bandwidth in bytes. function Server:setBandwidthLimit(incoming, outgoing) return self.host:bandwidth_limit(incoming, outgoing) end --- Get the last time when network events were serviced. -- @treturn number Timestamp of the last time events were serviced. function Server:getLastServiceTime() return self.host:service_time() end --- Get the number of allocated slots for peers. -- @treturn number Number of allocated slots. function Server:getMaxPeers() return self.maxPeers end --- Get the number of allocated channels. -- Channels are zero-indexed, e.g. 16 channels allocated means that the -- maximum channel that can be used is 15. -- @treturn number Number of allocated channels. function Server:getMaxChannels() return self.maxChannels end --- Set the maximum number of channels. -- @tparam number limit The maximum number of channels allowed. If it is 0, -- then the maximum number of channels available on the system will be used. function Server:setMaxChannels(limit) self.host:channel_limit(limit) end --- Set the timeout to wait for packets. -- @tparam number timeout Time to wait for incoming packets in milliseconds. The -- initial default is 0. function Server:setMessageTimeout(timeout) self.messageTimeout = timeout end --- Get the timeout for packets. -- @treturn number Time to wait for incoming packets in milliseconds. -- initial default is 0. function Server:getMessageTimeout() return self.messageTimeout end --- Get the socket address of the host. -- @treturn string A description of the socket address, in the format -- "A.B.C.D:port" where A.B.C.D is the IP address of the used socket. function Server:getSocketAddress() return self.host:get_socket_address() end --- Connects to servers. local Client = {} local Client_mt = {__index = Client} --- Set the send mode for the next outgoing message. -- The mode will be reset after the next message is sent. The initial default -- is "reliable". -- @tparam string mode A valid send mode. -- @see SEND_MODES function Client:setSendMode(mode) if not isValidSendMode(mode) then self:log("warning", "Tried to use invalid send mode: '" .. mode .. "'. Defaulting to reliable.") mode = "reliable" end self.sendMode = mode end --- Set the default send mode for all future outgoing messages. -- The initial default is "reliable". -- @tparam string mode A valid send mode. -- @see SEND_MODES function Client:setDefaultSendMode(mode) if not isValidSendMode(mode) then self:log("error", "Tried to set default send mode to invalid mode: '" .. mode .. "'") error("Tried to set default send mode to invalid mode: '" .. mode .. "'") end self.defaultSendMode = mode end --- Set the send channel for the next outgoing message. -- The channel will be reset after the next message. Channels are zero-indexed -- and cannot exceed the maximum number of channels allocated. The initial -- default is 0. -- @tparam number channel Channel to send data on. function Client:setSendChannel(channel) if channel > (self.maxChannels - 1) then self:log("warning", "Tried to use invalid channel: " .. channel .. " (max is " .. self.maxChannels - 1 .. "). Defaulting to 0.") channel = 0 end self.sendChannel = channel end --- Set the default send channel for all future outgoing messages. -- The initial default is 0. -- @tparam number channel Channel to send data on. function Client:setDefaultSendChannel(channel) self.defaultSendChannel = channel end --- Reset all send options to their default values. function Client:resetSendSettings() self.sendMode = self.defaultSendMode self.sendChannel = self.defaultSendChannel end --- Connect to the chosen server. -- @treturn boolean Status indicating whether or not the connection was successful. -- @todo Actually return the status. function Client:connect() -- number of channels for the client and server must match self.connection = self.host:connect(self.address .. ":" .. self.port, self.maxChannels) self.connectId = self.connection:connect_id() return true end --- Disconnect from the server, if connected. The client will disconnect the -- next time that network messages are sent. -- @tparam ?number code A code to associate with this disconnect event. -- @todo Pass the code into the disconnect callback on the server function Client:disconnect(code) code = code or 0 self.connection:disconnect(code) end --- Disconnect from the server, if connected. The client will disconnect after -- sending all queued packets. -- @tparam ?number code A code to associate with this disconnect event. -- @todo Pass the code into the disconnect callback on the server function Client:disconnectLater(code) code = code or 0 self.connection:disconnect_later(code) end --- Disconnect from the server, if connected. The client will disconnect immediately. -- @tparam ?number code A code to associate with this disconnect event. -- @todo Pass the code into the disconnect callback on the server function Client:disconnectNow(code) code = code or 0 self.connection:disconnect_now(code) end --- Check for network events and handle them. function Client:update() local event = self.host:service(self.messageTimeout) while event do if event.type == "connect" then self:_activateTriggers("connect", event.data) self:log(event.type, "Connected to " .. tostring(self.connection)) elseif event.type == "receive" then local message = bitser.loads(event.data) local event = message[1] local data = message[2] self:_activateTriggers(event, data) self:log(event, data) elseif event.type == "disconnect" then self:_activateTriggers("disconnect", event.data) self:log(event.type, "Disconnected from " .. tostring(self.connection)) end event = self.host:service() end end --- Send a message to the server. -- @tparam string event The event to trigger with this message. -- @param data The data to send. function Client:emit(event, data) local message = {event, data} local serializedMessage = nil -- 'Data' = binary data class in Love if type(data) == "userdata" and data.type and data:typeOf("Data") then message[2] = data:getString() serializedMessage = bitser.dumps(message) else serializedMessage = bitser.dumps(message) end self.connection:send(serializedMessage, self.sendChannel, self.sendMode) self.packetsSent = self.packetsSent + 1 self:resetSendSettings() end --- Add a callback to an event. -- @tparam string event The event that will trigger the callback. -- @tparam function callback The callback to be triggered. -- @treturn function The callback that was passed in. --@usage --client:on("connect", function(data) -- print("Connected to the server!") --end) function Client:on(event, callback) return self.listener:addCallback(event, callback) end --- Set the data format for an event. -- @tparam string event The event to set the data format for. -- @tparam table format The data format. function Client:setDataFormat(event, format) return self.listener:setDataFormat(event, format) end function Client:_activateTriggers(event, data) local result = self.listener:trigger(event, data, client) self.packetsReceived = self.packetsReceived + 1 if not result then self:log("warning", "Tried to activate trigger: '" .. tostring(event) .. "' but it does not exist.") end end --- Remove a specific callback for an event. -- @tparam function callback The callback to remove. -- @treturn boolean Whether or not the callback was removed. --@usage --local callback = client:on("chatMessage", function(message) -- print(message) --end) --client:removeCallback(callback) function Client:removeCallback(callback) return self.listener:removeCallback(callback) end --- Log an event. -- Alias for Client.logger:log. -- @tparam string event The type of event that happened. -- @tparam string data The message to log. --@usage --if somethingBadHappened then -- client:log("error", "Something bad happened!") --end function Client:log(event, data) return self.logger:log(event, data) end --- Get the total sent data since the server was created. -- @treturn number The total sent data in bytes. function Client:getTotalSentData() return self.host:total_sent_data() end --- Get the total received data since the server was created. -- @treturn number The total received data in bytes. function Client:getTotalReceivedData() return self.host:total_received_data() end --- Get the total number of packets (messages) sent since the client was created. -- Everytime a message is sent or received, the corresponding figure is incremented. -- Therefore, this is not necessarily an accurate indicator of how many packets were actually -- exchanged over the network. -- @treturn number The total number of sent packets. function Client:getTotalSentPackets() return self.packetsSent end --- Get the total number of packets (messages) received since the client was created. -- @treturn number The total number of received packets. -- @see Client:getTotalSentPackets function Client:getTotalReceivedPackets() return self.packetsReceived end --- Set the incoming and outgoing bandwidth limits. -- @tparam number incoming The maximum incoming bandwidth in bytes. -- @tparam number outgoing The maximum outgoing bandwidth in bytes. function Client:setBandwidthLimit(incoming, outgoing) return self.host:bandwidth_limit(incoming, outgoing) end --- Get the last time when network events were serviced. -- @treturn number Timestamp of the last time events were serviced. function Client:getLastServiceTime() return self.host:service_time() end --- Get the number of allocated channels. -- Channels are zero-indexed, e.g. 16 channels allocated means that the -- maximum channel that can be used is 15. -- @treturn number Number of allocated channels. function Client:getMaxChannels() return self.maxChannels end --- Set the maximum number of channels. -- @tparam number limit The maximum number of channels allowed. If it is 0, -- then the maximum number of channels available on the system will be used. function Client:setMaxChannels(limit) self.host:channel_limit(limit) end --- Set the timeout to wait for packets. -- @tparam number timeout Time to wait for incoming packets in milliseconds. The initial -- default is 0. function Client:setMessageTimeout(timeout) self.messageTimeout = timeout end --- Get the timeout for packets. -- @treturn number Time to wait for incoming packets in milliseconds. -- initial default is 0. function Client:getMessageTimeout() return self.messageTimeout end --- Return the round trip time (RTT, or ping) to the server, if connected. -- It can take a few seconds for the time to approach an accurate value. -- @treturn number The round trip time. function Client:getRoundTripTime() if self.connection then return self.connection:round_trip_time() end end --- Set how frequently to ping the server. -- The round trip time is updated each time a ping is sent. The initial -- default is 500ms. -- @tparam number interval The interval, in milliseconds. function Client:setPingInterval(interval) if self.connection then self.connection:ping_interval(interval) end end --- Get the unique connection id, if connected. -- @treturn number The connection id. function Client:getConnectId() if self.connection then return self.connection:connect_id() end end --- Get the current connection state, if connected. -- @treturn string The connection state. -- @see CONNECTION_STATES function Client:getState() if self.connection then return self.connection:state() end end --- Get the index of the enet peer. All peers of an ENet host are kept in an array. This function finds and returns the index of the peer of its host structure. -- @treturn number The index of the peer. function Client:getIndex() if self.connection then return self.connection:index() end end --- Change the probability at which unreliable packets should not be dropped. -- @tparam number interval Interval, in milliseconds, over which to measure lowest mean RTT. (default: 5000ms) -- @tparam number acceleration Rate at which to increase the throttle probability as mean RTT declines. (default: 2) -- @tparam number deceleration Rate at which to decrease the throttle probability as mean RTT increases. function Client:setThrottle(interval, acceleration, deceleration) interval = interval or 5000 acceleration = acceleration or 2 deceleration = deceleration or 2 if self.connection then self.connection:throttle_configure(interval, acceleration, deceleration) end end --- Set the parameters for attempting to reconnect if a timeout is detected. -- @tparam ?number limit A factor that is multiplied with a value that based on the average round trip time to compute the timeout limit. (default: 32) -- @tparam ?number minimum Timeout value in milliseconds that a reliable packet has to be acknowledged if the variable timeout limit was exceeded. (default: 5000) -- @tparam ?number maximum Fixed timeout in milliseconds for which any packet has to be acknowledged. function Client:setTimeout(limit, minimum, maximum) limit = limit or 32 minimum = minimum or 5000 maximum = maximum or 30000 if self.connection then self.connection:timeout(limit, minimum, maximum) end end --- Get the socket address of the host. -- @treturn string A description of the socket address, in the format "A.B.C.D:port" where A.B.C.D is the IP address of the used socket. function Client:getSocketAddress() return self.host:get_socket_address() end --- Get the enet_peer that has the given index. -- @treturn enet_peer The underlying enet peer object. function Client:getPeerByIndex(index) return self.host:get_peer(index) end --- Creates a new Server object. -- @tparam ?string address Hostname or IP address to bind to. (default: "localhost") -- @tparam ?number port Port to listen to for data. (default: 22122) -- @tparam ?number maxPeers Maximum peers that can connect to the server. (default: 64) -- @tparam ?number maxChannels Maximum channels available to send and receive data. (default: 1) -- @tparam ?number inBandwidth Maximum incoming bandwidth (default: 0) -- @tparam ?number outBandwidth Maximum outgoing bandwidth (default: 0) -- @return A new Server object. -- @see Server -- @usage --local sock = require "sock" -- -- -- Local server hosted on localhost:22122 (by default) --server = sock.newServer() -- -- -- Local server only, on port 1234 --server = sock.newServer("localhost", 1234) -- -- -- Server hosted on static IP <IP_ADDRESS>, on port 22122 --server = sock.newServer("<IP_ADDRESS>", 22122) -- -- -- Server hosted on any IP, on port 22122 --server = sock.newServer("*", 22122) -- -- -- Limit peers to 10, channels to 2 --server = sock.newServer("*", 22122, 10, 2) -- -- -- Limit incoming/outgoing bandwidth to 1kB/s (1000 bytes/s) --server = sock.newServer("*", 22122, 10, 2, 1000, 1000) sock.newServer = function(address, port, maxPeers, maxChannels, inBandwidth, outBandwidth) address = address or "localhost" port = port or 22122 maxPeers = maxPeers or 64 maxChannels = maxChannels or 1 inBandwidth = inBandwidth or 0 outBandwidth = outBandwidth or 0 local server = setmetatable({ address = address, port = port, host = nil, messageTimeout = 0, maxChannels = maxChannels, maxPeers = maxPeers, sendMode = "reliable", defaultSendMode = "reliable", sendChannel = 0, defaultSendChannel = 0, peers = {}, clients = {}, listener = newListener(), logger = newLogger("SERVER"), packetsSent = 0, packetsReceived = 0, }, Server_mt) -- ip, max peers, max channels, in bandwidth, out bandwidth -- number of channels for the client and server must match server.host = enet.host_create(server.address .. ":" .. server.port, server.maxPeers, server.maxChannels) if not server.host then error("Failed to create the host. Is there another server running on :"..server.port.."?") end server:setBandwidthLimit(inBandwidth, outBandwidth) return server end --- Creates a new Client instance. -- @tparam ?string/peer serverOrAddress Usually the IP address or hostname to connect to. It can also be an enet peer. (default: "localhost") -- @tparam ?number port port number of the server to connect to. (default: 22122) -- @tparam ?number maxChannels maximum channels available to send and receive data. (default: 1) -- @return A new Client object. -- @see Client -- @usage --local sock = require "sock" -- -- -- Client that will connect to localhost:22122 (by default) --client = sock.newClient() -- -- -- Client that will connect to localhost:1234 --client = sock.newClient("localhost", 1234) -- -- -- Client that will connect to <IP_ADDRESS>:1234, using two channels -- -- NOTE: Server must also allocate two channels! --client = sock.newClient("<IP_ADDRESS>", 1234, 2) sock.newClient = function(serverOrAddress, port, maxChannels) serverOrAddress = serverOrAddress or "localhost" port = port or 22122 maxChannels = maxChannels or 1 local client = setmetatable({ address = nil, port = nil, host = nil, connection = nil, connectId = nil, messageTimeout = 0, maxChannels = maxChannels, sendMode = "reliable", defaultSendMode = "reliable", sendChannel = 0, defaultSendChannel = 0, listener = newListener(), logger = newLogger("CLIENT"), packetsReceived = 0, packetsSent = 0, }, Client_mt) -- Two different forms for client creation: -- 1. Pass in (address, port) and connect to that. -- 2. Pass in (enet peer) and set that as the existing connection. -- The first would be the common usage for regular client code, while the -- latter is mostly used for creating clients in the server-side code. -- First form: (address, port) if port ~= nil and type(port) == "number" and serverOrAddress ~= nil and type(serverOrAddress) == "string" then client.address = serverOrAddress client.port = port client.host = enet.host_create() -- Second form: (enet peer) elseif type(serverOrAddress) == "userdata" then client.connection = serverOrAddress client.connectId = client.connection:connect_id() end return client end return sock


Attachments
SQL Attachments
Note
You can execute this SQL on any Sql Server engine, but in samples we will use the express instance. To simplify samples, we can use integrated security.
Define schema
CREATE DATABASE SamplesDb
GO
USE SamplesDb
GO
CREATE TABLE tUsers(
UserName NVARCHAR(50),
Married BIT
)
GO
CREATE PROCEDURE spFindUsersByUserName
@uname NVARCHAR(50)
AS
SELECT * FROM tUsers WHERE UserName=@uname
GO
Fill data
INSERT INTO tUsers VALUES
('Dzianis', 0),
('Alex', 0),
('Katrin', 1)
After this step data of table will be as:
| UserName | Marrried |
|---|---|
| Dzianis | 0 |
| Alex | 0 |
| Katrin | 1 |
Connection String
Server=(localdb)\mssqllocaldb;Database=SamplesDb;Trusted_Connection=True;Integrated Security=True;


%rebase('osnova.html')
It is important to remember that the database comes from an american online store and therefore contains more types of local drinks than others from around the world.
We can see that the most represented type of wine is actually "blend", where the wine is made from several types of grapes mixed together. There is a large amount of Chardonnay, Pinot Noir and Cabernet Sauvignon but quite a few other types of wine are also well represented.
The largest amount of wines, more than 500, comes from the US which makes sense, since the online store where the data was taken from is american. France is right behind though and after all it probably is the most famous wine country so that is no surprise. Italy is the third and also produces a large number of wines. More than 200 italian wines are in the database. The database also contains quite a lot of wines from Spain, Australia and Portugal. It's somewhat surprising that Isreal produces quite a few wines sold in the online store. Around 80 wines in the database come from other countries which produce only a small amount of wines sold in the store.
Now let's have a look at the number of wiens frome different wine-producing regions. We see that the largest by far is California producing the majority of US wines at more than 400 entries. Following that are several French regions and Tuscany. We notice that France has the most large wine-producing regions, even thought Italy and the US also feature on the list two times.
We can see that IPA is the type of beer that is best represented. After that, there are also a lot of pale ales, stouts and lager/pilsner beers. There is a pretty large amount of other types, that all have a noticable amount of entries.
Looking at the country data for beers shows us that englad produces the largest variety of beers by a large margin. After that it's the US, Belgium and Scotland. Germany and Denmark also produce a pretty big variety of drinks. The countries after all feature much less than 50 different drinks.


@(sections: java.util.List[models.Section]) @main("MSCS510 Demo") { This is cool! Sections: @sections.size() sections! Name: Section Number: is published?: @for(section <- sections) { Hi this is section: @section.name! } }


Musical Events
The first Wednesday 'free-block' of each month at the Coffee House is a special time. Join us from 12:30pm to 2:30pm for some music you don't want to miss!
September
Melanie Morris entertains with her melodic folk style. Check out the podcast! CDs are now available.


---
title: Home
description: A brief summary about who I am, what I do, and my portfolio as a software developer.
image: /assets/home.png
# Number of columns for projects, cannot be 0 (obviously)
project_columns: 2
---
{% include resume/about.html %}
{% include resume/skills.html collapsed=true %}
{% include resume/experience.html collapsed=true %}
{% include resume/community.html collapsed=true %}
{% include resume/contests.html collapsed=true %}
{% include resume/projects.html project_columns=page.project_columns collapsed=true %}


PublicApi
ui.grid.cellNav
Public Api for cellNav feature
returns an array containing the current selection
array is empty if no selection has occurred
returns the current (or last if Grid does not have focus) focused row and column
value is null if no selection has occurred
returns the index in the order in which the GridRowColumn was selected, returns -1 if the GridRowColumn isn't selected
| Param | Type | Details |
|---|---|---|
| rowCol | object | the rowCol to evaluate |


Abandoned baby giraffe watched over by dog in South Africa
SOUTH AFRICA -- A dog and a baby giraffe became unlikely friends at The Rhino Orphanage in Limpopo Province, South Africa.
The baby giraffe was found in a comatose state after being abandoned by his mother
Jazz, who was just 2 days old when he arrived at orphanage, was rescued after being discovered by a farmer.
"They brought in this little giraffe, he was about 2 days old when he came in. He was very weak, his mother had left him alone, so we had to put him onto IV fluids and yes he is doing a lot better now," said orphanage caretaker, Janie Van Heerden. "Hunter's 'human' rescued Jazz and has been nursing him back to health all under Hunter's watchful eye."
Van Heerdan says Jazz was put on IV fluids to build up his strength and is now recovering under the watchful eye of his friend and protector, Hunter the dog.
Jazz is doing much better and will likely be released back into the wild very soon.


net config server
Allows modifications to the server service. Entered with no parameters give details of the current configuration
Options:
/autodisconnect:<minutes> Number of minutes an account may be inactive before disconnection. Default is 15, valid range between 1 and 65535. -1 means never disconnected.
/srvcomment:"text" Set the comment for the machine
/hidden:<yes or no> Specified is the computer is hidden in the listing of computers


Welcome to ISA-16, the Sixteenth Workshop on Interoperable Semantic Annotation, is the most recent edition of a series of joint workshops of the ACL Special Interest Group on Computational Semantics (SIGSEM) and ISO TC 37/SC 4 (Language Resources) WG 2: Semantic annotation.
These workshops bring together experts in the annotation of semantic information as expressed in text, speech, gestures, graphics, video, images, and in multiple modalities combined. Examples of semantic annotation include the markup of events, time, space, dialogue acts, discourse relations, semantic roles, reference relations, and quantification for which the ISO organization pursues the establishment of annotation standards, in order to support the creation of interoperable semantic resources.
| Harry Bunt | Annotation of Quantification: The Current State of ISO 24617-12 | |
| Pranav Goel, Suhan Prabhu, Alok Debnath, Priyank Modi and Manish Shrivastava | Hindi TimeBank: An ISO-TimeML Annotated Reference Corpus | |
| Lars Hellan | Interoperable Semantic Annotation | |
| Alexander Henlein, Giuseppe Abrami, Attila Kett and Alexander Mehler | Transfer of ISOSpace into a 3D Environment for Annotations and Applications | |
| Kiyong Lee | Shallow Semantics for the Interpretation of Annotation Structures Carrying Spatio-temporal Information in Language | |
| Constanza Marini and Elisabetta Jezek | Annotating Croatian Semantic Type Coercions in CROATPAS | |
| Johanna Melly, Gabriel Luthier and Andrei Popescu-Belis | A Consolidated Dataset for Knowledge-based Question Generation using Predicate Mapping of Linked Data | |
| Volha Petukhova and Harry Bunt | Adapting the ISO 24617-2 Dialogue Act Annotation Scheme for Modelling Medical Consultations | |
| Suhan Prabhu, Ujwal Narayan, Alok Debnath, Sumukh S and Manish Shrivastava | Detection and Annotation of Events in Kannada | |
| John Roberto and Brian Davies | Towards the Ontologization of the Outsider Art Domain: Position Paper | |
| Svetlana Sheremetyeva | Towards Creating Interoperable Resources for Conceptual Annotation of Multilingual Domain Corpora |
Besides a main track, ISA-16 will feature two specialised tracks, focused on:
These topics relate to a recently started project on developing an ISO annotation standard for quantification (ISO/WD 24617-12, 'QuantML'), and a proposal for developing an ISO standard for the representation of visual information ('VoxML'), respectively.
Both specialised tracks consist of a pre-conference on-line portion and an on-site portion during the ISA-16 workshop on Tuesday, May 12. For the pre-conference on-line portion participants are invited to submit their commented annotations and representations for a batch of example items that are provided with documentation and guidelines of the envisaged annotation and representation schemes.
| Submission Link | The ISA-16 submission site |
| Submission Format | A PDF formatted based on the LREC Author's Kit |
The ISA-16 workshop is hosted by the LREC 2020 conference and is held at the conference center Le Palais du Pharo in Marseille.
| 1st March, 2020 | Deadline for submitting papers |
| 18th March, 2020 | Notification of Acceptance |
| 20th March, 2020 | Submission of commented annotations for Quantification or Visualization track |
| 1st April, 2020 | Submission of Camera Ready Papers |
| 12th May, 2020 |


Flask+OpenCV+YOLO-lite
Detection Application by OpenCV run in Flask, We Used a no-GPU Model: YOLO-Lite!
Copyright © 2019.徐静
GitHub
- Collect from
项目地址


'It kind of breaks my heart'
by Darren HandschuhDonations may be down at the Upper Room Mission, but people are still getting the help they need.
Naomi Rouck, mission general manager, said donations are down in general, including the annual Blessing in a Backpack program.
“They are coming in slowly,” she said. “Our goal is to have 280. There are a lot of other places that are looking for things but we are reaching out far and wide to get the backpacks in.”
The backpack program encourages people to fill a backpack with basic items like socks, small notepads, pens, gloves, toques or other small items.
They will then be handed out at the mission's Christmas party Dec. 23.
“I have faith our goals will be met and we will be able to hand them out to those who need them,” said Rouck.
For many of the recipients, it could be the only gift they get all year.
But the backpack program is not the only area where donations are lagging.
Rouck said the mission has a regular group of donors, but overall things are slower.
“I have actually found donations are really down this year. I know everybody is struggling this time of year to look after their own needs and that is totally understandable,” she said. “But I know there are single families out there and seniors and people on the street who could use that little bit of extra help, it kind of breaks my heart.”
But that does not mean people are going without.
“I try to figure out ways to make things a little easier for everybody,” she said, adding it can be a challenge at times.
“We basically rely on cash donations to make the mission run and they are way, way down this year so it has been quite the struggle for us.”
But Rouck has faith that things will get better.
“I really know things will turn around for the mission and we will be in a better situation,” she said. “People always come by with what we need. I have a lot of faith in that.”
Backpacks and other donations can be dropped off at the mission, 3403 27 Ave., during regular business hours.


If you do not redirect please visit : https://arthemicofficial.github.io/Radmin-Laravel-Admin-Starter-Kit/


{% extends "registrasion/invoice_.html" %} {% block payment_actions %} Pay this invoice (dummy) {{ block.super }} {% endblock %}


1LDK
2DK
Load FloorPlan
Save FloorPlan
Load FloorPlan
Add Room
Living Dining Kitchen (LDK)
Dining Kitchen (DK)
Living (L)
Dining (D)
Kitchen (K)
Room (R)
Bath (B)
Unit Bath (UB)
Powder Room (PR)
Water Closet (WC)
Entrance (E)
Passage (P)
Add Bond
-1
Change Room Size
+1
Delete Room
Delete Bond


Magic Lamp
Main
Alarms
Config
Debug
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday
Apply
Reset


Significant Separation of Orientation Data, Plot
Significant separation of clusters in the distribution is carried out.
The statistic parameters of the clusters
are calculated as shown in the program 8 (Statistics). Sperical aperture and cone
of confidence are drawn with the cluster
centre
(centre of gravity) as centre. In the
menu confidence the probability level
saved in Fabric.ini can be changed.
In the menu "Colour" the colours of all
plot parameters saved in Fabric.ini can
be
changed. In the menu "Symbol for data point" the plot symbol can be
changed.
The menu "Diagram" ist the same as in the
other programs.


KMOUTH(1) KDE User's Manual KMOUTH(1) NAME kmouth - A type-and-say front end for speech synthesizers SYNOPSIS kmouth [filename] [KDE Generic Options] [Qt(TM) Generic Options] DESCRIPTION KMouth is an application that enables persons that cannot speak to let their computers speak.It includes a history of spoken sentences from which the user can select sentences to be re-spoken. Note that KMouth does not include speech synthesizer. Instead it requires a speech synthesizer installed in the system. OPTIONS filename The history file to open. SEE ALSO espeak(1), festival(1), mbrola(1), freetts(1) More detailed user documentation is available from help:/kmouth (either enter this URL into Konqueror, or run khelpcenter help:/kmouth). There is also further information available at the KDE Accessibility project web site[1]. AUTHORS KMouth was written by Gunnar Schmi Dt<EMAIL_ADDRESS>AUTHOR Lauri Watts<EMAIL_ADDRESS>Author. NOTES 1. KDE Accessibility project web site http://accessibility.kde.org/ K Desktop Environment 2010-09-15 KMOUTH(1)


Norway’s slalom superstar
An Olympic slalom bronze medallist at the age of 19 at Sochi 2014, Henrik Kristoffersen became the world No1 in the event in the 2015/16 season, before finding his domination challenged by Austria’s Marcel Hirscher. Between them, the two have topped most slalom and giant slalom podiums since then, with the Norwegian collecting his second Olympic slalom medal, a silver, behind the Austrian at PyeongChang 2018.
Where it all began
Kristoffersen was born on 2 July 1994 in Lørenskog in the Norwegian county of Akeshus, just a stone’s throw from Marikollen ski resort, where he cut his teeth as an Alpine skier. Presented with his first pair of skis at the age of four, young Kristoffersen was also a motocross fanatic, and he has continued to integrate this physically demanding sport into his training schedule.
It was during his childhood years that the rising Norwegian star first met Kjetil Andre Aamodt, whom he describes on his website as a “great role model, great skier and a great person.” The most decorated Alpine skier in Olympic history and a three-time Olympic Super-G champion between 1992 and 2006, Aamodt said Kristoffersen was the most talented young skier he had seen since Marcel Hirscher.
Living up to that praise, the youngster scored the first major victories of his career in February 2009, aged only 14, winning the slalom and the giant events at the Trofeo Toppolino in Italy, a competition widely regarded as the unofficial world junior championships.
A brilliant junior career
The young Norwegian talent then won the giant slalom title at the 2012 World Junior Ski Championships in Roccaraso (ITA). “(It’s an) indescribable feeling. There will be five more gold medals before the end of my junior career,” he wrote afterwards.
Aside from that triumph in Italy, where he also picked up silvers in the slalom and combined, Kristoffersen collected world junior gold in the combined in Mont Saint-Anne (CAN) in 2013, and the giant and slalom in Jasná (SVK) in 2014 – a double he repeated on home snow in Hafjell a year later.
Olympic bronze at 19
The Norwegian made his FIS World Cup debut in Krjanska Gora in March 2012, and made his first visit to the podium in November 2013, pocketing slalom bronze in Levi (FIN). His first victory came in the run-up to the Olympic Winter Games in Sochi, in the night race in Schladming (AUT) the following January. After triumphing in what he described as “the biggest slalom race you can win”, Kristoffersen was hoisted aloft by Hirscher and Felix Neureuther, the two men he pipped to victory.
At Sochi 2014, Kristoffersen found himself a lowly 15th after the first run of the slalom in Rosa Khutor, fully 1.79 seconds behind Austrian veteran Mario Matt. A treacherous second run then saw a number of competitors ski out or lose their way entirely, while Kristoffersen conjured up a superb performance to shoot him up the standings.
Light on his skis, precise and full of attacking intent, he eventually finished third, behind Hirscher who took silver, and 0.88 seconds adrift of the victorious Matt. Aged only 19, the Norwegian became the youngest male medallist in Olympic Alpine skiing history. “It is not far between heaven and hell,” he posted on his website. “Bronze in my first Olympic Games. Creative course setting in the second run by the legend Ante Kostelic.”
The world’s best
Building on his Sochi bronze, Kristoffersen went on to become world slalom No1. In the process, he restored Norway – a global powerhouse of winter sports – to the pinnacle of Alpine skiing, following several lean years for the country’s skiers.
After winning his first World Cup giant slalom in Méribel (FRA) in March 2015, he enjoyed a remarkable run of success in the slalom in the 2015/16 season. In recording six consecutive victories, he became the first skier in history to win the four classic races (Adelboden (SUI), Wengen (SUI), Schladming and Kitzbühel (AUT)) in the same season. That stunning sequence helped him claim the slalom crystal globe at the end of the campaign.
Hirscher for company
Kristoffersen was on unstoppable form between the gates once again the following season, posting wins on Val d’Isère’s imposing Face de Bellevarde (FRA), and in Madonna di Campiglio (ITA), Abelboden, Wengen, Kitzbühel and Schladming, where he held off a storming Hirscher on the second run. The Austrian’s greater consistency over the campaign earned him the slalom crystal globe, however.
The following World Cup season saw the Norwegian take his total of slalom and giant slalom wins to 16 and climb onto every slalom podium (nine, including a victory in Kitzbühel) and six giant slalom podiums out of eight. On virtually every occasion, however, he was beaten by Hirscher and had to make do with second place in both the slalom and giant slalom standings.
Back on the Olympic podium
When the two faced off in the giant slalom at PyeongChang 2018, Kristoffersen lay a lowly 10th after a first run dominated by Hirscher. The Norwegian came back strongly on his second descent, however, flowing between the gates and taking risk after risk to post the fastest time. The last man down the hill, Hirscher held his nerve to take the gold, 1.27 seconds ahead of his Norwegian rival, with Alexis Pinturault of France taking the bronze.
“People say that when you finish second at the Games you haven’t won silver but you’ve lost the gold. Well, today I won it,” said Kristoffersen afterwards. “Marcel is unbeatable in the giant right now and I’ve just decided to train and to fight as hard as I can so I can beat him one day. Right now, though, he’s just better than everyone else.”
Four days later, Kristoffersen gave himself a golden chance of defending his slalom title by posting the fastest time in the first run, while Hirscher skied out after just a few gates. The same fate befell the Norwegian on his second run, however, leaving Swedish veteran Andre Myhrer to claim the glory.
“I was a little bit late coming out of that first hairpin and I took the next gate too wide and my boot hit the snow,” explained the unfortunate Kristoffersen. “I was up pretty quick - if I hadn’t had the pole between my legs, it was possible that I would have been okay. But that’s life, it happens.”
At the end of a campaign he had spent in the limelight but short of victories, the Norwegian said: “It was a great season, with some big improvements for me. Next season starts tomorrow and I’m going to be going for points.” It is a season in which he will be hoping to finally get the better of Hirscher in his two events.


SAG 2019: Bala Devi's Brace Powers Indian Women's Football Team to Third Successive Gold
Bala Devi was the star of the show again as she struck a brace on either side of the half time to guide India to a comfortable victory over the home side in the final.
The Indian women’s football team won its third consecutive gold medal at the South Asian Games after beating hosts Nepal 2-0 in the summit clash on Monday. Bala Devi was the star of the show again as she struck a brace on either side of the half time to guide India to a comfortable victory over the home side in the final.
The 29-year-old striker also ended as the top-scorer of the tournament, netting five goals in four matches.
India went on the offensive from the very start, as the likes of Sangita Basfore and Ratanbala Devi dominated proceedings from the middle of the park.
Ratanbala fetched the first clear-cut chance for India as she connected a Ranjana Chanu cross but her shot flew over the crossbar.
India goalkeeper Aditi Chauhan, who has had a fantastic tournament by not conceding a single goal in five matches, turned provider when she found Bala, with a long ball upfield. The mercurial striker duly converted the chance to give India the lead in the 18th minute.
India had another chance around the half-hour mark, when a Ranjana corner was headed down by captain Ashalata Devi towards Sandhiya, but the latter was flagged offside.
Aditi made a couple of impressive saves on either side of the half-time break to keep India in the lead.
Head coach Maymol Rocky soon brought on Manisha in place of Sandhiya, and the move had an instant impact. An intricate move was converted into a chance by Ratanbala, who played Bala through on goal. Bala finished off the chance to score her second goal of the match in the 56th minute.
The Indians kept dominating the game even after taking the 2-0 lead and the Nepalese were unable to create chances.
For breaking news and live news updates, like us on Facebook or follow us on Twitter and Instagram. Read more on Sports Latest News on India.com.
Story Timeline
- 09 Dec 2019 SAG 2019: Bala Devi's Brace Powers Indian Women's Football Team to Third Successive Gold
- 07 Dec 2019 SAG 2019: Indian Women's Football Team Beats Nepal to Enter Final
- 07 Dec 2019 ISL: ATK End NorthEast United FC's Unbeaten Run to go on Top
- 05 Dec 2019 ISL: Messi, Chermiti on Target as Mumbai And Kerala Share Spoils
Comments - Join the Discussion


Waiting for Trav and Sarah to arrive for the day out in Portland. Wondering if crying Coraline needs a nap before we start.
8/15/2009, 10:53:23 AM
Favs: 0
Retweets: 0
link--- layout: tweet ---
Waiting for Trav and Sarah to arrive for the day out in Portland. Wondering if crying Coraline needs a nap before we start.
8/15/2009, 10:53:23 AM
Favs: 0
Retweets: 0
link


CIS 128 - Introduction to Mobile Web Apps
Index
Homework 1
Short Task 2
Homework 2
Short Task 5
Homework 3
Homework 4


Home
Contact
About
Projects
Websites
George Carlin Tribute
Wix Site
Websites with Api's
Twitch Streamer Status
Wikipedia Search
Gadgets
Calculator
Pomodoro Clock
Digital Clock
Games
Simon
Tic Tac Toe
Battleship
Weather Location
I
Toggle Metric
3 Day Forecast
1
2
3
4
5
6
7


About / Email / GitHub / Google Scholar / Twitter
Page design thanks to Leonid Keselman's adaptation of Jon Barron's website.


 Director-writer Andrew L. Stone keeps the terse story tense despite the almost certain proof the murder suspect is indeed the guilty one, but he leaves open that it is not absolutely certain until the climactic scene.
Whitney Cameron (Joseph Cotten) is marvelous as the concerned uncle who rushes to the NYC hospital from his home in Boston to see how his deceased brother's daughter Polly is doing after going into a convulsion. His brother recently died and his wife Lynne (Jean Peters) is taking care of Polly and her younger brother Doug, children from his former marriage. When Polly seems to have recovered but goes into another convulsion late at night and suddenly dies, Dr. Stevenson, the one treating her, doesn't understand how and wants an autopsy performed, but Lynne refuses.
In a hurry to get back to work, Cameron is about to leave, but the family lawyer Fred Sargent (Merrill) and his news reporter wife Maggie (McLeod) become suspicious of the way the child died, suspecting that she's been poisoned. Maggie reminds Cameron that his brother died the same way, while saying "Don't touch my feet." His brother's death was classified as caused by sleeping sickness, a virus infection, but there wasn't an autopsy performed. At first, Cameron is reluctant to believe that the very loving, prim and pretty Lynne is capable of murder. But the motive soon becomes clear when Fred says he drew up his brother's will and it states that his wife gets to inherit the entire estate if the two children are deceased.
 They then call in for questioning all the servants and Lynne, and she becomes the prime suspect. In their investigation they find that the child was poisoned in the hospital and that her last medicine didn't come from the pharmacy but that Lynne brought it from her own pharmacy. When they try to indict her on murder charges, the judge throws out the case for lack of sufficient evidence. The pain on all the faces of those who are now sure that Lynne is the poisoner, relates to how helpless they feel because the system doesn't work. The fear now is that young Doug will be killed next and they can't do anything to prevent it.
Lynne informs Cameron that she plans to take Doug by boat to Europe for a year's vacation to get him to forget the ordeal he just went through. Cameron is afraid that she will get away with poisoning him there and comes up with an extreme plan to thwart her. He surprises her by joining her on the voyage and trying to romance her so that she doesn't suspect he is suspicious of her. His ultimate plan is to poison her, but when the time comes for him to do it, he can't go through with it. But while having a last drink in her room, he discovers in her bedroom a bottle of aspirins, with a few pills having a different marking on them. He surmises that those pills will be used to poison Doug. So he takes one of them and puts it into her drink and has the ship's detective there in case she confesses. She has 5-10 minutes to get the doctor to help before the poison sets in for the painful convulsions to begin. But she doesn't call the doctor until Cameron leaves the room, as she decides that her two choices are between the electric chair or dying from the poison, and decides to take her chances in court. In court, she is convicted and sentenced to life.
This is a well-acted thriller and it is directed with some nice touches, as when Cotten is romancing her on the deck and a door opens to a burst of laughter from some ship party goers. Also, in the climactic scene, it was so well done, as we were almost absolutely certain that she's guilty, but when she refuses to call the doctor for help and acts indignant toward Cotten for playing on her hospitality and deceptively using her under false pretenses, as she questions what kind of man he is. At that point the air went out of Cotten, as he thought maybe it wasn't murder, but the druggist made an honest mistake, after all these kind of accidents happen all the time.
REVIEWED ON 11/10/2000 GRADE: B-
Dennis Schwartz: "Ozus' World Movie Reviews"
http://www.sover.net/~ozus
<EMAIL_ADDRESS>© ALL RIGHTS RESERVED DENNIS SCHWARTZ
The review above was posted to the
rec.arts.movies.reviews newsgroup (de.rec.film.kritiken for German reviews).
The Internet Movie Database accepts no responsibility for the contents of the
review and has no editorial control. Unless stated otherwise, the copyright
belongs to the author.
Please direct comments/criticisms of the review to relevant newsgroups.
Broken URLs inthe reviews are the responsibility of the author.
The formatting of the review is likely to differ from the original due
to ASCII to HTML conversion.
Related links: index of all rec.arts.movies.reviews reviews


--- title: Anton Outkine permalink: / ---
I do coding. Lots of it, actually. But mainly this coding:
I first got into coding through video games, and have never stopped making them. I particularly enjoy 2D platformers, but sometimes I change up my style.
I consider myself a fullstack developer, although I have a bit better with front-end. I particularly value good design, and try to be as minimalistic as possible in all of my projects.
100% best language for beginners
800 lbs gorilla, why would anyone use it?
Minimalism - key to a good language
Not really programming
*sigh*


{% extends "layout_bsa.html" %} {% block page_title %} What is your status? - Claim your NHS Pension {% endblock %} {% block content %} {% include "includes/phase_banner_beta.html" %} {% include "includes/back.html" %} Claim your NHS Pension What is the member's reason for retirement? Competitive tendering Premature retirement Redundancy Organisational change Premature retirement (CPLNHS) Premature retirement (other) Redundancy (CPLNHS) Other closures Redundancy Other redundancies Early retirement Next {% endblock %}
Claim your NHS Pension


i
Reporter
Speak up. Ask for help.
Home
Login
Register
Login
Remember Me
Forgot Password?
Don't have an account yet?
Create an account


4 Word Representations
This chapter offers a general overview of how documents and words are viewed and processed during topic modeling.
4.1 Bag of Words
LDA processes documents as a ‘bag of words’. Bag of words means you view the frequency of the words in a document with no regard for the order the words appeared in.
Obviously there is going to be some information lost in this process, but our goal with topic modeling is to be able to view the ‘big picture’ from a large number of documents. Alternate way of thinking about it: I have a vocabulary of 100k words used across 1 million documents, then I use LDA to look 500 topics. I just narrowed down my feature space from 100k features to 500 features (dimensionality reduction).
Possible examples: Scrabble in reverse
4.2 Word Counts
For the purposes of LDA we need a way of summarizing our documents. We need to keep count of the following:
- Topic Term Matrix (variable name: n_topic_term_count, dimensions: Topic x Vocabulary): In the case of LDA, a single unique word can be assigned a different topic for each instance of the word (or generated from more than 1 topic in the case of a generative model.)
- Document Topic Matrix (variable name: n_doc_topic_count, dimensions: Document x Topics): This is the total number of words assigned to each topic in each document.
- Topic sum vector (variable name: n_topic_sum, dimensions: 1 x Topics): The topic sum vector keeps count of the total number of words in the entire corpus (all docs together) assigned to each topic. This is just a column sum of the document topic matrix.
The structures above are necessary for performing inference tasks utilizing LDA. We will go over how these structures are initialized, updated, and utilized in Chapter 6.
4.3 Plug and Play LDA
Considering most people are not going to be writing LDA code from scratch, it is worth mentioning that when using most software packages for LDA the only structure you will need to build is a term-document matrix. This is a matrix that has a single row for each term in the vocabulary and a single column for each document. This is a common input across packages such as Gensim (Python), Scikit-learn (Python), and TopicModels (R) (some are doc-term, some are term doc, so double check the package docs). Mallet uses something similar, but the user only needs to make a document file. Nearly all of the packages mentioned also have additional helper functions to convert lists of documents to the required input format.


The top panel is usefull for displaying messages or menu on a mobile or tablet sized device.
You can change the height of all top panels in the _settings.scss
file or
individually by adding a custom rule in app.scss
, such as
#topPanel .panel{ min-height: 100px; }


Top Movies of the Week
Click To View 2 Movies of the week
Click to view 5 movies of the week
Click To view 7 movies of the week
Click To view 10 movies of the week


Home
Sales
Salmon cookies shop
The best cookies shop with 5 branches around the world
taste our fresh delicious cookies.


About Me:
Gavin Reid
Full-Stack web developer.
| Midlothian, TX 76065 |<PHONE_NUMBER> |<EMAIL_ADDRESS>|
Gavin's Portfolio
Contact Me
Name: Gavin Reid
Email:
<EMAIL_ADDRESS>gavinreid0Github:
gavinreid0Click Here to Download Resume:
Link to PDF


Display the datepicker embedded in the page instead of in an overlay. Simply call .datepicker_ta() on a div instead of an input.


{% extends 'ringapp/base.html' %} {% block title %}Home{% endblock %} {% block body_block %}
A repository of rings, their properties, and more ring theory stuff.
See a list of rings on record.
All rings » Commutative rings »
You can search for rings by their properties. If you are only interested in commutative rings, try the specialized search with expanded, commutative-only properties.
There are a lot of ways you could help, even if you don't know what specifically what yet.
View a list of resources for studying ring theory.
If you like this site, then you also might like these sites:


Apocalypse Today
Right now, you're probably feeling pretty good. After all, life in the developed world is comfortable- probably more comfortable than it's been for the average human being throughout all of recored history.
But don't get too smug. There's still plenty of horrific ways it could all fall apart. In this article, you'll learn about a few of our favorites.
Mayan Doomsday
Skeptics suggest that the Mayan calendar simply rolls to a new 5,126-year era after 2012, and doesn't actually predict a life-ending apocalypse. But given that the long-dead Mayans were wrong about virtually everything else, why should we trust them on this?
Robot Takeover
Not quite as frightening as a Vampire Takeover or Living-Dead Takeover, a robot rebellion is still a disquieting thought.
Unexplained Singularity
We don't know how the universe started, so we can't be sure it won't just end, maybe today, and maybe with nothing more exciting than a puff of anti-matter and a slight fizzing noise.
Runaway Climate Change
Dismissed by some, Al Gore's prophecy of doom may still come true. If it does, we may have to contend with vicious storms, widespread food shortages, and surly air conditioning repairman
Global Epidemic
Some time in the future, a lethal virus could strike. Predictions differ about the source of the disease, but candidates include monkeys in the African jungle, bioterrorists, birds and pigs with the flu, warriors from the future, and alien race, hospitals that use too many antibiotics, vampires, the CIA, and unwashed brussel sprouts. Whatever the source, it's clearly bad news.


NMIMS is an educational empire with a 35 year legacy. A multi-disciplinary University with four campuses and 11 schools that include Management, Engineering, Pharmacy, Architecture, Commerce, Business Economics, Science, Law, Aviation, Liberal Arts & Distance Learning.
Today more than 12000 students and 450 full time faculty members are part of India's most sought after academic community. (We did not write the last line, their website did.)
NMIMS Kirit P. Mehta School of Law established in the year 2013 approved by the Bar Council of India (yes our degrees are legitimate) aims to provide quality legal education in order to meet the challenges in the field of law.
“We at Kirit P. Mehta School of Law inculcate and nourish all the professional virtues in a well calculated and planned manner”, is what the website says. And by “well calculated and planned manner” they mean, a daunting trimester pattern, 80% attendance policy and students who really appreciate extra-curricular activities as a well deserved de-stress mechanism that they can avail of once in a while, amidst the biblical life lesson serving monster that is-School of Law.
We could say Meraki is a cultural extravaganza but in reality, it is a melting pot of dead volunteers’ sacrifices, our committee head’s non-existent social lives, millions of rejected conventional event ideas, a thousand arguments with guards to let us use the classrooms beyond 7pm and all the chilly cheese maggi that was consumed during committee meetings. Meraki 2017 resembles a Karan Johar film-very dramatic, really expensive (ask our sponsorship team), running high on exaggerated emotions and an all out masala entertainer you’d watch at least once. We’ll even play Arijit Singh for a bit.
We’ve made our memes, bribed our volunteers with free food and customised t-shirts, had a long arduous committee meeting where democracy prevailed and each department’s best conspiracy theories were voted for, just for you to participate in and win all our fancy prizes in kind.
There’s a competition for everyone at Meraki, from the dancers to the painters to the singers to the actors and even the gamers. Our goal this October is to give you a platform to shed your inhibitions, show case your talents and give yourself that much needed break from NMIMS.
And no, our theme song isn’t the NMIMS Anthem, we promise.


The Wisdom, Order, and Harmony of the Government of God.
The Kingdom of God is the government of God, on the earth, or in the heavens. The earth, and all the planetary systems, are governed by the Lord; they are upheld by his power, and are sustained, directed, and controlled by his will. We are told, that "..." XXSCRIP If all things, visible and invisible, are made by and for him, he governs and sustains all worlds to us known, together with the earth on which we live. If he governs them, they are under his dominion, subject to his laws, and controlled by his will and power.
I wish here to be understood, that at present I am writing to believers in the Bible. I may hereafter give my reasons for this faith; at the present I refer to the Scriptures without this.
If the planets move beautifully, and harmoniously in their several spheres, that beauty and harmony are the result of the intelligence and wisdom that exist in his mind. If on this earth we have day and night, summer and winter, seed time and harvest, with the various changes of the seasons; this regularity, beauty, order, and harmony, are the effects of the wisdom of God.
There are two kinds of rule on the earth; one with which man has nothing directly to do, another in which he is intimately concerned. The first of these applies to the works of God alone, and His government and control of those works; the second, to the moral government, wherein man is made an agent. There is a very striking difference between the two, and the comparison is certainly not creditable to man; and however he may feel disposed to vaunt himself of his intelligence, when he reflects he will feel like Job did when he said, (xlii 6) "..."
In God's government there is perfect order, harmony, beauty, magnificence, and grandeur; in the government of man, confusion, disorder, instability, misery, discord, and death. In the first, the most consummate wisdom and power are manifested; in the second, ignorance, imbecility, and weakness. The first displays the comprehension, light, glory, beneficence, and intelligence of God; the second, the folly, littleness, darkness, and incompetency of man. The contemplation of the first elevates the mind, expands the capacity, produces grateful reflections, and fills the mind with wonder, admiration, and enlivening hopes; the contemplation of the second produces doubt, distrust, and uncertainty, and fills the mind with gloomy apprehensions. In a word, the one is the work of God, and the other that of man.
In order to present the subject in a clear light, I shall briefly point out some of the leading features of the two governments.
The first, then, is that over which God has the sole control, such as the heavens and the earth, for "..." It may be well here to say a few words on His moral government, in the heavens. All we can learn of that is very imperfectly set forth in the Scriptures. It would seem, however, that all was perfect order, for "..." "..." And in the creation of the fish, the fowls, the beasts, the creeping things, and man, it was done in the councils of God. The word was, Let us do this, and it was done. It would seem, then, that that government is perfect in its operations, for all the mandates of God are carried out with the greatest exactitude and perfection. God spake, chaos heard, and the world was formed.
We find also that transgression is punished; when Satan rebelled he was cast out of heaven, and with him those who sinned.
Here, then, in these things consummate wisdom was manifested, and power to carry it out.
The plan of redemption was also made thousands of years ago. Jesus is spoken of by the prophets as being "..." The future destiny of this earth is also spoken of by prophecy; the binding of Satan; the destruction, and redemption of the world; its celestial destiny; its becoming as a sea of glass; the descent of the new Jerusalem from heaven; the destruction of iniquity by a power exercised in the heavens, associated with one on the earth; and a time is spoken of where John says--"..." XXSCRIP But I shall let this pass for the present, and content myself with saying on this subject, that in the councils of God, in the eternal world, all these things were understood: for if He gave prophets wisdom to testify of these things, they obtained their knowledge from Him, and He could not impart what He did not know; but "..." XXSCRIP 18 God, then, has a moral government in the heavens, and it is the development of that government that is manifested in the works of creation; as Paul says, "..." XXSCRIP
But when we speak of the heavens, we mean also the planetary system; for the world, and other worlds are governed by principles independent of man. The power that causes this earth to roll on its axis, and regulates the planets in their diurnal and annual motions, is beyond man's control. Their revolutions and spheres are fixed by nature's God, and they are so beautifully arranged, and nicely balanced, that an astronomer can calculate the return of a planet scores of years beforehand, with the greatest precision and accuracy. And who can contemplate, without admiration, those stupendous worlds, rolling through the immensity of space at such an amazing velocity, moving regularly in their given spheres without coming into collision, and reflect that they have done so for thousands of years. Our earth has its day and night, summer and winter, and seed time and harvest. Well may the poet say that they--
"..."
And here let me remark how different is this to the works of man. We see, then, the power of God manifested in their preservation and guidance; but when we reflect a little further, that while our planetary system rolls in perfect order round the sun, there are other systems which perform their revolutions round their suns; and the whole of these, our system with its center, and other systems with their centers, roll round another grand center: and the whole of those, and innumerable others, equally as great, stupendous, and magnificent, roll round another more great, glorious, and resplendent, till numbers, magnificence, and glory, drown the thought, we are led to exclaim with the prophet, "..."
XXSCRIP Without referring again to the motions of our earth, and the beautiful regularity and precision of the whole of this elegant machinery, we will turn our attention a little to the works of creation as found on the earth. The make, construction, and adaptation of each for its proper sphere, are the work of God; and they are all controlled by His wisdom and power, independent of man. In the conformation of the birds, the beasts, the fishes, the reptiles, the grains, herbs, plants, and trees, we see a striking exemplification of this fact. No matter which way we turn our attention, the same order and intelligence are displayed. The fish in their organization are peculiarly adapted to their proper element; the birds and beasts to theirs; the amphibious animals to theirs. The nicely organized machinery of their bodies; their bones, muscles, skins, feathers, scales, or hair; the formation of their bodies, their manner of living, together with the nature of their food, and their particular adaptation to the various elements and climates which they occupy, are all so many marked evidences of skill, forethought, intelligence and power. We will here notice a few examples. Plunge bird, beast, or man, into the water, and let them remain there, and they will soon die; take a fish out of the water, and death ensues; yet all are happy, and move with perfect enjoyment in their proper spheres. Elevate a man, beast, or fish, into the air, and let them fall, and they will be bruised to death; but the bird, with its wings, light bones, and fragile body, is peculiarly adapted to the aerial element in which it moves, and is perfectly at home; while the brute creation and men feel as much so on the earth.  The same principle is developed in their arrangement and position on the earth. Those that inhabit a southern climate are peculiarly adapted to that situation; while those that inhabit a northern are equally fitted for theirs.
Take the reindeer and polar bear to the torrid zone, and they would be out of their proper latitude, and would probably die. Remove the elephant, lion, or tiger, to Iceland or Greenland, and leave them to their own resources, and they would inevitably perish.
We will notice for a moment the construction of their systems. Each one is possessed with muscular strength, or agility, according to its position, wants, or dangers, and there is a beauty, a symmetry, and a perfection about all God's works, which baffle and defy human intelligence to copy. An artist is considered talented if he can make, after years of toil, a striking likeness of any of those things, either on canvas, or in marble. But when he has done, it is only a dead outline; remove a little paint, or tear the canvas, and its beauty is destroyed; break the arm of a statue, and we see nothing but a mutilated stone. But take a man, for example, and remove the skin, there is still order and beauty; remove the flesh, there is still workmanship and skill, and the bones, the flesh, the muscles, the arteries and veins, and the nerves, and the lungs, not to forget the exquisite fineness of the sensitive organs, manifesting a skill, a forethought, a wisdom, and a power, as much above that of man as the heavens are above the earth.
We see the power, wisdom, and government of God, displayed in the amazing strength of some of the largest of the brute creation; as also in the fineness and delicacy, of the arrangement of the smaller. And while we admire the stupendous power of the elephant, we are equally struck with the fineness, delicacy, and beauty of some of the smaller insects. The prescience, and intelligence of God, are as much manifested in arranging the bones, muscles, arteries, and digestive organs of the smallest animaculæ, as in the construction of the horse, rhinoceros, elephant, or whale. I might touch upon the organization of plants, herbs, trees, and fruits; their various compositions, modes of nourishment, manner of propagating their kind, etc.; but enough has already been said upon this subject. It is one that no one will dispute upon; Jew and Gentile, black and white, Christian and Heathen, philosopher and fool, all have one faith on this subject.
I have briefly touched upon it for the purpose of presenting in a clear light the imbecility and weakness of man; for wherever we turn our attention, we see power, wisdom, prescience, order, forethought, beauty, grandeur and magnificence.
These are the works of God, and shew His skill, workmanship, glory, and intelligence. They reflect His divine power, and shew in unmistakeable characters the wisdom of his government, and the order that prevails in that part of creation over which He has the sole and unlimited control.
We can perceive very clearly that what God has done, is rightly done. It is not governed by instability and disorder, but continues from eternity to eternity to bear the impress of Jehovah.


{{job.snippet}}
Job Title: {{job.jobtitle}} Location: {{job.formattedLocationFull}} Date Posted: {{job.date | date}} {{job.snippet}} Competitive Not Competitive {{job.numLikes}} save(s)


A THIN LINE BETWEEN LOVE AND HATE A film review by Chuck Dowling Copyright 1997 Chuck Dowling
A Thin Line Between Love and Hate (1996) NO STARS out of ***** - C:Martin Lawrence, Lynn Whitfield, Bobby Brown, Della Reese, Roger E. Mosley.
Martin Lawrence stars in, wrote, produced, directed, and supervised the music of this absolutely dreadful "movie" about what might be one of the most unlikable main characters in movie history.
 Immediately, NO ONE should care what happens to this man. He soon meets his match (Whitfield), who leads him on for days and then decides to take revenge when she realizes she's been used and lied to. Immediately, EVERYONE should be rooting for this woman. However, the film is written so that Lawrence is the good guy in the situation, and Whitfield is just a crazy psycho.
Pauly Shore is a brilliant comedian compared to Lawrence, and I absolutely HATE Pauly Shore. Also, like Adam Sandler in "Billy Madison", not once in the film can Lawrence say a line of dialogue normally. At least one word in each sentence has to be exaggerated for no reason whatsoever and after about five minutes it couldn't be more irritating. "A Thin Line Between Love and Hate" is easily one of the worst films of the year and most definately one of the worst "comedies" of all time. I think that being murdered would be funnier than this movie.
-- Chuck Dowling Visit Chuck's Movie Reviews at http://users.southeast.net/~chuckd21/ Over 1,600 movies rated and/or reviewed! Movie news, box office reports, film related links, and reader's polls and reviews.
The review above was posted to the
rec.arts.movies.reviews newsgroup (de.rec.film.kritiken for German reviews).
The Internet Movie Database accepts no responsibility for the contents of the
review and has no editorial control. Unless stated otherwise, the copyright
belongs to the author.
Please direct comments/criticisms of the review to relevant newsgroups.
Broken URLs inthe reviews are the responsibility of the author.
The formatting of the review is likely to differ from the original due
to ASCII to HTML conversion.
Related links: index of all rec.arts.movies.reviews reviews


Welcome to PythonVehicleControl’s documentation!¶
Vehicle¶
This module defines classes required for defining vehicle parameters and movement of the vehicle (dynamics).
Example:
import matplotlib.pyplot as plt
import math
import vehicle
car = vehicle.Vehicle()
N = 100
output_hist = np.zeros((car.model.C.shape[0],N))
state_hist = np.zeros((car.model.A.shape[0],N))
x_hist = np.zeros((N,1))
y_hist = np.zeros((N,1))
yaw_hist = np.zeros((N,1))
for i in range(N):
state_hist[:,i] = car.states.flatten()
output_hist[:,i],x_hist[i,0],y_hist[i,0],yaw_hist[i,0] = car.move(10*math.pi/180)
plt.figure()
plt.plot(state_hist[LAT_POS,:], label='lat. pos. [m]')
plt.plot(state_hist[LAT_VEL,:], label='lat. vel. [m/s]')
plt.plot(state_hist[YAW,:], label='yaw [rad]')
plt.plot(state_hist[YAW_RATE,:],label='yaw rate [rad/s]')
plt.legend()
plt.grid(True)
plt.show()
-
class
vehicle.
Vehicle
(Vx=20, m=1600, Iz=1705, lf=1.4, lr=1.4, Caf=66243, Car=66243, sample_time=0.01, init_state=None)[source]¶ This class defines vehicle motion and related functions for simulating the movement of a vehicle.
Arguments:
Vx
(float) Vehicle speed [m/s]
m
(float) Vehicle mass [kg]
Iz
(float) Moment of inertia along z-axis [kg*m^2]
lf
(float) Distance to the front axle from CG [m]
lr
(float) Distance to the rear axle from CG [m]
Caf
(float) Front tire cornering stiffness [N/rad]
Car
(float) Rear tire cornering stiffness [N/rad]
sample_time
(float) Sample time for discretizing the dynamics model
init_state
([float, float, float, float]) Initial state of the vehicle (lateral position[m], lateral velocity[m/s], yaw angle[rad], yaw rate[rad/s])
Variables:
states
([float,float,float,float]) Current states (lateral position[m], lateral velocity[m/s], yaw angle[rad], yaw rate[rad/s])
x
(float) Global x position of vehicle [m]
y
(float) Global y position of vehicle [m]
model
(VehicleModel) Vehicle dynamics model
measured
([float, float]) Measured quantities (lateral velocity[m/s], yaw rate[rad/s])
-
get_dynamics_model
(sample_time=None)[source]¶ Returns the discrete state-space dynamics model. If sample_time is specified, returns discretized model which is based on specified sample time (this may not correspond to the actual state-space model of the current Vehicle object). This function is intended to be used for implementing model-based control, such as MPC.
Returns:
A
(numpy array) Discrete ystem matrix A
B
(numpy array) Discrete input matrix B
C
(numpy array) Discrete output matrix C
D
(numpy array) Initial condition D
-
move
(steering_ang)[source]¶ “Moves” vehicle 1 time step forward with given steering angle input [rad]. Use this function to execute 1 time step of simulation with internal dynamics model.
Arguments:
steering_ang
(float) Wheel steering angle [rad]. Positive angle is right side.
Returns:
Measured Outputs
([float, float]) Lateral velocity [m/s] and yaw rate [rad/s]
x
(float) Global x position of the vehicle [m]
y
(float) Global y position of the vehicle [m]
yaw
(float) Heading angle of the vehicle [rad]
-
-
class
vehicle.
VehicleModel
(params, sample_time)[source]¶ This class implements basic vehicle dynamics model in discrete state-space form.
Arguments:
params
(CarParams) Collection of parameters (e.g. mass, length, etc.) required for the bicycle model
sample_time
(float) Sample time
Variables:
A
(numpy array) System matrix
B
(numpy array) Input matrix
C
(numpy array) Output matrix
D
(numpy array) Initial condition
-
get_model_from_params
(params, sample_time)[source]¶ Calculate and return the discretized state-space model.
Arguments:
params
(CarParams) Collection of parameters (e.g. mass, length, etc.) required for bicycle model.
sample_time
(float) Sample time to be used for discretizing the state-space model.
Returns:
A
(numpy array) Discrete system matrix A
B
(numpy array) Discrete input matrix B
C
(numpy array) Discrete output matrix C
D
(numpy array) Initial condition D
-
update_model
(params, sample_time=None)[source]¶ Update the vehicle model based on updated params or sample time.
Arguments:
params
(CarParams) Collection of parameters (e.g. mass, length, etc.) required for the bicycle model
sample_time
(float) Sample time to be used for discretizing the state-space model.
-
Visualizer¶
This module defines classes required for visualization.
Example:
import visualizer
def update_func(): # define update-function to be called by visualizer for each time-step
pass
view = Visualizer(update_func)
view.entry_point()
-
class
visualizer.
Visualizer
(update_func, name='Simulator', refresh_rate=50)[source]¶ This class uses pyqtgraph and is a primary interface for interacting with the PyQt window.
Arguments:
update_func
(function) Function to be executed by PyQt at each time step
name
(string) Name of the window
refresh_rate
(float) Update rate of the view [ms]
Controller¶
This module is a collection of algorithms for control of lateral vehicle dynamics.
-
class
controller.
Controller
(NU=1, NY=1, T=1, dt=0.1)[source]¶ Generic controller parent class with some common variables (e.g. sample time, number of states, etc.)
-
class
controller.
MPC
(getModel, NU=1, NY=1, T=5, dt=0.1, output_weight=None, input_weight=None, input_lim=None, input_rate_lim=None)[source]¶ Generic model predictive controller class. Uses linearized state-space model. Convex optimization is solved through cvxpy module.
Arguments:
getModel
(function) Function which should return linearized state-space dynamics model as numpy array
NU
(int) Number of controlled states
NY
(int) Number of outputs
T
(int) Number of reference points along trajectory
dt
(float) Sample time [sec] of the controller
output_weight
(float) Weight for control of the output
input_weight
(float) Weight for input constraint control
input_lim
(float) Control input limit
input_rate_lim
(float) Control input rate limit
-
class
controller.
PID
(NU=1, NY=1, T=1, dt=0.1, P_Gain=1, I_Gain=0.1, D_Gain=0.01, weight_split_T=None, weight_split_Y=None)[source]¶ PID controller with variable number of control points along desired path. Control parameters are lateral position and heading. For each control point & parameter, there is a corresponding normalized weight which adds up to 1. Therefore, a single PID controller is used to control all of the controlled states and points along desired path.
Arguments:
NU
(int) Number of controlled states
NY
(int) Number of outputs
T
(int) Number of reference points along trajectory
dt
(float) Sample time [sec] of the controller
P_Gain
(float) Proportional gain of the controller
I_Gain
(float) Integral gain of the controller
D_Gain
(float) Derivative gain of the controller
weight_split_T
(list) Determine how to split weight on reference points (e.g. [0.5, 0.5] for equal split on all reference points)
weight_split_Y
(list) Determine how to split weight on measured output (e.g. [0.5, 0.5] for equal split on position and heading control)
Variables:
weight_table
(numpy array) NY-by-T array of normalized weight for each controlled point & parameter
Planner¶
This module implements simulated perception (e.g. lane detection from camera) and basic path planning algorithms.
-
class
path_planner.
Planner
(ax, ay, res=0.1)[source]¶ Perception and path planning class. This class takes waypooints as input and generats smooth path (currently cubic-spline from PythonRobotics). Also, this class acts as a basic perception module in that it calculates local ‘detected’ path from the global path, which simulates the functionality of perception stack. From the local path, the reference states (e.g. lateral position & heading along the path) can be computed for control.
Arguments:
ax
(list) Floats specifying x coordinates of waypoints
ay
(list) Floats specifying y coordinates of waypoints
res
(float) Spatial resolution [m] of path points
-
calc_nearest_index
(x_pos, y_pos, search_mid_pt=0, search_range=5)[source]¶ Calculate the index & distance of the closest path point from the current location specified in the input.
Arguments:
x_pos
(list) global x coordinate of current position
y_pos
(list) global y coordinate of current position
search_mid_pt
(int) reference index to include in search (0 is at current vehicle position)
search_range
(float) distance [m] to perform forward/backward search from ‘search_mid_pt’
Returns:
ind
(int) index of the closest path coordinate from the global path
mind
(float) distance to the closest path cooridnate
-
detect_local_path
(x_offset, y_offset, ang)[source]¶ Mimic camera detection of path by generating relative path to follow with respect to vehicle coordinate. This function takes global path coordinates in x, y, and heading and generates relative path with respect to vehicle.
Arguments:
x_offset
(float) vehicle global x coordinate [m]
y_offset
(float) vehicle global y coordinate [m]
ang
(float) vehicle heading angle [rad]
Variables:
x
(list) global x coordinates of desired path
y
(list) global y coordinates of desired path
yaw
(list) heading angle [rad] of path at [self.x, self.y] points
Returns:
rel_x
(list) x coordinates of path w.r.t. vehicle
rel_y
(list) y coordinates of path w.r.t. vehicle
rel_yaw
(list) desired heading angle w.r.t. vehicle
-
rotate
(x, y, rad, origin=(0, 0), relative=True)[source]¶ Rotate a point around a given point.
Arguments:
x
(float) x coordinate of the point to rotate
y
(float) y coordinate of the point to rotate
rad
(float) angle to rotate the [x,y] point by
origin
([float,float]) optional input to speicify origin of rotation
relative
(boolean) if set to True, do not tralate the [x,y] point back to origin after rotation
Returns:
qx
(list) rotated x coordinate of input [x,y]
qy
(list) rotated y coordinate of input [x,y]
-
Simulator¶
This module defines classes needed for executing simulation. All of the simulation-related aspects (e.g. simulation step, updating graphics, executing and coordinating object movement) are done by the simulator module.
-
class
simulator.
Simulator
[source]¶ This class manages all aspects of simulation and owns data-related classes (e.g. vehicle) and view-related classes (e.g. visualizer).
Arguments:
sample_time
(float) sample time of the simulation. Changing sample time will update discrete dynamics of member objects.
Variables:
N
(int) Maximum log data size
currentStep
(int) Current simulation step
view
(Visualizer) Main view for display
Graphics¶
This module implements child classes of default PyQtGraph OpenGL items for visualization. Some classes (e.g. GLBoxItem) needed to be defined to change their origin (from left bottem corner to center of object) for easier manipualtion. Some classes (e.g. lines and scatter) were extended to provide functions for changing their data in each simulation time step with a common function name to provide consistency.
-
class
gl_items.
Box
(name, size=(1, 1, 1), color='g', data=(0, 0, 0, 0, 0, 0))[source]¶ Box object is a child class of pyqtgraph.opengl.GLBoxItem. The location of the box position is at the origin, which is the x,y center of the vehicle and at 0 height (i.e. center of the bottom surface).
Arguments:
name
(string) name of the object
size
([float,float,float]) length,width,height of the box
-
class
gl_items.
Grid
(size=(50, 50, 1), **kwargs)[source]¶ This class adds grid to the 3D view and is a child class of pyqtgraph.opengl.GLGridItem.
-
class
gl_items.
Line
(name, color='g', data=None, **kwargs)[source]¶ Line is a child class of pyqtgraph.opengl.GLLinePlotItem. This class can be used to draw line items (e.g. paths, lanes, etc.) in 3D environment. Line.setData function can be used to update the data in each time step.
Arguments:
name
(string) name of the object
data
(numpy array) 3-by-n array of (x,y,z) coordinates for plot. 2-by-n also works (assume 2D)
-
setData
(data=None, **kwargs)[source]¶ Update the data displayed by this item. All arguments are optional; for example it is allowed to update vertex positions while leaving colors unchanged, etc.
Arguments:
pos
(N,3) array of floats specifying point locations.
color
(N,4) array of floats (0.0-1.0) or tuple of floats specifying a single color for the entire item.
width
float specifying line width
antialias
enables smooth line drawing
mode
- ‘lines’: Each pair of vertexes draws a single line
segment.
- ‘line_strip’: All vertexes are drawn as a
continuous set of line segments.
-
-
class
gl_items.
Scatter
(name, color=[1.0, 1.0, 0.0, 0.5], size=13, data=array([0, 0]), **kwargs)[source]¶ Scatter is a child class of pyqtgraph.opengl.GLScatterPlotItem. This class can be used to draw points (e.g. spheres, point-cloud, etc.) in 3D environment. Scatter.setData function can be used to update the data in each time step.
Arguments:
name
(string) name of the object
size
(float) size of the points
data
(numpy array) 3-by-n array of (x,y,z) coordinates for plot. 2-by-n also works (assume 2D)
-
setData
(data=None, **kwargs)[source]¶ Update the data displayed by this item. All arguments are optional; for example it is allowed to update spot positions while leaving colors unchanged, etc.
Arguments:
pos
(N,3) array of floats specifying point locations.
color
(N,4) array of floats (0.0-1.0) specifying spot colors OR a tuple of floats specifying a single color for all spots.
size
(N,) array of floats specifying spot sizes or a single value to apply to all spots.
pxMode
If True, spot sizes are expressed in pixels. Otherwise, they are expressed in item coordinates.
-