<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class=""><blockquote type="cite" class=""><div class="">for the synchronous callback execution, that's simply a variant of OvmsDuktape::DuktapeRequestCallback() using the already existing DuktapeDispatchWait() method along with passing the OvmsWriter* in dmsg.writer. Could be named "…ExecuteCallback" to reflect the synchronous nature.</div></blockquote><div class=""><br class=""></div>Ok. That is simple.<div class=""><br class=""></div><div class=""><blockquote type="cite" class=""><div class="">There is no need to extend OvmsCommand. The DuktapeObject is meant to be used as a general binding of arbitrary system objects to Duktape, so the system objects don't need to be extended.</div></blockquote><div class=""><br class=""></div>I agree it is not necessary; my suggestion is purely to clean it up and enhance functionality. The problem at the moment is that OvmsCommand execute callbacks can only be to function callbacks (not objects). It doesn’t even use the c++ bind function callback mechanism (like notification, etc, for example). It is what it is, and changing now is very hard.</div><div class=""><br class=""></div><div class="">However, making the execute function virtual is relatively simple and allows the object to be virtualised. Alternatively, we could provide an alternative proper c++ function callback variant.</div><div class=""><br class=""></div><div class="">Without that, we need something kludgy like DukOvmsCommandRegisterRun that has to try to find the DuktapeConsoleCommand command in order to be able to call the execute callback in duktape. The current implementation needs to keep a map of OvmsCommand => DuktapeConsoleCommand in order to do this (which is messy).</div><div class=""><br class=""></div><div class=""><blockquote type="cite" class=""><div class="">My API design would be like this:</div></blockquote><div class=""><br class=""></div>OK</div><div class=""><br class=""></div><div class=""><blockquote type="cite" class=""><div class="">The forced unregistering limits registering commands to library plugins (which don't have an unload operation yet). I think this is an unnecessary limitation, I would like to be able to register a command by adding a registration call to "ovmsmain.js" or by executing a script. Of course that means commands registered that way need to be unregistered explicitly, but that's up to the script developers & users then. I don't think we should limit them here without good reason.<br class=""><br class="">On a Duktape unload/reload operation, all JS objects get finalized anyway because of heap destruction, so deletion would happen automatically by the DuktapeConsoleCommand finalizer. If we add some module unload operation later on, modules may e.g. define a cleanup callback, or we can add the module filename to the DuktapeObject registry -- the latter option would also automatically apply to all DuktapeObject instances, eliminating the need for separate registries for other system bindings potentially to be added in the future.<br class=""><br class="">Implicit OvmsCommand deletions can happen as the order of deletion is undefined. Inhibiting this should be a straight forward use of the reference count though: on registration of a sub command, simply Ref() all parent commands registered by Duktape as well. Unref() them after the sub command has been removed, the last Unref() on a parent then automatically deletes it (the first Ref() is done by the coupling).</div></blockquote><div class=""><br class=""></div>OK. My only concern would be timing. Say there is a script that is run, registers and command, then exits. The command will be cleaned up in the next duktape memory clean. But before then, the command could be called. If everything is running in that one duktape thread, there should be no crash, I think, but it sounds scary to me.</div><div class=""><br class=""></div><div class="">Overall, your suggested approach sounds ok.</div><div class=""><br class=""></div><div class="">Regards, Mark.<br class=""><div><br class=""><blockquote type="cite" class=""><div class="">On 8 Sep 2020, at 4:13 PM, Michael Balzer <<a href="mailto:dexter@expeedo.de" class="">dexter@expeedo.de</a>> wrote:</div><br class="Apple-interchange-newline"><div class="">
  
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" class="">
  
  <div class="">
    Mark,<br class="">
    <br class="">
    for the synchronous callback execution, that's simply a variant of
    OvmsDuktape::DuktapeRequestCallback() using the already existing
    DuktapeDispatchWait() method along with passing the OvmsWriter* in
    dmsg.writer. Could be named "…ExecuteCallback" to reflect the
    synchronous nature.<br class="">
    <br class="">
    There is no need to extend OvmsCommand. The DuktapeObject is meant
    to be used as a general binding of arbitrary system objects to
    Duktape, so the system objects don't need to be extended.<br class="">
    <br class="">
    From my first check of your implementation, I would probably:<br class="">
    <ul class="">
      <li class="">Redefine the register API to expect an object with an
        "execute" (and later possibly also a "validate") callback, and
        bind the DuktapeCommandObject to that object. A "fail" callback
        could also make sense.<br class="">
      </li>
      <li class="">Delegate the full OvmsCommand life cycle and execution
        handling to the DuktapeCommandObject.</li>
      <li class="">Remove the forced unregistering of commands on script file
        unloading.<br class="">
      </li>
      <li class="">Inhibit implicit sub command deletions by parent deletions.<br class="">
      </li>
    </ul>
    My API design would be like this:<br class="">
    <br class="">
    <tt class="">// Command registration:<br class="">
      var cmd = OvmsCommand.Register({</tt><tt class=""><br class="">
    </tt><tt class="">  parent: "test",</tt><tt class=""><br class="">
    </tt><tt class="">  name: "dukcommand",</tt><tt class=""><br class="">
    </tt><tt class="">  title: "Test/demonstrate Duktape command registration",</tt><tt class=""><br class="">
    </tt><tt class="">  usage: "Pass 1 - 3 arguments",</tt><tt class=""><br class="">
    </tt><tt class="">  min: 1,</tt><tt class=""><br class="">
    </tt><tt class="">  max: 3,</tt><tt class=""><br class="">
    </tt><tt class="">  execute: function(verbosity, argv) {},</tt><tt class=""><br class="">
    </tt><tt class="">  fail: function(error) {}</tt><tt class=""><br class="">
    </tt><tt class="">});<br class="">
      <br class="">
    </tt><tt class=""><tt class="">// Command deregistration:<br class="">
      </tt>OvmsCommand.Unregister({ parent: "test", name: "dukcommand"
      });<br class="">
      // …or in case we still have the cmd object, simply:<br class="">
      OvmsCommand.Unregister(cmd);<br class="">
      <br class="">
    </tt><br class="">
    The OvmsCommand execution callback would be a bound method of the
    DuktapeCommandObject instance. The method passes the verbosity and
    arguments vector to ExecuteMethod() via the data pointer using a
    custom struct. CallMethod() then pushes these on the Duktape stack
    and calls the "execute" property.<br class="">
    <br class="">
    The forced unregistering limits registering commands to library
    plugins (which don't have an unload operation yet). I think this is
    an unnecessary limitation, I would like to be able to register a
    command by adding a registration call to "ovmsmain.js" or by
    executing a script. Of course that means commands registered that
    way need to be unregistered explicitly, but that's up to the script
    developers & users then. I don't think we should limit them here
    without good reason.<br class="">
    <br class="">
    On a Duktape unload/reload operation, all JS objects get finalized
    anyway because of heap destruction, so deletion would happen
    automatically by the DuktapeConsoleCommand finalizer. If we add some
    module unload operation later on, modules may e.g. define a cleanup
    callback, or we can add the module filename to the DuktapeObject
    registry -- the latter option would also automatically apply to all
    DuktapeObject instances, eliminating the need for separate
    registries for other system bindings potentially to be added in the
    future.<br class="">
    <br class="">
    Implicit OvmsCommand deletions can happen as the order of deletion
    is undefined. Inhibiting this should be a straight forward use of
    the reference count though: on registration of a sub command, simply
    Ref() all parent commands registered by Duktape as well. Unref()
    them after the sub command has been removed, the last Unref() on a
    parent then automatically deletes it (the first Ref() is done by the
    coupling).<br class="">
    <br class="">
    Regards,<br class="">
    Michael<br class="">
    <br class="">
    <br class="">
    <div class="moz-cite-prefix">Am 08.09.20 um 09:06 schrieb Mark
      Webb-Johnson:<br class="">
    </div>
    <blockquote type="cite" cite="mid:0D6C31A8-544C-4FD6-A9C6-1500D3CE6F66@webb-johnson.net" class="">
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" class="">
      Michael,
      <div class=""><br class="">
      </div>
      <div class="">Very clear, and very helpful. Only thing I would
        suggest would be to have a minimum example in the documentation.
        The bare minimum required for an implementation of an object.</div>
      <div class=""><br class="">
      </div>
      <div class="">Reading through what you write, it seems the correct
        approach is:</div>
      <div class=""><br class="">
      </div>
      <div class="">
        <ul class="MailOutline">
          <li class="">Extend OvmsCommand to have a virtual
            ExecuteCommand method (same parameters as ‘m_execute’
            callback).</li>
          <li class="">Extend OvmsCommand::execute to check if m_execute
            is null, then call ‘ExecuteCommand’ instead.</li>
          <li class="">Perhaps do the same for m_validate in
            OvmsCommand.</li>
          <li class="">Then, the duktape implementation can be object
            (rather than callback function) based, as your DuktapeObject
            expects. The javascript would call a function to register a
            command, with a command object to be used for the callback.</li>
          <li class="">Need to extend the DuktapeObject system to
            support a synchronous command (presumably implemented with a
            passed mutex like we do in several other parts of the
            system).</li>
        </ul>
      </div>
      <div class=""><br class="">
      </div>
      <div class="">Is that correct, and what you were expecting? Or any
        other suggestions?</div>
      <div class=""><br class="">
      </div>
      <div class="">Regards, Mark.</div>
      <div class=""><br class="">
        <div class="">
          <blockquote type="cite" class="">
            <div class="">On 7 Sep 2020, at 2:55 AM, Michael Balzer <<a href="mailto:dexter@expeedo.de" class="" moz-do-not-send="true">dexter@expeedo.de</a>> wrote:</div>
            <br class="Apple-interchange-newline">
            <div class="">
              <meta http-equiv="Content-Type" content="text/html;
                charset=UTF-8" class="">
              <div class=""> Mark,<br class="">
                <br class="">
                <blockquote type="cite" class="">
                  <div class="">Still struggling with this. It seems
                    like your DuktapeObject will do this, but I can’t
                    work out how it works.</div>
                </blockquote>
                <br class="">
                I admit my documentation has some shortcomings. I'll try
                to fill that gap first:<br class="">
                <br class="">
                <br class="">
                <b class=""><u class="">Concept #1: Reference counting</u></b><br class="">
                <br class="">
                A DuktapeObject is meant to be a C++/OS extension of a
                Javascript object, for example to hold some system
                binding associated with the JS object.<br class="">
                <br class="">
                The primary goal is to provide asynchronous operations
                on the system side, initiating JS callbacks when
                finishing/failing. For example a HTTP operation can be
                started by a script, passing some "done" callback. The
                system then starts the network operation asynchronously,
                the JS task can continue processing other scripts. Once
                the network operation is done, the DuktapeObject sends a
                request to the Duktape task to execute the "done"
                callback on itself.<br class="">
                <br class="">
                So the DuktapeObject is normally shared by multiple
                contexts and parallel operations. Asynchronous operation
                also means the JS object or context may already be gone
                when the operation finishes. So the DuktapeObject needs
                to stay locked in memory independent of the JS context.<br class="">
                <br class="">
                That's implemented by counting the active references to
                the DuktapeObject (methods Ref() / Unref()). The last
                Unref() automatically deletes the DuktapeObject
                instance.<br class="">
                <br class="">
                For example: JS requests a network operation. The
                initial JS binding (see coupling) sets the reference
                count to 1. The DuktapeObject starts the network
                request, increasing the ref count to 2. If the JS
                context now dies (decreasing the reference count), the
                DuktapeObject will still remain valid until the network
                operation returns.<br class="">
                <br class="">
                As the Ref/Unref operations need a (recursive) mutex,
                that's also part of the DuktapeObject and exposed by the
                API for other uses: Lock() / Unlock().<br class="">
                <br class="">
                <br class="">
                <b class=""><u class="">Concept #2: Coupling</u></b><br class="">
                <br class="">
                Javascript does not have a destructor concept. JS
                objects get deleted by heap destruction or by the
                garbage collector when no reference to the JS object is
                left. In both cases, the actual object deletion is
                called "finalization", and a special finalizer
                method/callback can be installed on a JS object to be
                called just before the object gets deallocated. That is
                done by the DuktapeObject::Couple() method (implicitly
                called when constructed directly with a JS object
                reference).<br class="">
                <br class="">
                There is no way to force finalization on a JS object. So
                a DuktapeObject cannot tell Duktape to delete it's
                coupled object, that means a DuktapeObject should
                normally not be deleted from outside the Duktape
                context, at least not if still coupled to the JS object.
                Coupling and decoupling can only be done in the Duktape
                context.<br class="">
                <br class="">
                The standard finalizer DuktapeObject::Finalizer() simply
                decouples, automatically deleting itself if the coupling
                was the last reference. This is a virtual method, so can
                be overridden as necessary.<br class="">
                <br class="">
                The coupling operation additionally adds a hidden
                pointer to the DuktapeObject instance in the JS object.
                That allows to check for and retreive associated
                DuktapeObject instances from any JS object, which is
                provided by the GetInstance() call.<br class="">
                <br class="">
                <br class="">
                <b class=""><u class="">Concept #3: Registration</u></b><br class="">
                <br class="">
                For asynchronous operations, it's normally very
                convenient to have a "fire & forget" API. Example
                from the documentation:<br class="">
                <pre id="codecell13" style="box-sizing: border-box; font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", Courier, monospace; font-size: 12px; white-space: pre; margin: 0px; padding: 12px; display: block; overflow: auto; line-height: 1.4; color: rgb(64, 64, 64); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;" class=""><span class="nx" style="box-sizing: border-box;">VFS</span><span class="p" style="box-sizing: border-box;">.</span><span class="nx" style="box-sizing: border-box;">Save</span><span class="p" style="box-sizing: border-box;">({</span>
  <span class="nx" style="box-sizing: border-box;">path</span><span class="o" style="box-sizing: border-box; color: rgb(102, 102, 102);">:</span> <span class="s2" style="box-sizing: border-box; color: rgb(186, 33, 33);">"/sd/mydata/telemetry.json"</span><span class="p" style="box-sizing: border-box;">,</span>
  <span class="nx" style="box-sizing: border-box;">data</span><span class="o" style="box-sizing: border-box; color: rgb(102, 102, 102);">:</span> <span class="nx" style="box-sizing: border-box;">Duktape</span><span class="p" style="box-sizing: border-box;">.</span><span class="nx" style="box-sizing: border-box;">enc</span><span class="p" style="box-sizing: border-box;">(</span><span class="s1" style="box-sizing: border-box; color: rgb(186, 33, 33);">'jx'</span><span class="p" style="box-sizing: border-box;">,</span> <span class="nx" style="box-sizing: border-box;">telemetry</span><span class="p" style="box-sizing: border-box;">),</span>
  <span class="nx" style="box-sizing: border-box;">fail</span><span class="o" style="box-sizing: border-box; color: rgb(102, 102, 102);">:</span> <span class="kd" style="box-sizing: border-box; color: rgb(0, 128, 0); font-weight: bold;">function</span><span class="p" style="box-sizing: border-box;">(</span><span class="nx" style="box-sizing: border-box;">error</span><span class="p" style="box-sizing: border-box;">)</span> <span class="p" style="box-sizing: border-box;">{</span>
    <span class="nx" style="box-sizing: border-box;">print</span><span class="p" style="box-sizing: border-box;">(</span><span class="s2" style="box-sizing: border-box; color: rgb(186, 33, 33);">"Error saving telemetry: "</span> <span class="o" style="box-sizing: border-box; color: rgb(102, 102, 102);">+</span> <span class="nx" style="box-sizing: border-box;">error</span><span class="p" style="box-sizing: border-box;">);</span>
  <span class="p" style="box-sizing: border-box;">}</span>
<span class="p" style="box-sizing: border-box;">});</span>
</pre>
                I.e. you simply pass the operation arguments including
                the done/fail callbacks to an API method and don't need
                to care about storing a reference to some handle. In JS
                that normally means the object used won't have any
                reference left after the call, so would be deleted by
                the garbage collector on the next run.<br class="">
                <br class="">
                To avoid garbage collection and lock the JS object in
                memory, we need to store a reference to it in a "public"
                place. Duktape provides a special public place for this,
                hidden from scripts, called the global stash.
                DuktapeObject maintains a dedicated global object
                registry in that stash.<br class="">
                <br class="">
                Adding and removing the coupled object reference to/from
                that registry is done by the Register() and Deregister()
                methods.<br class="">
                <br class="">
                So for asynchronous system operations, or system
                integrations that shall be persistent, you normally do a
                Register() call together with the coupling, unless some
                ressource isn't available. Deregistration is then
                normally done when all pending JS callbacks have been
                executed, or when the persistent system integration has
                been unbound.<br class="">
                <br class="">
                Other API designs are possible here: if you'd rather
                like the script needing to store a reference to your
                operation handle, you don't need to do a registration.
                The object will then be deleted (finalized) by the
                garbage collector automatically after the script deletes
                the reference.<br class="">
                <br class="">
                <br class="">
                <b class=""><u class="">Concept #4: Callback invocation</u></b><br class="">
                <br class="">
                Triggers on the system side, for example a finished or
                failed network operation, shall normally trigger a JS
                method execution.<br class="">
                <br class="">
                JS callback methods are simply passed as part of the
                arguments object in modern JS APIs. This allows to pass
                simple function definitions inline, as well as to
                reference a separately defined general handler function.
                JS allows functions to be excuted in the context of any
                object, and callbacks normally are executed in the
                context of the API object. This adds even more
                convenience, as the callbacks can easily access the
                other API arguments still stored in the object, as well
                as additional data added by the call.<br class="">
                <br class="">
                JS callbacks cannot be executed directly from any system
                context, they need to run in the Duktape context. So the
                DuktapeObject callback invocation mechanism includes a
                general method to request a callback execution by
                Duktape: RequestCallback()<br class="">
                <br class="">
                Note: RequestCallback() is an asynchronous operation. A
                synchronous variant can be added if necessary (and
                probably will be for command execution from a console).<br class="">
                <br class="">
                A pending callback automatically increments the
                reference count, so the object is locked in memory until
                the callback has been executed (or aborted) by the
                Duktape task.<br class="">
                <br class="">
                The callback invocation API provides a void* for simple
                data (e.g. a fixed string) to be passed to the callback
                method, but for more complex data, you will normally
                fill some DuktapeObject member variables before invoking
                the callback.<br class="">
                <br class="">
                In Duktape context, the callback invocation translates
                the data returned or provided by the system side into
                the Duktape callback arguments and then runs the
                callback (if the object actually has the requested
                callback set). The default implementation for this is
                DuktapeObject::CallMethod(), which can be used directly
                for simple callbacks without arguments. For more complex
                handling, override this with your custom implementation.<br class="">
                <br class="">
                The callbacks are by default executed on the coupled JS
                object, so data can also be transported by setting
                properties on that object. The callback can then simply
                access them via "this".<br class="">
                <br class="">
                To simplify callback invocation from code parts that may
                run outside or inside Duktape, it's convenient to allow
                calling CallMethod() without a Duktape context, and let
                CallMethod() translate that into a RequestCallback()
                call as necessary. Pattern:<br class="">
                <br class="">
                <tt class="">duk_ret_t
                  DuktapeHTTPRequest::CallMethod(duk_context *ctx, const
                  char* method, void* data /*=NULL*/)</tt><tt class=""><br class="">
                </tt><tt class="">  {</tt><tt class=""><br class="">
                </tt><tt class="">  if (!ctx)</tt><tt class=""><br class="">
                </tt><tt class="">    {</tt><tt class=""><br class="">
                </tt><tt class="">    RequestCallback(method, data);</tt><tt class=""><br class="">
                </tt><tt class="">    return 0;</tt><tt class=""><br class="">
                </tt><tt class="">    }</tt><tt class=""><br class="">
                    …<br class="">
                </tt><br class="">
                A CallMethod() implementation isn't limited to executing
                a single callback. A common example is an API defining
                "done" & "fail" callbacks, as well as a general
                final "always" callback. <tt class="">DuktapeHTTPRequest::CallMethod()</tt>
                also serves as an example implementation for this.<br class="">
                <br class="">
                <br class="">
                <br class="">
                Wow… that's become more to write & read than I
                expected. Please provide some feedback: is that
                explanation sufficient & clear? I'll refine it for
                the developer docs then.<br class="">
                <br class="">
                Regards,<br class="">
                Michael<br class="">
                <br class="">
                <br class="">
                <div class="moz-cite-prefix">Am 01.09.20 um 19:52
                  schrieb Michael Balzer:<br class="">
                </div>
                <blockquote type="cite" cite="mid:c19950a1-cf6e-928f-9ddd-90c444bb3d15@expeedo.de" class="">
                  <meta http-equiv="Content-Type" content="text/html;
                    charset=UTF-8" class="">
                  Mark,<br class="">
                  <br class="">
                  I'll have a look.<br class="">
                  <br class="">
                  Regards,<br class="">
                  Michael<br class="">
                  <br class="">
                  <br class="">
                  <div class="moz-cite-prefix">Am 01.09.20 um 07:30
                    schrieb Mark Webb-Johnson:<br class="">
                  </div>
                  <blockquote type="cite" cite="mid:9393E6BF-E1C9-495C-88CE-16082D7678A0@webb-johnson.net" class="">
                    <meta http-equiv="Content-Type" content="text/html;
                      charset=UTF-8" class="">
                    Michael,
                    <div class=""><br class="">
                    </div>
                    <div class="">Still struggling with this. It seems
                      like your DuktapeObject will do this, but I can’t
                      work out how it works.</div>
                    <div class=""><br class="">
                    </div>
                    <div class="">Here are some notes one what I have
                      done so far:</div>
                    <div class=""><br class="">
                    </div>
                    <div class="">
                      <ol class="MailOutline">
                        <li class="">Created a
                          stub DuktapeConsoleCommand (derived from
                          DuktapeObject) in ovms_duktape.{h,cpp}. This
                          should hold enough to be able to call the
                          javascript callback method for that object. It
                          also stores the module filename (so the
                          registration can be removed when the module is
                          unloaded).<br class="">
                          <br class="">
                        </li>
                        <li class="">Provide a DuktapeCommandMap
                          m_cmdmap in ovms_duktape.{h,cpp} OvmsDuktape
                          class that stores a mapping from OvmsCommand
                          to DuktapeConsoleCommand.<br class="">
                          <br class="">
                        </li>
                        <li class="">Created
                          a OvmsDuktape::RegisterDuktapeConsoleCommand
                          in ovms_duktape.{h,cpp) that (a) creates the
                          OvmsCommand() object, (b) registers it, (c)
                          creates the DuktapeConsoleCommand() object,
                          and (d) updates a map from
                          OvmsCommand->DuktapeConsoleCommand. There
                          Is also a single
                          callback DukOvmsCommandRegisterRun designed to
                          be run by all.<br class="">
                          <br class="">
                        </li>
                        <li class="">Created
                          hooks NotifyDuktapeModuleLoad, NotifyDuktapeModuleUnload,
                          and NotifyDuktapeModuleUnloadAll in
                          OvmsDuktape. The javascript module is
                          identified by filename (path to module or
                          script on vfs, usually, but may also be an
                          internal module). The Unload functions look
                          through the m_cmdmap and unregister commands
                          for javascript modules being unloaded.<br class="">
                          <br class="">
                        </li>
                        <li class="">Provide an implementation for
                          ovms_command DukOvmsCommandRegister to support
                          registering commands from Javascript modules.
                          This should extract the details, and then call
                          OvmsDuktape::RegisterDuktapeConsoleCommand to
                          do the actual registration. This has been
                          implemented, except for the callback method
                          (and somehow passing that method from
                          Javascript in the OvmsCommand.Register
                          javascript call).<br class="">
                          <br class="">
                        </li>
                        <li class="">Provide a stub implementation for <span style="caret-color: rgb(0, 0, 0);" class="">DukOvmsCommandRegisterRun.
                            This uses m_cmdmap to lookup the </span><font class="">DuktapeConsoleCommand object for
                            the command to be run. It should execute the
                            callback method (but that part is not yet
                            implemented).</font></li>
                      </ol>
                    </div>
                    <div class=""><br class="">
                    </div>
                    <div class=""><br class="">
                    </div>
                    <div class="">I still need help with #5 and #6. What
                      needs to be implemented in DuktapeConsoleCommand,
                      and how is the parameter in <span style="caret-color: rgb(0, 0, 0);" class="">OvmsCommand.Register
                        used to store the callback (#5)? Then how to
                        callback the command method from</span> <span style="caret-color: rgb(0, 0, 0);" class="">DukOvmsCommandRegisterRun</span><span style="caret-color: rgb(0, 0, 0);" class=""> (#6)?
                        If you have time, it is probably much quicker
                        for you to simply make those changes.</span></div>
                    <div class=""><span style="caret-color: rgb(0, 0,
                        0);" class=""><br class="">
                      </span></div>
                    <div class=""><font class="">An alternative
                        implementation would be to do something like
                        the pubsub framework, where the mapping
                        command->callback is done from within a
                        javascript module. That I could do, but it seems
                        your </font><span style="caret-color: rgb(0, 0,
                        0);" class="">DuktapeObject can do it better.</span><font class=""> </font></div>
                    <div class=""><br class="">
                    </div>
                    <div class="">Thanks, Mark.<br class="">
                      <div class=""><br class="">
                        <blockquote type="cite" class="">
                          <div class="">On 15 Jul 2020, at 3:34 PM,
                            Michael Balzer <<a href="mailto:dexter@expeedo.de" class="" moz-do-not-send="true">dexter@expeedo.de</a>>
                            wrote:</div>
                          <br class="Apple-interchange-newline">
                          <div class="">
                            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" class="">
                            <div class=""> Mark,<br class="">
                              <br class="">
                              yes, I needed that persistence for the
                              HTTP and VFS classes, but I also needed to
                              be able to couple a dynamic C++ instance
                              with a JS object and have a mechanism to
                              prevent garbage collection while the C++
                              side is still in use. If the C++ side is
                              no longer needed, the JS finalizer also
                              needs to imply the C++ instance can be
                              deleted.<br class="">
                              <br class="">
                              That is all implemented by DuktapeObject.
                              DuktapeObject also provides JS method
                              invocation on the coupled JS object and a
                              mutex for concurrency protection.<br class="">
                              <br class="">
                              We probably need some more framework
                              documentation than the header comments
                              (applies to all of our framework
                              classes…):<br class="">
                              <br class="">
                              <tt class="">/***************************************************************************************************</tt><tt class=""><br class="">
                              </tt><tt class=""> * DuktapeObject:
                                coupled C++ / JS object</tt><tt class=""><br class="">
                              </tt><tt class=""> * </tt><tt class=""><br class="">
                              </tt><tt class=""> *  Intended for API
                                methods to attach internal API state to
                                a JS object and provide</tt><tt class=""><br class="">
                              </tt><tt class=""> *    a standard
                                callback invocation interface for JS
                                objects in local scopes.</tt><tt class=""><br class="">
                              </tt><tt class=""> *  </tt><tt class=""><br class="">
                              </tt><tt class=""> *  - Override
                                CallMethod() to implement specific
                                method calls</tt><tt class=""><br class="">
                              </tt><tt class=""> *  - Override
                                Finalize() for specific destruction in
                                JS context (garbage collection)</tt><tt class=""><br class="">
                              </tt><tt class=""> *  - call Register() to
                                prevent normal garbage collection (but
                                not heap destruction)</tt><tt class=""><br class="">
                              </tt><tt class=""> *  - call Ref() to
                                protect against deletion (reference
                                count)</tt><tt class=""><br class="">
                              </tt><tt class=""> *  - call Lock() to
                                protect concurrent access (recursive
                                mutex)</tt><tt class=""><br class="">
                              </tt><tt class=""> *  </tt><tt class=""><br class="">
                              </tt><tt class=""> *  - GetInstance()
                                retrieves the DuktapeObject associated
                                with a JS object if any</tt><tt class=""><br class="">
                              </tt><tt class=""> *  - Push() pushes the
                                JS object onto the Duktape stack</tt><tt class=""><br class="">
                              </tt><tt class=""> *  </tt><tt class=""><br class="">
                              </tt><tt class=""> *  Note: the
                                DuktapeObject may persist after the JS
                                object has been finalized, e.g.</tt><tt class=""><br class="">
                              </tt><tt class=""> *    if some callbacks
                                are pending after the Duktape heap has
                                been destroyed.</tt><tt class=""><br class="">
                              </tt><tt class=""> *    Use IsCoupled() to
                                check if the JS object is still
                                available.</tt><tt class=""><br class="">
                              </tt><tt class=""> *  </tt><tt class=""><br class="">
                              </tt><tt class=""> *  Ref/Unref:</tt><tt class=""><br class="">
                              </tt><tt class=""> *    Normal life cycle
                                is from construction to finalization.
                                Pending callbacks extend</tt><tt class=""><br class="">
                              </tt><tt class=""> *    the life until the
                                last callback has been processed. A
                                subclass may extend the life</tt><tt class=""><br class="">
                              </tt><tt class=""> *    by calling Ref(),
                                which increases the reference count.
                                Unref() deletes the instance</tt><tt class=""><br class="">
                              </tt><tt class=""> *    if no references
                                are left.</tt><tt class=""><br class="">
                              </tt><tt class=""> */</tt><tt class=""><br class="">
                              </tt><br class="">
                              You normally just need to use
                              Register/Deregister & Ref/Unref, and
                              to implement the constructor and
                              CallMethod. Coupling of the instances
                              normally is done on construction, as a JS
                              object is normally already needed for the
                              parameters and can simply be attached to.<br class="">
                              <br class="">
                              Have a look at DuktapeHTTPRequest,
                              DuktapeVFSLoad and DuktapeVFSSave, these
                              are the current subclasses using this.<br class="">
                              <br class="">
                              For the command registration I would
                              probably couple the OvmsCommand instance
                              with a JS command object providing an
                              execution method.<br class="">
                              <br class="">
                              Tell me if you need more info.<br class="">
                              <br class="">
                              Regards,<br class="">
                              Michael<br class="">
                              <br class="">
                              <br class="">
                              <div class="moz-cite-prefix">Am 15.07.20
                                um 08:12 schrieb Mark Webb-Johnson:<br class="">
                              </div>
                              <blockquote type="cite" cite="mid:B291C8AF-8382-4F45-87E3-3DA555E068BE@webb-johnson.net" class="">
                                <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" class="">
                                <div class=""><br class="">
                                </div>
                                <div class="">@Michael this is probably
                                  for you.</div>
                                <div class=""><br class="">
                                </div>
                                <div class="">I am trying to implement
                                  javascript command registration. The
                                  idea is that a javascript module can
                                  call something like:</div>
                                <div class=""><br class="">
                                </div>
                                <div class="">
                                  <blockquote style="caret-color: rgb(0,
                                    0, 0); margin: 0px 0px 0px 40px;
                                    border: none; padding: 0px;" class="">OvmsCommand.Register(basecommand,
                                    name, title, callbackfn, usage, min,
                                    max)</blockquote>
                                  <div style="caret-color: rgb(0, 0,
                                    0);" class=""><br class="">
                                  </div>
                                </div>
                                <div style="caret-color: rgb(0, 0, 0);" class="">Then we reflect that into
                                  MyCommandApp.RegisterCommand, and keep
                                  a track of which command is for which
                                  javascript callbackfn. When the
                                  command is executed, we pass it into
                                  duktape.</div>
                                <div style="caret-color: rgb(0, 0, 0);" class=""><br class="">
                                </div>
                                <div style="caret-color: rgb(0, 0, 0);" class="">I also have tracking for
                                  javascript module loading and
                                  unloading, so I can
                                  DeregisterCommand() if duktape is
                                  reloaded (and also protected against
                                  commands being registered in
                                  short-lived scripts run from the
                                  command line).</div>
                                <div style="caret-color: rgb(0, 0, 0);" class=""><br class="">
                                </div>
                                <div style="caret-color: rgb(0, 0, 0);" class="">To implement this, I need to
                                  store the callbackfn as a persistent
                                  reference to a duktape javascript
                                  function.</div>
                                <div class=""><br class="">
                                </div>
                                <div class="">The issue with callback
                                  function references in duktape is
                                  summarised here:</div>
                                <div class=""><br class="">
                                </div>
                                <blockquote style="margin: 0 0 0 40px;
                                  border: none; padding: 0px;" class="">
                                  <div class=""><a href="https://wiki.duktape.org/howtonativepersistentreferences" class="" moz-do-not-send="true">https://wiki.duktape.org/howtonativepersistentreferences</a></div>
                                  <div class=""><br class="">
                                  </div>
                                  <div class=""><i class="">When a
                                      Duktape/C function is called,
                                      Duktape places the call arguments
                                      on the value stack. While the
                                      arguments are on the value stack,
                                      they're guaranteed to be reachable
                                      and the Duktape/C function can
                                      safely work with the arguments.<br class="">
                                      <br class="">
                                      However, when the Duktape/C
                                      function returns, the value stack
                                      is unwound and references in the
                                      function's value stack frame are
                                      lost. If the last reference to a
                                      particular value was in the
                                      function's value stack frame, the
                                      value will be garbage collected
                                      when the function return is
                                      processed.</i></div>
                                </blockquote>
                                <div class=""><br class="">
                                </div>
                                <div class="">The standard approach is
                                  to store the reference back in the
                                  duktape duk_push_global_stash so it
                                  won’t get garbage-collected. But, that
                                  seems messy.</div>
                                <div class=""><br class="">
                                </div>
                                <div class="">I see that Michael has
                                  already implemented something that
                                  seems similar in ovms_script.{h, cpp},
                                  for the async http callbacks.
                                  Presumably to avoid this issue. But,
                                  the approach seems very different, and
                                  I am not sure if it is stopping _all_
                                  garbage collection for the duration of
                                  the async query, or just that
                                  particular object being garbage
                                  collected. The work seems extensive
                                  (quite a few objects involved).</div>
                                <div class=""><br class="">
                                </div>
                                <div class="">So @Michael, any
                                  suggestions for this? I don’t want to
                                  reinvent the wheel...</div>
                                <div class=""><br class="">
                                </div>
                                <div class="">Regards, Mark.</div>
                                <br class="">
                                <fieldset class="mimeAttachmentHeader"></fieldset>
                                <pre class="moz-quote-pre" wrap="">_______________________________________________
