<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="">I think the map wouldn't be necessary for the command invocation, but it would still be necessary to check/get the associated DuktapeConsoleCommand of a parent OvmsCommand pointer.</div></blockquote><div class=""><br class=""></div>My intention here is to avoid the risk of maps of pointers (where the object pointed to is destructed by something externally. I guess we can address this by removing from the map within the destructor of the object? But also need to be careful to mutex protect that.<div class=""><br class=""></div><div class="">I was trying to find a way of avoiding that map altogether. For example if the DuktapeConsoleCommand object was also derived from OvmsCommand.</div><div class=""><br class=""></div><div class="">Perhaps with the now std::function implementation we can register the command callback direct to a handler in the DuktapeConsoleCommand object, and keep a reference to the registered OvmsCommand object there?<br class=""><div><br class=""></div><div>I’m not sure this has been much of an issue before, as commands don’t often get deregistered. Certainly not in the normal course of operation. But with javascript Duktape it could be fairly command (for example every time the scripts are reloaded).</div><div><br class=""></div><div>I do think that there is great opportunity here for Duktape - not just for commands and extensions, but also for simpler vehicle implementations (for example polling ones). The barrier to entry for development is certainly dramatically lower than with c++ espressif IDE, and the dynamic development environment should help. It is worth the effort to get this right.</div><div><br class=""></div><div>Regards, Mark.</div><div><br class=""><blockquote type="cite" class=""><div class="">On 12 Sep 2020, at 12:11 AM, 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="">
    <br class="">
    <div class="moz-cite-prefix">Am 11.09.20 um 08:53 schrieb Mark
      Webb-Johnson:<br class="">
    </div>
    <blockquote type="cite" cite="mid:8CFF9592-1921-47C5-AA2E-1F76BF40D9F6@webb-johnson.net" class="">
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" class="">
      <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>
    </blockquote>
    <br class="">
    That slipped my attention, but upgrading OvmsCommand to accept any
    function type should be simply changing m_execute and m_validate to
    std::function, or do I miss something?<br class="">
    <br class="">
    <blockquote type="cite" cite="mid:8CFF9592-1921-47C5-AA2E-1F76BF40D9F6@webb-johnson.net" class="">
      <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>
    </blockquote>
    <br class="">
    I think the map wouldn't be necessary for the command invocation,
    but it would still be necessary to check/get the associated
    DuktapeConsoleCommand of a parent OvmsCommand pointer.<br class="">
    <br class="">
    <blockquote type="cite" cite="mid:8CFF9592-1921-47C5-AA2E-1F76BF40D9F6@webb-johnson.net" class="">
      <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>
    </blockquote>
    <br class="">
    I would keep the command registered until it's explicitly
    unregistered or deleted by a heap destruction. I also suggest
    keeping a Ref() on the DuktapeConsoleCommand until the command has
    been fully deregistered…<br class="">
    <br class="">
    Hm. Deregistration can still occur concurrently to the OvmsCommand
    deletion… I think we haven't taken care of concurrent command
    deregistration at all, there is no lock/mutex in OvmsCommandApp. So
    currently any subsystem deregistering it's commands can crash the
    system if one of those commands is just about to be executed. Maybe
    we need to address this in OvmsCommandApp or …Map.<br class="">
    <br class="">
    Regards,<br class="">
    Michael<br class="">
    <br class="">
    <br class="">
    <blockquote type="cite" cite="mid:8CFF9592-1921-47C5-AA2E-1F76BF40D9F6@webb-johnson.net" class="">
      <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 class=""><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="" 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="">
                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" 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>
              </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>