Trying to track 12 hour shifts

Eddy D

Member
Join Date
Feb 2021
Location
Nebraska
Posts
11
So at my plant our shift schedule is 12 hour shifts 7 days a week.
There is 4 shifts A,B,C,D.
A and C are the day shifts. 6am to 6pm
B and D are the night shifts. 6pm to 6am
The order of the shifts is AB AB CD CD AB AB AB CD CD AB AB CD CD CD
This encompasses a two week period, after that it starts over again.

I am trying to display the current shift on an HMI.

- right now I have a counter that increments at shift change.
-If the hour equals 6 then shift change
-If the hour equals 18 then shift change

-Each shift in a two week period is assigned a number
A=1 B=2 A=3 B=4 C=5 D=6 C=7 D=8.......

-If the counter.acc is equal to 1 then it is A shift
-If the counter.acc is equal to 2 then it is B shift....and so on

-Doing it like this I have to have 28 EQU instuctions. 7 for each shift.

-1 problem that I can think of is that if the PLC is powered down will the counter lose its .acc value? Then the shift will be messed up.

-I think there would be a better way of doing this. Maybe using the day of the week? Any help would be appreciated!
 
Last edited:
I would suggest you perform this against the PLC clock instead of counters. With that said, prerequisite is to keep the PLC clock synchronized with a master clock. I usually calculate this at the SCADA layer, so my suggestions here are not proven in the PLC, but concepts should be very similar.


  1. Sync PLC clock with Master clock on the network.
  2. Use the PLC clock and a function to output week of year, and day of week. You should be able to use the WeekDate_ISO8601 AOI in Rockwell's sample library to do this (I have not verified functionality of this AOI).
  3. Calculate your shifts (note there might already be code in the sample library and the following is just an exercise).

pseudo code:
Code:
// It's likely you will need to make some adjustments to align
// your start week, and day usually Sunday or Monday and from 
// the ISO calendar. Do some googling and make sure you 
 // consider when the week number begins at (0 or 1), week day
// begins at (0 or 1), partial/full week in January
// (read up on ISO 8601)

 
weekNumber = <from AOI>
dayOfWeek = <from AOI>
currentHour = <from system clock, should be 24hr format>


// Check against week number to determine which shift sequence
// to align.
// ODD: AB AB CD CD AB AB AB
// EVEN: CD CD AB AB CD CD CD
 if weekNubmer is odd:

    if dayOfWeek == 1, 2, 5, 6, 7
        if currentHour between (6, 18)
           shift = 'A'
        else
           shift = 'B'
    
    else
        if currentHour between(6,18)
           shift = 'C'
        else
           shift = 'D'

 else
    if dayOfWeek == 1, 2, 5, 6, 7
        if currentHour between (6, 18)
           shift = 'C'
        else
           shift = 'D'
    
    else
        if currentHour between(6,18)
           shift = 'A'
        else
           shift = 'B'
 
If this is a Logix 5000 platform, one approach will be to use a string whose contents is ABABCDCD... The shift counter will index into the DATA array within the STRING type to pull the character corresponding to the current shift when the clock hour changes to 6 and 18.

In order to do this you will need to periodically scan the GSV instruction for WALLCLOCK time, using the local time specification. Then a one-shot on the hour equaling 6 or 18 adds one to an index. When the index reaches 14, reset to zero.

This will work through a power cycle if the battery (on older processors) is good. The watch-out will be after a program download, which may require resynchronizing the index for the current shift. (While you could avoid the GSV with a recurring 12-hour timer, this would need to be set up to deal with run-mode transitions and lost time when the PLC is not in run mode.)

Rockwell has sample code AOIs for computing day of the week from the date elements in the WALLCLOCKTIME. That will add some complexity, but only depends on the PLC clock being correct.

More complexity can be added with automatic daylight saving time adjustment via AOI for the PLC clock. Otherwise you will need to manually do this if it is necessary to change the shift letter at the correct time.
 
Last edited:
Shift Counting

It is far better to use the PLC clock than to count out shifts. Allen Bradley made a very very poor choice to not include Day of Week in newer PLCs.

Another general comment is that sometimes time of day comparisons for calculating things like shifts are easier to do if you combine hour and minute into a single integer. Take the hour times 100 and add the minute. That way to can do comparisons on single integers instead of having to do much more complex separate comparisons on hours and minutes. If you want to know if the shift is between 7:30 AM and 3:30 PM the comparison looks like:

Time = PLC_Clock_Hours * 100 + PLC_Clock_Minutes

IF Time >= 730 and Time < 1530 THEN
Shift_Indicator = 1
else
Shift_Indicator = 0

The times 100 makes the resulting integer much more readable with the PLC programming software than it would be if you did a times 60.
 