OvmsDev mailing list
<a class="moz-txt-link-abbreviated" href="mailto:OvmsDev@lists.openvehicles.com" moz-do-not-send="true">OvmsDev@lists.openvehicles.com</a>
<a class="moz-txt-link-freetext" href="http://lists.openvehicles.com/mailman/listinfo/ovmsdev" moz-do-not-send="true">http://lists.openvehicles.com/mailman/listinfo/ovmsdev</a>
</pre>
                              </blockquote>
                              <br class="">
                              <pre class="moz-signature" cols="72">-- 
Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal
Fon 02333 / 833 5735 * Handy 0176 / 206 989 26
</pre>
                            </div>
_______________________________________________<br class="">
                            OvmsDev mailing list<br class="">
                            <a href="mailto:OvmsDev@lists.openvehicles.com" class="" moz-do-not-send="true">OvmsDev@lists.openvehicles.com</a><br class="">
                            <a class="moz-txt-link-freetext" href="http://lists.openvehicles.com/mailman/listinfo/ovmsdev" moz-do-not-send="true">http://lists.openvehicles.com/mailman/listinfo/ovmsdev</a><br class="">
                          </div>
                        </blockquote>
                      </div>
                      <br class="">
                    </div>
                    <br class="">
                    <fieldset class="mimeAttachmentHeader"></fieldset>
                    <pre class="moz-quote-pre" wrap="">_______________________________________________
