2021-02-06 03:53:55 +03:00
|
|
|
namespace jacdac._rolemgr {
|
|
|
|
const roleSettingPrefix = "#jdr:"
|
|
|
|
|
|
|
|
export function clearRoles() {
|
|
|
|
settings.list(roleSettingPrefix).forEach(settings.remove)
|
2021-05-19 18:35:56 +03:00
|
|
|
jacdac.bus.clearAttachCache()
|
2021-02-06 03:53:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
export function getRole(devid: string, servIdx: number) {
|
|
|
|
return settings.readString(roleSettingPrefix + devid + ":" + servIdx)
|
|
|
|
}
|
|
|
|
|
|
|
|
export function setRole(devid: string, servIdx: number, role: string) {
|
|
|
|
const key = roleSettingPrefix + devid + ":" + servIdx
|
2022-04-25 21:40:53 +03:00
|
|
|
if (role) {
|
|
|
|
// drop query
|
|
|
|
const i = role.indexOf("?")
|
|
|
|
const name = i < 0 ? role : role.substr(0, i)
|
|
|
|
settings.writeString(key, name)
|
2023-10-27 21:50:26 +03:00
|
|
|
console.add(
|
|
|
|
jacdac.logPriority,
|
|
|
|
`role: set ${name} -> ${devid}:${servIdx}`
|
|
|
|
)
|
|
|
|
} else {
|
2022-04-25 21:40:53 +03:00
|
|
|
settings.remove(key)
|
2023-10-27 21:50:26 +03:00
|
|
|
console.add(
|
|
|
|
jacdac.logPriority,
|
|
|
|
`role: clear binding ${devid}:${servIdx}`
|
|
|
|
)
|
2022-04-25 21:40:53 +03:00
|
|
|
}
|
2021-05-19 18:35:56 +03:00
|
|
|
jacdac.bus.clearAttachCache()
|
2021-02-06 03:53:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
class DeviceWrapper {
|
|
|
|
bindings: RoleBinding[] = []
|
|
|
|
score = -1
|
2023-10-27 21:50:26 +03:00
|
|
|
constructor(public device: Device) {}
|
2021-02-06 03:53:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
class RoleBinding {
|
|
|
|
boundToDev: Device
|
|
|
|
boundToServiceIdx: number
|
|
|
|
|
2023-10-27 21:50:26 +03:00
|
|
|
constructor(
|
|
|
|
public readonly role: string,
|
|
|
|
public readonly serviceClass: number,
|
|
|
|
public readonly roleQuery: ClientRoleQuery
|
|
|
|
) {}
|
2021-02-06 03:53:55 +03:00
|
|
|
|
|
|
|
host() {
|
2023-10-27 21:50:26 +03:00
|
|
|
if (this.roleQuery.device) return this.roleQuery.device
|
2021-02-06 03:53:55 +03:00
|
|
|
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
|
2023-10-27 21:50:26 +03:00
|
|
|
if (
|
|
|
|
dev == this.boundToDev &&
|
|
|
|
serviceIdx == this.boundToServiceIdx
|
|
|
|
) {
|
2021-02-06 03:53:55 +03:00
|
|
|
return
|
2022-04-25 21:40:53 +03:00
|
|
|
}
|
2021-02-06 03:53:55 +03:00
|
|
|
if (this.boundToDev)
|
|
|
|
setRole(this.boundToDev.deviceId, this.boundToServiceIdx, null)
|
|
|
|
devwrap.bindings[serviceIdx] = this
|
|
|
|
setRole(dev.deviceId, serviceIdx, this.role)
|
|
|
|
this.boundToDev = dev
|
|
|
|
this.boundToServiceIdx = serviceIdx
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-08 11:10:21 +03:00
|
|
|
class ServerBindings {
|
2021-02-06 03:53:55 +03:00
|
|
|
bindings: RoleBinding[] = []
|
2023-10-27 21:50:26 +03:00
|
|
|
constructor(public host: string) {}
|
2021-02-06 03:53:55 +03:00
|
|
|
|
|
|
|
get fullyBound() {
|
|
|
|
return this.bindings.every(b => b.boundToDev != null)
|
|
|
|
}
|
|
|
|
|
|
|
|
// candidate devices are ordered by [numBound, numPossible, device_id]
|
|
|
|
// where numBound is number of clients already bound to this device
|
|
|
|
// and numPossible is number of clients that can possibly be additionally bound
|
|
|
|
scoreFor(devwrap: DeviceWrapper, select = false) {
|
|
|
|
let numBound = 0
|
|
|
|
let numPossible = 0
|
|
|
|
const dev = devwrap.device
|
2023-10-27 21:50:26 +03:00
|
|
|
let missing: RoleBinding[] = []
|
2021-02-06 03:53:55 +03:00
|
|
|
for (const b of this.bindings) {
|
|
|
|
if (b.boundToDev) {
|
2021-06-19 03:20:03 +03:00
|
|
|
if (b.boundToDev == dev) numBound++
|
2021-02-06 03:53:55 +03:00
|
|
|
} else {
|
|
|
|
missing.push(b)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-27 21:50:26 +03:00
|
|
|
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++) {
|
2021-02-06 03:53:55 +03:00
|
|
|
// if service is already bound to some client, move on
|
2021-06-19 03:20:03 +03:00
|
|
|
if (devwrap.bindings[serviceIndex]) continue
|
2021-02-06 03:53:55 +03:00
|
|
|
|
2023-10-27 21:50:26 +03:00
|
|
|
const serviceClass = dev.serviceClassAt(serviceIndex)
|
|
|
|
for (let mi = 0; mi < missing.length; ++mi) {
|
|
|
|
if (missing[mi].serviceClass == serviceClass) {
|
2021-02-06 03:53:55 +03:00
|
|
|
// we've got a match!
|
|
|
|
numPossible++ // this can be assigned
|
|
|
|
// in fact, assign if requested
|
|
|
|
if (select) {
|
2023-10-27 21:50:26 +03:00
|
|
|
missing[mi].select(devwrap, serviceIndex)
|
2021-09-13 21:00:34 +03:00
|
|
|
console.add(
|
|
|
|
jacdac.logPriority,
|
2023-10-27 21:50:26 +03:00
|
|
|
`bind order: ${missing[mi].role} -> ${dev.shortId}:${serviceIndex} -> ${missing[mi].boundToDev}:${missing[mi].boundToServiceIdx}`
|
|
|
|
)
|
2021-02-06 03:53:55 +03:00
|
|
|
}
|
|
|
|
// this one is no longer missing
|
2023-10-27 21:50:26 +03:00
|
|
|
missing.splice(mi, 1)
|
2021-02-06 03:53:55 +03:00
|
|
|
// move on to the next service in announce
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// if nothing can be assigned, the score is zero
|
2021-06-19 03:20:03 +03:00
|
|
|
if (numPossible == 0) return 0
|
2021-02-06 03:53:55 +03:00
|
|
|
|
|
|
|
// otherwise the score is [numBound, numPossible], lexicographic
|
|
|
|
// numPossible can't be larger than ~64, leave it a few more bits
|
|
|
|
return (numBound << 8) | numPossible
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-29 22:19:15 +03:00
|
|
|
function maxIn<T>(
|
|
|
|
arr: T[],
|
|
|
|
cmp: (a: T, b: T) => number,
|
|
|
|
cmdTie: (a: T, b: T) => number
|
|
|
|
) {
|
2021-02-06 03:53:55 +03:00
|
|
|
let maxElt = arr[0]
|
|
|
|
for (let i = 1; i < arr.length; ++i) {
|
2021-11-29 22:19:15 +03:00
|
|
|
const c = cmp(maxElt, arr[i])
|
|
|
|
if (c < 0 || (c == 0 && cmdTie(maxElt, arr[i]) < 0)) maxElt = arr[i]
|
2021-02-06 03:53:55 +03:00
|
|
|
}
|
|
|
|
return maxElt
|
|
|
|
}
|
|
|
|
|
2021-04-08 11:10:21 +03:00
|
|
|
export class RoleManagerServer extends Server {
|
2021-06-19 03:20:03 +03:00
|
|
|
private _oldBindingsHash: number
|
2021-05-19 18:35:56 +03:00
|
|
|
public autoBind = true
|
2021-03-15 11:14:49 +03:00
|
|
|
|
2021-02-06 03:53:55 +03:00
|
|
|
constructor() {
|
2022-04-11 19:26:43 +03:00
|
|
|
super(jacdac.SRV_ROLE_MANAGER)
|
2021-02-06 03:53:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
public handlePacket(packet: JDPacket) {
|
2021-06-19 03:20:03 +03:00
|
|
|
this.autoBind = this.handleRegBool(
|
|
|
|
packet,
|
|
|
|
jacdac.RoleManagerReg.AutoBind,
|
|
|
|
this.autoBind
|
|
|
|
)
|
2021-02-06 03:53:55 +03:00
|
|
|
|
|
|
|
switch (packet.serviceCommand) {
|
2021-03-07 13:38:49 +03:00
|
|
|
case jacdac.RoleManagerReg.AllRolesAllocated | CMD_GET_REG:
|
2021-06-19 03:20:03 +03:00
|
|
|
this.sendReport(
|
|
|
|
JDPacket.jdpacked(
|
|
|
|
jacdac.RoleManagerReg.AllRolesAllocated |
|
2023-10-27 21:50:26 +03:00
|
|
|
CMD_GET_REG,
|
2022-04-11 19:26:43 +03:00
|
|
|
jacdac.RoleManagerRegPack.AllRolesAllocated,
|
2021-06-19 03:20:03 +03:00
|
|
|
[
|
|
|
|
jacdac.bus.allClients.every(
|
|
|
|
c => c.broadcast || !!c.device
|
|
|
|
)
|
|
|
|
? 1
|
|
|
|
: 0,
|
|
|
|
]
|
|
|
|
)
|
|
|
|
)
|
2021-02-06 03:53:55 +03:00
|
|
|
break
|
2021-03-07 13:38:49 +03:00
|
|
|
case jacdac.RoleManagerCmd.SetRole:
|
2021-03-15 11:57:11 +03:00
|
|
|
if (packet.data.length >= 9)
|
2021-06-19 03:20:03 +03:00
|
|
|
setRole(
|
|
|
|
packet.data.slice(0, 8).toHex(),
|
|
|
|
packet.data[8],
|
|
|
|
packet.data.slice(9).toString()
|
|
|
|
)
|
2021-02-06 03:53:55 +03:00
|
|
|
break
|
2022-01-05 20:00:03 +03:00
|
|
|
case jacdac.RoleManagerCmd.ListRoles:
|
2021-06-19 03:20:03 +03:00
|
|
|
OutPipe.respondForEach(
|
|
|
|
packet,
|
|
|
|
jacdac.bus.allClients,
|
|
|
|
packName
|
|
|
|
)
|
2021-02-06 03:53:55 +03:00
|
|
|
break
|
2021-03-07 13:38:49 +03:00
|
|
|
case jacdac.RoleManagerCmd.ClearAllRoles:
|
2021-02-06 03:53:55 +03:00
|
|
|
clearRoles()
|
|
|
|
break
|
2021-11-09 04:02:03 +03:00
|
|
|
default:
|
|
|
|
packet.possiblyNotImplemented()
|
|
|
|
break
|
2021-02-06 03:53:55 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
function packName(c: Client) {
|
2021-06-19 03:20:03 +03:00
|
|
|
const devid = c.device
|
|
|
|
? Buffer.fromHex(c.device.deviceId)
|
|
|
|
: Buffer.create(8)
|
2021-02-06 03:53:55 +03:00
|
|
|
const servidx = c.device ? c.serviceIndex : 0
|
2021-06-19 03:20:03 +03:00
|
|
|
return jdpack("b[8] u32 u8 s", [
|
|
|
|
devid,
|
|
|
|
c.serviceClass,
|
|
|
|
servidx,
|
|
|
|
c.role,
|
|
|
|
])
|
2021-02-06 03:53:55 +03:00
|
|
|
}
|
|
|
|
}
|
2021-02-09 13:54:37 +03:00
|
|
|
|
2021-03-15 10:53:44 +03:00
|
|
|
private bindingHash() {
|
|
|
|
let r = ""
|
2021-05-19 18:35:56 +03:00
|
|
|
const n = jacdac.bus.allClients.length
|
2021-05-19 02:36:07 +03:00
|
|
|
for (let i = 0; i < n; ++i) {
|
2021-06-19 03:20:03 +03:00
|
|
|
const client = jacdac.bus.allClients[i]
|
2023-10-27 21:50:26 +03:00
|
|
|
r += `${client.role || ""}:${
|
|
|
|
client.broadcast ||
|
2021-06-19 03:20:03 +03:00
|
|
|
(client.device && client.device.deviceId) ||
|
|
|
|
""
|
2023-10-27 21:50:26 +03:00
|
|
|
}:${client.serviceIndex}`
|
2021-03-15 10:53:44 +03:00
|
|
|
}
|
|
|
|
const buf = Buffer.fromUTF8(r)
|
2021-06-19 03:20:03 +03:00
|
|
|
return buf.hash(32)
|
2021-03-15 10:53:44 +03:00
|
|
|
}
|
|
|
|
|
2021-05-19 18:35:56 +03:00
|
|
|
bindRoles() {
|
2021-06-19 03:20:03 +03:00
|
|
|
if (!this.running) return
|
2022-06-02 03:52:12 +03:00
|
|
|
|
|
|
|
// 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) {
|
|
|
|
const query = cl.roleQuery
|
|
|
|
if (query && query.device === "self") {
|
|
|
|
// check if we have a server running on this device
|
|
|
|
// that matches
|
|
|
|
const serviceClass = cl.serviceClass
|
2023-10-27 21:50:26 +03:00
|
|
|
const services = jacdac.bus.servers.filter(
|
|
|
|
server => server.serviceClass == serviceClass
|
|
|
|
)
|
2022-06-02 03:52:12 +03:00
|
|
|
if (services.length) {
|
|
|
|
if (cl.device != jacdac.bus.selfDevice) {
|
|
|
|
// we have a server running on this device
|
|
|
|
// so we can unbind it
|
|
|
|
//console.log(`unbind role: reassign to self`)
|
|
|
|
setRole(cl.device.deviceId, cl.serviceIndex, "")
|
|
|
|
} else {
|
|
|
|
const serviceOffset = query.serviceOffset
|
2023-10-27 21:50:26 +03:00
|
|
|
if (
|
|
|
|
!isNaN(serviceOffset) &&
|
|
|
|
(!services[query.serviceOffset] ||
|
|
|
|
services[query.serviceOffset]
|
|
|
|
.serviceIndex != cl.serviceIndex)
|
|
|
|
) {
|
2022-06-02 03:52:12 +03:00
|
|
|
//console.log(`unbind role: reassign to service offset`)
|
2023-10-27 21:50:26 +03:00
|
|
|
setRole(
|
|
|
|
cl.device.deviceId,
|
|
|
|
cl.serviceIndex,
|
|
|
|
""
|
|
|
|
)
|
2022-06-02 03:52:12 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-23 01:35:22 +03:00
|
|
|
if (jacdac.bus.unattachedClients.length == 0) {
|
2022-06-02 03:52:12 +03:00
|
|
|
//console.log(`all clients bound`)
|
2021-06-19 03:20:03 +03:00
|
|
|
this.checkChanges()
|
2021-02-09 13:54:37 +03:00
|
|
|
return
|
2021-03-24 13:24:14 +03:00
|
|
|
}
|
2023-10-27 21:50:26 +03:00
|
|
|
this.log(
|
|
|
|
`autobind: devs:${jacdac.bus.devices.length}, clients:${jacdac.bus.allClients.length}, unbound:${jacdac.bus.unattachedClients.length}`
|
|
|
|
)
|
2021-02-09 13:54:37 +03:00
|
|
|
|
|
|
|
const bindings: RoleBinding[] = []
|
2021-05-19 18:35:56 +03:00
|
|
|
const wraps = bus.devices.map(d => new DeviceWrapper(d))
|
2021-02-09 13:54:37 +03:00
|
|
|
|
2021-05-19 18:35:56 +03:00
|
|
|
for (const cl of jacdac.bus.allClients) {
|
2021-02-10 05:59:06 +03:00
|
|
|
if (!cl.broadcast && cl.role) {
|
2023-10-27 21:50:26 +03:00
|
|
|
const b = new RoleBinding(
|
|
|
|
cl.role,
|
|
|
|
cl.serviceClass,
|
|
|
|
cl.roleQuery
|
|
|
|
)
|
2021-02-09 13:54:37 +03:00
|
|
|
if (cl.device) {
|
|
|
|
b.boundToDev = cl.device
|
|
|
|
b.boundToServiceIdx = cl.serviceIndex
|
|
|
|
for (const w of wraps)
|
|
|
|
if (w.device == cl.device) {
|
|
|
|
w.bindings[cl.serviceIndex] = b
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
bindings.push(b)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-08 11:10:21 +03:00
|
|
|
let servers: ServerBindings[] = []
|
2021-02-09 13:54:37 +03:00
|
|
|
|
|
|
|
// Group all clients by host
|
|
|
|
for (const b of bindings) {
|
|
|
|
const hn = b.host()
|
2021-04-08 11:10:21 +03:00
|
|
|
let h = servers.find(h => h.host == hn)
|
2021-02-09 13:54:37 +03:00
|
|
|
if (!h) {
|
2021-04-08 11:10:21 +03:00
|
|
|
h = new ServerBindings(hn)
|
|
|
|
servers.push(h)
|
2021-02-09 13:54:37 +03:00
|
|
|
}
|
|
|
|
h.bindings.push(b)
|
|
|
|
}
|
|
|
|
|
2023-10-27 21:50:26 +03:00
|
|
|
this.log(`found ${servers.length} servers`)
|
2021-02-09 13:54:37 +03:00
|
|
|
// exclude hosts that have already everything bound
|
2021-04-08 11:10:21 +03:00
|
|
|
servers = servers.filter(h => !h.fullyBound)
|
2021-02-09 13:54:37 +03:00
|
|
|
|
2023-10-27 21:50:26 +03:00
|
|
|
this.log(`binding ${servers.length} servers`)
|
2022-06-02 03:52:12 +03:00
|
|
|
|
2021-04-08 11:10:21 +03:00
|
|
|
while (servers.length > 0) {
|
2021-02-09 13:54:37 +03:00
|
|
|
// Get host with maximum number of clients (resolve ties by name)
|
|
|
|
// This gives priority to assignment of "more complicated" hosts, which are generally more difficult to assign
|
2021-06-19 03:20:03 +03:00
|
|
|
const h = maxIn(
|
|
|
|
servers,
|
2021-11-29 22:19:15 +03:00
|
|
|
(a, b) => a.bindings.length - b.bindings.length,
|
|
|
|
(a, b) => b.host.compare(a.host)
|
2021-06-19 03:20:03 +03:00
|
|
|
)
|
2022-06-02 03:52:12 +03:00
|
|
|
//console.log(`bind server: ${h.host}, ${h.bindings.length} bindings`)
|
2021-06-19 03:20:03 +03:00
|
|
|
|
2022-06-02 03:52:12 +03:00
|
|
|
for (const d of wraps) {
|
|
|
|
d.score = h.scoreFor(d)
|
|
|
|
//console.log(`bind score: ${d.device.deviceId} ${d.score}`)
|
|
|
|
}
|
2021-06-19 03:20:03 +03:00
|
|
|
|
|
|
|
const dev = maxIn(
|
|
|
|
wraps,
|
2021-11-29 22:19:15 +03:00
|
|
|
(a, b) => a.score - b.score,
|
|
|
|
(a, b) => b.device.deviceId.compare(a.device.deviceId)
|
2021-06-19 03:20:03 +03:00
|
|
|
)
|
2021-02-09 13:54:37 +03:00
|
|
|
if (dev.score == 0) {
|
|
|
|
// nothing can be assigned, on any device
|
2022-06-02 03:52:12 +03:00
|
|
|
//console.log(`bind server: ${h.host}, nothing can be assigned`)
|
2021-04-08 11:10:21 +03:00
|
|
|
servers.removeElement(h)
|
2021-02-09 13:54:37 +03:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// assign services in order of names - this way foo/servo1 will be assigned before foo/servo2
|
2022-06-02 03:52:12 +03:00
|
|
|
// in list of advertised services and svro
|
|
|
|
h.bindings.sort((a, b) => {
|
|
|
|
let c = a.serviceClass - b.serviceClass
|
|
|
|
if (c) return c
|
|
|
|
c = a.roleQuery.serviceOffset - b.roleQuery.serviceOffset
|
|
|
|
if (c) return c
|
|
|
|
return a.role.compare(b.role)
|
|
|
|
})
|
2021-02-09 13:54:37 +03:00
|
|
|
|
|
|
|
// "recompute" score, assigning names in process
|
2022-04-25 21:40:53 +03:00
|
|
|
const score = h.scoreFor(dev, true)
|
2022-06-02 03:52:12 +03:00
|
|
|
//console.log(`bind score: ${score} (${h.fullyBound ? "bound" : "needs bindings"})`)
|
2021-02-09 13:54:37 +03:00
|
|
|
|
|
|
|
// if everything bound on this host, remove it from further consideration
|
2021-06-19 03:20:03 +03:00
|
|
|
if (h.fullyBound) servers.removeElement(h)
|
2021-02-09 13:54:37 +03:00
|
|
|
else {
|
|
|
|
// otherwise, remove bindings on the current device, to update sort order
|
|
|
|
// it's unclear we need this
|
2021-06-19 03:20:03 +03:00
|
|
|
h.bindings = h.bindings.filter(
|
|
|
|
b => b.boundToDev != dev.device
|
|
|
|
)
|
2021-02-09 13:54:37 +03:00
|
|
|
}
|
|
|
|
}
|
2021-06-19 03:20:03 +03:00
|
|
|
this.checkChanges()
|
2021-03-24 13:24:14 +03:00
|
|
|
}
|
2021-02-09 13:54:37 +03:00
|
|
|
|
2021-03-24 13:24:14 +03:00
|
|
|
private checkChanges() {
|
2021-02-09 13:54:37 +03:00
|
|
|
// notify clients that something changed
|
2021-06-19 03:20:03 +03:00
|
|
|
const newHash = this.bindingHash()
|
2021-03-15 11:14:49 +03:00
|
|
|
if (this._oldBindingsHash !== newHash) {
|
2021-06-19 03:20:03 +03:00
|
|
|
this._oldBindingsHash = newHash
|
2021-03-24 14:07:23 +03:00
|
|
|
this.log(`roles: bindings changed`)
|
2021-02-09 13:54:37 +03:00
|
|
|
this.sendChangeEvent()
|
2021-03-15 11:14:49 +03:00
|
|
|
} else {
|
|
|
|
//console.log(`auto bind: no changes`)
|
|
|
|
}
|
2021-02-09 13:54:37 +03:00
|
|
|
}
|
2021-02-06 03:53:55 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace jacdac {
|
2022-04-08 18:39:19 +03:00
|
|
|
//% fixedInstance whenUsed weight=1 block="role manager"
|
2021-04-21 17:09:39 +03:00
|
|
|
export const roleManagerServer = new _rolemgr.RoleManagerServer()
|
2021-06-19 03:20:03 +03:00
|
|
|
}
|