UniSet @VERSION@
JSEngine.h
1/*
2 * Copyright (c) 2025 Pavel Vainerman.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as
6 * published by the Free Software Foundation, version 2.1.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Lesser Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16// --------------------------------------------------------------------------
17#ifndef JSEngine_H_
18// --------------------------------------------------------------------------
19#include <unordered_map>
20extern "C" {
21#include "quickjs/quickjs.h"
22}
23#include "UInterface.h"
24#include "JHttpServer.h"
25#include "JSModbusClient.h"
26#ifdef JS_OPCUA_ENABLED
27#include "JSOPCUAClient.h"
28#endif
29// --------------------------------------------------------------------------
30namespace uniset
31{
32 namespace jshelper
33 {
34 struct JSPrivateData;
35 }
36 // ----------------------------------------------------------------------
37 struct JSOptions
38 {
39 size_t jsLoopCount = { 5 };
40 size_t httpLoopCount = { 5 };
41 size_t httpMaxQueueSize = { 100 };
42 size_t httpMaxThreads = { 3 };
43 size_t httpMaxRequestQueue = { 50 };
44 std::chrono::milliseconds httpResponseTimeout = { std::chrono::milliseconds(5000) };
45 std::chrono::milliseconds httpQueueWaitTimeout = {std::chrono::milliseconds(5000) };
46 bool esmModuleMode = { false };
47 };
48 // ----------------------------------------------------------------------
50 {
51 public:
52 explicit JSEngine( const std::string& jsfile,
53 std::vector<std::string>& searchPaths,
54 std::shared_ptr<UInterface>& ui,
55 JSOptions& opts );
56 virtual ~JSEngine();
57
58 inline std::shared_ptr<DebugStream> log() noexcept
59 {
60 return mylog;
61 }
62
63 inline std::shared_ptr<DebugStream> js_log() noexcept
64 {
65 return jslog;
66 }
67
68 inline std::shared_ptr<DebugStream> http_log() noexcept
69 {
70 return httpserv->log();
71 }
72
73 void init();
74 bool isActive();
75
76 void start();
77 void stop();
78 void askSensors( UniversalIO::UIOCommand cmd );
79 void sensorInfo( const uniset::SensorMessage* sm );
80 void updateOutputs();
81 void step();
82
83 protected:
84 void initJS();
85 void freeJS();
86 void initGlobal( JSContext* ctx );
87 void exportAllFunctionsFromTimerModule();
88 void createUInterfaceObject();
89 void createUnisetObject();
90 void createResponsePrototype( JSContext* ctx );
91 void createRequestAtoms(JSContext* ctx);
92 void createRequestPrototype(JSContext* ctx);
93 void jsLoop();
94
95 private:
96 JSValue jsReqProto_ = { JS_UNDEFINED };
97 JSValue jsResProto_ = { JS_UNDEFINED };
98
99 bool reqAtomsInited_ = false;
100 struct JSReqAtom
101 {
102 JSAtom method, uri, version, url, path, query, headers, body;
103 } reqAtoms_{};
104
105 std::atomic_bool activated = { false };
106 std::atomic_bool stopped = { false };
107 std::shared_ptr<DebugStream> mylog;
108 std::string jsfile;
109 std::vector<std::string> searchPaths;
110 std::shared_ptr<UInterface> ui;
111 JSRuntime* rt = { nullptr };
112 JSContext* ctx = { nullptr };
113 uint8_t* jsbuf = { nullptr };
114 jshelper::JSPrivateData* moduleLoaderData = { nullptr };
115 std::shared_ptr<DebugStream> jslog = { nullptr };
116 std::shared_ptr<uniset::JHttpServer> httpserv = { nullptr };
117 JSOptions opts;
118 std::shared_ptr<uniset::ObjectIndex> oind;
119
120 struct jsSensor
121 {
123 std::string name;
124 bool set( JSContext* ctx, JSValue& global, int64_t v );
125 };
126
127 std::unordered_map<uniset::ObjectId, jsSensor> inputs;
128 std::unordered_map<uniset::ObjectId, jsSensor> outputs;
129 std::list<JSValue> stepFunctions;
130 std::list<JSValue> stopFunctions;
131
132 JSValue jsFnStep = { JS_UNDEFINED };
133 JSValue jsFnStart = { JS_UNDEFINED };
134 JSValue jsFnStop = { JS_UNDEFINED };
135 JSValue jsFnTimers = { JS_UNDEFINED };
136 JSValue jsFnOnSensor = { JS_UNDEFINED };
137 JSValue jsGlobal = { JS_UNDEFINED };
138 JSValue jsModule = { JS_UNDEFINED };
139 JSValue jsFnHttpRequest = { JS_UNDEFINED };
140 JHttpServer::HandlerFn httpHandleFn;
141 std::shared_ptr<JSModbusClient> modbusClient;
142
143 JSValue js_ui_getValue(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
144 JSValue js_ui_askSensor(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
145 JSValue js_ui_setValue(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
146 JSValue js_uniset_StepCb(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
147 JSValue js_uniset_StopCb(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
148 JSValue js_uniset_httpStart(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
149 JSValue js_log(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
150 JSValue js_log_level(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
151 JSValue js_modbus_connectTCP(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
152 JSValue js_modbus_disconnect(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
153 JSValue js_modbus_read01(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
154 JSValue js_modbus_read02(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
155 JSValue js_modbus_read03(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
156 JSValue js_modbus_read04(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
157 JSValue js_modbus_write05(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
158 JSValue js_modbus_write06(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
159 JSValue js_modbus_write0F(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
160 JSValue js_modbus_write10(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
161 JSValue js_modbus_diag08(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
162 JSValue js_modbus_read4314(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
163#ifdef JS_OPCUA_ENABLED
164 std::shared_ptr<JSOPCUAClient> opcuaClient;
165 JSValue js_opcua_connect(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
166 JSValue js_opcua_disconnect(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
167 JSValue js_opcua_read(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
168 JSValue js_opcua_write(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
169#endif
170 // Статические обертки для вызова нестатических методов
171 static JSValue jsUiGetValue_wrapper(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
172 static JSValue jsUiAskSensor_wrapper(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
173 static JSValue jsUiSetValue_wrapper(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
174 static JSValue jsLog_wrapper(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
175 static JSValue jsLogLevel_wrapper(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
176 static JSValue jsUniSetStepCb_wrapper(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
177 static JSValue jsUniSetStopCb_wrapper(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
178 static JSValue jsUniSetHttpStart_wrapper(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
179 static JSValue jsModbusConnectTCP_wrapper(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
180 static JSValue jsModbusDisconnect_wrapper(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
181 static JSValue jsModbusRead01_wrapper(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
182 static JSValue jsModbusRead02_wrapper(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
183 static JSValue jsModbusRead03_wrapper(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
184 static JSValue jsModbusRead04_wrapper(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
185 static JSValue jsModbusWrite05_wrapper(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
186 static JSValue jsModbusWrite06_wrapper(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
187 static JSValue jsModbusWrite0F_wrapper(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
188 static JSValue jsModbusWrite10_wrapper(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
189 static JSValue jsModbusDiag08_wrapper(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
190 static JSValue jsModbusRead4314_wrapper(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
191#ifdef JS_OPCUA_ENABLED
192 static JSValue jsOpcuaConnect_wrapper(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
193 static JSValue jsOpcuaDisconnect_wrapper(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
194 static JSValue jsOpcuaRead_wrapper(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
195 static JSValue jsOpcuaWrite_wrapper(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv);
196#endif
197 // http convert
198 static JSValue jsMakeRequest(JSContext* ctx, JSValueConst& jsReqProto_, JSReqAtom& atom, const JHttpServer::RequestSnapshot& r);
199 static JSValue jsMakeResponse(JSContext* ctx, JSValueConst& jsResProto_, JHttpServer::ResponseAdapter* ad);
200 static void jsApplyResponseObject(JSContext* ctx, JSValue ret, JHttpServer::ResponseSnapshot& out);
201 static void jsApplyResponseAdapter( const JHttpServer::ResponseAdapter& ad, JHttpServer::ResponseSnapshot& out );
202 JSValue jsMakeBitsReply( const ModbusRTU::BitsBuffer& buf );
203 JSValue jsMakeRegisterReply( const ModbusRTU::ReadOutputRetMessage& msg );
204 JSValue jsMakeRegisterReply( const ModbusRTU::ReadInputRetMessage& msg );
205 JSValue jsMakeDiagReply( const ModbusRTU::DiagnosticRetMessage& msg );
206 JSValue jsMakeWriteAck( ModbusRTU::ModbusData start, ModbusRTU::ModbusData count );
207 JSValue jsMakeWriteSingleAck( const ModbusRTU::WriteSingleOutputRetMessage& msg );
208 JSValue jsMakeModbusBoolAck( const ModbusRTU::ForceSingleCoilRetMessage& msg );
209 JSValue jsMake4314Reply( const ModbusRTU::MEIMessageRetRDI& msg );
210
211 // "синтаксический сахар" для логов
212#ifndef myinfo
213#define myinfo if( log()->debugging(Debug::INFO) ) log()->info()
214#endif
215#ifndef mywarn
216#define mywarn if( log()->debugging(Debug::WARN) ) log()->warn()
217#endif
218#ifndef mycrit
219#define mycrit if( log()->debugging(Debug::CRIT) ) log()->crit()
220#endif
221#ifndef mylog1
222#define mylog1 if( log()->debugging(Debug::LEVEL1) ) log()->level1()
223#endif
224#ifndef mylog2
225#define mylog2 if( log()->debugging(Debug::LEVEL2) ) log()->level2()
226#endif
227#ifndef mylog3
228#define mylog3 if( log()->debugging(Debug::LEVEL3) ) log()->level3()
229#endif
230#ifndef mylog4
231#define mylog4 if( log()->debugging(Debug::LEVEL4) ) log()->level4()
232#endif
233#ifndef mylog5
234#define mylog5 if( log()->debugging(Debug::LEVEL5) ) log()->level5()
235#endif
236#ifndef mylog6
237#define mylog6 if( log()->debugging(Debug::LEVEL6) ) log()->level6()
238#endif
239#ifndef mylog7
240#define mylog7 if( log()->debugging(Debug::LEVEL7) ) log()->level7()
241#endif
242#ifndef mylog8
243#define mylog8 if( log()->debugging(Debug::LEVEL8) ) log()->level8()
244#endif
245#ifndef mylog9
246#define mylog9 if( log()->debugging(Debug::LEVEL9) ) log()->level9()
247#endif
248#ifndef mylogany
249#define mylogany log()->any()
250#endif
251 };
252 // ----------------------------------------------------------------------
253} // end of namespace uniset
254// --------------------------------------------------------------------------
255#endif
Definition JSEngine.h:50
Definition MessageType.h:127
Definition AccessConfig.h:30
long ObjectId
Definition UniSetTypes_i.idl:30
Definition JHttpServer.h:77
Definition JHttpServer.h:94
Definition JHttpServer.h:86
Definition JSEngine.h:38
Definition ModbusTypes.h:353
Definition ModbusTypes.h:1238
Definition ModbusTypes.h:1310
Definition ModbusTypes.h:699
Definition ModbusTypes.h:602
Definition JSHelpers.h:48