what platform? A-B Logix 5000? 500? Siemens? Ladder? ST? FBD?


The program by @Paullys needs to be tweaked: on even week's Mondays before 0600 it should yield D, not B; subtracting 6 from the hour, with roll-"under" for negative results on the hour day of week and on odd-even week should fix it, and the hour comparison becomes less than 12.


The low order bit (bit 0) of the ASCII code for the shift name toggles at every shift change, and bit 1 toggles after 2d and 4 shifts, 2d and four shifts, then 3d and four shifts. Starting on Saturday of even weeks, the sequences are ABAB CDCD ABAB CD(CD) odd weeks, and CDCDC ABAB CDCD AB(AB) even weeks, with the parenthesized items never used.



But the sequence would need to be initialized so the absolute code needs to be written anyway, which brings us to @Mispeld's approach: a string of ABABCDCDABABABCDCDABABCDCDCD and a counter of the number of seconds since the last even week's Monday's noons, divided by 43200 and rounded, modulo 28, as the index. Does your platform have a modulo instruction?



Someone recently posted that they had an AOI for DST.
 
Overview

  • Assumptions
  • Key values of unity
  • Choose the shift epoch, an even week's Monday 0600
  • Adjust for time zone and DST
  • Count of seconds since last even week's Monday 0600-1800 shift
  • Count of shifts since last even week's Monday 0600-1800 shift
  • Extract shift letter from 28-character string or array
  • Conclusion
Assumptions

The values of unity (one; 1.0)

  • 86400s/d
  • 43200s/half-day = 86400s/d * 1/2d/half-day
  • 3600s/h (DST offset)
  • 604800s/wk = 86400s/d x 7d/wk
  • 1209600s/fortnight = 86400s/d x 14d/fortnight
  • 1000000μs/s
Choose the shift epoch

  • There is no such thing as an absolute time; any time is always expressed relative to some base epoch
  • Logix 5000 since V16 uses Unix epoch, 01-Jan-1970 00:00:00, as base for CurrentValue obtained via GSV of WALLCLOCK
  • 01-Jan-1970 was a Thursday, so 05-Jan-1970 0:00:00 was a Monday, add 4d * 86400s/d + 6h * 3600s/h = 345600s + 21600s = 367200s
  • If 05-Jan-1970 was Monday of an odd week, 367200 + 1wk x 604800s/wk = 972000s = 12-Jan-1970 06:00:00 to get the even week epoch
  • Use https://www.epochconverter.com/ to determine a more recent even week's Monday shift epoch e.g. 04-Jan-2021 06:00:00 = 1609740000s since Unix epoch
  • N.B. 04-Jan-2021 is 1.6Gs, within shouting distance of ~2.1Gs, i.e. of positive limit of 32-bits signed DINT. Using DINTs for seconds since Unix epoch fails after 18-Jan-2038 22:14:07.
Adjust for time zone and DST

  • I *think* GSV-obtained WALLCLOCKTIME object's CurrentValue is adjusted for time zone and DST
  • See also CSTOffset and ApplyDST, and the CST object Coordinated System Time (UTC?)
  • If so, then whatever CurrentValue is returned via GSV/WALLCLOCKTIME increments exactly 86.4E9μs (=86400s) from any local 0600 to the next local 0600, even if there was a DST event between those 0600s i.e. even if there was 3600s (1h) more or less actual time between them.
Count of seconds since last even week's Monday 0600-1800 shift

  • Use GSV to retrieve WALLCLOCKTIME; puts time since Unix epoch into CurrentValue
  • CurrentValue will be either single LINT (C*Logix 5x80) or DINT array currentus[2] (C*Logix 5x70) in units of microseconds (μs)
  • If C*Logix 5x80,
    • Use MUL to multiply LINT .CurrentValue by 1e-6. put result in LREAL currents
    • Use SUB to subtract base shift epoch (above, e.g. 367300) from LREAL currents, put result in DINT icurrents
  • If C*Logix 5x70
    • It's a mess since we need to keep track of integral μs with 32-bit data types
    • This would be more compact in a compute statement, but care should be taken to not lose precision
    • Multiply DINT[0] by 1e-6, put result in DINT icurrents
    • If DINT[0] is less than 0, add 1 to DINT[1]
    • Use MUL to multiply DINT[1] by 4294, put result in DINT itemporary
    • Use ADD to add DINT itemporary to DINT icurrents, put result in DINT icurrents
    • Use MUL to multiply DINT[1] by 0.967296, put result in DINT itemporary
    • Use ADD to add DINT itemporary to DINT icurrents, put result in DINT icurrents
    • Use SUB to subtract base time from icurrents, put result in icurrents
  • Use MOD to get icurrents modulo 1209600, put result in DINT icurrents
  • Use MOD to get icurrents modulo 43200, put result in DINT imods
  • Use SUB to subtract imods from icurrents, put result in icurrents
  • Use DIV to divide icurrents by 43200, put result in ishifts
