Signal management

This commit is contained in:
Richard Ramos 2020-05-13 16:20:50 -04:00 committed by Iuri Matias
parent dfcffeae62
commit ba2fc5eb73
7 changed files with 103 additions and 28 deletions

View File

@ -28,7 +28,6 @@ QtObject:
self.names.add(chatId)
self.endInsertRows()
method rowCount(self: ChatsModel, index: QModelIndex = nil): int =
return self.names.len

View File

@ -8,6 +8,7 @@ import status/utils
import status/core as status
import status/chat as status_chat
import status/test as status_test
import status/types as types
proc mainProc() =
# From QT docs:
@ -18,13 +19,16 @@ proc mainProc() =
var app = newQApplication()
defer: app.delete() # Defer will run this just before mainProc() function ends
var appState = state.newAppState()
echo appState.title
var chatsModel = newChatsModel();
defer: chatsModel.delete
var engine = newQQmlApplicationEngine()
defer: engine.delete()
status.init()
status.init(appState)
status_test.setupNewAccount()
discard status_test.addPeer("enode://2c8de3cbb27a3d30cbb5b3e003bc722b126f5aef82e2052aaef032ca94e0c7ad219e533ba88c70585ebd802de206693255335b100307645ab5170e88620d2a81@47.244.221.14:443")
@ -44,8 +48,7 @@ proc mainProc() =
let chatsVariant = newQVariant(chatsModel)
defer: chatsVariant.delete
var appState = state.newAppState()
echo appState.title
appState.subscribe(proc () =
chatsModel.names = @[]
@ -55,6 +58,7 @@ proc mainProc() =
)
status.startMessenger()
appState.addChannel("test")
appState.addChannel("test2")
@ -62,11 +66,24 @@ proc mainProc() =
engine.setRootContextProperty("chatsModel", chatsVariant)
engine.load("../ui/main.qml")
# EXAMPLE: this will be triggered once a message is received
appState.onSignal(SignalType.Message, proc(myMessage: string): void =
echo "I received a message: ", myMessage
);
# Handle signals as part of the state
var signalWorker: Thread[AppState]
signalWorker.createThread(proc(s:AppState) = s.processSignals, appState)
defer: signalWorker.joinThread()
# Qt main event loop is entered here
# The termination of the loop will be performed when exit() or quit() is called
app.exec()
when isMainModule:
mainProc()
GC_fullcollect()

View File

@ -1,2 +1,2 @@
--threads:on
--tlsEmulation:off
--tlsEmulation:off

View File

@ -1,16 +1,33 @@
import status/types
import tables
type Channel = object
type
ChatChannel = object
name*: string
type
Subscriber* = proc ()
Subscriber* = proc ()
SignalSubscriber* = proc(p0: string)
Signal = object
signalType*: SignalType
content*: string
var signalChannel: Channel[Signal]
type AppState* = ref object
title*: string
channels*: seq[Channel]
subscribers*: seq[Subscriber]
title*: string
channels*: seq[ChatChannel]
subscribers*: seq[Subscriber]
signalSubscribers*: Table[SignalType, seq[SignalSubscriber]]
proc newAppState*(): AppState =
result = AppState(title: "hello")
result = AppState(
title: "hello",
signalSubscribers: initTable[SignalType, seq[SignalSubscriber]]()
)
signalChannel.open()
proc subscribe*(self: AppState, subscriber: Subscriber) =
self.subscribers.add(subscriber)
@ -20,5 +37,37 @@ proc dispatch*(self: AppState) =
subscriber()
proc addChannel*(self: AppState, name: string) =
self.channels.add(Channel(name: name))
self.channels.add(ChatChannel(name: name))
self.dispatch()
#####################
# Signal Handling
proc processSignals*(self: AppState) =
## Polls the signal channel and push the message to each subscriber
{.gcsafe.}:
while(true):
let tried = signalChannel.tryRecv()
if tried.dataAvailable and self.signalSubscribers.hasKey(tried.msg.signalType):
for subscriber in self.signalSubscribers[tried.msg.signalType]:
subscriber(tried.msg.content)
defer:
signalChannel.close()
proc addToChannel(s: Signal) {.thread.} =
signalChannel.send(s)
proc nextSignal*(self: AppState, signalType: SignalType, jsonMessage: string) =
## This is called by the signal handler for each signal received and
## adds it to the signal channel for being consumed by the SignalSubscribers
let signal: Signal = Signal(signalType: signalType, content: jsonMessage)
var worker: Thread[Signal]
createThread(worker, addToChannel, signal)
worker.joinThread()
proc onSignal*(self: AppState, signalType: SignalType, subscriber: SignalSubscriber) =
## Register a callback that will be executed once
## a signal is received from status-go
if not self.signalSubscribers.hasKey(signalType):
self.signalSubscribers[signalType] = @[]
self.signalSubscribers[signalType].add(subscriber)

View File

@ -2,12 +2,13 @@ import libstatus
import signals
import types
import chat
import "../state"
proc setSignalHandler(signalHandler: SignalCallback) =
libstatus.setSignalEventCallback(signalHandler)
proc init*() =
setSignalHandler(onSignal)
proc init*(state: AppState) =
setSignalHandler(onSignal(state))
proc startMessenger*() =
chat.startMessenger()

View File

@ -1,19 +1,19 @@
import types
import json
import "../state" as state
var onSignal*: SignalCallback = proc(p0: cstring): void =
setupForeignThreadGc()
# TODO: Dispatch depending on message type $jsonSignal["type"].getStr
# Consider also have an intermediate object with an enum for type
# So you do not have to deal with json objects but with a nim type
proc onSignal*(state: AppState): SignalCallback =
result = proc(p0: cstring): void =
setupForeignThreadGc()
let jsonSignal = ($p0).parseJson
let signalType = $jsonSignal["type"].getStr
let jsonSignal = ($p0).parseJson
let messageType = $jsonSignal["type"].getStr
case signalType:
of "messages.new":
state.nextSignal(SignalType.Message, $jsonSignal)
else:
state.nextSignal(SignalType.Unknown, $jsonSignal)
case messageType:
of "messages.new":
echo $p0
else:
discard messageType #TODO: do something
tearDownForeignThreadGc()
tearDownForeignThreadGc()

View File

@ -1 +1,10 @@
import hashes
type SignalCallback* = proc(eventMessage: cstring): void
type SignalType* {.pure.} = enum
Message = "messages.new"
Wallet = "wallet"
NodeStarted = "node.started"
Unknown
#TODO: add missing types