Skip to content

Commit

Permalink
Typesupport abstraction (#20)
Browse files Browse the repository at this point in the history
* tmp

* progress

* progress

* progress

* cleanup
  • Loading branch information
Pylgos authored Oct 26, 2023
1 parent dd1f842 commit 73ae24f
Show file tree
Hide file tree
Showing 12 changed files with 346 additions and 197 deletions.
18 changes: 11 additions & 7 deletions src/rclnim/clients.nim
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ type
ClientBase* = SharedPtr[ClientBaseObj]

ClientObj[T: SomeService] = object of ClientBaseObj
typesupport: ServiceTypesupport[T]

Client*[T: SomeService] = SharedPtr[ClientObj[T]]

Expand All @@ -22,7 +23,8 @@ disallowCopy ClientBaseObj

proc createClient*[T: SomeService](node: Node, serviceName: string, qos: QoSProfile): Client[T] =
result = newSharedPtr(ClientObj[T])
result[].handle = newClientHandle(node.handle, getServiceTypeSupport(T), serviceName, qos)
result[].typesupport = getServiceTypesupport[T]()
result[].handle = newClientHandle(node.handle, result[].typesupport.rosidlTypesupport, serviceName, qos)
result[].waitable = result[].handle.toWaitable()

proc createClient*(node: Node, T: typedesc[SomeService], serviceName: string, qos: QoSProfile): Client[T] =
Expand All @@ -38,29 +40,31 @@ proc waitable*[T](self: ClientRecv[T]): Waitable =
self.client[].waitable

proc send*[T](self: Client[T], req: T.Request): ClientRecv[T] =
var cReq = default(T.Request.CType)
nimMessageToC(req, cReq)
let rosReq = self[].typesupport.encodeRequest(req)
defer: self[].typesupport.deleteRequest(rosReq)
var num: int64 = 0
wrapError rcl_send_request(self[].handle.getRclClient(), addr cReq, addr num)
wrapError rcl_send_request(self[].handle.getRclClient(), rosReq, addr num)
result.client = self
result.sequenceNum = num

proc takeResponse*[T](self: ClientRecv[T], resp: var T.Response): bool =
var
cResp = default(T.Response.CType)
rosResp = self.client[].typesupport.createResponse()
info = default(rmw_service_info_t)
ret: rcl_ret_t
info.request_id.sequence_number = self.sequenceNum.int64
withLock self.client[].handle.getLock():
withLock getRclGlobalLock():
ret = rcl_take_response_with_info(self.client[].handle.getRclClient(), addr info, addr cResp)
ret = rcl_take_response_with_info(self.client[].handle.getRclClient(), addr info, rosResp)
doAssert info.request_id.sequence_number == self.sequenceNum
case ret
of RCL_RET_OK:
cMessageToNim(cResp, resp)
resp = self.client[].typesupport.decodeResponse(rosResp)
true
of RCL_RET_CLIENT_TAKE_FAILED:
self.client[].typesupport.deleteResponse(rosResp)
false
else:
self.client[].typesupport.deleteResponse(rosResp)
wrapError ret
unreachable()
16 changes: 8 additions & 8 deletions src/rclnim/handles.nim
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ proc getRclNode*(n: NodeHandle): ptr rcl_node_t =

proc newSubscriptionHandle*(
node: NodeHandle,
typeSupport: MessageTypeSupport,
typesupport: ptr rosidl_message_type_support_t,
topicName: string,
qos: QoSProfile): SubscriptionHandle =
result = newSharedPtr(SubscriptionHandleObj)
Expand All @@ -197,7 +197,7 @@ proc newSubscriptionHandle*(
opts.qos = qos.toRmw()
try:
wrapError rcl_subscription_init(
addr result.rclSubscription, node.getRclNode(), cast[ptr rosidl_message_type_support_t](typeSupport), topicName, addr opts)
addr result.rclSubscription, node.getRclNode(), typesupport, topicName, addr opts)
finally:
wrapError rcl_subscription_options_fini(addr opts)
# result.lock.initLock()
Expand All @@ -212,7 +212,7 @@ proc getRclSubscription*(s: SubscriptionHandle): ptr rcl_subscription_t =

proc newPublisherHandle*(
node: NodeHandle,
typeSupport: MessageTypeSupport,
typesupport: ptr rosidl_message_type_support_t,
topicName: string,
qos: QoSProfile): PublisherHandle =
result = newSharedPtr(PublisherHandleObj)
Expand All @@ -222,7 +222,7 @@ proc newPublisherHandle*(
var opts = rcl_publisher_get_default_options()
opts.qos = qos.toRmw()
wrapError rcl_publisher_init(
addr result.rclPublisher, node.getRclNode(), cast[ptr rosidl_message_type_support_t](typeSupport), topicName, addr opts)
addr result.rclPublisher, node.getRclNode(), typesupport, topicName, addr opts)
result.initialized = true

proc getRclPublisher*(s: PublisherHandle): ptr rcl_publisher_t =
Expand All @@ -231,7 +231,7 @@ proc getRclPublisher*(s: PublisherHandle): ptr rcl_publisher_t =

proc newServiceHandle*(
node: NodeHandle,
typeSupport: ServiceTypeSupport,
typesupport: ptr rosidl_service_type_support_t,
serviceName: string,
qos: QoSProfile): ServiceHandle =
result = newSharedPtr(ServiceHandleObj)
Expand All @@ -241,7 +241,7 @@ proc newServiceHandle*(
var opts = rcl_service_get_default_options()
opts.qos = qos.toRmw()
wrapError rcl_service_init(
addr result.rclService, node.getRclNode(), cast[ptr rosidl_service_type_support_t](typeSupport), serviceName, addr opts)
addr result.rclService, node.getRclNode(), typesupport, serviceName, addr opts)
result.lock = createShared(Lock)
result.lock[].initLock()
result.initialized = true
Expand All @@ -255,7 +255,7 @@ proc getLock*(s: ServiceHandle): var Lock =

proc newClientHandle*(
node: NodeHandle,
typeSupport: ServiceTypeSupport,
typesupport: ptr rosidl_service_type_support_t,
serviceName: string,
qos: QoSProfile): ClientHandle =
result = newSharedPtr(ClientHandleObj)
Expand All @@ -265,7 +265,7 @@ proc newClientHandle*(
var opts = rcl_client_get_default_options()
opts.qos = qos.toRmw()
wrapError rcl_client_init(
addr result.rclClient, node.getRclNode(), cast[ptr rosidl_service_type_support_t](typeSupport), serviceName, addr opts)
addr result.rclClient, node.getRclNode(), typesupport, serviceName, addr opts)
result.lock = createShared(Lock)
result.lock[].initLock()
result.initialized = true
Expand Down
63 changes: 13 additions & 50 deletions src/rclnim/private/interfacebindgen.nim
Original file line number Diff line number Diff line change
Expand Up @@ -113,26 +113,6 @@ proc toNimType(t: Type): string =
of tkFixedArray: fmt"system.array[{t.length}, {t.elemType[].toNimType}]"
of tkBoundedArray, tkUnboundedArray: fmt"system.seq[{t.elemType[].toNimType}]"

proc toCType(t: Type): string =
case t.kind
of tkBool: "system.bool"
of tkByte: "system.byte"
of tkChar: "system.uint8"
of tkF32: "system.float32"
of tkF64: "system.float64"
of tkU8: "system.uint8"
of tkU16: "system.uint16"
of tkU32: "system.uint32"
of tkU64: "system.uint64"
of tkI8: "system.int8"
of tkI16: "system.int16"
of tkI32: "system.int32"
of tkI64: "system.int64"
of tkStr: fmt"module_rclnim_rosinterfaces.CMessageSequence[char]"
of tkObject: fmt"{mangledTypeName(t.packageName, t.typeName)}.CType"
of tkFixedArray: fmt"system.array[{t.length}, {t.elemType[].toCType}]"
of tkBoundedArray, tkUnboundedArray: fmt"module_rclnim_rosinterfaces.CMessageSequence[{t.elemType[].toCType}]"

proc toNimLiteral(v: Literal, t: Type): string =
case v.kind
of litBool:
Expand Down Expand Up @@ -163,7 +143,11 @@ proc genImports(idl: RosInterfaceDef, outDir, libPath: string): string =
path = outDir/dep.pkgName/"msg"/dep.typeName.camelCaseToSnakeCase & ".nim"
mangledName = mangledModuleName(dep.pkgName, dep.typeName)
result.addLine fmt"import {relativePath(path, dir).escape()} as {mangledName}"
result.addLine fmt"import {libPath.escape} as module_rclnim_rosinterfaces"
let
# interfaceModulePath = libPath/"rosinterfaces.nim"
pragmaModulePath = libPath/"private/interfacepragmas.nim"
# result.addLine fmt"import {escape(interfaceModulePath)} as module_rclnim_rosinterfaces"
result.addLine fmt"import {escape(pragmaModulePath)} as module_interface_pragmas"
result.addLine "from ../detail/typesupport import nil"

proc genDetail(outDir: string, pkg: Package) =
Expand All @@ -177,11 +161,14 @@ proc genDoc(doc: string): string =
result.addLine fmt" ## {line}"
result.removeSuffix('\n')

proc genObj(msg: RosMsgDef, moduleName, typeName: string, doExport: bool): string =
proc genPragma(idl: RosInterfaceDef): string =
result.add "{.packageNamePragma(" & idl.pkgName.escape & "), typeNamePragma(" & idl.typeName.escape & ").}"

proc genObj(msg: RosMsgDef, moduleName, typeName: string, doExport: bool, objPragma = ""): string =
if doExport:
result.addLine fmt"type {typeName}* = object"
result.addLine fmt"type {typeName}*{objPragma} = object"
else:
result.addLine fmt"type {typeName} = object"
result.addLine fmt"type {typeName}{objPragma} = object"
result.addLine genDoc(msg.doc)
for f in msg.fields:
result.add fmt" {f.name.sanitizedFieldName}*: {f.typ.toNimType}{f.toFieldDefaultValueAsgn}"
Expand All @@ -201,32 +188,10 @@ proc genObj(msg: RosMsgDef, moduleName, typeName: string, doExport: bool): strin

result.addLine

result.addLine fmt"type {typeName}_CType = object"
for f in msg.fields:
result.addLine fmt" {f.name.sanitizedFieldName}*: {f.typ.toCType}"

result.addLine

result.addLine fmt"template CType*(_: typedesc[{qualifiedTypeName}]): untyped ="
result.addLine fmt" {moduleName}.{typeName}_CType"

result.addLine

proc genTypeTraitTemplate(typeName: string, funcName: string, value: bool): string =
result.addLine fmt"template {funcName}*(_: typedesc[{typeName}]): bool ="
result.addLine fmt" {value}"

proc genGetCTypeSupport(idl: RosInterfaceDef, typ: string): string =
let (funcName, subDir, typeSupportType) =
case idl.kind
of rikMessage: ("get_message_type_support_handle", "msg", "MessageTypeSupport")
of rikService: ("get_service_type_support_handle", "srv", "ServiceTypeSupport")
else:
doAssert false
("", "", "")
let typeSupportPragma = "{." & &"importc: \"rosidl_typesupport_c__{funcName}__{idl.pkgName}__{subDir}__{idl.typeName}\", cdecl" & ".}"
result.addLine fmt"proc getCTypeSupport*(_: typedesc[{typ}]): module_rclnim_rosinterfaces.{typeSupportType} {typeSupportPragma}"

proc processMsg(idl: RosInterfaceDef, outDir, libPath: string) =
let
moduleName = idl.typeName.camelCaseToSnakeCase
Expand All @@ -237,8 +202,7 @@ proc processMsg(idl: RosInterfaceDef, outDir, libPath: string) =

s.addLine genImports(idl, outDir, libPath)

s.addLine genObj(idl.message, moduleName, idl.typeName, true)
s.addLine genGetCTypeSupport(idl, qualifiedTypeName)
s.addLine genObj(idl.message, moduleName, idl.typeName, true, genPragma(idl))
s.addLine genTypeTraitTemplate(qualifiedTypeName, "isRosMessageType", true)

createDir(path.parentDir)
Expand All @@ -259,15 +223,14 @@ proc processSrv(idl: RosInterfaceDef, outDir, libPath: string) =
s.add genObj(idl.request, moduleName, requestName, true)
s.add genObj(idl.response, moduleName, responseName, true)

s.addLine fmt"type {typeName}* = object"
s.addLine fmt"type {typeName}*{genPragma(idl)} = object"
s.addLine
s.addLine fmt"template Request*(_: typedesc[{qualifiedTypeName}]): untyped ="
s.addLine fmt" {qualifiedTypeName}_Request"
s.addLine
s.addLine fmt"template Response*(_: typedesc[{qualifiedTypeName}]): untyped ="
s.addLine fmt" {qualifiedTypeName}_Response"
s.addLine
s.addLine genGetCTypeSupport(idl, qualifiedTypeName)
s.addLine genTypeTraitTemplate(qualifiedTypeName, "isRosServiceType", true)
createDir(path.parentDir)
writeFile(path, s)
Expand Down
3 changes: 3 additions & 0 deletions src/rclnim/private/interfacepragmas.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@

template packageNamePragma*(name: static string) {.pragma.}
template typeNamePragma*(name: static string) {.pragma.}
12 changes: 7 additions & 5 deletions src/rclnim/publishers.nim
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import "."/[rcl, errors, handles, nodes, qosprofiles, typesupports, rosinterfaces]
import "."/[rcl, errors, handles, nodes, qosprofiles, typesupports, rosinterfaces, typesupports]
import concurrent/[smartptrs]


Expand All @@ -9,12 +9,14 @@ type
PublisherBase* = SharedPtr[PublisherBaseObj]

PublisherObj[T] = object of PublisherBaseObj
typesupport: MessageTypesupport[T]

Publisher*[T] = SharedPtr[PublisherObj[T]]

proc createPublisher*[T: SomeMessage](node: Node, topicName: string, qos: QoSProfile): Publisher[T] =
result = newSharedPtr(PublisherObj[T])
result[].handle = newPublisherHandle(node.handle, getMessageTypeSupport(T), topicName, qos)
result[].typesupport = getMessageTypesupport[T]()
result[].handle = newPublisherHandle(node.handle, result[].typesupport.rosidlTypesupport, topicName, qos)

proc createPublisher*(node: Node, T: typedesc[SomeMessage], topicName: string, qos: QoSProfile): Publisher[T] =
createPublisher[T](node, topicName, qos)
Expand All @@ -23,6 +25,6 @@ proc handle*(self: PublisherBase | Publisher): PublisherHandle =
self[].handle

proc publish*[T](pub: Publisher[T], msg: T) =
var cMsg = default(T.CType)
nimMessageToC(msg, cMsg)
wrapError rcl_publish(pub.handle.getRclPublisher(), addr cMsg, nil)
let encoded = pub[].typesupport.encode(msg)
defer: pub[].typesupport.delete(encoded)
wrapError rcl_publish(pub.handle.getRclPublisher(), encoded, nil)
2 changes: 1 addition & 1 deletion src/rclnim/rosinterfaceimporters.nim
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ proc writeImportStmt(path: string, module: Module, res: var NimNode) =
macro importInterface*(first: untyped, rest: varargs[untyped]): untyped =
let modules = parseImport(first & rest.toSeq)
let pkgName = modules[0].pkg
let libPath = currentSourcePath/../"rosinterfaces.nim"
let libPath = currentSourcePath.parentDir()

let res = gorgeEx(fmt"{helperExePath.quoteShell} {pkgName.quoteShell} {getBindingDir().quoteShell} {getAltBindingDir().quoteShell} {libPath.quoteShell}")
if res.exitCode != 0:
Expand Down
Loading

0 comments on commit 73ae24f

Please sign in to comment.