OvmsDev mailing list
<a class="moz-txt-link-abbreviated" href="mailto:OvmsDev@lists.openvehicles.com" moz-do-not-send="true">OvmsDev@lists.openvehicles.com</a>
<a class="moz-txt-link-freetext" href="http://lists.openvehicles.com/mailman/listinfo/ovmsdev" moz-do-not-send="true">http://lists.openvehicles.com/mailman/listinfo/ovmsdev</a>
</pre>
                  </blockquote>
                  <br class="">
                  <pre class="moz-signature" cols="72">-- 
Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal
Fon 02333 / 833 5735 * Handy 0176 / 206 989 26</pre>
                  <br class="">
                  <fieldset class="mimeAttachmentHeader"></fieldset>
                  <pre class="moz-quote-pre" wrap="">_______________________________________________
OvmsDev mailing list
<a class="moz-txt-link-abbreviated" href="mailto:OvmsDev@lists.openvehicles.com" moz-do-not-send="true">OvmsDev@lists.openvehicles.com</a>
<a class="moz-txt-link-freetext" href="http://lists.openvehicles.com/mailman/listinfo/ovmsdev" moz-do-not-send="true">http://lists.openvehicles.com/mailman/listinfo/ovmsdev</a>
</pre>
                </blockquote>
                <br class="">
                <pre class="moz-signature" cols="72">-- 
Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal
Fon 02333 / 833 5735 * Handy 0176 / 206 989 26</pre>
              </div>
              _______________________________________________<br class="">
              OvmsDev mailing list<br class="">
              <a href="mailto:OvmsDev@lists.openvehicles.com" class="" moz-do-not-send="true">OvmsDev@lists.openvehicles.com</a><br class="">
              <a class="moz-txt-link-freetext" href="http://lists.openvehicles.com/mailman/listinfo/ovmsdev">http://lists.openvehicles.com/mailman/listinfo/ovmsdev</a><br class="">
            </div>
          </blockquote>
        </div>
        <br class="">
      </div>
      <br class="">
      <fieldset class="mimeAttachmentHeader"></fieldset>
      <pre class="moz-quote-pre" wrap="">_______________________________________________
OvmsDev mailing list
<a class="moz-txt-link-abbreviated" href="mailto:OvmsDev@lists.openvehicles.com">OvmsDev@lists.openvehicles.com</a>
<a class="moz-txt-link-freetext" href="http://lists.openvehicles.com/mailman/listinfo/ovmsdev">http://lists.openvehicles.com/mailman/listinfo/ovmsdev</a>
</pre>
    </blockquote>
    <br class="">
    <pre class="moz-signature" cols="72">-- 
Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal
Fon 02333 / 833 5735 * Handy 0176 / 206 989 26</pre>
  </div>

_______________________________________________<br class="">OvmsDev mailing list<br class=""><a href="mailto:OvmsDev@lists.openvehicles.com" class="">OvmsDev@lists.openvehicles.com</a><br class="">http://lists.openvehicles.com/mailman/listinfo/ovmsdev<br class=""></div></blockquote></div><br class=""></div></body></html>