I am trying to read value from an opcua server in my sveltekit app using node-opcua. I can successfully connect and read the values from my dev machine but when I deploy the app on IIS I get the error below
Error: EEXIST: file already exists, mkdir 'C:Windowssystem32configsystemprofileAppDataRoamingnode-opcua-default-nodejsConfigPKI'
at Object.mkdirSync (node:fs:1373:26)
at mkdir (C:inetpubAreaTrackingwwwrootbuildnode_modulesnode-opcua-pkidistlibtoolboxcommon2.js:48:22)
at new CertificateManager (C:inetpubAreaTrackingwwwrootbuildnode_modulesnode-opcua-pkidistlibpkicertificate_manager.js:202:29)
at new OPCUACertificateManager (C:inetpubAreaTrackingwwwrootbuildnode_modulesnode-opcua-certificate-managerdistcertificate_manager.js:42:9)
at getDefaultCertificateManager (C:inetpubAreaTrackingwwwrootbuildnode_modulesnode-opcua-certificate-managerdistcertificate_manager.js:127:12)
at new ClientBaseImpl (C:inetpubAreaTrackingwwwrootbuildnode_modulesnode-opcua-clientdistprivateclient_base_impl.js:296:114)
at new OPCUAClientImpl (C:inetpubAreaTrackingwwwrootbuildnode_modulesnode-opcua-clientdistprivateopcua_client_impl.js:287:9)
at OPCUAClient.create (C:inetpubAreaTrackingwwwrootbuildnode_modulesnode-opcua-clientdistopcua_client.js:10:16)
at file:///C:/inetpub/AreaTracking/wwwroot/build/server/chunks/_server-bf75dda4.js:58:28
at ModuleJob.run (node:internal/modules/esm/module_job:222:25) {
errno: -4075,
code: 'EEXIST',
syscall: 'mkdir',
path: 'C:\Windows\system32\config\systemprofile\AppData\Roaming\node-opcua-default-nodejs\Config\PKI'
}
Since it is IIS I think it does not have access to this folder. I have tried to save the pki files in the same directory as the +server.js file that is reading these values. But I am still getting the same error. I can see the PKI files are being generated on the server but it does not seem to be reading from the generated files. Here is the +server.js file that is reading these values and genereating the pki files.
import {
OPCUAClient,
AttributeIds,
UserTokenType,
MessageSecurityMode,
SecurityPolicy,
OPCUACertificateManager
} from 'node-opcua';
import * as path from 'path';
import * as fs from 'fs';
const opcServerEndpoint = "endpoint";
const username = "USR";
const password = "PWD";
// Get the directory path of the current module file
const currentDir = new URL('.', import.meta.url).pathname;
// Resolve the pki directory relative to the current module file
const pkiDir = path.resolve(currentDir.slice(1), 'pki');
// Log the PKI directory path for debugging purposes
console.log('PKI directory:', pkiDir);
// Ensure the PKI directory exists or create it if it doesn't
try {
fs.mkdirSync(pkiDir, { recursive: true });
console.log('PKI directory created successfully');
} catch (err) {
console.error('Error creating PKI directory:', err);
}
// Check if certificate files exist, and generate them if not
if (!fs.existsSync(path.join(pkiDir, 'own', 'cert.pem'))) {
console.log('Generating certificate files...');
const certificateManager = new OPCUACertificateManager({
rootFolder: pkiDir, // Specify the root folder for certificate management
keySize: 2048, // Set the key size for certificate generation
// Specify the location for storing generated certificates
// @ts-ignore
location: path.join(pkiDir, 'own'),
});
// Initialize the certificate manager
certificateManager.initialize()
.then(() => console.log('Certificate files generated successfully'))
.catch(err => console.error('Error generating certificate files:', err));
}
// Initialize the OPC UA client options
const clientOptions = {
applicationName: "RejectTrackingClient", // Specify the name of the application
endpointMustExist: true, // Ensure that the specified endpoint exists
securityMode: MessageSecurityMode.SignAndEncrypt, // Set the security mode
securityPolicy: SecurityPolicy.Basic256Sha256, // Set the security policy
connectionStrategy: {
maxRetry: 5, // Maximum number of connection retries
initialDelay: 1000, // Initial delay before the first connection retry (in milliseconds)
maxDelay: 10000 // Maximum delay between connection retries (in milliseconds)
},
// Specify the certificate manager to use for secure communication
certificateManager: new OPCUACertificateManager({
automaticallyAcceptUnknownCertificate: true, // Automatically accept unknown certificates
rootFolder: pkiDir, // Set the root folder for certificate management
keySize: 2048, // Set the key size for certificate generation
// Specify the location for storing generated certificates
// @ts-ignore
location: path.join(pkiDir, 'own'),
}),
};
const client = OPCUAClient.create(clientOptions);
/**
* @type {import("node-opcua").ClientSession | null}
*/
let session = null;
let isConnected = false;
/**
* @type {string | number | NodeJS.Timeout | null | undefined}
*/
let inactivityTimer = null;
async function initializeOpcClient() {
try {
if (!client) {
// Create the OPC UA client with proper security settings
// @ts-ignore
client = OPCUAClient.create({
endpointMustExist: true,
securityMode: MessageSecurityMode.SignAndEncrypt,
securityPolicy: SecurityPolicy.Basic256Sha256,
connectionStrategy: {
maxRetry: 5,
initialDelay: 1000,
maxDelay: 10000
},
// @ts-ignore
certificateManager: new OPCUACertificateManager({
keySize: 2048,
// @ts-ignore
location: pkiDir
})
});
// Enable detailed logging
client.on("backoff", (retry, delay) => {
console.log(`Retrying to connect to ${opcServerEndpoint}, attempt ${retry}, next attempt in ${delay / 1000} seconds`);
});
}
console.log('Connecting to OPC UA server...');
await client.connect(opcServerEndpoint);
console.log('Connected to OPC UA server');
isConnected = true;
// Create a session with username and password
console.log('Creating session...');
session = await client.createSession({
userName: username,
password: password,
type: UserTokenType.UserName
});
console.log('Session created');
} catch (error) {
console.error('Error initializing OPC UA client:', error);
// @ts-ignore
throw new Error(`Failed to initialize OPC UA client: ${error.message}`);
}
}
async function closeOpcClient() {
try {
if (session) {
await session.close();
session = null;
console.log('Session closed');
}
if (client) {
await client.disconnect();
// @ts-ignore
client = null;
console.log('Client disconnected');
}
isConnected = false;
} catch (error) {
console.error('Error closing OPC UA client:', error);
}
}
function resetInactivityTimer() {
if (inactivityTimer) {
clearTimeout(inactivityTimer);
}
inactivityTimer = setTimeout(async () => {
console.log('Inactivity timeout reached, closing connection...');
await closeOpcClient();
}, 25000); // 25 seconds
}
async function readOpcData() {
try {
// Ensure the client and session are initialized and connected
if (!isConnected || !session) {
await initializeOpcClient();
}
// Define an array of nodeIds to read
const nodeIds = [
"ns=2;s=POS_1_ID",
"ns=2;s=POS_1_LEN",
"ns=2;s=POS_1_TCK",
"ns=2;s=POS_1_WIDTH"
];
// Perform read operations for each nodeId
const tagValues = [];
for (const nodeId of nodeIds) {
const nodeToRead = {
nodeId: nodeId,
attributeId: AttributeIds.Value,
};
// @ts-ignore
const dataValue = await session.read(nodeToRead);
// Convert Int8Array to string if the value is an array of numbers
if (Array.isArray(dataValue.value.value) && dataValue.value.value.every(val => typeof val === 'number')) {
const stringValue = dataValue.value.value.map(val => String.fromCharCode(val)).join('');
tagValues.push(stringValue);
} else {
tagValues.push(dataValue.value.value);
}
}
return tagValues;
} catch (error) {
console.error('Error in readOpcData:', error);
// @ts-ignore
throw new Error(`Failed to read OPC data: ${error.message}`);
} finally {
// Reset the inactivity timer after a successful read
resetInactivityTimer();
}
}
export async function GET() {
try {
const data = await readOpcData();
console.log(data);
return new Response(JSON.stringify({ data }), { status: 200 });
} catch (error) {
console.error('Error in GET handler:', error);
if (error instanceof Error) {
return new Response(JSON.stringify({ error: error.message }), { status: 500 });
} else {
return new Response(JSON.stringify({ error: "An unknown error occurred" }), { status: 500 });
}
}
}
// Graceful shutdown
process.on('SIGINT', async () => {
console.log('Received SIGINT, shutting down gracefully...');
try {
await closeOpcClient();
} catch (error) {
console.error('Error during shutdown:', error);
} finally {
process.exit(0);
}
});
process.on('SIGTERM', async () => {
console.log('Received SIGTERM, shutting down gracefully...');
try {
await closeOpcClient();
} catch (error) {
console.error('Error during shutdown:', error);
} finally {
process.exit(0);
}
});