Skip to content

Commit

Permalink
use srvo hint when binding roles
Browse files Browse the repository at this point in the history
  • Loading branch information
pelikhan committed Oct 27, 2023
1 parent efec9ed commit c48c77a
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 56 deletions.
122 changes: 87 additions & 35 deletions rolemgr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,38 +17,49 @@ namespace jacdac._rolemgr {
const i = role.indexOf("?")
const name = i < 0 ? role : role.substr(0, i)
settings.writeString(key, name)
console.add(jacdac.logPriority, `role: set ${name} -> ${devid}:${servIdx}`)
}
else {
console.add(
jacdac.logPriority,
`role: set ${name} -> ${devid}:${servIdx}`
)
} else {
settings.remove(key)
console.add(jacdac.logPriority, `role: clear binding ${devid}:${servIdx}`)
console.add(
jacdac.logPriority,
`role: clear binding ${devid}:${servIdx}`
)
}
jacdac.bus.clearAttachCache()
}

class DeviceWrapper {
bindings: RoleBinding[] = []
score = -1
constructor(public device: Device) { }
constructor(public device: Device) {}
}

class RoleBinding {
boundToDev: Device
boundToServiceIdx: number

constructor(public readonly role: string, public readonly serviceClass: number, public readonly roleQuery: ClientRoleQuery) { }
constructor(
public readonly role: string,
public readonly serviceClass: number,
public readonly roleQuery: ClientRoleQuery
) {}

host() {
if (this.roleQuery.device)
return this.roleQuery.device
if (this.roleQuery.device) return this.roleQuery.device
const slashIdx = this.role.indexOf("/")
if (slashIdx < 0) return this.role
else return this.role.slice(0, slashIdx - 1)
}

select(devwrap: DeviceWrapper, serviceIdx: number) {
const dev = devwrap.device
if (dev == this.boundToDev && serviceIdx == this.boundToServiceIdx) {
if (
dev == this.boundToDev &&
serviceIdx == this.boundToServiceIdx
) {
return
}
if (this.boundToDev)
Expand All @@ -62,7 +73,7 @@ namespace jacdac._rolemgr {

class ServerBindings {
bindings: RoleBinding[] = []
constructor(public host: string) { }
constructor(public host: string) {}

get fullyBound() {
return this.bindings.every(b => b.boundToDev != null)
Expand All @@ -75,7 +86,7 @@ namespace jacdac._rolemgr {
let numBound = 0
let numPossible = 0
const dev = devwrap.device
const missing: RoleBinding[] = []
let missing: RoleBinding[] = []
for (const b of this.bindings) {
if (b.boundToDev) {
if (b.boundToDev == dev) numBound++
Expand All @@ -84,26 +95,54 @@ namespace jacdac._rolemgr {
}
}

const sbuf = dev.services
for (let idx = 4; idx < sbuf.length; idx += 4) {
const serviceIndex = idx >> 2
const nsrv = dev.serviceClassLength

// find next one in order of serviceOffset
for (let mi = 0; mi < missing.length; ++mi) {
const mis = missing[mi]
if (mis.roleQuery.serviceOffset >= 0) {
// lookup the serviceIndex
const serviceIndex = dev.serviceIndexAtOffset(
mis.serviceClass,
mis.roleQuery.serviceOffset
)
if (serviceIndex < 0 || devwrap.bindings[serviceIndex])
continue
// we've got a match!
numPossible++ // this can be assigned
// in fact, assign if requested
if (select) {
mis.select(devwrap, serviceIndex)
console.add(
jacdac.logPriority,
`bind srvo: ${missing[mi].role} -> ${dev.shortId}:${serviceIndex} -> ${missing[mi].boundToDev}:${missing[mi].boundToServiceIdx}`
)
}
}
}

missing = missing.filter(m => !m.boundToDev)

// find next one in order
for (let serviceIndex = 1; serviceIndex < nsrv; serviceIndex++) {
// if service is already bound to some client, move on
if (devwrap.bindings[serviceIndex]) continue

const serviceClass = sbuf.getNumber(NumberFormat.UInt32LE, idx)
for (let i = 0; i < missing.length; ++i) {
if (missing[i].serviceClass == serviceClass) {
const serviceClass = dev.serviceClassAt(serviceIndex)
for (let mi = 0; mi < missing.length; ++mi) {
if (missing[mi].serviceClass == serviceClass) {
// we've got a match!
numPossible++ // this can be assigned
// in fact, assign if requested
if (select) {
missing[i].select(devwrap, serviceIndex)
missing[mi].select(devwrap, serviceIndex)
console.add(
jacdac.logPriority,
`bind: ${missing[i].role} -> ${dev.shortId}:${serviceIndex} -> ${missing[i].boundToDev}:${missing[i].boundToServiceIdx}`)
`bind order: ${missing[mi].role} -> ${dev.shortId}:${serviceIndex} -> ${missing[mi].boundToDev}:${missing[mi].boundToServiceIdx}`
)
}
// this one is no longer missing
missing.splice(i, 1)
missing.splice(mi, 1)
// move on to the next service in announce
break
}
Expand Down Expand Up @@ -152,7 +191,7 @@ namespace jacdac._rolemgr {
this.sendReport(
JDPacket.jdpacked(
jacdac.RoleManagerReg.AllRolesAllocated |
CMD_GET_REG,
CMD_GET_REG,
jacdac.RoleManagerRegPack.AllRolesAllocated,
[
jacdac.bus.allClients.every(
Expand Down Expand Up @@ -206,10 +245,11 @@ namespace jacdac._rolemgr {
const n = jacdac.bus.allClients.length
for (let i = 0; i < n; ++i) {
const client = jacdac.bus.allClients[i]
r += `${client.role || ""}:${client.broadcast ||
r += `${client.role || ""}:${
client.broadcast ||
(client.device && client.device.deviceId) ||
""
}:${client.serviceIndex}`
}:${client.serviceIndex}`
}
const buf = Buffer.fromUTF8(r)
return buf.hash(32)
Expand All @@ -218,7 +258,6 @@ namespace jacdac._rolemgr {
bindRoles() {
if (!this.running) return

//console.log("bind roles")
// sanity check and unbind any self roles if the self device is running
for (const cl of jacdac.bus.allClients) {
if (!cl.broadcast && cl.role && !!cl.device) {
Expand All @@ -227,7 +266,9 @@ namespace jacdac._rolemgr {
// check if we have a server running on this device
// that matches
const serviceClass = cl.serviceClass
const services = jacdac.bus.servers.filter(server => server.serviceClass == serviceClass)
const services = jacdac.bus.servers.filter(
server => server.serviceClass == serviceClass
)
if (services.length) {
if (cl.device != jacdac.bus.selfDevice) {
// we have a server running on this device
Expand All @@ -236,12 +277,18 @@ namespace jacdac._rolemgr {
setRole(cl.device.deviceId, cl.serviceIndex, "")
} else {
const serviceOffset = query.serviceOffset
if (!isNaN(serviceOffset) && (
!services[query.serviceOffset]
|| services[query.serviceOffset].serviceIndex != cl.serviceIndex
)) {
if (
!isNaN(serviceOffset) &&
(!services[query.serviceOffset] ||
services[query.serviceOffset]
.serviceIndex != cl.serviceIndex)
) {
//console.log(`unbind role: reassign to service offset`)
setRole(cl.device.deviceId, cl.serviceIndex, "")
setRole(
cl.device.deviceId,
cl.serviceIndex,
""
)
}
}
}
Expand All @@ -254,16 +301,20 @@ namespace jacdac._rolemgr {
this.checkChanges()
return
}
//this.log(
// `autobind: devs:${jacdac.bus.devices.length}, clients:${jacdac.bus.allClients.length}, unbound:${jacdac.bus.unattachedClients.length}`
//)
this.log(
`autobind: devs:${jacdac.bus.devices.length}, clients:${jacdac.bus.allClients.length}, unbound:${jacdac.bus.unattachedClients.length}`
)

const bindings: RoleBinding[] = []
const wraps = bus.devices.map(d => new DeviceWrapper(d))

for (const cl of jacdac.bus.allClients) {
if (!cl.broadcast && cl.role) {
const b = new RoleBinding(cl.role, cl.serviceClass, cl.roleQuery)
const b = new RoleBinding(
cl.role,
cl.serviceClass,
cl.roleQuery
)
if (cl.device) {
b.boundToDev = cl.device
b.boundToServiceIdx = cl.serviceIndex
Expand All @@ -290,10 +341,11 @@ namespace jacdac._rolemgr {
h.bindings.push(b)
}

this.log(`found ${servers.length} servers`)
// exclude hosts that have already everything bound
servers = servers.filter(h => !h.fullyBound)

//console.log(`binding: ${servers.length} servers`)
this.log(`binding ${servers.length} servers`)

while (servers.length > 0) {
// Get host with maximum number of clients (resolve ties by name)
Expand Down
38 changes: 29 additions & 9 deletions routing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ namespace jacdac {
c.serviceIndex
)
) {
log(`rebound ${c.roleName}`)
newClients.push(c)
occupied[c.serviceIndex] = 1
} else {
Expand All @@ -229,7 +230,10 @@ namespace jacdac {

this.emit(DEVICE_ANNOUNCE, dev)

if (this.unattachedClients.length == 0) return
if (this.unattachedClients.length == 0) {
log(`all clients attached`)
return
}

for (let i = 4; i < dev.services.length; i += 4) {
if (occupied[i >> 2]) continue
Expand All @@ -239,11 +243,11 @@ namespace jacdac {
)
for (const cc of this.unattachedClients) {
if (cc.serviceClass == serviceClass) {
console.add(
jacdac.logPriority,
`reattach: ${cc.role} -> ${dev.shortId}:${i >> 2}`
)
if (cc._attach(dev, serviceClass, i >> 2)) break
log(`reattach: ${cc.role} -> ${dev.shortId}:${i >> 2}`)
if (cc._attach(dev, serviceClass, i >> 2)) {
log(`success`)
break
}
}
}
}
Expand Down Expand Up @@ -1387,7 +1391,7 @@ namespace jacdac {
query.serviceIndex != undefined &&
query.serviceIndex == serviceIdx
) {
log(`role: match ${role} (dev:srvi)`)
log(`role: match ${role} (dev+srvi)`)
return true
}
// precise service offset match
Expand All @@ -1396,14 +1400,15 @@ namespace jacdac {
query.serviceOffset ==
this.serviceOffsetAt(serviceClass, serviceIdx)
) {
log(`role: match ${role} (dev:srvo)`)
log(`role: match ${role} (dev+srvo)`)
return true
}
// pick first match
if (
query.serviceIndex != undefined &&
query.serviceOffset != undefined
) {
log(`role: match ${role} (dev:!srv)`)
log(`role: match ${role} (dev+!srv)`)
return true
}
}
Expand All @@ -1421,6 +1426,21 @@ namespace jacdac {
return this.services.length >> 2
}

public serviceIndexAtOffset(
serviceClass: number,
serviceOffset: number
) {
let offset = 0
const n = this.serviceClassLength
for (let i = 0; i < n; ++i) {
if (this.serviceClassAt(i) === serviceClass) {
if (offset === serviceOffset) return i
offset++
}
}
return -1
}

private serviceOffsetAt(
serviceClass: number,
serviceIndex: number
Expand Down
45 changes: 33 additions & 12 deletions servo/test.microbit.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,35 @@
//jacdac.logPriority = ConsolePriority.Log
namespace test {
//% fixedInstance whenUsed
export const leftServo = new modules.ServoClient("leftServo?srvo=0")
//% fixedInstance whenUsed
export const middleServo = new modules.ServoClient("middleServo?srvo=1")
//% fixedInstance whenUsed
export const rightServo = new modules.ServoClient("rightServo?srvo=2")
}

forever(() => {
modules.servo1.run(50)
// modules.servo2.run(50)
// modules.servo3.run(50)
basic.pause(1000)
modules.servo1.run(0)
// modules.servo2.run(0)
// modules.servo3.run(0)
// basic.pause(1000)
})
console.log("middle")
basic.clearScreen()
led.plot(2, 0)
test.middleServo.setAngle(90)
test.rightServo.setAngle(0)
test.leftServo.setAngle(0)
pause(2000)

modules.servo1.start()
//modules.servo2.start()
//modules.servo3.start()
console.log("right")
basic.clearScreen()
led.plot(4, 0)
test.leftServo.setAngle(0)
test.middleServo.setAngle(0)
test.rightServo.setAngle(90)
pause(2000)

console.log("left")
basic.clearScreen()
led.plot(0, 0)
test.leftServo.setAngle(90)
test.middleServo.setAngle(0)
test.rightServo.setAngle(0)
pause(2000)
})

0 comments on commit c48c77a

Please sign in to comment.