wiegand-daemon
Version:
Local network daemon to connect Wiegand access controller and remote server. 微耕门禁控制板局域网守护进程,用于与远端服务器通讯
181 lines (157 loc) • 4.7 kB
text/typescript
import dgram, { Socket as UdpSocket } from "dgram";
import { Socket as TcpSocket, AddressInfo } from "net";
import WgCtl, { parseData } from "wiegand-control";
import getLocalIp from "./utils/getLocalIp";
import env from "dotenv";
env.config();
const localIp = getLocalIp();
console.log(`[DMN] Local ip address is ${localIp}.`);
const localPort = +(process.env.LOCAL_PORT || 6000);
const remotePort = +(process.env.REMOTE_PORT || 8000);
const remoteHost = process.env.REMOTE_HOST;
const storeId = process.env.STORE_ID;
const searchTimeout = +(process.env.SEARCH_TIMEOUT || 3000);
const searchInterval = +(process.env.SEARCH_INTERVAL || 300000);
if (!remoteHost || !storeId) {
throw new Error("invalid_config");
}
let controllerBySerial: { [serial: number]: WgCtl } = {};
let searchingControllerBySerial: { [serial: number]: WgCtl } = {};
const socket = dgram.createSocket("udp4"); // local network using udp
const client = new TcpSocket(); // remote network using tcp
socket.on("error", err => {
console.log(`[UDP] Error:\n${err.stack}.`);
socket.close();
});
socket.on("message", (msg, rinfo) => {
const message = parseData(msg);
console.log(
`[UDP] Got message from ${rinfo.address}:${rinfo.port}.`,
JSON.stringify(message)
);
if (message.funcName === "Search") {
searchingControllerBySerial[message.serial] = new WgCtl(
socket,
message.serial,
localIp,
localPort,
message.ip
);
} else {
client.write(msg, err => {
if (err) {
console.error(err.message);
return;
}
});
}
});
socket.on("listening", async () => {
const address = socket.address() as AddressInfo;
console.log(`[UDP] Listening ${address.address}:${address.port}.`);
console.log(`[TCP] Connecting ${remoteHost}:${remotePort}...`);
client.connect(remotePort, remoteHost);
client.setTimeout(1000);
});
socket.bind(localPort);
setInterval(async () => {
await searchAndReportLocalControllers(socket, client);
}, searchInterval);
client.on("connect", async () => {
const address = client.remoteAddress;
const port = client.remotePort;
console.log(`[TCP] Connected to ${address}:${port}.`);
client.setTimeout(360000);
searchAndReportLocalControllers(socket, client);
// TODO send local ip to remote server
});
client.on("timeout", () => {
console.log("[TCP] Connection timeout.");
client.destroy(new Error("timeout"));
});
client.on("close", () => {
console.log(`[TCP] Closed, reconnect in 10 seconds.`);
setTimeout(() => {
client.connect(remotePort, remoteHost);
}, 10000);
});
client.on("error", err => {
console.error(`[TCP] Error: ${err.message}.`);
});
client.on("data", async data => {
// console.log(`[TCP] got remote data\n`, data);
if (data.length !== 64) {
console.log("[TCP] Data:", data.toString());
return;
}
const parsedData = parseData(data);
const serial = parsedData.serial;
let controller: WgCtl;
if (!serial) {
controller = new WgCtl(socket);
} else if (controllerBySerial[serial]) {
controller = controllerBySerial[serial];
} else {
console.error(`[DMN] Controller ${serial} not found, trying re-search.`);
new WgCtl(socket).search();
return;
}
controller.sendData(
data.readUInt8(1),
Buffer.from(
data
.slice(8)
.toString("hex")
.replace(/(00)*$/, ""),
"hex"
)
);
});
async function searchAndReportLocalControllers(
socket: UdpSocket,
client: TcpSocket
) {
socket.setBroadcast(true);
searchingControllerBySerial = {};
new WgCtl(socket).search();
await new Promise(resolve => {
setTimeout(async () => {
resolve();
}, searchTimeout);
});
socket.setBroadcast(false);
const searchingSerials = Object.keys(searchingControllerBySerial);
const serials = Object.keys(controllerBySerial);
if (
!(
searchingSerials.length === serials.length &&
searchingSerials.every(serial => serials.includes(serial))
)
) {
console.log(
`[UDP] Search timeout, controller changed:`,
Object.keys(searchingControllerBySerial).join(",")
);
}
console.log(
`[TCP] Reporting searched controllers: ${Object.keys(
searchingControllerBySerial
).join(",")}`
);
if (client.writable) {
client.write(
`store ${JSON.stringify({
storeId,
serials: Object.keys(searchingControllerBySerial).map(s => +s)
})}\r\n`
);
}
controllerBySerial = searchingControllerBySerial;
if (searchingSerials.length < serials.length) {
console.warn(
`[DMN] Some controller is lost, controllers left: ${Object.keys(
controllerBySerial
).join(",")}`
);
}
}