I have written a custom logger and wanted to add the name of POUs as the pszComponent for easier debugging.
I wrote a custom FB_init which accepts as input the name of the POU as a string and encountered puzzling behavior (to me at least).
Could someone explain to me why the following compiles
VAR
name : STRING := __POUNAME();
test : FB_logAdd(sCmpName:=name);
END_VAR
but the following
VAR
test : FB_logAdd(sCmpName:=__POUNAME());
END_VAR
throws the following compiler error?
[ERROR] Internal error:System.NullReferenceException: Object reference not set to an instance of an object. at _3S.CoDeSys.LanguageModelManager.CompileContext.NeedsExternalFunctionCall(IOperatorExpression op, ICompiledType type, String& stFunctionName, TypeClass& tcWithType) at _3S.CoDeSys.Compiler35160.ExpressionTypifier.(_IOperatorExpression ) at _3S.CoDeSys.LanguageModelManager.OperatorExpression.Accept(IExprementVisitor visitor) at _3S.CoDeSys.Compiler35160.InterfaceCompiler.(_IVariable , _ISignature , ISignature , ISignature , IScope5 , _ICompileContext , IList`1 , IVariable[] , ExpressionTypifier , TypeChecker , ErrorVisitor ) at _3S.CoDeSys.Compiler35160.InterfaceCompiler.(IList`1& , IVariable[] , _IVariable , IScope5 , _ICompileContext , _ISignature , ISignature , ISignature , ExpressionTypifier , TypeChecker , ErrorVisitor ) at _3S.CoDeSys.Compiler35160.InterfaceCompiler.(_IVariable , IScope5 , _ICompileContext , _ISignature , ISignature , ISignature , ExpressionTypifier , TypeChecker , ErrorVisitor ) at _3S.CoDeSys.Compiler35160.InterfaceCompiler.(_IVariable , IScope5 , _ICompileContext , _ISignature , ExpressionTypifier , TypeChecker , ErrorVisitor , ISignature , ISignature ) at _3S.CoDeSys.Compiler35160.InterfaceCompiler.(_IVariable , IScope5 , _ICompileContext , _ISignature ) at _3S.CoDeSys.Compiler35160.InterfaceCompiler.(_ISignature , IScope5 , _ICompileContext ) at _3S.CoDeSys.Compiler35160.Compiler.(_ICompileContext , _IPreCompileContext , _IPreCompileContext , _ICompileContext , Boolean , Boolean , Boolean& , IProgressCallback ) at _3S.CoDeSys.Compiler35160.Compiler.(_ICompileContext , Boolean , Boolean , _IPreCompileContext , _IPreCompileContext , _ICompileContext , Boolean , Boolean& , IProgressCallback ) at _3S.CoDeSys.Compiler35160.Compiler.(Guid , Boolean , Boolean , Boolean , Boolean& , _ICompileContext& , _ICompileContext& , IProgressCallback , Boolean , Boolean , Boolean )
Is it because __POUNAME() behaves like a function block instead of a function like I assumed?
Which is why
VAR
test : FB_logAdd(pCmpName:=ADR(__POUNAME()));
END_VAR
also works if I change the input to a string pointer?
Edit 1
My initial FB_init looked like this
//FB_Init is always available implicitly and it is used primarily for initialization.
//The return value is not evaluated. For a specific influence, you can also declare the
//methods explicitly and provide additional code there with the standard initialization
//code. You can evaluate the return value.
METHOD FB_Init: BOOL
VAR_INPUT
bInitRetains: BOOL; // TRUE: the retain variables are initialized (reset warm / reset cold)
bInCopyCode: BOOL; // TRUE: the instance will be copied to the copy code afterward (online change)
sCmpName : STRING;
END_VAR
THIS^.sCmpName := sCmpName;
Since I wanted to initialize the function block with FB_logAdd(sCmpName:=__POUNAME()) (which still throws the above error)
If I use FB_logAdd(sCmpName:=THIS^.___POUNAME()), it throws the compiler error
C0004: '__POUNAME' is no component of 'THIS^'
which makes sense since __POUNAME() is an operator and doesn't actually belong to the parent function block (I think)
Related
How can I do a function to get the current year with 4 digits using ELM 0.19.1? I have read something but nothing works with 0.19.1.
Signature:
getCurrentYear : Int
Execution:
getCurrentYear => 2020
Edit:
Maybe executing new Date().getFullYear() javascript code?
The simplest way would be to pass the year in via flags when you start the app, since the current year isn't likely to change in the course of the application running. In that case, you can use the snippet of JavaScript you suggested (ellie example):
Elm.Main.init({
node: document.querySelector('main'),
flags: {
year: new Date().getFullYear(),
}
});
module Main exposing (main)
import Browser
import Html exposing (Html, p, text)
type alias Flags =
{ year : Int }
main : Program Flags Model Msg
main =
Browser.element
{ init = \flags -> ( Model flags.year, Cmd.none )
, view = view
, update = update
, subscriptions = \_ -> Sub.none
}
type alias Model =
{ year : Int }
type Msg
= NoOp
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
NoOp ->
( model, Cmd.none )
view : Model -> Html Msg
view model =
p [] [ text "The year is ", text (String.fromInt model.year) ]
Alternatively, you can use Time.now to request the current time, as Robin Zigmond's answer suggests, however that is pointing to Elm 0.18 documentation (for elm-lang/core instead of elm/time). For 0.19, you need both a Time.Posix and a Time.Zone in order to call Time.toYear. You can chain Time.now (a Task producing a Posix value) and Time.here (a Task producing a Zone with the current time zone offset) to retrieve those values in one Cmd. Here's an example (also on ellie)
module Main exposing (main)
import Browser
import Html exposing (Html, p, text)
import Task exposing (Task)
import Time
type alias Flags =
{ year : Int }
main : Program () Model Msg
main =
Browser.element
{ init = \() -> ( Model 0, whatYearIsIt |> Task.perform GotYear )
, view = view
, update = update
, subscriptions = \_ -> Sub.none
}
whatYearIsIt : Task x Int
whatYearIsIt =
Task.map2 Time.toYear Time.here Time.now
type alias Model =
{ year : Int }
type Msg
= GotYear Int
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
GotYear year ->
( { model | year = year }, Cmd.none )
view : Model -> Html Msg
view model =
p [] [ text "The year is ", text (String.fromInt model.year) ]
As I already said in my comment, it's impossible to define a function in Elm that returns the current year. You have to get such information from the Elm runtime system (which is basically JavaScript, but you don't have to write it yourself). This happens via commands, where you tell the runtime system to do something for you. But note that you can't simply retrieve the "return value" of that command and get it back into your Elm code. Instead you have to pass it into a function that can convert it into a "message" (see basic Elm Architecture tutorial here, it's fundamental to understand this before you can do anything with Elm) - this then allows you to store the value in your Model and thereby display it in your app.
These patterns do take some getting your head around, especially if you're not used to pure functional programming - but once you get used to it the benefits are huge, including a near guaranteed absence of runtime errors, and greatly enhanced ability to reason about your code.
For getting the year specifically, it looks like you need this library, which gives you (as now) a Task rather than a Cmd. You can use Task.perform to convert it to a command, which is documented here - in fact it even gives an example that matches your use case quite closely - I'll copy it here for posterity:
import Time -- elm install elm/time
import Task
type Msg
= Click
| Search String
| NewTime Time.Posix
getNewTime : Cmd Msg
getNewTime =
Task.perform NewTime Time.now
You'll have to fill this in to fit your own use case, in particular your own Msg type. But it gives a good basic outline. To get the user's current year, you need to replace the Time.Posix type with Int, and the Time.now command with (Task.map2 Time.toYear Time.here Time.now), as explained by #bdukes in his answer.
Please Note: I could not a find a solution that could work here, so I used ports for the same.
I need to use Browser.Events.onKeyDown for which I have also written a decoder, I need to create some shortcuts for my web app thus I need to prevent default behavior of Meta key (on Mac) and Ctrl key (on other Os)
In my Subscription method I am using the following.
But there is no exposed way of using prevent Default.
let
decoder : Decode.Decoder Msg
decoder =
keyDecoder
|> Decode.andThen
(\( keyCode, ctrlKey ) ->
case keyCode of
39 ->
Decode.succeed <| ShortCutNext
37 ->
Decode.succeed <| ShortCutPrevious
_ ->
Decode.fail ""
)
in
Sub.batch
[ Browser.Events.onKeyDown decoder]
keyDecoder : Decode.Decoder ( Int, Bool )
keyDecoder =
Decode.map2 (\a -> \b -> ( a, b ))
(Decode.field "keyCode" Decode.int)
(Decode.field "metaKey" Decode.bool)
Note: The event is on the page itself and not on any element such as textarea thus Html.Events.custom "keydown" options decoder is not applicable.
I am trying to get the x and y coordinates of the mouse during a mouse-wheel-move event in the Elm 0.19 programming language.
I attempt it with this package. See under "Advanced Usage":
https://package.elm-lang.org/packages/mpizenberg/elm-pointer-events/3.1.0/Html-Events-Extra-Wheel
The package itself did not describe a clear example so I looked for an example in a similar package.
See the example under "advanced usage" in this page:
https://package.elm-lang.org/packages/mpizenberg/elm-pointer-events/3.1.0/Html-Events-Extra-Mouse
This example is very similar to what I need, but I can also not get this to work. Get exactly the same problem.
Here is my code adapted from the example to fit with mouse wheel:
module WheelDecoder exposing(..)
import Html exposing (div, text)
import Html.Events.Extra.Wheel as Wheel
import Json.Decode as Decode
type alias WheelEventWithOffsetXY =
{ wheelEvent : Wheel.Event
, offsetXY : {x: Float, y: Float}
}
decodeWeelWithOffsetXY : Decode.Decoder WheelEventWithOffsetXY
decodeWeelWithOffsetXY =
Decode.map2 WheelEventWithOffsetXY
Wheel.eventDecoder
offsetXYDecoder
offsetXYDecoder : Decode.Decoder {x: Float, y: Float}
offsetXYDecoder =
Decode.map2 (\a b -> {x=a,y=b})
(Decode.field "offsetY" Decode.float)
(Decode.field "offsetY" Decode.float)
type Msg
= WheelOffsetXY {x: Float, y: Float}
view =
div
[ (onWheelOffsetXY (\wheelEvent -> WheelOffsetXY (wheelEvent.offsetXY))) ]
[ (text "mousewheel here") ]
onWheelOffsetXY : (WheelEventWithOffsetXY -> msg) -> Html.Attribute msg
onWheelOffsetXY tag =
let
options = { stopPropagation = True, preventDefault = True }
func = Decode.map tag decodeWeelWithOffsetXY
attribute = Wheel.onWithOptions options func
in
attribute
When I try to compile with "elm make" I get the following error:
-- TYPE MISMATCH -------------------------------------- src/Map/WheelDecoder.elm
The 2nd argument to `onWithOptions` is not what I expect:
39| attribute = Wheel.onWithOptions options func
^^^^
This `func` value is a:
Decode.Decoder msg
But `onWithOptions` needs the 2nd argument to be:
Wheel.Event -> msg
Hint: I always figure out the argument types from left to right. If an argument
is acceptable, I assume it is “correct” and move on. So the problem may actually
be in one of the previous arguments!
This error message makes sense as I can see there is a type mismatch, but I have no clue about how to solve it.
It seems like Wheel.eventDecoder was meant to work with Html.Events.on or Html.Events.onWithOptions rather than Wheel.onWithOptions. These were removed in 0.19 in favor of Html.Events.custom, however, which is slightly different. Replacing onWheelOffsetXY with this seems to work:
onWheelOffsetXY : (WheelEventWithOffsetXY -> msg) -> Html.Attribute msg
onWheelOffsetXY tag =
let
options message =
{ message = message
, stopPropagation = True
, preventDefault = True
}
decoder =
decodeWeelWithOffsetXY
|> Decode.map tag
|> Decode.map options
in
Html.Events.custom "wheel" decoder
PS: There's a typo in decodeWeelWithOffsetXY, btw. I've left the typo in place.
PPS: Also, you're looking at outdated documentation. Here's the documentation for the latest version.
I'm using a Freemarker macro for managing the content of two maps.
<#macro MACRO_NAME v>
<#nested v.val, v.msg>
</#macro>
Fruit map is structured like this
Map("fruits" -> Map("val", Map("banana" -> "yellow, "orange" -> "orange"), "msg" -> null))
And the other map, animals, looks like this
Map("animals" -> Map("insects" -> Map("butterflies" -> Map("val" -> Map("Hasora anura" -> "blue", Hasora badra -> "yellow"), "msg" -> null))))
It is ok if val is null, because I've a check in my Freemarker code. If fruits.val is null the following code will output false, false (the correct behavior)
${fruits.val???c}
<#MACRO_NAME fruits; fu, fuMsg>
${fu???c}
</#MACRO_NAME>
${animals.insects.butterflies.val???c}
<#MACRO_NAME animals.insects.butterflies; butterflies, buMsg>
${butterflies???c}
</#MACRO_NAME>
But my Problem is that if animals.insects.butterflies.val is null, this will output false, true but it should be false, false too.
I've have no idea why this happens.
wow a really weird behavior I think
the answer is: never, never name the loop var like your map key.
<#MACRO_NAME animals.insects.butterflies; bu, buMsg> instead of
<#MACRO_NAME animals.insects.butterflies; butterflies, buMsg> will work...
it was just a naming problem.
A SubjectTeacherPeriod has a num_attribute_map, which is a map that maps certain attributes (such as "boringness") with their respective scores. I use the following code to sum attributes (such as "boringness") over each day of the week.
But a certain line causes an error.
rule "insertAttributeDayTotal"
//salience 1 // Do these rules first (optional, for performance)
when
$sum_regression_constraint : SumRegressionConstraint(
$class : class_,
$attribute : attribute//,
//$weight : weight;
)
$day_of_week : DayOfWeek()
$attribute_day_total : Number() from accumulate(
SubjectTeacherPeriod(
//period != null,
period.class_ == $class,
period.dayOfWeek == $day_of_week,
$total : num_attribute_map[$attribute] //PROBLEM LINE
),
sum($total)
)
then
//System.out.println("BUCKET TOTAL "+$id+" "+$bucket_total.intValue());
insertLogical(new AttributeDaySum($class, $attribute, $day_of_week, $attribute_day_total.intValue()));
end
The error is:
jesvin#Jesvin-Technovia:~/dev/drools/timetabler$ java -server in.co.technovia.timetabler.TimeTableApp
Exception in thread "main" java.lang.IllegalStateException: There are errors in the scoreDrl's:
Variables can not be used inside bindings. Variable [$attribute] is being used in binding 'num_attribute_map[$attribute]' : [Rule name='insertAttributeDayTotal']
Rule Compilation error : [Rule name='insertAttributeDayTotal']
in/co/technovia/timetabler/domain/Rule_insertAttributeDayTotal_bb39fd28b3c8457cb8d86fc15b34a0e7.java (7:905) : Syntax error on token "null", invalid Type
in/co/technovia/timetabler/domain/Rule_insertAttributeDayTotal_bb39fd28b3c8457cb8d86fc15b34a0e7.java (9:1050) : $total cannot be resolved
SubjectTeacherPeriod has the curious num_attribute_map so that I can define attributes at runtime. If I wanted a boringness (int) attribute for SubjectTeacherPeriod, I can do num_attribute_map.put("boringness",1) instead of adding a new attribute to SubjectTeacherPeriod.
A SumRegressionConstraint cares about a particular $attribute. That attribute's value is stored in num_attribute_map of SubjectTeacherPeriod. I want to access num_attribute_map[$attribute] but this problem shows up.
What am I doing wrong?
Is there any other way to get dynamic attributes to work?
At the moment, you can't bind a variable to expressions, only to field names. So instead of binding it to:
$total : num_attribute_map[$attribute]
Bind it to:
$total : num_attribute_map
Then, you can use the expression on the function. If you are using the MVEL dialect:
sum( $total[$attribute] )
Or if you are using the java dialect:
sum( $total.get($attribute) )