Extract shift letter

  • Extract letter at offset ishifts from string "ABABCDCDABABABCDCDABABCDCDCD"
Conclusion

  • It's more trouble than what OP is doing now, but it does handle power cycles and determines the odd-even week correctly
  • It may break in 2038.
  • This could be run every rising edge of bit 25 of the .CurrentValue from the GSV/WALLCLOCKTIME, which is roughly every 67s or 1 minute.
 
Last edited:
Here 'tis, with DINTs: View attachment fortnights.zip; only 15 instructions so not too bad.

Apparently RSLogix 5000 cannot multiply a LINT by a float.

I set the computer timezone to GMT+0000 so I did not have to mess about timezone and DST; OP may not have that freedom, but it will only mean there will be an offset to unix_s_shift0

fortnights_data_00.png

Monday 11.Jan 2021 06:00 chosen as base shift (unix_s_shift0 = 1610344800 s past Unix epoch), so also +14 => 25.Jan, +14d => 08.Feb, +14d => 22.Feb, +14d => 08.Mar, +14d => 22.Mar, +14d => 05.Apr, +14d => 19.Apr i.e. tonight/tomorrow, so tonight will be last shift in the fortnight

fortnights_prog_00.png

Rung 0: GSV WallClockTime.CurrentValue, i.e. current time, into DINT[2] array unix_us (μs since Unix epoch); unix_us_lint is loaded from GSV WallClockTime.CurrentValue, but not used

Rung 1: convert unix_us[0] from μs to unix_s s in ; adjust unix_us[1] by 1 if unix_us[0] is negative

fortnights_prog_01.png

Rung 2: convert unix_us[1] from 4Gμs to s, add to unix_s

Rung 3: subtract base shift time unix_s_shift0 (11-Jan-2021 06:00:00) from unix_s; use MOD to get s since last base shift; reduce to multiple of one shift (43200s); result is in fortnight_s; the explicit MODs and SUBs may not be necessary, but I've been burned by A-B rounding of integer divides before, so this makes the math explicit and immune to those problems.

Rung 4: convert fortnight_s into 1-based shift_number; use shift_number to extract current single-character shift from all 28-character shifts.
 
Last edited:
You have the same shift rotation that we have at my plant. By far the easiest way to do this, if you have any kind of SCADA system, is to create the schedule in excel and import that into a SQL server table. That way you just query the date and time, and let the SCADA system pull this shift from SQL. The upside of this is you have a single source of truth, and if you have to use it for some reporting later on, it's already created.
 
N.B. both of these solutions are attempts at robust implementations of @Mispeld's approach in previous post #3 of this thread. I am assuming OP has no HMI/SCADA to work with, because as @Christoff84 notes in the previous post, this is better dealt with someplace other than in the PLC.

If OP has C*Logix 5x80, the following might work [Update: the MID instruction needs to be on its own rung; it cannot be on Rung 6 after the LES instruction]. It is only 7 instructions. The algorithm assumes the DIV instruction applied to LINTs will round up to the next LINT integer value if the remainder is equal to or greater than half of the LINT divisor; if that is not true, then

  • Increase tag us_unix_shift0 value by 21600000000 (half a shift in μs),
  • Remove the LES instruction on Rung 6,
  • Change [Source B] to 1 in the ADD instruction on Rung 6,

fortnights_data_lint.png

fortnights_prog_lint.png
 
I don't claim to be an expert in the Logix5000 platform, as most of my work is done in the 500 platform, but couldn't you brute force this using the FSC instruction? If you made an array with:
[ShiftStart (yymmddHH)], [ShiftEnd (yymmddHH)], [Shift {A,B,C,D}]

and then construct the current time in yymmddHH format and do a >=[ShiftStart] and < [ShiftEnd] comparison?

You're talking 720 rows in the array per year.
 

Similar Topics

Does anyone know about any online breakdowns of Controlnet? I have 3 upcoming projects using it and would like to fast track the learning curve a...
Replies
5
Views
2,362
I can't seem to get the Panel View Plus 7 standard edition to downgrade to V_11, when I check the AB downloads and compatibility websites for this...
Replies
1
Views
97
Hi I used to be able to launch PLCsim without any problem. Now it tells me " STEP 7 Professional Licence is required to simulate this PLC"...
Replies
15
Views
454
Hello! When trying to load the hardware configuration I get error 0050-133 2 2458, check the diagnostic buffer. When checking the buffer, it says...
Replies
4
Views
128
Back
Top Bottom