Skip to content

Commit

Permalink
fix(nrplus): clear recognized lines
Browse files Browse the repository at this point in the history
  • Loading branch information
coderbyheart committed Oct 26, 2023
1 parent 63d7626 commit eec5bc1
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 137 deletions.
3 changes: 2 additions & 1 deletion lambda/parseSinkMessages.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import type { KinesisStreamEvent } from 'aws-lambda'
import { parser } from '../nrplus/messageStreamParser.js'
import { PCCLines, PDCLines } from '../nrplus/messages.js'

const parserInstance = parser()
const parserInstance = parser([PCCLines, PDCLines])
parserInstance.onMessage((deviceId, message) => {
console.log(JSON.stringify({ deviceId, message }))
})
Expand Down
27 changes: 9 additions & 18 deletions nrplus/messageStreamParser.spec.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
import { describe, it, mock } from 'node:test'
import { describe, it } from 'node:test'
import assert from 'node:assert/strict'
import { readFile } from 'node:fs/promises'
import { parser } from './messageStreamParser.js'
import { PCCLines, PDCLines } from './messages.js'

void describe('messageStreamParser', () => {
void it('should parse sink messages', async () => {
const lines = (await readFile('./nrplus/nrplus-logs.txt', 'utf-8')).split(
'\n',
)

const onMessageCallback = mock.fn(() => undefined)
const p = parser()
p.onMessage(onMessageCallback)
const messages: Record<string, Record<string, unknown>[]> = {}
const p = parser([PDCLines, PCCLines])
p.onMessage(
(deviceId, message) =>
(messages[deviceId] = [...(messages[deviceId] ?? []), message]),
)
for (const line of lines) {
p.addLine('nrplus-gw-jagu', line)
}

assert.deepEqual(onMessageCallback.mock.calls[0]?.arguments, [
'nrplus-gw-jagu',
assert.deepEqual(messages['nrplus-gw-jagu'], [
{
time: '358881336898',
snr: '88',
Expand All @@ -34,10 +37,6 @@ void describe('messageStreamParser', () => {
sduData: '{"data":"Yes, hello","modem_temp":"33"}',
ieType: 'none',
},
])

assert.deepEqual(onMessageCallback.mock.calls[1]?.arguments, [
'nrplus-gw-jagu',
{
time: '359572555405',
status: 'valid - PDC can be received',
Expand All @@ -49,10 +48,6 @@ void describe('messageStreamParser', () => {
mcs: '0',
txPowerDBm: '-12',
},
])

assert.deepEqual(onMessageCallback.mock.calls[2]?.arguments, [
'nrplus-gw-jagu',
{
time: '364412319378',
snr: '96',
Expand All @@ -69,10 +64,6 @@ void describe('messageStreamParser', () => {
sduData: '{"data":"Yes, hello","modem_temp":"33"}',
ieType: 'none',
},
])

assert.deepEqual(onMessageCallback.mock.calls[3]?.arguments, [
'nrplus-gw-jagu',
{
time: '365111832209',
status: 'valid - PDC can be received',
Expand Down
164 changes: 46 additions & 118 deletions nrplus/messageStreamParser.ts
Original file line number Diff line number Diff line change
@@ -1,132 +1,60 @@
/*
PCC: Physical Control Channel
PDC: Physical Data Channel
MCS: Modulation and Coding Scheme
SDU: Service Data Unit
type MessageListener = (
deviceId: string,
message: Record<string, unknown>,
) => void

PCC comes before each PDC.
PDC contains the actual MAC PDU/SDU.
The content of the PDUs in here are just fMAC specific.
*/
const parseMessages = (
lines: string[],
messageDef: Readonly<Array<RegExp>>,
): Record<string, unknown> | null => {
const result: RegExpExecArray['groups'][] = []

type PDCInfo = {
time: string // e.g. '358881336898'
snr: string // e.g. '88'
RSSI: string // e.g. '-60'
len: string // e.g. '83'
type: string // e.g. 'Data SDU'
expectedRXRSSI: string // e.g. '-60'
seqNbr: string // e.g. '366'
networkId: string // e.g. '22'
transmitterId: string // e.g. '40'
receiverId: string // e.g. '38'
sduLastSeenSeqNbr: string // e.g. '1'
sduDataLength: string // e.g. '40'
sduData: string // e.g. '{"data":"Yes, hello","modem_temp":"33"}'
ieType: string // e.g. 'none'
}

const PDCLines = [
/^PDC received \(time (?<time>[0-9]+)\): snr (?<snr>[0-9]+), RSSI (?<RSSI>[-0-9]+), len (?<len>[0-9]+)/,
/^Received data:/,
/^\s+Type:\s+(?<type>.+)/,
/^\s+Power control:/,
/^\s+Expected RX RSSI level \(dBm\):\s+(?<expectedRXRSSI>[-0-9]+)/,
/^\s+Seq nbr:\s+(?<seqNbr>[0-9]+)/,
/^\s+Network ID:\s+(?<networkId>[0-9]+)/,
/^\s+Transmitter long ID:\s+(?<transmitterId>[0-9]+)/,
/^\s+Receiver long ID:\s+(?<receiverId>[0-9]+)/,
/^\s+SDU last seen seq nbr:\s+(?<sduLastSeenSeqNbr>[0-9]+)/,
/^\s+SDU data length:\s+(?<sduDataLength>[0-9]+)/,
/^\s+SDU data:\s+(?<sduData>.+)/,
/^\s+IE type:\s+(?<ieType>.+)/,
] as const

type PCCInfo = {
time: string // e.g. '365111832209'
status: string // e.g. 'valid - PDC can be received'
snr: string // e.g. '61'
stf_start_time: string // e.g. '365111814178'
networkId: string // e.g. '22'
transmitterId: string // e.g. '39'
receiverId: string // e.g. '38'
MCS: string // e.g. '0'
txPowerDBm: string // e.g. '-12'
}

/*
PCC received (time 359572555405): status: \"valid - PDC can be received\", snr 83, stf_start_time 359572537298
phy header: short nw id 22, transmitter id 39
receiver id: 38
MCS 0, TX pwr: -12 dBm
*/

const PCCLines = [
/^PCC received \(time (?<time>[0-9]+)\): status: "(?<status>[^"]+)", snr (?<snr>[0-9]+), stf_start_time (?<stfStartTime>[0-9]+)/,
/^\s+phy header: short nw id (?<networkId>[0-9]+), transmitter id (?<transmitterId>[0-9]+)/,
/^\s+receiver id: (?<receiverId>[0-9]+)/,
/^\s+MCS (?<mcs>[0-9]+), TX pwr: (?<txPowerDBm>-[0-9]+) dBm/,
] as const

type MessageListener = (deviceId: string, message: PDCInfo | PCCInfo) => void

const StreamParser = () => {
let index = 0
let lastResult = 0
const lines: string[] = []

const readLines = (
regExps: Readonly<RegExp[]>,
): Record<string, unknown> | null => {
const result: RegExpExecArray['groups'][] = []
const startIndex = index
for (const regExp of regExps) {
const match = regExp.exec(lines[index] ?? '')
if (match === null) {
index = startIndex
return null
}
for (const line of lines) {
const lineDef = messageDef[result.length]
if (lineDef === undefined) {
// No more lines to match
break
}
const match = lineDef.exec(line)
if (match !== null) {
result.push(match.groups)
index++
}
index--
return result.reduce(
(obj, groups) => ({ ...obj, ...groups }),
{} as Record<string, unknown>,
)
}

const parseLines = () => {
let result: unknown | null = null
while (index++ < lines.length - 1) {
const pdcData = readLines(PDCLines)
if (pdcData !== null) {
lastResult = index
result = pdcData
continue
}
const pccData = readLines(PCCLines)
if (pccData !== null) {
lastResult = index
result = pccData
continue
}
}
index = lastResult
return result
}
// Nothing found
if (result.length === 0) return null

// Not all lines matched
if (result.length !== messageDef.length) return null

// All lines matched
return result.reduce(
(obj, groups) => ({ ...obj, ...groups }),
{} as Record<string, unknown>,
)
}

const StreamParser = (messageDefinitions: Array<Readonly<Array<RegExp>>>) => {
let lines: string[] = []

return {
add: (data: string) => {
lines.push(data)
return parseLines()
for (const def of messageDefinitions) {
const res = parseMessages([...lines], def)
if (res !== null) {
lines = []
return res
}
}
return null
},
}
}

export const parser = (): {
export const parser = (
messageDefinitions: Array<Readonly<Array<RegExp>>>,
): {
addLine: (device: string, line: string) => void
onMessage: (fn: MessageListener) => void
} => {
Expand All @@ -135,11 +63,11 @@ export const parser = (): {
return {
addLine: (device, line) => {
if (parser[device] === undefined) {
parser[device] = StreamParser()
parser[device] = StreamParser(messageDefinitions)
}
const maybeResult = parser[device]?.add(line)
const maybeResult = parser[device]?.add(line) ?? null
if (maybeResult !== null) {
listeners.map((fn) => fn(device, maybeResult as PDCInfo))
listeners.map((fn) => fn(device, maybeResult))
}
},
onMessage: (fn) => {
Expand Down
62 changes: 62 additions & 0 deletions nrplus/messages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
PCC: Physical Control Channel
PDC: Physical Data Channel
MCS: Modulation and Coding Scheme
SDU: Service Data Unit
PCC comes before each PDC.
PDC contains the actual MAC PDU/SDU.
The content of the PDUs in here are just fMAC specific.
*/

export type PDCInfo = {
time: string // e.g. '358881336898'
snr: string // e.g. '88'
RSSI: string // e.g. '-60'
len: string // e.g. '83'
type: string // e.g. 'Data SDU'
expectedRXRSSI: string // e.g. '-60'
seqNbr: string // e.g. '366'
networkId: string // e.g. '22'
transmitterId: string // e.g. '40'
receiverId: string // e.g. '38'
sduLastSeenSeqNbr: string // e.g. '1'
sduDataLength: string // e.g. '40'
sduData: string // e.g. '{"data":"Yes, hello","modem_temp":"33"}'
ieType: string // e.g. 'none'
}

export const PDCLines: Readonly<Array<RegExp>> = [
/^PDC received \(time (?<time>[0-9]+)\): snr (?<snr>[0-9]+), RSSI (?<RSSI>[-0-9]+), len (?<len>[0-9]+)/,
/^Received data:/,
/^\s+Type:\s+(?<type>.+)/,
/^\s+Power control:/,
/^\s+Expected RX RSSI level \(dBm\):\s+(?<expectedRXRSSI>[-0-9]+)/,
/^\s+Seq nbr:\s+(?<seqNbr>[0-9]+)/,
/^\s+Network ID:\s+(?<networkId>[0-9]+)/,
/^\s+Transmitter long ID:\s+(?<transmitterId>[0-9]+)/,
/^\s+Receiver long ID:\s+(?<receiverId>[0-9]+)/,
/^\s+SDU last seen seq nbr:\s+(?<sduLastSeenSeqNbr>[0-9]+)/,
/^\s+SDU data length:\s+(?<sduDataLength>[0-9]+)/,
/^\s+SDU data:\s+(?<sduData>.+)/,
/^\s+IE type:\s+(?<ieType>.+)/,
] as const

export type PCCInfo = {
time: string // e.g. '365111832209'
status: string // e.g. 'valid - PDC can be received'
snr: string // e.g. '61'
stf_start_time: string // e.g. '365111814178'
networkId: string // e.g. '22'
transmitterId: string // e.g. '39'
receiverId: string // e.g. '38'
MCS: string // e.g. '0'
txPowerDBm: string // e.g. '-12'
}

export const PCCLines: Readonly<Array<RegExp>> = [
/^PCC received \(time (?<time>[0-9]+)\): status: "(?<status>[^"]+)", snr (?<snr>[0-9]+), stf_start_time (?<stfStartTime>[0-9]+)/,
/^\s+phy header: short nw id (?<networkId>[0-9]+), transmitter id (?<transmitterId>[0-9]+)/,
/^\s+receiver id: (?<receiverId>[0-9]+)/,
/^\s+MCS (?<mcs>[0-9]+), TX pwr: (?<txPowerDBm>-[0-9]+) dBm/,
] as const

0 comments on commit eec5bc1

Please sign in to comment.