Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions awlsim/core/systemblocks/system_sfc.pxd.in
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ from awlsim.core.systemblocks.system_sfc_m4 cimport *
from awlsim.core.systemblocks.system_sfc_m3 cimport *
from awlsim.core.systemblocks.system_sfc_m2 cimport *
from awlsim.core.systemblocks.system_sfc_m1 cimport *
from awlsim.core.systemblocks.system_sfc_4 cimport *
from awlsim.core.systemblocks.system_sfc_21 cimport *
from awlsim.core.systemblocks.system_sfc_46 cimport *
from awlsim.core.systemblocks.system_sfc_47 cimport *
Expand Down
2 changes: 2 additions & 0 deletions awlsim/core/systemblocks/system_sfc.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from awlsim.core.systemblocks.system_sfc_m3 import * #+cimport
from awlsim.core.systemblocks.system_sfc_m2 import * #+cimport
from awlsim.core.systemblocks.system_sfc_m1 import * #+cimport
from awlsim.core.systemblocks.system_sfc_4 import * #+cimport
from awlsim.core.systemblocks.system_sfc_21 import * #+cimport
from awlsim.core.systemblocks.system_sfc_46 import * #+cimport
from awlsim.core.systemblocks.system_sfc_47 import * #+cimport
Expand All @@ -42,6 +43,7 @@
-2 : SFCm2, # __REBOOT
-1 : SFCm1, # __SFC_NOP

4 : SFC4, # READ_RTM
21 : SFC21, # FILL
46 : SFC46, # STP
47 : SFC47, # WAIT
Expand Down
7 changes: 7 additions & 0 deletions awlsim/core/systemblocks/system_sfc_4.pxd.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from awlsim.common.cython_support cimport *
from awlsim.core.systemblocks.systemblocks cimport *

cdef class SFC4(SFC):
cdef public list __meters

cpdef run(self)
87 changes: 87 additions & 0 deletions awlsim/core/systemblocks/system_sfc_4.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# -*- coding: utf-8 -*-
#
# AWL simulator - SFCs
#
# Copyright 2026 QuackS7 contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#

from __future__ import division, absolute_import, print_function, unicode_literals
#from awlsim.common.cython_support cimport * #@cy
from awlsim.common.compat import *

from awlsim.common.exceptions import *
from awlsim.common.util import *

from awlsim.core.systemblocks.systemblocks import * #+cimport
from awlsim.core.blockinterface import *
from awlsim.core.memory import * #+cimport


class SFC4(SFC): #+cdef
name = (4, "READ_RTM", "read runtime meter")

interfaceFields = {
BlockInterfaceField.FTYPE_IN : (
BlockInterfaceField(name="NR", dataType="BYTE"),
),
BlockInterfaceField.FTYPE_OUT : (
BlockInterfaceField(name="RET_VAL", dataType="INT"),
BlockInterfaceField(name="CQ", dataType="BOOL"),
BlockInterfaceField(name="CV", dataType="INT"),
),
}

def __init__(self, cpu):
SFC.__init__(self, cpu)
# 8 runtime meters, each (running: bool, hours: int).
# Until SFC 2 SET_RTM / SFC 3 CTRL_RTM are added, every meter
# reads back (False, 0) — the correct state for a CPU that has
# never started any runtime meter.
self.__meters = [[False, 0] for _ in range(8)]

def run(self): #+cpdef
#@cy cdef S7StatusWord s
#@cy cdef uint32_t nr

s = self.cpu.statusWord

nr = AwlMemoryObject_asScalar(
self.fetchInterfaceFieldByName("NR"))

if nr > 7:
# Siemens SFC_e §6.5 mandates flat specific code 0x8080 for
# NR out of range, not the general-code E_RPARM encoding.
# See docs/siemens/SFC_e (1).pdf §6.5 and rowlf's FLAG-B
# ruling (2026-04-17, user-ratified).
self.storeInterfaceFieldByName("RET_VAL",
make_AwlMemoryObject_fromScalar(0x8080, 16))
self.storeInterfaceFieldByName("CQ",
make_AwlMemoryObject_fromScalar(0, 1))
self.storeInterfaceFieldByName("CV",
make_AwlMemoryObject_fromScalar(0, 16))
s.BIE = 0
return

running, hours = self.__meters[nr]

self.storeInterfaceFieldByName("RET_VAL",
make_AwlMemoryObject_fromScalar(0, 16))
self.storeInterfaceFieldByName("CQ",
make_AwlMemoryObject_fromScalar(1 if running else 0, 1))
self.storeInterfaceFieldByName("CV",
make_AwlMemoryObject_fromScalar(hours & 0xFFFF, 16))
s.BIE = 1
85 changes: 85 additions & 0 deletions tests/tc500_systemblocks/sfc/sfc4.awl
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
ORGANIZATION_BLOCK OB 1
VAR_TEMP
NR_TMP : BYTE;
RET_TMP : INT;
CQ_TMP : BOOL;
CV_TMP : INT;
END_VAR
BEGIN
// Test SFC 4: READ_RTM


// Valid meter number (0) on a fresh CPU: meter stopped, value 0.
L B#16#00
T #NR_TMP
CALL SFC 4 (
NR := #NR_TMP,
RET_VAL := #RET_TMP,
CQ := #CQ_TMP,
CV := #CV_TMP,
)
__ASSERT== __STW BIE, 1
L #RET_TMP
__ASSERT== __ACCU 1, W#16#0000
L #CV_TMP
__ASSERT== __ACCU 1, W#16#0000
U #CQ_TMP
__ASSERT== __STW VKE, 0


// Valid meter number (7, the maximum) - same expected state.
L B#16#07
T #NR_TMP
CALL SFC 4 (
NR := #NR_TMP,
RET_VAL := #RET_TMP,
CQ := #CQ_TMP,
CV := #CV_TMP,
)
__ASSERT== __STW BIE, 1
L #RET_TMP
__ASSERT== __ACCU 1, W#16#0000
L #CV_TMP
__ASSERT== __ACCU 1, W#16#0000
U #CQ_TMP
__ASSERT== __STW VKE, 0


// Invalid meter number (8): E_RPARM on parameter 1, BIE cleared.
L B#16#08
T #NR_TMP
CALL SFC 4 (
NR := #NR_TMP,
RET_VAL := #RET_TMP,
CQ := #CQ_TMP,
CV := #CV_TMP,
)
__ASSERT== __STW BIE, 0
L #RET_TMP
__ASSERT== __ACCU 1, W#16#8080
L #CV_TMP
__ASSERT== __ACCU 1, W#16#0000
U #CQ_TMP
__ASSERT== __STW VKE, 0


// Invalid meter number (255, max BYTE): same error path.
L B#16#FF
T #NR_TMP
CALL SFC 4 (
NR := #NR_TMP,
RET_VAL := #RET_TMP,
CQ := #CQ_TMP,
CV := #CV_TMP,
)
__ASSERT== __STW BIE, 0
L #RET_TMP
__ASSERT== __ACCU 1, W#16#8080
L #CV_TMP
__ASSERT== __ACCU 1, W#16#0000
U #CQ_TMP
__ASSERT== __STW VKE, 0


CALL SFC 46 // STOP CPU
END_ORGANIZATION_BLOCK