signal feature prototype

This commit is contained in:
david 2025-04-01 19:09:11 -07:00
parent e8a007a4a9
commit 15f73327ee
6 changed files with 82 additions and 26 deletions

View File

@ -8,7 +8,7 @@ export default class HttpServer {
private httpServer: http.Server; private httpServer: http.Server;
private port: number; private port: number;
private root: string; private root: string;
public constructor(port: number, root: string, tune: (ch: string, adp?: number) => void, getChannels: ()=>string[]) { public constructor(port: number, root: string, tune: (ch: string, adp?: number) => void, getChannels: ()=>string[], getSignal: (adapter:number)=>object) {
this.port = port; this.port = port;
this.root = root; this.root = root;
this.httpServer = http.createServer((req, res) => { this.httpServer = http.createServer((req, res) => {
@ -27,6 +27,11 @@ export default class HttpServer {
body = JSON.stringify(getChannels()); body = JSON.stringify(getChannels());
status = 200; status = 200;
break; break;
case "signal":
const adapter = parseInt(url.searchParams.get('adapter'));
body = JSON.stringify(getSignal(adapter));
status = 200;
break;
} }
break; break;

View File

@ -23,7 +23,10 @@ const tune = (reqChannel: string, reqAdapter?: number) => {
const getChannels = () => const getChannels = () =>
zap.getChannels(); zap.getChannels();
const httpServer = new HttpServer(HTTP_PORT, STATIC_ROOT, tune, getChannels); const getSignal = (adapter: number) =>
zap.getSignal(adapter)
const httpServer = new HttpServer(HTTP_PORT, STATIC_ROOT, tune, getChannels, getSignal);
const tvWebSocket0 = new TVWebSocket(WS_PORT, TV_DEV_0); const tvWebSocket0 = new TVWebSocket(WS_PORT, TV_DEV_0);
const tvWebSocket1 = new TVWebSocket(WS_PORT + 1, TV_DEV_1); const tvWebSocket1 = new TVWebSocket(WS_PORT + 1, TV_DEV_1);
httpServer.start(); httpServer.start();

View File

@ -26,6 +26,8 @@ body {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
text-align: center; text-align: center;
align-items: center;
justify-content: center;
h1 {} h1 {}
p {} p {}
@ -35,7 +37,7 @@ body {
} }
.content{ .content{
display: flex; display: flex;
flex-direction: column; flex-direction: row;
gap: 1em; gap: 1em;
.player{ .player{

View File

@ -18,12 +18,17 @@
<video id="video0" autoplay playsinline controls></video> <video id="video0" autoplay playsinline controls></video>
<div id="channel-container-0" class="channel-group"></div> <div id="channel-container-0" class="channel-group"></div>
<button onClick="tune(0)">Tune</button> <button onClick="tune(0)">Tune</button>
<p id="signal-0">N/A</p>
<button onClick="getSignal(0)">Get Signal</button>
</div> </div>
<div class="player"> <div class="player">
<video id="video1" autoplay playsinline controls></video> <video id="video1" autoplay playsinline controls></video>
<div id="channel-container-1" class="channel-group"></div> <div id="channel-container-1" class="channel-group"></div>
<button onClick="tune(1)">Tune</button> <button onClick="tune(1)">Tune</button>
<p id="signal-1">N/A</p>
<button onClick="getSignal(1)">Get Signal</button>
</div> </div>
</main> </main>
</div> </div>

View File

@ -1,53 +1,61 @@
const PLAYERS = 2; const PLAYERS = 2;
const populateChannels = (players:number) => { const populateChannels = (players: number) => {
fetch('/api/list').then(async (res) => { fetch('/api/list').then(async (res) => {
const channelNames: string[] = await res.json() const channelNames: string[] = await res.json()
for(let i = 0; i < players; ++i){ for (let i = 0; i < players; ++i) {
const radioGroup = document.getElementById(`channel-container-${i}`); const radioGroup = document.getElementById(`channel-container-${i}`);
if (!radioGroup) { if (!radioGroup) {
throw new Error("Radio group not found") throw new Error("Radio group not found")
} }
radioGroup.innerHTML = '' radioGroup.innerHTML = ''
channelNames.forEach((channelName,_) => { channelNames.forEach((channelName, _) => {
const id = `radio-${channelName}`; const id = `radio-${i}-${channelName}`;
const input = document.createElement('input'); const input = document.createElement('input');
input.type = "radio" input.type = "radio"
input.name = `channel-radio-${i}`; input.name = `channel-radio-${i}`;
input.value = channelName; input.value = `${channelName}`;
input.id = id input.id = id
const lbl = document.createElement("label"); const lbl = document.createElement("label");
lbl.htmlFor = id; lbl.htmlFor = id;
lbl.textContent = channelName; lbl.textContent = channelName;
// Wrap in a div or line
const wrapper = document.createElement("div"); const wrapper = document.createElement("div");
wrapper.appendChild(input); wrapper.appendChild(input);
wrapper.appendChild(lbl); wrapper.appendChild(lbl);
radioGroup.appendChild(wrapper) radioGroup.appendChild(wrapper)
}) })
} }
}).catch(err => { }).catch(err => {
console.log("nope ",err) console.log("nope ", err)
}) })
} }
const tune = (adapter=0) =>{ const tune = (adapter = 0) => {
const choice = document.querySelector<HTMLInputElement>(`input[name="channel-radio-${adapter}"]:checked`) const choice = document.querySelector<HTMLInputElement>(`input[name="channel-radio-${adapter}"]:checked`)
const channel = choice?.value; const channel = choice?.value;
if(channel){ if (channel) {
fetch(`/api/tune/${channel}?adapter=${adapter}`, {method:'PUT'}) fetch(`/api/tune/${channel}?adapter=${adapter}`, { method: 'PUT' })
} }
}
const getSignal = (adapter = 0) => {
const signalElement = document.getElementById(`signal-${adapter}`);
if (!signalElement) {
return;
}
fetch(`/api/signal?adapter=${adapter}`).then(res =>
res.json()
).then((strength: any) => {
signalElement.innerHTML = `Signal: ${strength.signal} C/N: ${strength.cn}`
})
} }
populateChannels(PLAYERS); populateChannels(PLAYERS);

View File

@ -5,6 +5,10 @@ export interface IZap {
process: ChildProcessWithoutNullStreams | null, process: ChildProcessWithoutNullStreams | null,
channel: string, channel: string,
adapter: 0 | 1 adapter: 0 | 1
strength: {
signal: string;
cn: string;
},
} }
export default class Zap { export default class Zap {
@ -12,16 +16,28 @@ export default class Zap {
private zap1: IZap; private zap1: IZap;
private channelNameList: string[]; private channelNameList: string[];
private fileName: string; private fileName: string;
private regex = /Signal=\s*(-?\d+(\.\d+)?dBm)\s+C\/N=\s*(\d+(\.\d+)?dB)/;
public constructor(fileName = "dvb_channel.conf", channel = "ION") { public constructor(fileName = "dvb_channel.conf", channel = "ION") {
const zap0: IZap = { const zap0: IZap = {
process: null, process: null,
channel, channel,
adapter: 0 adapter: 0,
strength: {
signal: "None",
cn: "None"
}
} }
const zap1: IZap = { const zap1: IZap = {
process: null, process: null,
channel, channel,
adapter: 1 adapter: 1,
strength: {
signal: "None",
cn: "None"
}
} }
this.zap0 = zap0; this.zap0 = zap0;
this.zap1 = zap1; this.zap1 = zap1;
@ -37,6 +53,18 @@ export default class Zap {
return this.channelNameList; return this.channelNameList;
} }
public getSignal(adapter: number) {
if(adapter == 0){
return this.zap0.strength;
}
else if (adapter == 1){
return this.zap1.strength;
}
else {
return {signal: 'N/A', cn: 'N/A'}
}
}
private nextChannel(channel: string) :string { private nextChannel(channel: string) :string {
const size = this.channelNameList.length; const size = this.channelNameList.length;
const currentIndex = this.channelNameList.indexOf(channel); const currentIndex = this.channelNameList.indexOf(channel);
@ -108,7 +136,12 @@ export default class Zap {
if (/Lock/.test(output)) { if (/Lock/.test(output)) {
clearTimeout(lockTimer); clearTimeout(lockTimer);
const match = output.match(this.regex);
zap.channel = verifiedChannel; zap.channel = verifiedChannel;
zap.strength = {
signal: match[1],
cn: match[3]
}
resolve(zap); resolve(zap);
} }